Skip to content

Commit ddd869d

Browse files
authored
Unrolled build for #143689
Rollup merge of #143689 - pmur:murp/external-rt-optimized-compiler-builtins, r=Kobzol,tgross35 Allow linking a prebuilt optimized compiler-rt builtins library Extend the <target>.optimized-compiler-builtins bootstrap option to accept a path to a prebuilt compiler-rt builtins library, and update compiler-builtins to enable optimized builtins without building compiler-rt builtins.
2 parents ace9a74 + 148a07c commit ddd869d

File tree

12 files changed

+162
-59
lines changed

12 files changed

+162
-59
lines changed

bootstrap.example.toml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,11 @@
407407
#build.profiler = false
408408

409409
# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics.
410-
# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
411-
# sources are available.
410+
# Choosing true requires the LLVM submodule to be managed by bootstrap (i.e. not external)
411+
# so that `compiler-rt` sources are available.
412+
#
413+
# Setting this to a path removes the requirement for a C toolchain, but requires setting the
414+
# path to an existing library containing the builtins library from LLVM's compiler-rt.
412415
#
413416
# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
414417
# order to run `x check`.
@@ -1041,13 +1044,15 @@
10411044
#runner = <none> (string)
10421045

10431046
# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics
1044-
# on this target.
1045-
# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
1046-
# sources are available.
1047+
# on this target. Choosing true requires the LLVM submodule to be managed by bootstrap
1048+
# (i.e. not external) so that `compiler-rt` sources are available.
1049+
#
1050+
# Setting this to a path removes the requirement for a C toolchain, but requires setting the
1051+
# path to an existing library containing the builtins library from LLVM's compiler-rt.
10471052
#
10481053
# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
10491054
# order to run `x check`.
1050-
#optimized-compiler-builtins = build.optimized-compiler-builtins (bool)
1055+
#optimized-compiler-builtins = build.optimized-compiler-builtins (bool or path)
10511056

10521057
# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
10531058
# This overrides the global `rust.jemalloc` option. See that option for more info.

library/compiler-builtins/compiler-builtins/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ to be added as an explicit dependency in `Cargo.toml`.
1010

1111
[`compiler-rt`]: https://github.com/llvm/llvm-project/tree/1b1dc505057322f4fa1110ef4f53c44347f52986/compiler-rt
1212

13+
## Configuration
14+
15+
`compiler-builtins` can be configured with the following environment variables when the `c` feature
16+
is enabled:
17+
18+
- `LLVM_COMPILER_RT_LIB`
19+
- `RUST_COMPILER_RT_ROOT`
20+
21+
See `build.rs` for details.
22+
1323
## Contributing
1424

1525
See [CONTRIBUTING.md](CONTRIBUTING.md).

library/compiler-builtins/compiler-builtins/build.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -540,20 +540,28 @@ mod c {
540540
sources.extend(&[("__emutls_get_address", "emutls.c")]);
541541
}
542542

