Skip to content

Make LLD build forward-compatible with LLVM 16 #106000

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

Merged
merged 2 commits into from
Dec 22, 2022
Merged
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
5 changes: 0 additions & 5 deletions src/bootstrap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ name = "sccache-plus-cl"
path = "bin/sccache-plus-cl.rs"
test = false

[[bin]]
name = "llvm-config-wrapper"
path = "bin/llvm-config-wrapper.rs"
test = false

[dependencies]
cmake = "0.1.38"
fd-lock = "3.0.8"
Expand Down
24 changes: 0 additions & 24 deletions src/bootstrap/bin/llvm-config-wrapper.rs

This file was deleted.

2 changes: 1 addition & 1 deletion src/bootstrap/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ impl<'a> Builder<'a> {
/// check build or dry-run, where there's no need to build all of LLVM.
fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
if self.config.llvm_enabled() && self.kind != Kind::Check && !self.config.dry_run() {
let llvm_config = self.ensure(native::Llvm { target });
let native::LlvmResult { llvm_config, .. } = self.ensure(native::Llvm { target });
if llvm_config.is_file() {
return Some(llvm_config);
}
Expand Down
7 changes: 4 additions & 3 deletions src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
if builder.is_rust_llvm(target) {
cargo.env("LLVM_RUSTLLVM", "1");
}
let llvm_config = builder.ensure(native::Llvm { target });
let native::LlvmResult { llvm_config, .. } = builder.ensure(native::Llvm { target });
cargo.env("LLVM_CONFIG", &llvm_config);
if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
cargo.env("CFG_LLVM_ROOT", s);
Expand Down Expand Up @@ -1341,9 +1341,10 @@ impl Step for Assemble {
}

if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) {
let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host });
let native::LlvmResult { llvm_config, .. } =
builder.ensure(native::Llvm { target: target_compiler.host });
if !builder.config.dry_run() {
let llvm_bin_dir = output(Command::new(llvm_config_bin).arg("--bindir"));
let llvm_bin_dir = output(Command::new(llvm_config).arg("--bindir"));
let llvm_bin_dir = Path::new(llvm_bin_dir.trim());

// Since we've already built the LLVM tools, install them to the sysroot.
Expand Down
7 changes: 5 additions & 2 deletions src/bootstrap/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::channel;
use crate::compile;
use crate::config::TargetSelection;
use crate::doc::DocumentationFormat;
use crate::native;
use crate::tarball::{GeneratedTarball, OverlayKind, Tarball};
use crate::tool::{self, Tool};
use crate::util::{exe, is_dylib, output, t, timeit};
Expand Down Expand Up @@ -1927,7 +1928,9 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir
builder.install(&llvm_dylib_path, dst_libdir, 0o644);
}
!builder.config.dry_run()
} else if let Ok(llvm_config) = crate::native::prebuilt_llvm_config(builder, target) {
} else if let Ok(native::LlvmResult { llvm_config, .. }) =
native::prebuilt_llvm_config(builder, target)
{
let mut cmd = Command::new(llvm_config);
cmd.arg("--libfiles");
builder.verbose(&format!("running {:?}", cmd));
Expand Down Expand Up @@ -2137,7 +2140,7 @@ impl Step for Bootstrap {
let tarball = Tarball::new(builder, "bootstrap", &target.triple);

let bootstrap_outdir = &builder.bootstrap_out;
for file in &["bootstrap", "llvm-config-wrapper", "rustc", "rustdoc", "sccache-plus-cl"] {
for file in &["bootstrap", "rustc", "rustdoc", "sccache-plus-cl"] {
tarball.add_file(bootstrap_outdir.join(exe(file, target)), "bootstrap/bin", 0o755);
}

Expand Down
109 changes: 46 additions & 63 deletions src/bootstrap/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ use crate::util::get_clang_cl_resource_dir;
use crate::util::{self, exe, output, t, up_to_date};
use crate::{CLang, GitRepo};

#[derive(Clone)]
pub struct LlvmResult {
/// Path to llvm-config binary.
/// NB: This is always the host llvm-config!
pub llvm_config: PathBuf,
/// Path to LLVM cmake directory for the target.
pub llvm_cmake_dir: PathBuf,
}

pub struct Meta {
stamp: HashStamp,
build_llvm_config: PathBuf,
res: LlvmResult,
out_dir: PathBuf,
root: String,
}
Expand Down Expand Up @@ -64,15 +73,22 @@ impl LdFlags {
pub fn prebuilt_llvm_config(
builder: &Builder<'_>,
target: TargetSelection,
) -> Result<PathBuf, Meta> {
) -> Result<LlvmResult, Meta> {
builder.config.maybe_download_ci_llvm();

// If we're using a custom LLVM bail out here, but we can only use a
// custom LLVM for the build triple.
if let Some(config) = builder.config.target_config.get(&target) {
if let Some(ref s) = config.llvm_config {
check_llvm_version(builder, s);
return Ok(s.to_path_buf());
let llvm_config = s.to_path_buf();
let mut llvm_cmake_dir = llvm_config.clone();
llvm_cmake_dir.pop();
llvm_cmake_dir.pop();
llvm_cmake_dir.push("lib");
llvm_cmake_dir.push("cmake");
llvm_cmake_dir.push("llvm");
return Ok(LlvmResult { llvm_config, llvm_cmake_dir });
}
}

Expand All @@ -84,8 +100,9 @@ pub fn prebuilt_llvm_config(
llvm_config_ret_dir.push("build");
}
llvm_config_ret_dir.push("bin");

let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", builder.config.build));
let llvm_cmake_dir = out_dir.join("lib/cmake/llvm");
let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir };

let stamp = out_dir.join("llvm-finished-building");
let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
Expand All @@ -96,7 +113,7 @@ pub fn prebuilt_llvm_config(
Using a potentially stale build of LLVM; \
This may not behave well.",
);
return Ok(build_llvm_config);
return Ok(res);
}

if stamp.is_done() {
Expand All @@ -110,10 +127,10 @@ pub fn prebuilt_llvm_config(
stamp.path.display()
));
}
return Ok(build_llvm_config);
return Ok(res);
}

Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
Err(Meta { stamp, res, out_dir, root: root.into() })
}

