Skip to content

Reduce usage of compiler_for in bootstrap #145310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions bootstrap.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,9 @@
# want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code.
#build.vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false }

# Typically the build system will build the Rust compiler twice. The second
# compiler, however, will simply use its own libraries to link against. If you
# would rather to perform a full bootstrap, compiling the compiler three times,
# If you build the compiler more than twice (stage3+) or the standard library more than once
# (stage 2+), the third compiler and second library will get uplifted from stage2 and stage1,
# respectively. If you would like to disable this uplifting, and rather perform a full bootstrap,
# then you can set this option to true.
#
# This is only useful for verifying that rustc generates reproducible builds.
Expand Down
183 changes: 84 additions & 99 deletions src/bootstrap/src/core/build_steps/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ use crate::{
debug, trace,
};

/// Build a standard library for the given `target` using the given `compiler`.
/// Build a standard library for the given `target` using the given `build_compiler`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Std {
pub target: TargetSelection,
pub compiler: Compiler,
/// Compiler that builds the standard library.
pub build_compiler: Compiler,
/// Whether to build only a subset of crates in the standard library.
///
/// This shouldn't be used from other steps; see the comment on [`Rustc`].
Expand All @@ -54,10 +55,10 @@ pub struct Std {
}

impl Std {
pub fn new(compiler: Compiler, target: TargetSelection) -> Self {
pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
Self {
target,
compiler,
build_compiler,
crates: Default::default(),
force_recompile: false,
extra_rust_args: &[],
Expand Down Expand Up @@ -121,7 +122,7 @@ impl Step for Std {
trace!(force_recompile);

run.builder.ensure(Std {
compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
build_compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
target: run.target,
crates,
force_recompile,
Expand All @@ -143,7 +144,7 @@ impl Step for Std {
skip_all,
fields(
target = ?self.target,
compiler = ?self.compiler,
build_compiler = ?self.build_compiler,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: I think this will merge-conflict with #145340

force_recompile = self.force_recompile
),
),
Expand All @@ -152,8 +153,8 @@ impl Step for Std {
let target = self.target;

// We already have std ready to be used for stage 0.
if self.compiler.stage == 0 {
let compiler = self.compiler;
if self.build_compiler.stage == 0 {
let compiler = self.build_compiler;
builder.ensure(StdLink::from_std(self, compiler));

return;
Expand All @@ -162,9 +163,10 @@ impl Step for Std {
let build_compiler = if builder.download_rustc() && self.force_recompile {
// When there are changes in the library tree with CI-rustc, we want to build
// the stageN library and that requires using stageN-1 compiler.
builder.compiler(self.compiler.stage.saturating_sub(1), builder.config.host_target)
builder
.compiler(self.build_compiler.stage.saturating_sub(1), builder.config.host_target)
} else {
self.compiler
self.build_compiler
};

// When using `download-rustc`, we already have artifacts for the host available. Don't
Expand Down Expand Up @@ -201,51 +203,50 @@ impl Step for Std {

let mut target_deps = builder.ensure(StartupObjects { compiler: build_compiler, target });

let compiler_to_use =
builder.compiler_for(build_compiler.stage, build_compiler.host, target);
trace!(?compiler_to_use);

if compiler_to_use != build_compiler
// Never uplift std unless we have compiled stage 1; if stage 1 is compiled,
// uplift it from there.
//
// FIXME: improve `fn compiler_for` to avoid adding stage condition here.
&& build_compiler.stage > 1
// Stage of the stdlib that we're building
let stage = build_compiler.stage;

// If we're building a stage2+ libstd, full bootstrap is
// disabled and we have a stage1 libstd already compiled for the given target,
// then simply uplift a previously built stage1 library.
if build_compiler.stage > 1
&& !builder.config.full_bootstrap
// This estimates if a stage1 libstd exists for the given target. If we're not
// cross-compiling, it should definitely exist by the time we're building a stage2
// libstd.
// Or if we are cross-compiling, and we are building a cross-compiled rustc, then that
// rustc needs to link to a cross-compiled libstd, so again we should have a stage1
// libstd for the given target prepared.
// Even if we guess wrong in the cross-compiled case, the worst that should happen is
// that we build a fresh stage1 libstd below, and then we immediately uplift it, so we
// don't pay the libstd build cost twice.
&& (target == builder.host_target || builder.config.hosts.contains(&target))
{
trace!(
?compiler_to_use,
?build_compiler,
"build_compiler != compiler_to_use, uplifting library"
);
let build_compiler_for_std_to_uplift = builder.compiler(1, builder.host_target);
builder.std(build_compiler_for_std_to_uplift, target);

builder.std(compiler_to_use, target);
let msg = if compiler_to_use.host == target {
let msg = if build_compiler_for_std_to_uplift.host == target {
format!(
"Uplifting library (stage{} -> stage{})",
compiler_to_use.stage, build_compiler.stage
"Uplifting library (stage{} -> stage{stage})",
build_compiler_for_std_to_uplift.stage
)
} else {
format!(
"Uplifting library (stage{}:{} -> stage{}:{})",
compiler_to_use.stage, compiler_to_use.host, build_compiler.stage, target
"Uplifting library (stage{}:{} -> stage{stage}:{target})",
build_compiler_for_std_to_uplift.stage, build_compiler_for_std_to_uplift.host,
)
};

builder.info(&msg);

// Even if we're not building std this stage, the new sysroot must
// still contain the third party objects needed by various targets.
self.copy_extra_objects(builder, &build_compiler, target);

builder.ensure(StdLink::from_std(self, compiler_to_use));
builder.ensure(StdLink::from_std(self, build_compiler_for_std_to_uplift));
return;
}

trace!(
?compiler_to_use,
?build_compiler,
"compiler == compiler_to_use, handling not-cross-compile scenario"
);

target_deps.extend(self.copy_extra_objects(builder, &build_compiler, target));

// We build a sysroot for mir-opt tests using the same trick that Miri does: A check build
Expand Down Expand Up @@ -313,7 +314,7 @@ impl Step for Std {
}

fn metadata(&self) -> Option<StepMetadata> {
Some(StepMetadata::build("std", self.target).built_by(self.compiler))
Some(StepMetadata::build("std", self.target).built_by(self.build_compiler))
}
}

Expand Down Expand Up @@ -679,6 +680,14 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car
cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)");
}

/// Link all libstd rlibs/dylibs into a sysroot of `target_compiler`.
///
/// Links those artifacts generated by `compiler` to the `stage` compiler's
/// sysroot for the specified `host` and `target`.
///
/// Note that this assumes that `compiler` has already generated the libstd
/// libraries for `target`, and this method will find them in the relevant
/// output directory.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StdLink {
pub compiler: Compiler,
Expand All @@ -694,7 +703,7 @@ impl StdLink {
pub fn from_std(std: Std, host_compiler: Compiler) -> Self {
Self {
compiler: host_compiler,
target_compiler: std.compiler,
target_compiler: std.build_compiler,
target: std.target,
crates: std.crates,
force_recompile: std.force_recompile,
Expand All @@ -709,14 +718,6 @@ impl Step for StdLink {
run.never()
}

/// Link all libstd rlibs/dylibs into the sysroot location.
///
/// Links those artifacts generated by `compiler` to the `stage` compiler's
/// sysroot for the specified `host` and `target`.
///
/// Note that this assumes that `compiler` has already generated the libstd
/// libraries for `target`, and this method will find them in the relevant
/// output directory.
#[cfg_attr(
feature = "tracing",
instrument(
Expand Down Expand Up @@ -987,14 +988,8 @@ impl Rustc {
}

impl Step for Rustc {
/// We return the stage of the "actual" compiler (not the uplifted one).
///
/// By "actual" we refer to the uplifting logic where we may not compile the requested stage;
/// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in
/// specific situations where we request stage X from other steps. However we may end up
/// uplifting it from stage Y, causing the other stage to fail when attempting to link with
/// stage X which was never actually built.
type Output = u32;
type Output = ();

const ONLY_HOSTS: bool = true;
const DEFAULT: bool = false;

Expand Down Expand Up @@ -1042,7 +1037,7 @@ impl Step for Rustc {
fields(previous_compiler = ?self.build_compiler, target = ?self.target),
),
)]
fn run(self, builder: &Builder<'_>) -> u32 {
fn run(self, builder: &Builder<'_>) {
let build_compiler = self.build_compiler;
let target = self.target;

Expand All @@ -1058,7 +1053,7 @@ impl Step for Rustc {
&sysroot,
builder.config.ci_rustc_dev_contents(),
);
return build_compiler.stage;
return;
}

// Build a standard library for `target` using the `build_compiler`.
Expand All @@ -1072,31 +1067,33 @@ impl Step for Rustc {
builder.info("WARNING: Use `--keep-stage-std` if you want to rebuild the compiler when it changes");
builder.ensure(RustcLink::from_rustc(self, build_compiler));

return build_compiler.stage;
return;
}

let compiler_to_use =
builder.compiler_for(build_compiler.stage, build_compiler.host, target);
if compiler_to_use != build_compiler {
builder.ensure(Rustc::new(compiler_to_use, target));
let msg = if compiler_to_use.host == target {
format!(
"Uplifting rustc (stage{} -> stage{})",
compiler_to_use.stage,
build_compiler.stage + 1
)
// The stage of the compiler that we're building
let stage = build_compiler.stage + 1;

// If we are building a stage3+ compiler, and full bootstrap is disabled, and we have a
// previous rustc available, we will uplift a compiler from a previous stage.
if build_compiler.stage >= 2
&& !builder.config.full_bootstrap
&& (target == builder.host_target || builder.hosts.contains(&target))
{
// If we're cross-compiling, the earliest rustc that we could have is stage 2.
// If we're not cross-compiling, then we should have rustc stage 1.
let stage_to_uplift = if target == builder.host_target { 1 } else { 2 };
let rustc_to_uplift = builder.compiler(stage_to_uplift, target);
let msg = if rustc_to_uplift.host == target {
format!("Uplifting rustc (stage{} -> stage{stage})", rustc_to_uplift.stage,)
} else {
format!(
"Uplifting rustc (stage{}:{} -> stage{}:{})",
compiler_to_use.stage,
compiler_to_use.host,
build_compiler.stage + 1,
target
"Uplifting rustc (stage{}:{} -> stage{stage}:{target})",
rustc_to_uplift.stage, rustc_to_uplift.host,
)
};
builder.info(&msg);
builder.ensure(RustcLink::from_rustc(self, compiler_to_use));
return compiler_to_use.stage;
builder.ensure(RustcLink::from_rustc(self, rustc_to_uplift));
return;
}

// Build a standard library for the current host target using the `build_compiler`.
Expand Down Expand Up @@ -1173,8 +1170,6 @@ impl Step for Rustc {
self,
builder.compiler(build_compiler.stage, builder.config.host_target),
));

build_compiler.stage
}

fn metadata(&self) -> Option<StepMetadata> {
Expand Down Expand Up @@ -1983,12 +1978,18 @@ impl Step for Sysroot {
}
}

/// Prepare a compiler sysroot.
///
/// The sysroot may contain various things useful for running the compiler, like linkers and
/// linker wrappers (LLD, LLVM bitcode linker, etc.).
///
/// This will assemble a compiler in `build/$target/stage$stage`.
#[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)]
pub struct Assemble {
/// The compiler which we will produce in this step. Assemble itself will
/// take care of ensuring that the necessary prerequisites to do so exist,
/// that is, this target can be a stage2 compiler and Assemble will build
/// previous stages for you.
/// that is, this can be e.g. a stage2 compiler and Assemble will build
/// the previous stages for you.
pub target_compiler: Compiler,
}

Expand All @@ -2006,11 +2007,6 @@ impl Step for Assemble {
});
}

/// Prepare a new compiler from the artifacts in `stage`
///
/// This will assemble a compiler in `build/$host/stage$stage`. The compiler
/// must have been previously produced by the `stage - 1` builder.build
/// compiler.
#[cfg_attr(
feature = "tracing",
instrument(
Expand Down Expand Up @@ -2148,7 +2144,7 @@ impl Step for Assemble {
target_compiler.stage - 1,
builder.config.host_target,
);
let mut build_compiler =
let build_compiler =
builder.compiler(target_compiler.stage - 1, builder.config.host_target);

// Build enzyme
Expand All @@ -2172,24 +2168,13 @@ impl Step for Assemble {
}

// Build the libraries for this compiler to link to (i.e., the libraries
// it uses at runtime). NOTE: Crates the target compiler compiles don't
// link to these. (FIXME: Is that correct? It seems to be correct most
// of the time but I think we do link to these for stage2/bin compilers
// when not performing a full bootstrap).
// it uses at runtime).
debug!(
?build_compiler,
"target_compiler.host" = ?target_compiler.host,
"building compiler libraries to link to"
);
let actual_stage = builder.ensure(Rustc::new(build_compiler, target_compiler.host));
// Current build_compiler.stage might be uplifted instead of being built; so update it
// to not fail while linking the artifacts.
debug!(
"(old) build_compiler.stage" = build_compiler.stage,
"(adjusted) build_compiler.stage" = actual_stage,
"temporarily adjusting `build_compiler.stage` to account for uplifted libraries"
);
build_compiler.stage = actual_stage;
builder.ensure(Rustc::new(build_compiler, target_compiler.host));

let mut codegen_backend_stamps = vec![];
{
Expand Down
Loading