543+
// Optionally, link against a prebuilt llvm compiler-rt containing the builtins
544+
// library. Only the builtins library is required. On many platforms, this is
545+
// available as a library named libclang_rt.builtins.a.
546+
let link_against_prebuilt_rt = env::var_os("LLVM_COMPILER_RT_LIB").is_some();
547+
543548
// When compiling the C code we require the user to tell us where the
544549
// source code is, and this is largely done so when we're compiling as
545550
// part of rust-lang/rust we can use the same llvm-project repository as
546551
// rust-lang/rust.
547552
let root = match env::var_os("RUST_COMPILER_RT_ROOT") {
548553
Some(s) => PathBuf::from(s),
554+
// If a prebuild libcompiler-rt is provided, set a valid
555+
// path to simplify later logic. Nothing should be compiled.
556+
None if link_against_prebuilt_rt => PathBuf::new(),
549557
None => {
550558
panic!(
551559
"RUST_COMPILER_RT_ROOT is not set. You may need to run \
552560
`ci/download-compiler-rt.sh`."
553561
);
554562
}
555563
};
556-
if !root.exists() {
564+
if !link_against_prebuilt_rt && !root.exists() {
557565
panic!("RUST_COMPILER_RT_ROOT={} does not exist", root.display());
558566
}
559567

@@ -569,7 +577,7 @@ mod c {
569577
let src_dir = root.join("lib/builtins");
570578
if target.arch == "aarch64" && target.env != "msvc" && target.os != "uefi" {
571579
// See below for why we're building these as separate libraries.
572-
build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg);
580+
build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg, link_against_prebuilt_rt);
573581

574582
// Some run-time CPU feature detection is necessary, as well.
575583
let cpu_model_src = if src_dir.join("cpu_model.c").exists() {
@@ -583,20 +591,45 @@ mod c {
583591
let mut added_sources = HashSet::new();
584592
for (sym, src) in sources.map.iter() {
585593
let src = src_dir.join(src);
586-
if added_sources.insert(src.clone()) {
594+
if !link_against_prebuilt_rt && added_sources.insert(src.clone()) {
587595
cfg.file(&src);
588596
println!("cargo:rerun-if-changed={}", src.display());
589597
}
590598
println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
591599
}
592600

593-
cfg.compile("libcompiler-rt.a");
601+
if link_against_prebuilt_rt {
602+
let rt_builtins_ext = PathBuf::from(env::var_os("LLVM_COMPILER_RT_LIB").unwrap());
603+
if !rt_builtins_ext.exists() {
604+
panic!(
605+
"LLVM_COMPILER_RT_LIB={} does not exist",
606+
rt_builtins_ext.display()
607+
);
608+
}
609+
if let Some(dir) = rt_builtins_ext.parent() {
610+
println!("cargo::rustc-link-search=native={}", dir.display());
611+
}
612+
if let Some(lib) = rt_builtins_ext.file_name() {
613+
println!(
614+
"cargo::rustc-link-lib=static:+verbatim={}",
615+
lib.to_str().unwrap()
616+
);
617+
}
618+
} else {
619+
cfg.compile("libcompiler-rt.a");
620+
}
594621
}
595622

596-
fn build_aarch64_out_of_line_atomics_libraries(builtins_dir: &Path, cfg: &mut cc::Build) {
623+
fn build_aarch64_out_of_line_atomics_libraries(
624+
builtins_dir: &Path,
625+
cfg: &mut cc::Build,
626+
link_against_prebuilt_rt: bool,
627+
) {
597628
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
598629
let outlined_atomics_file = builtins_dir.join("aarch64").join("lse.S");
599-
println!("cargo:rerun-if-changed={}", outlined_atomics_file.display());
630+
if !link_against_prebuilt_rt {
631+
println!("cargo:rerun-if-changed={}", outlined_atomics_file.display());
632+
}
600633

601634
cfg.include(&builtins_dir);
602635

@@ -609,6 +642,13 @@ mod c {
609642
for (model_number, model_name) in
610643
&[(1, "relax"), (2, "acq"), (3, "rel"), (4, "acq_rel")]
611644
{
645+
let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name);
646+
println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
647+
648+
if link_against_prebuilt_rt {
649+
continue;
650+
}
651+
612652
// The original compiler-rt build system compiles the same
613653
// source file multiple times with different compiler
614654
// options. Here we do something slightly different: we
@@ -632,9 +672,6 @@ mod c {
632672
.unwrap();
633673
drop(file);
634674
cfg.file(path);
635-
636-
let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name);
637-
println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
638675
}
639676
}
640677
}

src/bootstrap/src/core/build_steps/compile.rs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ use crate::core::builder;
2626
use crate::core::builder::{
2727
Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
2828
};
29-
use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection};
29+
use crate::core::config::{
30+
CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection,
31+
};
3032
use crate::utils::build_stamp;
3133
use crate::utils::build_stamp::BuildStamp;
3234
use crate::utils::exec::command;
@@ -574,29 +576,36 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car
574576
// If `compiler-rt` is available ensure that the `c` feature of the
575577
// `compiler-builtins` crate is enabled and it's configured to learn where
576578
// `compiler-rt` is located.
577-
let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) {
578-
// NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op.
579-
// But, the user could still decide to manually use an in-tree submodule.
580-
//
581-
// 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.
582-
// That's probably ok? At least, the difference wasn't enforced before. There's a comment in
583-
// the compiler_builtins build script that makes me nervous, though:
584-
// https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579
585-
builder.require_submodule(
586-
"src/llvm-project",
587-
Some(
588-
"The `build.optimized-compiler-builtins` config option \
589-
requires `compiler-rt` sources from LLVM.",
590-
),
591-
);
592-
let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt");
593-
assert!(compiler_builtins_root.exists());
594-
// The path to `compiler-rt` is also used by `profiler_builtins` (above),
595-
// so if you're changing something here please also change that as appropriate.
596-
cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
597-
" compiler-builtins-c"
598-
} else {
599-
""
579+
let compiler_builtins_c_feature = match builder.config.optimized_compiler_builtins(target) {
580+
CompilerBuiltins::LinkLLVMBuiltinsLib(path) => {
581+
cargo.env("LLVM_COMPILER_RT_LIB", path);
582+
" compiler-builtins-c"
583+
}
584+
CompilerBuiltins::BuildLLVMFuncs => {
585+
// NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce
586+
// `submodules = false`, so this is a no-op. But, the user could still decide to
587+
// manually use an in-tree submodule.
588+
//
589+
// NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt`
590+
// that doesn't match the LLVM we're linking to. That's probably ok? At least, the
591+
// difference wasn't enforced before. There's a comment in the compiler_builtins build
592+
// script that makes me nervous, though:
593+
// https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579
594+
builder.require_submodule(
595+
"src/llvm-project",
596+
Some(
597+
"The `build.optimized-compiler-builtins` config option \
598+
requires `compiler-rt` sources from LLVM.",
599+
),
600+
);
601+
let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt");
602+
assert!(compiler_builtins_root.exists());
603+
// The path to `compiler-rt` is also used by `profiler_builtins` (above),
604+
// so if you're changing something here please also change that as appropriate.
605+
cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
606+
" compiler-builtins-c"
607+
}
608+
CompilerBuiltins::BuildRustOnly => "",
600609
};
601610

602611
// `libtest` uses this to know whether or not to support

src/bootstrap/src/core/config/config.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ use crate::core::config::toml::rust::{
4646
};
4747
use crate::core::config::toml::target::Target;
4848
use crate::core::config::{
49-
DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo,
50-
StringOrBool, threads_from_config,
49+
CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt,
50+
RustcLto, SplitDebuginfo, StringOrBool, threads_from_config,
5151
};
5252
use crate::core::download::{
5353
DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt,
@@ -121,8 +121,7 @@ pub struct Config {
121121
pub patch_binaries_for_nix: Option<bool>,
122122
pub stage0_metadata: build_helper::stage0_parser::Stage0,
123123
pub android_ndk: Option<PathBuf>,
124-
/// Whether to use the `c` feature of the `compiler_builtins` crate.
125-
pub optimized_compiler_builtins: bool,
124+
pub optimized_compiler_builtins: CompilerBuiltins,
126125

127126
pub stdout_is_tty: bool,
128127
pub stderr_is_tty: bool,
@@ -1109,7 +1108,11 @@ impl Config {
11091108
let rustfmt_info = git_info(&exec_ctx, omit_git_hash, &src.join("src/tools/rustfmt"));
11101109

11111110
let optimized_compiler_builtins =
1112-
build_optimized_compiler_builtins.unwrap_or(channel != "dev");
1111+
build_optimized_compiler_builtins.unwrap_or(if channel == "dev" {
1112+
CompilerBuiltins::BuildRustOnly
1113+
} else {
1114+
CompilerBuiltins::BuildLLVMFuncs
1115+
});
11131116
let vendor = build_vendor.unwrap_or(
11141117
rust_info.is_from_tarball()
11151118
&& src.join("vendor").exists()
@@ -1672,11 +1675,11 @@ impl Config {
16721675
self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath)
16731676
}
16741677

1675-
pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool {
1678+
pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> &CompilerBuiltins {
16761679
self.target_config
16771680
.get(&target)
1678-
.and_then(|t| t.optimized_compiler_builtins)
1679-
.unwrap_or(self.optimized_compiler_builtins)
1681+
.and_then(|t| t.optimized_compiler_builtins.as_ref())
1682+
.unwrap_or(&self.optimized_compiler_builtins)
16801683
}
16811684

16821685
pub fn llvm_enabled(&self, target: TargetSelection) -> bool {

src/bootstrap/src/core/config/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,33 @@ impl<T> Merge for Option<T> {
218218
}
219219
}
220220

221+
#[derive(Clone, Debug, Default, Eq, PartialEq)]
222+
pub enum CompilerBuiltins {
223+
#[default]
224+
// Only build native rust intrinsic compiler functions.
225+
BuildRustOnly,
226+
// Some intrinsic functions have a C implementation provided by LLVM's
227+
// compiler-rt builtins library. Build them from the LLVM source included
228+
// with Rust.
229+
BuildLLVMFuncs,
230+
// Similar to BuildLLVMFuncs, but specify a path to an existing library
231+
// containing LLVM's compiler-rt builtins instead of compiling them.
232+
LinkLLVMBuiltinsLib(String),
233+
}
234+
235+
impl<'de> Deserialize<'de> for CompilerBuiltins {
236+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
237+
where
238+
D: Deserializer<'de>,
239+
{
240+
Ok(match Deserialize::deserialize(deserializer)? {
241+
StringOrBool::Bool(false) => Self::BuildRustOnly,
242+
StringOrBool::Bool(true) => Self::BuildLLVMFuncs,
243+
StringOrBool::String(path) => Self::LinkLLVMBuiltinsLib(path),
244+
})
245+
}
246+
}
247+
221248
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
222249
pub enum DebuginfoLevel {
223250
#[default]

src/bootstrap/src/core/config/tests.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order};
1717
use crate::core::build_steps::llvm;
1818
use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS;
1919
use crate::core::config::toml::TomlConfig;
20-
use crate::core::config::{LldMode, Target, TargetSelection};
20+
use crate::core::config::{CompilerBuiltins, LldMode, StringOrBool, Target, TargetSelection};
2121
use crate::utils::tests::git::git_test;
2222

2323
pub(crate) fn parse(config: &str) -> Config {
@@ -183,7 +183,11 @@ runner = "x86_64-runner"
183183
);
184184
assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes");
185185
assert!(!config.deny_warnings, "setting boolean value");
186-
assert!(config.optimized_compiler_builtins, "setting boolean value");
186+
assert_eq!(
187+
config.optimized_compiler_builtins,
188+
CompilerBuiltins::BuildLLVMFuncs,
189+
"setting boolean value"
190+
);
187191
assert_eq!(
188192
config.tools,
189193
Some(["cargo".to_string()].into_iter().collect()),
@@ -212,7 +216,7 @@ runner = "x86_64-runner"
212216
let darwin = TargetSelection::from_user("aarch64-apple-darwin");
213217
let darwin_values = Target {
214218
runner: Some("apple".into()),
215-
optimized_compiler_builtins: Some(false),
219+
optimized_compiler_builtins: Some(CompilerBuiltins::BuildRustOnly),
216220
..Default::default()
217221
};
218222
assert_eq!(

src/bootstrap/src/core/config/toml/build.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::collections::HashMap;
1111
use serde::{Deserialize, Deserializer};
1212

1313
use crate::core::config::toml::ReplaceOpt;
14-
use crate::core::config::{Merge, StringOrBool};
14+
use crate::core::config::{CompilerBuiltins, Merge, StringOrBool};
1515
use crate::{HashSet, PathBuf, define_config, exit};
1616

1717
define_config! {
@@ -65,7 +65,7 @@ define_config! {
6565
// NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally
6666
metrics: Option<bool> = "metrics",
6767
android_ndk: Option<PathBuf> = "android-ndk",
68-
optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
68+
optimized_compiler_builtins: Option<CompilerBuiltins> = "optimized-compiler-builtins",
6969
jobs: Option<u32> = "jobs",
7070
compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
7171
compiletest_allow_stage0: Option<bool> = "compiletest-allow-stage0",

src/bootstrap/src/core/config/toml/rust.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,9 @@ pub fn check_incompatible_options_for_ci_rustc(
269269
err!(current_profiler, profiler, "build");
270270

271271
let current_optimized_compiler_builtins =
272-
current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
272+
current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
273273
let optimized_compiler_builtins =
274-
ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
274+
ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
275275
err!(current_optimized_compiler_builtins, optimized_compiler_builtins, "build");
276276

277277
// We always build the in-tree compiler on cross targets, so we only care

0 commit comments

Comments
 (0)