Skip to content

Commit 28af00c

Browse files
committed
Support configuring the set of codegen backends to build per host triple
This allows building the compiler itself with one backend while using another backend at runtime. For example this allows compiling rustc to wasm using LLVM, while using Cranelift at runtime to produce actual code. Cranelift can't compile to wasm, but is perfectly capable of running on wasm. LLVM can compile to wasm, but can't run on wasm. [^1] [^1]: The prototype of this still requires a couple of other patches.
1 parent 5bd5d21 commit 28af00c

File tree

8 files changed

+74
-37
lines changed

8 files changed

+74
-37
lines changed

config.example.toml

+5
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,11 @@
829829
# target triples containing `-none`, `nvptx`, `switch`, or `-uefi`.
830830
#no-std = <platform-specific> (bool)
831831

832+
# This is an array of the codegen backends that will be compiled a rustc
833+
# compiled for this target, overriding the global rust.codegen-backends option.
834+
# See that option for more info.
835+
#codegen-backends = rust.codegen-backends (array)
836+
832837
# =============================================================================
833838
# Distribution options
834839
#

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

+8-8
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,7 @@ impl Step for Rustc {
10031003
pub fn rustc_cargo(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection, stage: u32) {
10041004
cargo
10051005
.arg("--features")
1006-
.arg(builder.rustc_features(builder.kind))
1006+
.arg(builder.rustc_features(builder.kind, target))
10071007
.arg("--manifest-path")
10081008
.arg(builder.src.join("compiler/rustc/Cargo.toml"));
10091009

@@ -1060,7 +1060,7 @@ pub fn rustc_cargo_env(
10601060
cargo.env("CFG_OMIT_GIT_HASH", "1");
10611061
}
10621062

1063-
if let Some(backend) = builder.config.default_codegen_backend() {
1063+
if let Some(backend) = builder.config.default_codegen_backend(target) {
10641064
cargo.env("CFG_DEFAULT_CODEGEN_BACKEND", backend);
10651065
}
10661066

@@ -1101,7 +1101,7 @@ pub fn rustc_cargo_env(
11011101
// build. If we are in a check build we still go ahead here presuming we've
11021102
// detected that LLVM is already built and good to go which helps prevent
11031103
// busting caches (e.g. like #71152).
1104-
if builder.config.llvm_enabled() {
1104+
if builder.config.llvm_enabled(target) {
11051105
let building_is_expensive =
11061106
crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).is_err();
11071107
// `top_stage == stage` might be false for `check --stage 1`, if we are building the stage 1 compiler
@@ -1250,7 +1250,7 @@ pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_";
12501250
fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool {
12511251
if path.path.to_str().unwrap().contains(&CODEGEN_BACKEND_PREFIX) {
12521252
let mut needs_codegen_backend_config = true;
1253-
for &backend in &run.builder.config.rust_codegen_backends {
1253+
for &backend in run.builder.config.codegen_backends(run.target) {
12541254
if path
12551255
.path
12561256
.to_str()
@@ -1287,7 +1287,7 @@ impl Step for CodegenBackend {
12871287
return;
12881288
}
12891289

1290-
for &backend in &run.builder.config.rust_codegen_backends {
1290+
for &backend in run.builder.config.codegen_backends(run.target) {
12911291
if backend == "llvm" {
12921292
continue; // Already built as part of rustc
12931293
}
@@ -1387,7 +1387,7 @@ fn copy_codegen_backends_to_sysroot(
13871387
return;
13881388
}
13891389

1390-
for backend in builder.config.rust_codegen_backends.iter() {
1390+
for backend in builder.config.codegen_backends(target) {
13911391
if backend == "llvm" {
13921392
continue; // Already built as part of rustc
13931393
}
@@ -1694,7 +1694,7 @@ impl Step for Assemble {
16941694
// to not fail while linking the artifacts.
16951695
build_compiler.stage = actual_stage;
16961696

1697-
for &backend in builder.config.rust_codegen_backends.iter() {
1697+
for &backend in builder.config.codegen_backends(target_compiler.host) {
16981698
if backend == "llvm" {
16991699
continue; // Already built as part of rustc
17001700
}
@@ -1779,7 +1779,7 @@ impl Step for Assemble {
17791779
}
17801780
}
17811781

1782-
if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) {
1782+
if builder.config.llvm_enabled(target_compiler.host) {
17831783
let llvm::LlvmResult { llvm_config, .. } =
17841784
builder.ensure(llvm::Llvm { target: target_compiler.host });
17851785
if !builder.config.dry_run() && builder.config.llvm_tools_enabled {

src/bootstrap/src/core/build_steps/dist.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ impl Step for CodegenBackend {
12781278
}
12791279

12801280
fn make_run(run: RunConfig<'_>) {
1281-
for &backend in &run.builder.config.rust_codegen_backends {
1281+
for &backend in run.builder.config.codegen_backends(run.target) {
12821282
if backend == "llvm" {
12831283
continue; // Already built as part of rustc
12841284
}
@@ -1302,7 +1302,7 @@ impl Step for CodegenBackend {
13021302
return None;
13031303
}
13041304

1305-
if !builder.config.rust_codegen_backends.contains(&self.backend) {
1305+
if !builder.config.codegen_backends(self.compiler.host).contains(&self.backend) {
13061306
return None;
13071307
}
13081308

src/bootstrap/src/core/build_steps/test.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1841,7 +1841,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
18411841

18421842
let mut llvm_components_passed = false;
18431843
let mut copts_passed = false;
1844-
if builder.config.llvm_enabled() {
1844+
if builder.config.llvm_enabled(compiler.host) {
18451845
let llvm::LlvmResult { llvm_config, .. } =
18461846
builder.ensure(llvm::Llvm { target: builder.config.build });
18471847
if !builder.config.dry_run() {
@@ -3155,7 +3155,8 @@ impl Step for CodegenCranelift {
31553155
return;
31563156
}
31573157

3158-
if !builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("cranelift")) {
3158+
if !builder.config.codegen_backends(run.target).contains(&INTERNER.intern_str("cranelift"))
3159+
{
31593160
builder.info("cranelift not in rust.codegen-backends. skipping");
31603161
return;
31613162
}
@@ -3277,7 +3278,7 @@ impl Step for CodegenGCC {
32773278
return;
32783279
}
32793280

3280-
if !builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("gcc")) {
3281+
if !builder.config.codegen_backends(run.target).contains(&INTERNER.intern_str("gcc")) {
32813282
builder.info("gcc not in rust.codegen-backends. skipping");
32823283
return;
32833284
}

src/bootstrap/src/core/builder.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1230,7 +1230,7 @@ impl<'a> Builder<'a> {
12301230
/// Note that this returns `None` if LLVM is disabled, or if we're in a
12311231
/// check build or dry-run, where there's no need to build all of LLVM.
12321232
fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
1233-
if self.config.llvm_enabled() && self.kind != Kind::Check && !self.config.dry_run() {
1233+
if self.config.llvm_enabled(target) && self.kind != Kind::Check && !self.config.dry_run() {
12341234
let llvm::LlvmResult { llvm_config, .. } = self.ensure(llvm::Llvm { target });
12351235
if llvm_config.is_file() {
12361236
return Some(llvm_config);
@@ -2113,7 +2113,8 @@ impl<'a> Builder<'a> {
21132113
};
21142114

21152115
if let Some(limit) = limit {
2116-
if stage == 0 || self.config.default_codegen_backend().unwrap_or_default() == "llvm"
2116+
if stage == 0
2117+
|| self.config.default_codegen_backend(target).unwrap_or_default() == "llvm"
21172118
{
21182119
rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
21192120
}

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

+31-4
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ pub struct Target {
581581
pub wasi_root: Option<PathBuf>,
582582
pub qemu_rootfs: Option<PathBuf>,
583583
pub no_std: bool,
584+
pub codegen_backends: Option<Vec<Interned<String>>>,
584585
}
585586

586587
impl Target {
@@ -1139,6 +1140,7 @@ define_config! {
11391140
wasi_root: Option<String> = "wasi-root",
11401141
qemu_rootfs: Option<String> = "qemu-rootfs",
11411142
no_std: Option<bool> = "no-std",
1143+
codegen_backends: Option<Vec<String>> = "codegen-backends",
11421144
}
11431145
}
11441146

@@ -1845,6 +1847,24 @@ impl Config {
18451847
target.profiler = cfg.profiler;
18461848
target.rpath = cfg.rpath;
18471849

1850+
if let Some(ref backends) = cfg.codegen_backends {
1851+
let available_backends = vec!["llvm", "cranelift", "gcc"];
1852+
1853+
target.codegen_backends = Some(backends.iter().map(|s| {
1854+
if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) {
1855+
if available_backends.contains(&backend) {
1856+
panic!("Invalid value '{s}' for 'target.{triple}.codegen-backends'. Instead, please use '{backend}'.");
1857+
} else {
1858+
println!("HELP: '{s}' for 'target.{triple}.codegen-backends' might fail. \
1859+
Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
1860+
In this case, it would be referred to as '{backend}'.");
1861+
}
1862+
}
1863+
1864+
INTERNER.intern_str(s)
1865+
}).collect());
1866+
}
1867+
18481868
config.target_config.insert(TargetSelection::from_user(&triple), target);
18491869
}
18501870
}
@@ -2227,8 +2247,8 @@ impl Config {
22272247
self.target_config.get(&target).map(|t| t.rpath).flatten().unwrap_or(self.rust_rpath)
22282248
}
22292249

2230-
pub fn llvm_enabled(&self) -> bool {
2231-
self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm"))
2250+
pub fn llvm_enabled(&self, target: TargetSelection) -> bool {
2251+
self.codegen_backends(target).contains(&INTERNER.intern_str("llvm"))
22322252
}
22332253

22342254
pub fn llvm_libunwind(&self, target: TargetSelection) -> LlvmLibunwind {
@@ -2247,8 +2267,15 @@ impl Config {
22472267
self.submodules.unwrap_or(rust_info.is_managed_git_subrepository())
22482268
}
22492269

2250-
pub fn default_codegen_backend(&self) -> Option<Interned<String>> {
2251-
self.rust_codegen_backends.get(0).cloned()
2270+
pub fn codegen_backends(&self, target: TargetSelection) -> &[Interned<String>] {
2271+
self.target_config
2272+
.get(&target)
2273+
.and_then(|cfg| cfg.codegen_backends.as_deref())
2274+
.unwrap_or(&self.rust_codegen_backends)
2275+
}
2276+
2277+
pub fn default_codegen_backend(&self, target: TargetSelection) -> Option<Interned<String>> {
2278+
self.codegen_backends(target).get(0).cloned()
22522279
}
22532280

22542281
pub fn git_config(&self) -> GitConfig<'_> {

src/bootstrap/src/core/sanity.rs

+17-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use std::path::PathBuf;
1616
use std::process::Command;
1717

1818
use crate::core::config::Target;
19-
use crate::utils::cache::INTERNER;
2019
use crate::utils::helpers::output;
2120
use crate::Build;
2221

@@ -88,19 +87,19 @@ pub fn check(build: &mut Build) {
8887
}
8988

9089
// We need cmake, but only if we're actually building LLVM or sanitizers.
91-
let building_llvm = build.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm"))
92-
&& build
93-
.hosts
94-
.iter()
95-
.map(|host| {
96-
build
90+
let building_llvm = build
91+
.hosts
92+
.iter()
93+
.map(|host| {
94+
build.config.llvm_enabled(*host)
95+
&& build
9796
.config
9897
.target_config
9998
.get(host)
10099
.map(|config| config.llvm_config.is_none())
101100
.unwrap_or(true)
102-
})
103-
.any(|build_llvm_ourselves| build_llvm_ourselves);
101+
})
102+
.any(|build_llvm_ourselves| build_llvm_ourselves);
104103

105104
let need_cmake = building_llvm || build.config.any_sanitizers_to_build();
106105
if need_cmake && cmd_finder.maybe_have("cmake").is_none() {
@@ -190,13 +189,16 @@ than building it.
190189
if !build.config.dry_run() {
191190
cmd_finder.must_have(build.cxx(*host).unwrap());
192191
}
193-
}
194192

195-
if build.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) {
196-
// Externally configured LLVM requires FileCheck to exist
197-
let filecheck = build.llvm_filecheck(build.build);
198-
if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests {
199-
panic!("FileCheck executable {filecheck:?} does not exist");
193+
if build.config.llvm_enabled(*host) {
194+
// Externally configured LLVM requires FileCheck to exist
195+
let filecheck = build.llvm_filecheck(build.build);
196+
if !filecheck.starts_with(&build.out)
197+
&& !filecheck.exists()
198+
&& build.config.codegen_tests
199+
{
200+
panic!("FileCheck executable {filecheck:?} does not exist");
201+
}
200202
}
201203
}
202204

src/bootstrap/src/lib.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -734,12 +734,12 @@ impl Build {
734734
}
735735

736736
/// Gets the space-separated set of activated features for the compiler.
737-
fn rustc_features(&self, kind: Kind) -> String {
737+
fn rustc_features(&self, kind: Kind, target: TargetSelection) -> String {
738738
let mut features = vec![];
739739
if self.config.jemalloc {
740740
features.push("jemalloc");
741741
}
742-
if self.config.llvm_enabled() || kind == Kind::Check {
742+
if self.config.llvm_enabled(target) || kind == Kind::Check {
743743
features.push("llvm");
744744
}
745745
// keep in sync with `bootstrap/compile.rs:rustc_cargo_env`
@@ -1560,7 +1560,8 @@ impl Build {
15601560
|| target
15611561
.map(|t| self.config.profiler_enabled(t))
15621562
.unwrap_or_else(|| self.config.any_profiler_enabled()))
1563-
&& (dep != "rustc_codegen_llvm" || self.config.llvm_enabled())
1563+
&& (dep != "rustc_codegen_llvm"
1564+
|| self.config.hosts.iter().any(|host| self.config.llvm_enabled(*host)))
15641565
{
15651566
list.push(*dep);
15661567
}

0 commit comments

Comments
 (0)