/// This retrieves the LLVM sha we *want* to use, according to git history.
Expand Down Expand Up @@ -223,7 +240,7 @@ pub struct Llvm {
}

impl Step for Llvm {
type Output = PathBuf; // path to llvm-config
type Output = LlvmResult;

const ONLY_HOSTS: bool = true;

Expand All @@ -236,7 +253,7 @@ impl Step for Llvm {
}

/// Compile LLVM for `target`.
fn run(self, builder: &Builder<'_>) -> PathBuf {
fn run(self, builder: &Builder<'_>) -> LlvmResult {
let target = self.target;
let target_native = if self.target.starts_with("riscv") {
// RISC-V target triples in Rust is not named the same as C compiler target triples.
Expand All @@ -252,11 +269,10 @@ impl Step for Llvm {
target.to_string()
};

let Meta { stamp, build_llvm_config, out_dir, root } =
match prebuilt_llvm_config(builder, target) {
Ok(p) => return p,
Err(m) => m,
};
let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) {
Ok(p) => return p,
Err(m) => m,
};

builder.update_submodule(&Path::new("src").join("llvm-project"));
if builder.llvm_link_shared() && target.contains("windows") {
Expand Down Expand Up @@ -430,7 +446,8 @@ impl Step for Llvm {

// https://llvm.org/docs/HowToCrossCompileLLVM.html
if target != builder.config.build {
let llvm_config = builder.ensure(Llvm { target: builder.config.build });
let LlvmResult { llvm_config, .. } =
builder.ensure(Llvm { target: builder.config.build });
if !builder.config.dry_run() {
let llvm_bindir = output(Command::new(&llvm_config).arg("--bindir"));
let host_bin = Path::new(llvm_bindir.trim());
Expand Down Expand Up @@ -480,7 +497,7 @@ impl Step for Llvm {
// tools and libs on all platforms.

if builder.config.dry_run() {
return build_llvm_config;
return res;
}

cfg.build();
Expand All @@ -490,7 +507,7 @@ impl Step for Llvm {
// for a versioned path like libLLVM-14.dylib. Manually create a symbolic
// link to make llvm-config happy.
if builder.llvm_link_shared() && target.contains("apple-darwin") {
let mut cmd = Command::new(&build_llvm_config);
let mut cmd = Command::new(&res.llvm_config);
let version = output(cmd.arg("--version"));
let major = version.split('.').next().unwrap();
let lib_name = match llvm_version_suffix {
Expand All @@ -509,18 +526,18 @@ impl Step for Llvm {
// LLVM after a configuration change, so to rebuild it the build files have to be removed,
// which will also remove these modified files.
if builder.config.llvm_bolt_profile_generate {
instrument_with_bolt_inplace(&get_built_llvm_lib_path(&build_llvm_config));
instrument_with_bolt_inplace(&get_built_llvm_lib_path(&res.llvm_config));
}
if let Some(path) = &builder.config.llvm_bolt_profile_use {
optimize_library_with_bolt_inplace(
&get_built_llvm_lib_path(&build_llvm_config),
&get_built_llvm_lib_path(&res.llvm_config),
&Path::new(path),
);
}

t!(stamp.write());

build_llvm_config
res
}
}

Expand Down Expand Up @@ -803,7 +820,8 @@ impl Step for Lld {
}
let target = self.target;

let llvm_config = builder.ensure(Llvm { target: self.target });
let LlvmResult { llvm_config, llvm_cmake_dir } =
builder.ensure(Llvm { target: self.target });

let out_dir = builder.lld_out(target);
let done_stamp = out_dir.join("lld-finished-building");
Expand Down Expand Up @@ -834,22 +852,6 @@ impl Step for Lld {
configure_cmake(builder, target, &mut cfg, true, ldflags);
configure_llvm(builder, target, &mut cfg);

// This is an awful, awful hack. Discovered when we migrated to using
// clang-cl to compile LLVM/LLD it turns out that LLD, when built out of
// tree, will execute `llvm-config --cmakedir` and then tell CMake about
// that directory for later processing. Unfortunately if this path has
// forward slashes in it (which it basically always does on Windows)
// then CMake will hit a syntax error later on as... something isn't
// escaped it seems?
//
// Instead of attempting to fix this problem in upstream CMake and/or
// LLVM/LLD we just hack around it here. This thin wrapper will take the
// output from llvm-config and replace all instances of `\` with `/` to
// ensure we don't hit the same bugs with escaping. It means that you
// can't build on a system where your paths require `\` on Windows, but
// there's probably a lot of reasons you can't do that other than this.
let llvm_config_shim = env::current_exe().unwrap().with_file_name("llvm-config-wrapper");

// Re-use the same flags as llvm to control the level of debug information
// generated for lld.
let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
Expand All @@ -860,36 +862,17 @@ impl Step for Lld {

cfg.out_dir(&out_dir)
.profile(profile)
.env("LLVM_CONFIG_REAL", &llvm_config)
.define("LLVM_CONFIG_PATH", llvm_config_shim)
.define("LLVM_CMAKE_DIR", llvm_cmake_dir)
.define("LLVM_INCLUDE_TESTS", "OFF");

// While we're using this horrible workaround to shim the execution of
// llvm-config, let's just pile on more. I can't seem to figure out how
// to build LLD as a standalone project and also cross-compile it at the
// same time. It wants a natively executable `llvm-config` to learn
// about LLVM, but then it learns about all the host configuration of
// LLVM and tries to link to host LLVM libraries.
//
// To work around that we tell our shim to replace anything with the
// build target with the actual target instead. This'll break parts of
// LLD though which try to execute host tools, such as llvm-tblgen, so
// we specifically tell it where to find those. This is likely super
// brittle and will break over time. If anyone knows better how to
// cross-compile LLD it would be much appreciated to fix this!
if target != builder.config.build {
cfg.env("LLVM_CONFIG_SHIM_REPLACE", &builder.config.build.triple)
.env("LLVM_CONFIG_SHIM_REPLACE_WITH", &target.triple)
.define(
"LLVM_TABLEGEN_EXE",
llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
);
// Use the host llvm-tblgen binary.
cfg.define(
"LLVM_TABLEGEN_EXE",
llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
);
}

// Explicitly set C++ standard, because upstream doesn't do so
// for standalone builds.
cfg.define("CMAKE_CXX_STANDARD", "14");

cfg.build();

t!(File::create(&done_stamp));
Expand Down Expand Up @@ -991,7 +974,7 @@ impl Step for Sanitizers {
return runtimes;
}

let llvm_config = builder.ensure(Llvm { target: builder.config.build });
let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build });
if builder.config.dry_run() {
return runtimes;
}
Expand Down
3 changes: 2 additions & 1 deletion src/bootstrap/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1575,7 +1575,8 @@ note: if you're sure you want to do this, please open an issue as to why. In the
let mut llvm_components_passed = false;
let mut copts_passed = false;
if builder.config.llvm_enabled() {
let llvm_config = builder.ensure(native::Llvm { target: builder.config.build });
let native::LlvmResult { llvm_config, .. } =
builder.ensure(native::Llvm { target: builder.config.build });
if !builder.config.dry_run() {
let llvm_version = output(Command::new(&llvm_config).arg("--version"));
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
Expand Down