diff --git a/bootstrap.example.toml b/bootstrap.example.toml index b59f112bdfb83..387cd367e2e27 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -1029,7 +1029,9 @@ # sources are available. # # Setting this to `false` generates slower code, but removes the requirement for a C toolchain in -# order to run `x check`. +# order to run `x check`. This may also be given a path to an existing build of the builtins +# runtime library from LLVM's compiler-rt. This option will override the same option under [build] +# section. #optimized-compiler-builtins = build.optimized-compiler-builtins (bool) # Link the compiler and LLVM against `jemalloc` instead of the default libc allocator. diff --git a/library/compiler-builtins/compiler-builtins/build.rs b/library/compiler-builtins/compiler-builtins/build.rs index 018899faf1d44..a80fa1f4861d6 100644 --- a/library/compiler-builtins/compiler-builtins/build.rs +++ b/library/compiler-builtins/compiler-builtins/build.rs @@ -548,12 +548,20 @@ mod c { sources.extend(&[("__emutls_get_address", "emutls.c")]); } + // Optionally, link against a prebuilt compiler-rt library to supply + // optimized intrinsics instead of compiling a subset of compiler-rt + // from source. + let link_against_prebuilt_rt = env::var_os("LLVM_BUILTIN_RT_LIB").is_some(); + // When compiling the C code we require the user to tell us where the // source code is, and this is largely done so when we're compiling as // part of rust-lang/rust we can use the same llvm-project repository as // rust-lang/rust. let root = match env::var_os("RUST_COMPILER_RT_ROOT") { Some(s) => PathBuf::from(s), + // If a prebuild libcompiler-rt is provided, set a valid + // path to simplify later logic. Nothing should be compiled. + None if link_against_prebuilt_rt => PathBuf::new(), None => { panic!( "RUST_COMPILER_RT_ROOT is not set. You may need to run \ @@ -561,7 +569,7 @@ mod c { ); } }; - if !root.exists() { + if !link_against_prebuilt_rt && !root.exists() { panic!("RUST_COMPILER_RT_ROOT={} does not exist", root.display()); } @@ -577,7 +585,7 @@ mod c { let src_dir = root.join("lib/builtins"); if target.arch == "aarch64" && target.env != "msvc" && target.os != "uefi" { // See below for why we're building these as separate libraries. - build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg); + build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg, link_against_prebuilt_rt); // Some run-time CPU feature detection is necessary, as well. let cpu_model_src = if src_dir.join("cpu_model.c").exists() { @@ -591,20 +599,43 @@ mod c { let mut added_sources = HashSet::new(); for (sym, src) in sources.map.iter() { let src = src_dir.join(src); - if added_sources.insert(src.clone()) { + if !link_against_prebuilt_rt && added_sources.insert(src.clone()) { cfg.file(&src); println!("cargo:rerun-if-changed={}", src.display()); } println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); } - cfg.compile("libcompiler-rt.a"); + if link_against_prebuilt_rt { + let rt_builtins_ext = PathBuf::from(env::var_os("LLVM_BUILTIN_RT_LIB").unwrap()); + if !rt_builtins_ext.exists() { + panic!( + "LLVM_BUILTIN_RT_LIB={} does not exist", + rt_builtins_ext.display() + ); + } + if let Some(dir) = rt_builtins_ext.parent() { + println!("cargo::rustc-link-search=native={}", dir.display()); + } + println!( + "cargo::rustc-link-lib=static:+verbatim={}", + rt_builtins_ext.to_str().unwrap() + ); + } else { + cfg.compile("libcompiler-rt.a"); + } } - fn build_aarch64_out_of_line_atomics_libraries(builtins_dir: &Path, cfg: &mut cc::Build) { + fn build_aarch64_out_of_line_atomics_libraries( + builtins_dir: &Path, + cfg: &mut cc::Build, + link_against_prebuilt_rt: bool, + ) { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let outlined_atomics_file = builtins_dir.join("aarch64").join("lse.S"); - println!("cargo:rerun-if-changed={}", outlined_atomics_file.display()); + if !link_against_prebuilt_rt { + println!("cargo:rerun-if-changed={}", outlined_atomics_file.display()); + } cfg.include(&builtins_dir); @@ -617,6 +648,13 @@ mod c { for (model_number, model_name) in &[(1, "relax"), (2, "acq"), (3, "rel"), (4, "acq_rel")] { + let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name); + println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); + + if link_against_prebuilt_rt { + continue; + } + // The original compiler-rt build system compiles the same // source file multiple times with different compiler // options. Here we do something slightly different: we @@ -640,9 +678,6 @@ mod c { .unwrap(); drop(file); cfg.file(path); - - let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name); - println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); } } } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 3e2bdc2d6b5ae..06fb445bb4b8d 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -567,25 +567,29 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // `compiler-builtins` crate is enabled and it's configured to learn where // `compiler-rt` is located. let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) { - // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. - // But, the user could still decide to manually use an in-tree submodule. - // - // NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt` that doesn't match the LLVM we're linking to. - // That's probably ok? At least, the difference wasn't enforced before. There's a comment in - // the compiler_builtins build script that makes me nervous, though: - // https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579 - builder.require_submodule( - "src/llvm-project", - Some( - "The `build.optimized-compiler-builtins` config option \ - requires `compiler-rt` sources from LLVM.", - ), - ); - let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); - assert!(compiler_builtins_root.exists()); - // The path to `compiler-rt` is also used by `profiler_builtins` (above), - // so if you're changing something here please also change that as appropriate. - cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); + if let Some(path) = builder.config.optimized_compiler_builtins_path(target) { + cargo.env("LLVM_BUILTIN_RT_LIB", path); + } else { + // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. + // But, the user could still decide to manually use an in-tree submodule. + // + // NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt` that doesn't match the LLVM we're linking to. + // That's probably ok? At least, the difference wasn't enforced before. There's a comment in + // the compiler_builtins build script that makes me nervous, though: + // https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579 + builder.require_submodule( + "src/llvm-project", + Some( + "The `build.optimized-compiler-builtins` config option \ + requires `compiler-rt` sources from LLVM.", + ), + ); + let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt"); + assert!(compiler_builtins_root.exists()); + // The path to `compiler-rt` is also used by `profiler_builtins` (above), + // so if you're changing something here please also change that as appropriate. + cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); + } " compiler-builtins-c" } else { "" diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 59693dc3e4cf6..b719b7f778bbb 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1741,10 +1741,18 @@ impl Config { pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool { self.target_config .get(&target) - .and_then(|t| t.optimized_compiler_builtins) + .and_then(|t| t.optimized_compiler_builtins.as_ref()) + .map(StringOrBool::is_string_or_true) .unwrap_or(self.optimized_compiler_builtins) } + pub fn optimized_compiler_builtins_path(&self, target: TargetSelection) -> Option<&str> { + match self.target_config.get(&target)?.optimized_compiler_builtins.as_ref()? { + StringOrBool::String(s) => Some(s), + StringOrBool::Bool(_) => None, + } + } + pub fn llvm_enabled(&self, target: TargetSelection) -> bool { self.codegen_backends(target).contains(&"llvm".to_owned()) } diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 50eba12aba747..c32e4384cf622 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -17,7 +17,7 @@ use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; use crate::core::build_steps::llvm; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; use crate::core::config::toml::TomlConfig; -use crate::core::config::{LldMode, Target, TargetSelection}; +use crate::core::config::{LldMode, StringOrBool, Target, TargetSelection}; use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { @@ -212,7 +212,7 @@ runner = "x86_64-runner" let darwin = TargetSelection::from_user("aarch64-apple-darwin"); let darwin_values = Target { runner: Some("apple".into()), - optimized_compiler_builtins: Some(false), + optimized_compiler_builtins: Some(StringOrBool::Bool(false)), ..Default::default() }; assert_eq!( diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 337276948b324..a93cf2a0fc734 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -45,7 +45,7 @@ define_config! { no_std: Option = "no-std", codegen_backends: Option> = "codegen-backends", runner: Option = "runner", - optimized_compiler_builtins: Option = "optimized-compiler-builtins", + optimized_compiler_builtins: Option = "optimized-compiler-builtins", jemalloc: Option = "jemalloc", } } @@ -77,7 +77,7 @@ pub struct Target { pub runner: Option, pub no_std: bool, pub codegen_backends: Option>, - pub optimized_compiler_builtins: Option, + pub optimized_compiler_builtins: Option, pub jemalloc: Option, }