Skip to content

Allow linking a prebuilt optimized compiler-rt builtins library #143689

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 2 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
17 changes: 11 additions & 6 deletions bootstrap.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,11 @@
#build.profiler = false

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

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

# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
# This overrides the global `rust.jemalloc` option. See that option for more info.
Expand Down
10 changes: 10 additions & 0 deletions library/compiler-builtins/compiler-builtins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ to be added as an explicit dependency in `Cargo.toml`.

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

## Configuration

`compiler-builtins` can be configured with the following environment variables when the `c` feature
is enabled:

- `LLVM_COMPILER_RT_LIB`
- `RUST_COMPILER_RT_ROOT`

See `build.rs` for details.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md).
Expand Down
55 changes: 46 additions & 9 deletions library/compiler-builtins/compiler-builtins/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,20 +540,28 @@ mod c {
sources.extend(&[("__emutls_get_address", "emutls.c")]);
}

// Optionally, link against a prebuilt llvm compiler-rt containing the builtins
// library. Only the builtins library is required. On many platforms, this is
// available as a library named libclang_rt.builtins.a.
let link_against_prebuilt_rt = env::var_os("LLVM_COMPILER_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 \
`ci/download-compiler-rt.sh`."
);
}
};
if !root.exists() {
if !link_against_prebuilt_rt && !root.exists() {
panic!("RUST_COMPILER_RT_ROOT={} does not exist", root.display());
}

Expand All @@ -569,7 +577,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() {
Expand All @@ -583,20 +591,45 @@ 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_COMPILER_RT_LIB").unwrap());
if !rt_builtins_ext.exists() {
panic!(
"LLVM_COMPILER_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());
}
if let Some(lib) = rt_builtins_ext.file_name() {
println!(
"cargo::rustc-link-lib=static:+verbatim={}",
lib.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);

Expand All @@ -609,6 +642,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
Expand All @@ -632,9 +672,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);
}
}
}
Expand Down
57 changes: 33 additions & 24 deletions src/bootstrap/src/core/build_steps/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ use crate::core::builder;
use crate::core::builder::{
Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
};
use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection};
use crate::core::config::{
CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection,
};
use crate::utils::build_stamp;
use crate::utils::build_stamp::BuildStamp;
use crate::utils::exec::command;
Expand Down Expand Up @@ -560,29 +562,36 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car
// If `compiler-rt` is available ensure that the `c` feature of the
// `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);
" compiler-builtins-c"
} else {
""
let compiler_builtins_c_feature = match builder.config.optimized_compiler_builtins(target) {
CompilerBuiltins::LinkLLVMBuiltinsLib(path) => {
cargo.env("LLVM_COMPILER_RT_LIB", path);
" compiler-builtins-c"
}
CompilerBuiltins::BuildLLVMFuncs => {
// 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"
}
CompilerBuiltins::BuildRustOnly => "",
};

// `libtest` uses this to know whether or not to support
Expand Down
19 changes: 11 additions & 8 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ use crate::core::config::toml::rust::{
};
use crate::core::config::toml::target::Target;
use crate::core::config::{
DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo,
StringOrBool, set, threads_from_config,
CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt,
RustcLto, SplitDebuginfo, StringOrBool, set, threads_from_config,
};
use crate::core::download::{
DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt,
Expand Down Expand Up @@ -122,8 +122,7 @@ pub struct Config {
pub patch_binaries_for_nix: Option<bool>,
pub stage0_metadata: build_helper::stage0_parser::Stage0,
pub android_ndk: Option<PathBuf>,
/// Whether to use the `c` feature of the `compiler_builtins` crate.
pub optimized_compiler_builtins: bool,
pub optimized_compiler_builtins: CompilerBuiltins,

pub stdout_is_tty: bool,
pub stderr_is_tty: bool,
Expand Down Expand Up @@ -1317,7 +1316,11 @@ impl Config {
}

config.optimized_compiler_builtins =
build_optimized_compiler_builtins.unwrap_or(config.channel != "dev");
build_optimized_compiler_builtins.unwrap_or(if config.channel == "dev" {
CompilerBuiltins::BuildRustOnly
} else {
CompilerBuiltins::BuildLLVMFuncs
});
config.compiletest_diff_tool = build_compiletest_diff_tool;
config.compiletest_use_stage0_libtest =
build_compiletest_use_stage0_libtest.unwrap_or(true);
Expand Down Expand Up @@ -1767,11 +1770,11 @@ impl Config {
self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath)
}

pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool {
pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> &CompilerBuiltins {
self.target_config
.get(&target)
.and_then(|t| t.optimized_compiler_builtins)
.unwrap_or(self.optimized_compiler_builtins)
.and_then(|t| t.optimized_compiler_builtins.as_ref())
.unwrap_or(&self.optimized_compiler_builtins)
}

pub fn llvm_enabled(&self, target: TargetSelection) -> bool {
Expand Down
27 changes: 27 additions & 0 deletions src/bootstrap/src/core/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,33 @@ impl<T> Merge for Option<T> {
}
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub enum CompilerBuiltins {
#[default]
// Only build native rust intrinsic compiler functions.
BuildRustOnly,
// Some intrinsic functions have a C implementation provided by LLVM's
// compiler-rt builtins library. Build them from the LLVM source included
// with Rust.
BuildLLVMFuncs,
// Similar to BuildLLVMFuncs, but specify a path to an existing library
// containing LLVM's compiler-rt builtins instead of compiling them.
LinkLLVMBuiltinsLib(String),
}

impl<'de> Deserialize<'de> for CompilerBuiltins {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(match Deserialize::deserialize(deserializer)? {
StringOrBool::Bool(false) => Self::BuildRustOnly,
StringOrBool::Bool(true) => Self::BuildLLVMFuncs,
StringOrBool::String(path) => Self::LinkLLVMBuiltinsLib(path),
})
}
}

#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
pub enum DebuginfoLevel {
#[default]
Expand Down
10 changes: 7 additions & 3 deletions src/bootstrap/src/core/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{CompilerBuiltins, LldMode, StringOrBool, Target, TargetSelection};
use crate::utils::tests::git::git_test;

pub(crate) fn parse(config: &str) -> Config {
Expand Down Expand Up @@ -183,7 +183,11 @@ runner = "x86_64-runner"
);
assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes");
assert!(!config.deny_warnings, "setting boolean value");
assert!(config.optimized_compiler_builtins, "setting boolean value");
assert_eq!(
config.optimized_compiler_builtins,
CompilerBuiltins::BuildLLVMFuncs,
"setting boolean value"
);
assert_eq!(
config.tools,
Some(["cargo".to_string()].into_iter().collect()),
Expand Down Expand Up @@ -212,7 +216,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(CompilerBuiltins::BuildRustOnly),
..Default::default()
};
assert_eq!(
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/src/core/config/toml/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::collections::HashMap;
use serde::{Deserialize, Deserializer};

use crate::core::config::toml::ReplaceOpt;
use crate::core::config::{Merge, StringOrBool};
use crate::core::config::{CompilerBuiltins, Merge, StringOrBool};
use crate::{HashSet, PathBuf, define_config, exit};

define_config! {
Expand Down Expand Up @@ -65,7 +65,7 @@ define_config! {
// NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally
metrics: Option<bool> = "metrics",
android_ndk: Option<PathBuf> = "android-ndk",
optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
optimized_compiler_builtins: Option<CompilerBuiltins> = "optimized-compiler-builtins",
jobs: Option<u32> = "jobs",
compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
compiletest_allow_stage0: Option<bool> = "compiletest-allow-stage0",
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/src/core/config/toml/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ pub fn check_incompatible_options_for_ci_rustc(
err!(current_profiler, profiler, "build");

let current_optimized_compiler_builtins =
current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
let optimized_compiler_builtins =
ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins.clone());
err!(current_optimized_compiler_builtins, optimized_compiler_builtins, "build");

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