From ed252e931e11759e163c2f63ff8054c12edf1899 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 26 Oct 2023 21:48:00 +0200 Subject: [PATCH 1/6] LLVM_TOOLS: Include llvm-link as a llvm tool --- src/bootstrap/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 938b95cc60e4b..6520b7ed08988 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -64,6 +64,7 @@ const LLVM_TOOLS: &[&str] = &[ "llvm-ar", // used for creating and modifying archive files "llvm-as", // used to convert LLVM assembly to LLVM bitcode "llvm-dis", // used to disassemble LLVM bitcode + "llvm-link", // Used to link LLVM bitcode "llc", // used to compile LLVM bytecode "opt", // used to optimize LLVM bytecode ]; From 222ce4fdb8fb5fbff8047014c3ffb73df0ef2693 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Tue, 6 Feb 2024 19:15:38 +0100 Subject: [PATCH 2/6] LLVM Bitcode Linker: Added crate --- Cargo.lock | 11 ++ Cargo.toml | 1 + src/bootstrap/src/core/build_steps/tool.rs | 1 + src/bootstrap/src/core/builder.rs | 1 + src/tools/llvm-bitcode-linker/Cargo.toml | 14 ++ src/tools/llvm-bitcode-linker/README.md | 5 + .../src/bin/llvm-bitcode-linker.rs | 62 +++++++ src/tools/llvm-bitcode-linker/src/lib.rs | 7 + src/tools/llvm-bitcode-linker/src/linker.rs | 163 ++++++++++++++++++ src/tools/llvm-bitcode-linker/src/opt.rs | 53 ++++++ src/tools/llvm-bitcode-linker/src/target.rs | 20 +++ 11 files changed, 338 insertions(+) create mode 100644 src/tools/llvm-bitcode-linker/Cargo.toml create mode 100644 src/tools/llvm-bitcode-linker/README.md create mode 100644 src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs create mode 100644 src/tools/llvm-bitcode-linker/src/lib.rs create mode 100644 src/tools/llvm-bitcode-linker/src/linker.rs create mode 100644 src/tools/llvm-bitcode-linker/src/opt.rs create mode 100644 src/tools/llvm-bitcode-linker/src/target.rs diff --git a/Cargo.lock b/Cargo.lock index 3290741f128f3..53bd088b55793 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2265,6 +2265,17 @@ checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da" name = "lld-wrapper" version = "0.1.0" +[[package]] +name = "llvm-bitcode-linker" +version = "0.0.1" +dependencies = [ + "anyhow", + "clap", + "thiserror", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lock_api" version = "0.4.11" diff --git a/Cargo.toml b/Cargo.toml index 5847a817e76fe..5dd315ef2f7c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "src/tools/expand-yaml-anchors", "src/tools/jsondocck", "src/tools/jsondoclint", + "src/tools/llvm-bitcode-linker", "src/tools/html-checker", "src/tools/bump-stage0", "src/tools/replace-version-placeholder", diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 889876f461df2..53dc1cff0aeba 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -795,6 +795,7 @@ tool_extended!((self, builder), Rls, "src/tools/rls", "rls", stable=true, tool_std=true; RustDemangler, "src/tools/rust-demangler", "rust-demangler", stable=false, tool_std=true; Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"]; + LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", stable=false, add_bins_to_sysroot = ["llvm-bitcode-linker"]; ); impl<'a> Builder<'a> { diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 4927c83760808..5e5d6d024ee82 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -763,6 +763,7 @@ impl<'a> Builder<'a> { tool::RustdocGUITest, tool::OptimizedDist, tool::CoverageDump, + tool::LlvmBitcodeLinker ), Kind::Check | Kind::Clippy | Kind::Fix => describe!( check::Std, diff --git a/src/tools/llvm-bitcode-linker/Cargo.toml b/src/tools/llvm-bitcode-linker/Cargo.toml new file mode 100644 index 0000000000000..a9210b562f330 --- /dev/null +++ b/src/tools/llvm-bitcode-linker/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "llvm-bitcode-linker" +version = "0.0.1" +description = "A self-contained linker for llvm bitcode" +license = "MIT OR Apache-2.0" +edition = "2021" +publish = false + +[dependencies] +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = {version = "0.3.0", features = ["std"] } +clap = { version = "4.3", features = ["derive"] } +thiserror = "1.0.24" diff --git a/src/tools/llvm-bitcode-linker/README.md b/src/tools/llvm-bitcode-linker/README.md new file mode 100644 index 0000000000000..9b8719e3a7762 --- /dev/null +++ b/src/tools/llvm-bitcode-linker/README.md @@ -0,0 +1,5 @@ +# LLVM Bitcode Linker +The LLVM bitcode linker can be used to link targets without any dependency on system libraries. +The code will be linked in llvm-bc before compiling to native code. For some of these targets +(e.g. ptx) there does not exist a sensible way to link the native format at all. A bitcode linker +is required to link code compiled for such targets. diff --git a/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs b/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs new file mode 100644 index 0000000000000..a92af71a36ed8 --- /dev/null +++ b/src/tools/llvm-bitcode-linker/src/bin/llvm-bitcode-linker.rs @@ -0,0 +1,62 @@ +use std::path::PathBuf; + +use clap::Parser; + +use llvm_bitcode_linker::{Optimization, Session, Target}; + +#[derive(Debug, Parser)] +/// Linker for embedded code without any system dependencies +pub struct Args { + /// Input files - objects, archives and static libraries. + /// + /// An archive can be, but not required to be, a Rust rlib. + files: Vec, + + /// A symbol that should be exported + #[arg(long)] + export_symbol: Vec, + + /// Input files directory + #[arg(short = 'L')] + input_dir: Vec, + + /// Target triple for which the code is compiled + #[arg(long)] + target: Target, + + /// The target cpu + #[arg(long)] + target_cpu: Option, + + /// Write output to the filename + #[arg(short, long)] + output: PathBuf, + + // Enable link time optimization + #[arg(long)] + lto: bool, + + /// Emit debug information + #[arg(long)] + debug: bool, + + /// The optimization level + #[arg(short = 'O', value_enum, default_value = "0")] + optimization: Optimization, +} + +fn main() -> anyhow::Result<()> { + tracing_subscriber::FmtSubscriber::builder().with_max_level(tracing::Level::DEBUG).init(); + + let args = Args::parse(); + + let mut linker = Session::new(args.target, args.target_cpu, args.output); + + linker.add_exported_symbols(args.export_symbol); + + for rlib in args.files { + linker.add_file(rlib); + } + + linker.lto(args.optimization, args.debug) +} diff --git a/src/tools/llvm-bitcode-linker/src/lib.rs b/src/tools/llvm-bitcode-linker/src/lib.rs new file mode 100644 index 0000000000000..48f918f631c0f --- /dev/null +++ b/src/tools/llvm-bitcode-linker/src/lib.rs @@ -0,0 +1,7 @@ +mod linker; +mod opt; +mod target; + +pub use linker::Session; +pub use opt::Optimization; +pub use target::Target; diff --git a/src/tools/llvm-bitcode-linker/src/linker.rs b/src/tools/llvm-bitcode-linker/src/linker.rs new file mode 100644 index 0000000000000..aa6b6443e4d90 --- /dev/null +++ b/src/tools/llvm-bitcode-linker/src/linker.rs @@ -0,0 +1,163 @@ +use std::path::PathBuf; + +use anyhow::Context; + +use crate::Optimization; +use crate::Target; + +#[derive(Debug)] +pub struct Session { + target: Target, + cpu: Option, + symbols: Vec, + + /// A file that `llvm-link` supports, like a bitcode file or an archive. + files: Vec, + + // Output files + link_path: PathBuf, + opt_path: PathBuf, + sym_path: PathBuf, + out_path: PathBuf, +} + +impl Session { + pub fn new(target: crate::Target, cpu: Option, out_path: PathBuf) -> Self { + let link_path = out_path.with_extension("o"); + let opt_path = out_path.with_extension("optimized.o"); + let sym_path = out_path.with_extension("symbols.txt"); + + Session { + target, + cpu, + symbols: Vec::new(), + files: Vec::new(), + link_path, + opt_path, + sym_path, + out_path, + } + } + + /// Add a file, like an rlib or bitcode file that should be linked + pub fn add_file(&mut self, path: PathBuf) { + self.files.push(path); + } + + /// Add a Vec of symbols to the list of exported symbols + pub fn add_exported_symbols(&mut self, symbols: Vec) { + self.symbols.extend(symbols); + } + + /// Reads every file that was added to the session and link them without optimization. + /// + /// The resulting artifact will be written to a file that can later be read to perform + /// optimizations and/or compilation from bitcode to the final artifact. + fn link(&mut self) -> anyhow::Result<()> { + tracing::info!("Linking {} files using llvm-link", self.files.len()); + + let llvm_link_output = std::process::Command::new("llvm-link") + .arg("--ignore-non-bitcode") + .args(&self.files) + .arg("-o") + .arg(&self.link_path) + .output() + .unwrap(); + + if !llvm_link_output.status.success() { + tracing::error!( + "llvm-link returned with Exit status: {}\n stdout: {}\n stderr: {}", + llvm_link_output.status, + String::from_utf8(llvm_link_output.stdout).unwrap(), + String::from_utf8(llvm_link_output.stderr).unwrap(), + ); + anyhow::bail!("llvm-link failed to link files {:?}", self.files); + } + + Ok(()) + } + + /// Optimize and compile to native format using `opt` and `llc` + /// + /// Before this can be called `link` needs to be called + fn optimize(&mut self, optimization: Optimization, mut debug: bool) -> anyhow::Result<()> { + let mut passes = format!("default<{}>", optimization); + + // FIXME(@kjetilkjeka) Debug symbol generation is broken for nvptx64 so we must remove them even in debug mode + if debug && self.target == crate::Target::Nvptx64NvidiaCuda { + tracing::warn!("nvptx64 target detected - stripping debug symbols"); + debug = false; + } + + // We add an internalize pass as the rust compiler as we require exported symbols to be explicitly marked + passes.push_str(",internalize,globaldce"); + let symbol_file_content = self.symbols.iter().fold(String::new(), |s, x| s + &x + "\n"); + std::fs::write(&self.sym_path, symbol_file_content) + .context(format!("Failed to write symbol file: {}", self.sym_path.display()))?; + + tracing::info!("optimizing bitcode with passes: {}", passes); + let mut opt_cmd = std::process::Command::new("opt"); + opt_cmd + .arg(&self.link_path) + .arg("-o") + .arg(&self.opt_path) + .arg(format!("--internalize-public-api-file={}", self.sym_path.display())) + .arg(format!("--passes={}", passes)); + + if !debug { + opt_cmd.arg("--strip-debug"); + } + + let opt_output = opt_cmd.output().unwrap(); + + if !opt_output.status.success() { + tracing::error!( + "opt returned with Exit status: {}\n stdout: {}\n stderr: {}", + opt_output.status, + String::from_utf8(opt_output.stdout).unwrap(), + String::from_utf8(opt_output.stderr).unwrap(), + ); + anyhow::bail!("opt failed optimize bitcode: {}", self.link_path.display()); + }; + + Ok(()) + } + + /// Compile the optimized bitcode file to native format using `llc` + /// + /// Before this can be called `optimize` needs to be called + fn compile(&mut self) -> anyhow::Result<()> { + let mut lcc_command = std::process::Command::new("llc"); + + if let Some(mcpu) = &self.cpu { + lcc_command.arg("--mcpu").arg(mcpu); + } + + let lcc_output = + lcc_command.arg(&self.opt_path).arg("-o").arg(&self.out_path).output().unwrap(); + + if !lcc_output.status.success() { + tracing::error!( + "llc returned with Exit status: {}\n stdout: {}\n stderr: {}", + lcc_output.status, + String::from_utf8(lcc_output.stdout).unwrap(), + String::from_utf8(lcc_output.stderr).unwrap(), + ); + + anyhow::bail!( + "llc failed to compile {} into {}", + self.opt_path.display(), + self.out_path.display() + ); + } + + Ok(()) + } + + /// Links, optimizes and compiles to the native format + pub fn lto(&mut self, optimization: crate::Optimization, debug: bool) -> anyhow::Result<()> { + self.link()?; + self.optimize(optimization, debug)?; + self.compile() + } +} diff --git a/src/tools/llvm-bitcode-linker/src/opt.rs b/src/tools/llvm-bitcode-linker/src/opt.rs new file mode 100644 index 0000000000000..93f0ec7e2d633 --- /dev/null +++ b/src/tools/llvm-bitcode-linker/src/opt.rs @@ -0,0 +1,53 @@ +use std::fmt::Display; +use std::fmt::Formatter; + +#[derive(Debug, Clone, Copy, Default, Hash, Eq, PartialEq, clap::ValueEnum)] +pub enum Optimization { + #[default] + #[value(name = "0")] + O0, + #[value(name = "1")] + O1, + #[value(name = "2")] + O2, + #[value(name = "3")] + O3, + #[value(name = "s")] + Os, + #[value(name = "z")] + Oz, +} + +#[derive(Debug, Clone, Copy, thiserror::Error)] +/// An invalid optimization level +#[error("invalid optimization level")] +pub struct InvalidOptimizationLevel; + +impl std::str::FromStr for Optimization { + type Err = InvalidOptimizationLevel; + + fn from_str(s: &str) -> Result { + match s { + "0" | "O0" => Ok(Optimization::O0), + "1" | "O1" => Ok(Optimization::O1), + "2" | "O2" => Ok(Optimization::O2), + "3" | "O3" => Ok(Optimization::O3), + "s" | "Os" => Ok(Optimization::Os), + "z" | "Oz" => Ok(Optimization::Oz), + _ => Err(InvalidOptimizationLevel), + } + } +} + +impl Display for Optimization { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match *self { + Optimization::O0 => write!(f, "O0"), + Optimization::O1 => write!(f, "O1"), + Optimization::O2 => write!(f, "O2"), + Optimization::O3 => write!(f, "O3"), + Optimization::Os => write!(f, "Os"), + Optimization::Oz => write!(f, "Oz"), + } + } +} diff --git a/src/tools/llvm-bitcode-linker/src/target.rs b/src/tools/llvm-bitcode-linker/src/target.rs new file mode 100644 index 0000000000000..d9f8ff3852b60 --- /dev/null +++ b/src/tools/llvm-bitcode-linker/src/target.rs @@ -0,0 +1,20 @@ +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, clap::ValueEnum)] +pub enum Target { + Nvptx64NvidiaCuda, +} + +#[derive(Debug, Clone, Copy, thiserror::Error)] +/// The target is not supported by this linker +#[error("unsupported target")] +pub struct UnsupportedTarget; + +impl std::str::FromStr for Target { + type Err = UnsupportedTarget; + + fn from_str(s: &str) -> Result { + match s { + "nvptx64-nvidia-cuda" => Ok(Target::Nvptx64NvidiaCuda), + _ => Err(UnsupportedTarget), + } + } +} From af42d2a4b286acfdaf98da2ad6fcb46978463c84 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Fri, 1 Dec 2023 16:39:09 +0100 Subject: [PATCH 3/6] NVPTX: Enable self-contained for the nvptx target --- compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index 33b79a1335948..47d6ef0c62243 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -1,3 +1,4 @@ +use crate::spec::LinkSelfContainedDefault; use crate::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, Target, TargetOptions}; pub fn target() -> Target { @@ -47,6 +48,9 @@ pub fn target() -> Target { // The LLVM backend does not support stack canaries for this target supports_stack_protector: false, + // Support using `self-contained` linkers like the llvm-bitcode-linker + link_self_contained: LinkSelfContainedDefault::True, + ..Default::default() }, } From 43f2055af5a0897876f25b6bdfa821dea8d5e4aa Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Tue, 6 Feb 2024 19:46:12 +0100 Subject: [PATCH 4/6] LLVM Bitcode Linker: Add as a linker known to the compiler --- compiler/rustc_codegen_ssa/src/back/link.rs | 23 +++- compiler/rustc_codegen_ssa/src/back/linker.rs | 101 +++++++++++++++++- compiler/rustc_target/src/spec/mod.rs | 22 +++- .../rustc_target/src/spec/tests/tests_impl.rs | 5 +- 4 files changed, 142 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index fcb3602b7349c..e70cc9b621616 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -24,6 +24,7 @@ use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::LinkSelfContainedComponents; use rustc_target::spec::LinkSelfContainedDefault; +use rustc_target::spec::LinkerFlavorCli; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -1350,6 +1351,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { } } LinkerFlavor::Bpf => "bpf-linker", + LinkerFlavor::Llbc => "llvm-bitcode-linker", LinkerFlavor::Ptx => "rust-ptx-linker", }), flavor, @@ -1367,8 +1369,17 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { // linker and linker flavor specified via command line have precedence over what the target // specification specifies - let linker_flavor = - sess.opts.cg.linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)); + let linker_flavor = match sess.opts.cg.linker_flavor { + // The linker flavors that are non-target specific can be directly translated to LinkerFlavor + Some(LinkerFlavorCli::Llbc) => Some(LinkerFlavor::Llbc), + Some(LinkerFlavorCli::Ptx) => Some(LinkerFlavor::Ptx), + // The linker flavors that corresponds to targets needs logic that keeps the base LinkerFlavor + _ => sess + .opts + .cg + .linker_flavor + .map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)), + }; if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) { return ret; } @@ -2338,8 +2349,12 @@ fn add_order_independent_options( }); } - if flavor == LinkerFlavor::Ptx { - // Provide the linker with fallback to internal `target-cpu`. + if flavor == LinkerFlavor::Llbc { + cmd.arg("--target"); + cmd.arg(sess.target.llvm_target.as_ref()); + cmd.arg("--target-cpu"); + cmd.arg(&codegen_results.crate_info.target_cpu); + } else if flavor == LinkerFlavor::Ptx { cmd.arg("--fallback-arch"); cmd.arg(&codegen_results.crate_info.target_cpu); } else if flavor == LinkerFlavor::Bpf { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e52efd8695558..b4e054417f31c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -153,6 +153,7 @@ pub fn get_linker<'a>( LinkerFlavor::Msvc(..) => Box::new(MsvcLinker { cmd, sess }) as Box, LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box, LinkerFlavor::Bpf => Box::new(BpfLinker { cmd, sess }) as Box, + LinkerFlavor::Llbc => Box::new(LlbcLinker { cmd, sess }) as Box, LinkerFlavor::Ptx => Box::new(PtxLinker { cmd, sess }) as Box, } } @@ -1824,7 +1825,7 @@ impl<'a> Linker for PtxLinker<'a> { } Lto::No => {} - }; + } } fn output_filename(&mut self, path: &Path) { @@ -1862,6 +1863,104 @@ impl<'a> Linker for PtxLinker<'a> { fn linker_plugin_lto(&mut self) {} } +/// The `self-contained` LLVM bitcode linker +pub struct LlbcLinker<'a> { + cmd: Command, + sess: &'a Session, +} + +impl<'a> Linker for LlbcLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} + + fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { + panic!("external dylibs not supported") + } + + fn link_staticlib_by_name( + &mut self, + _name: &str, + _verbatim: bool, + _whole_archive: bool, + _search_paths: &SearchPaths, + ) { + panic!("staticlibs not supported") + } + + fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { + self.cmd.arg(path); + } + + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + + fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { + self.cmd.arg("--debug"); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn optimize(&mut self) { + match self.sess.opts.optimize { + OptLevel::No => "-O0", + OptLevel::Less => "-O1", + OptLevel::Default => "-O2", + OptLevel::Aggressive => "-O3", + OptLevel::Size => "-Os", + OptLevel::SizeMin => "-Oz", + }; + } + + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn framework_path(&mut self, _path: &Path) { + panic!("frameworks not supported") + } + + fn full_relro(&mut self) {} + + fn partial_relro(&mut self) {} + + fn no_relro(&mut self) {} + + fn gc_sections(&mut self, _keep_metadata: bool) {} + + fn no_gc_sections(&mut self) {} + + fn pgo_gen(&mut self) {} + + fn no_crt_objects(&mut self) {} + + fn no_default_libraries(&mut self) {} + + fn control_flow_guard(&mut self) {} + + fn ehcont_guard(&mut self) {} + + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { + match _crate_type { + CrateType::Cdylib => { + for sym in symbols { + self.cmd.arg("--export-symbol").arg(sym); + } + } + _ => (), + } + } + + fn subsystem(&mut self, _subsystem: &str) {} + + fn linker_plugin_lto(&mut self) {} +} + pub struct BpfLinker<'a> { cmd: Command, sess: &'a Session, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3b6e15fad4661..7d2a6d3d861ae 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -123,6 +123,8 @@ pub enum LinkerFlavor { Bpf, /// Linker tool for Nvidia PTX. Ptx, + /// LLVM bitcode linker that can be used as a `self-contained` linker + Llbc, } /// Linker flavors available externally through command line (`-Clinker-flavor`) @@ -141,6 +143,7 @@ pub enum LinkerFlavorCli { EmCc, Bpf, Ptx, + Llbc, // Legacy stable values Gcc, @@ -160,6 +163,7 @@ impl LinkerFlavorCli { | LinkerFlavorCli::Msvc(Lld::Yes) | LinkerFlavorCli::EmCc | LinkerFlavorCli::Bpf + | LinkerFlavorCli::Llbc | LinkerFlavorCli::Ptx => true, LinkerFlavorCli::Gcc | LinkerFlavorCli::Ld @@ -219,6 +223,7 @@ impl LinkerFlavor { LinkerFlavorCli::Msvc(lld) => LinkerFlavor::Msvc(lld), LinkerFlavorCli::EmCc => LinkerFlavor::EmCc, LinkerFlavorCli::Bpf => LinkerFlavor::Bpf, + LinkerFlavorCli::Llbc => LinkerFlavor::Llbc, LinkerFlavorCli::Ptx => LinkerFlavor::Ptx, // Below: legacy stable values @@ -258,6 +263,7 @@ impl LinkerFlavor { LinkerFlavor::Msvc(..) => LinkerFlavorCli::Msvc(Lld::No), LinkerFlavor::EmCc => LinkerFlavorCli::Em, LinkerFlavor::Bpf => LinkerFlavorCli::Bpf, + LinkerFlavor::Llbc => LinkerFlavorCli::Llbc, LinkerFlavor::Ptx => LinkerFlavorCli::Ptx, } } @@ -272,6 +278,7 @@ impl LinkerFlavor { LinkerFlavor::Msvc(lld) => LinkerFlavorCli::Msvc(lld), LinkerFlavor::EmCc => LinkerFlavorCli::EmCc, LinkerFlavor::Bpf => LinkerFlavorCli::Bpf, + LinkerFlavor::Llbc => LinkerFlavorCli::Llbc, LinkerFlavor::Ptx => LinkerFlavorCli::Ptx, } } @@ -286,6 +293,7 @@ impl LinkerFlavor { LinkerFlavorCli::Msvc(lld) => (Some(Cc::No), Some(lld)), LinkerFlavorCli::EmCc => (Some(Cc::Yes), Some(Lld::Yes)), LinkerFlavorCli::Bpf | LinkerFlavorCli::Ptx => (None, None), + LinkerFlavorCli::Llbc => (None, None), // Below: legacy stable values LinkerFlavorCli::Gcc => (Some(Cc::Yes), None), @@ -340,7 +348,7 @@ impl LinkerFlavor { LinkerFlavor::WasmLld(cc) => LinkerFlavor::WasmLld(cc_hint.unwrap_or(cc)), LinkerFlavor::Unix(cc) => LinkerFlavor::Unix(cc_hint.unwrap_or(cc)), LinkerFlavor::Msvc(lld) => LinkerFlavor::Msvc(lld_hint.unwrap_or(lld)), - LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => self, + LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Llbc | LinkerFlavor::Ptx => self, } } @@ -355,8 +363,8 @@ impl LinkerFlavor { pub fn check_compatibility(self, cli: LinkerFlavorCli) -> Option { let compatible = |cli| { // The CLI flavor should be compatible with the target if: - // 1. they are counterparts: they have the same principal flavor. match (self, cli) { + // 1. they are counterparts: they have the same principal flavor. (LinkerFlavor::Gnu(..), LinkerFlavorCli::Gnu(..)) | (LinkerFlavor::Darwin(..), LinkerFlavorCli::Darwin(..)) | (LinkerFlavor::WasmLld(..), LinkerFlavorCli::WasmLld(..)) @@ -364,11 +372,14 @@ impl LinkerFlavor { | (LinkerFlavor::Msvc(..), LinkerFlavorCli::Msvc(..)) | (LinkerFlavor::EmCc, LinkerFlavorCli::EmCc) | (LinkerFlavor::Bpf, LinkerFlavorCli::Bpf) + | (LinkerFlavor::Llbc, LinkerFlavorCli::Llbc) | (LinkerFlavor::Ptx, LinkerFlavorCli::Ptx) => return true, + // 2. The linker flavor is independent of target and compatible + (LinkerFlavor::Ptx, LinkerFlavorCli::Llbc) => return true, _ => {} } - // 2. or, the flavor is legacy and survives this roundtrip. + // 3. or, the flavor is legacy and survives this roundtrip. cli == self.with_cli_hints(cli).to_cli() }; (!compatible(cli)).then(|| { @@ -387,6 +398,7 @@ impl LinkerFlavor { | LinkerFlavor::Unix(..) | LinkerFlavor::EmCc | LinkerFlavor::Bpf + | LinkerFlavor::Llbc | LinkerFlavor::Ptx => LldFlavor::Ld, LinkerFlavor::Darwin(..) => LldFlavor::Ld64, LinkerFlavor::WasmLld(..) => LldFlavor::Wasm, @@ -412,6 +424,7 @@ impl LinkerFlavor { | LinkerFlavor::Msvc(_) | LinkerFlavor::Unix(_) | LinkerFlavor::Bpf + | LinkerFlavor::Llbc | LinkerFlavor::Ptx => false, } } @@ -431,6 +444,7 @@ impl LinkerFlavor { | LinkerFlavor::Msvc(_) | LinkerFlavor::Unix(_) | LinkerFlavor::Bpf + | LinkerFlavor::Llbc | LinkerFlavor::Ptx => false, } } @@ -480,6 +494,7 @@ linker_flavor_cli_impls! { (LinkerFlavorCli::Msvc(Lld::No)) "msvc" (LinkerFlavorCli::EmCc) "em-cc" (LinkerFlavorCli::Bpf) "bpf" + (LinkerFlavorCli::Llbc) "llbc" (LinkerFlavorCli::Ptx) "ptx" // Legacy stable flavors @@ -2205,6 +2220,7 @@ fn add_link_args_iter( | LinkerFlavor::Unix(..) | LinkerFlavor::EmCc | LinkerFlavor::Bpf + | LinkerFlavor::Llbc | LinkerFlavor::Ptx => {} } } diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 3fefd60f7cd89..3be18ef3127d5 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -56,7 +56,10 @@ impl Target { LinkerFlavor::Msvc(..) => { assert_matches!(flavor, LinkerFlavor::Msvc(..)) } - LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => { + LinkerFlavor::EmCc + | LinkerFlavor::Bpf + | LinkerFlavor::Ptx + | LinkerFlavor::Llbc => { assert_eq!(flavor, self.linker_flavor) } } From 6a50d059a594f35d70af30a33a4b395616663f68 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Tue, 6 Feb 2024 20:09:55 +0100 Subject: [PATCH 5/6] Bootstrap: Add argument for building llvm bitcode linker --- config.example.toml | 4 ++++ src/bootstrap/configure.py | 2 ++ src/bootstrap/defaults/config.compiler.toml | 2 ++ src/bootstrap/defaults/config.dist.toml | 2 ++ src/bootstrap/defaults/config.library.toml | 2 ++ src/bootstrap/defaults/config.tools.toml | 2 ++ src/bootstrap/src/core/build_steps/compile.rs | 10 ++++++++++ src/bootstrap/src/core/config/config.rs | 4 ++++ src/bootstrap/src/utils/change_tracker.rs | 5 +++++ src/ci/docker/host-x86_64/dist-various-2/Dockerfile | 2 +- 10 files changed, 34 insertions(+), 1 deletion(-) diff --git a/config.example.toml b/config.example.toml index 4fbdccba91104..ddcd0ec02e0dd 100644 --- a/config.example.toml +++ b/config.example.toml @@ -679,6 +679,10 @@ # sysroot. #llvm-tools = true +# Indicates whether the `self-contained` llvm-bitcode-linker, will be made available +# in the sysroot +#llvm-bitcode-linker = false + # Whether to deny warnings in crates #deny-warnings = true diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 4257c0f7991a6..9c43160d455f7 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -54,6 +54,7 @@ def v(*args): o("profiler", "build.profiler", "build the profiler runtime") o("full-tools", None, "enable all tools") o("lld", "rust.lld", "build lld") +o("llvm-bitcode-linker", "rust.llvm-bitcode-linker", "build llvm bitcode linker") o("clang", "llvm.clang", "build clang") o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++") o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard") @@ -366,6 +367,7 @@ def apply_args(known_args, option_checking, config): set('rust.codegen-backends', ['llvm'], config) set('rust.lld', True, config) set('rust.llvm-tools', True, config) + set('rust.llvm-bitcode-linker', True, config) set('build.extended', True, config) elif option.name in ['option-checking', 'verbose-configure']: # this was handled above diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index e276f12621188..d93c5fd25a150 100644 --- a/src/bootstrap/defaults/config.compiler.toml +++ b/src/bootstrap/defaults/config.compiler.toml @@ -17,6 +17,8 @@ lto = "off" # Forces frame pointers to be used with `-Cforce-frame-pointers`. # This can be helpful for profiling at a small performance cost. frame-pointers = true +# Build the llvm-bitcode-linker as it is required for running nvptx tests +llvm-bitcode-linker = true [llvm] # Having this set to true disrupts compiler development workflows for people who use `llvm.download-ci-llvm = true` diff --git a/src/bootstrap/defaults/config.dist.toml b/src/bootstrap/defaults/config.dist.toml index 44efdf50b965f..f3c6ffc9bd510 100644 --- a/src/bootstrap/defaults/config.dist.toml +++ b/src/bootstrap/defaults/config.dist.toml @@ -16,6 +16,8 @@ download-ci-llvm = false # Make sure they don't get set when installing from source. channel = "nightly" download-rustc = false +# Build the llvm-bitcode-linker as it is required for running nvptx tests +llvm-bitcode-linker = true [dist] # Use better compression when preparing tarballs. diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/config.library.toml index 087544397f5ea..4a1a49b727522 100644 --- a/src/bootstrap/defaults/config.library.toml +++ b/src/bootstrap/defaults/config.library.toml @@ -10,6 +10,8 @@ bench-stage = 0 incremental = true # Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. lto = "off" +# Build the llvm-bitcode-linker as it is required for running nvptx tests +llvm-bitcode-linker = true [llvm] # Will download LLVM from CI if available on your platform. diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml index 6e6c366002792..94c8b724cbf8c 100644 --- a/src/bootstrap/defaults/config.tools.toml +++ b/src/bootstrap/defaults/config.tools.toml @@ -12,6 +12,8 @@ incremental = true # Using these defaults will download the stage2 compiler (see `download-rustc` # setting) and the stage2 toolchain should therefore be used for these defaults. download-rustc = "if-unchanged" +# Build the llvm-bitcode-linker as it is required for running nvptx tests +llvm-bitcode-linker = true [build] # Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile. diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 6a2bff5970f36..242fe3c12b998 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1843,6 +1843,16 @@ impl Step for Assemble { } } + if builder.config.llvm_bitcode_linker_enabled { + let src_path = builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker { + compiler: build_compiler, + target: target_compiler.host, + extra_features: vec![], + }); + let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); + builder.copy(&src_path, &libdir_bin.join(&tool_exe)); + } + // Ensure that `libLLVM.so` ends up in the newly build compiler directory, // so that it can be found when the newly built `rustc` is run. dist::maybe_install_llvm_runtime(builder, target_compiler.host, &sysroot); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 683e0a4302f94..ae5169e938390 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -236,6 +236,7 @@ pub struct Config { pub lld_mode: LldMode, pub lld_enabled: bool, pub llvm_tools_enabled: bool, + pub llvm_bitcode_linker_enabled: bool, pub llvm_cflags: Option, pub llvm_cxxflags: Option, @@ -1099,6 +1100,7 @@ define_config! { dist_src: Option = "dist-src", save_toolstates: Option = "save-toolstates", codegen_backends: Option> = "codegen-backends", + llvm_bitcode_linker: Option = "llvm-bitcode-linker", lld: Option = "lld", lld_mode: Option = "use-lld", llvm_tools: Option = "llvm-tools", @@ -1571,6 +1573,7 @@ impl Config { codegen_backends, lld, llvm_tools, + llvm_bitcode_linker, deny_warnings, backtrace_on_ice, verify_llvm_ir, @@ -1650,6 +1653,7 @@ impl Config { } set(&mut config.lld_mode, lld_mode); set(&mut config.lld_enabled, lld); + set(&mut config.llvm_bitcode_linker_enabled, llvm_bitcode_linker); if matches!(config.lld_mode, LldMode::SelfContained) && !config.lld_enabled diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index a348fa3584147..85dfe45111fa9 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -146,4 +146,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "a new `target.*.runner` option is available to specify a wrapper executable required to run tests for a target", }, + ChangeInfo { + change_id: 117458, + severity: ChangeSeverity::Info, + summary: "New option `rust.llvm-bitcode-linker` that will build the llvm-bitcode-linker.", + }, ]; diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index e90141bb9a961..abd109a6ea3f9 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -135,7 +135,7 @@ ENV TARGETS=$TARGETS,x86_64-unknown-uefi # Luckily one of the folders is /usr/local/include so symlink /usr/include/x86_64-linux-gnu/asm there RUN ln -s /usr/include/x86_64-linux-gnu/asm /usr/local/include/asm -ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --disable-docs \ +ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-llvm-bitcode-linker --disable-docs \ --set target.wasm32-wasi.wasi-root=/wasm32-wasip1 \ --set target.wasm32-wasip1.wasi-root=/wasm32-wasip1 \ --set target.wasm32-wasi-preview1-threads.wasi-root=/wasm32-wasi-preview1-threads \ From 843dd28d21d410d540515cf44172ad74ab5976c9 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Mon, 4 Mar 2024 19:22:05 +0100 Subject: [PATCH 6/6] NVPTX: Enable previously disabled tests --- tests/assembly/nvptx-arch-default.rs | 3 +-- tests/assembly/nvptx-arch-emit-asm.rs | 1 - tests/assembly/nvptx-arch-target-cpu.rs | 3 +-- .../nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs | 13 ++++++------- tests/assembly/nvptx-safe-naming.rs | 5 ++--- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/tests/assembly/nvptx-arch-default.rs b/tests/assembly/nvptx-arch-default.rs index bac09574f17c4..a621fd6dcb299 100644 --- a/tests/assembly/nvptx-arch-default.rs +++ b/tests/assembly/nvptx-arch-default.rs @@ -1,7 +1,6 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib +//@ compile-flags: --crate-type cdylib -Z unstable-options -Clinker-flavor=llbc //@ only-nvptx64 -//@ ignore-nvptx64 #![no_std] diff --git a/tests/assembly/nvptx-arch-emit-asm.rs b/tests/assembly/nvptx-arch-emit-asm.rs index d24035cc83146..e47f8e78e3679 100644 --- a/tests/assembly/nvptx-arch-emit-asm.rs +++ b/tests/assembly/nvptx-arch-emit-asm.rs @@ -1,7 +1,6 @@ //@ assembly-output: emit-asm //@ compile-flags: --crate-type rlib //@ only-nvptx64 -//@ ignore-nvptx64 #![no_std] diff --git a/tests/assembly/nvptx-arch-target-cpu.rs b/tests/assembly/nvptx-arch-target-cpu.rs index 212af20f4dea8..609ab297e636e 100644 --- a/tests/assembly/nvptx-arch-target-cpu.rs +++ b/tests/assembly/nvptx-arch-target-cpu.rs @@ -1,7 +1,6 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -C target-cpu=sm_50 +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_50 -Z unstable-options -Clinker-flavor=llbc //@ only-nvptx64 -//@ ignore-nvptx64 #![no_std] diff --git a/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs b/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs index a42d5dd3569df..ce1d732f36771 100644 --- a/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs +++ b/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs @@ -1,7 +1,6 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 -Z unstable-options -Clinker-flavor=llbc //@ only-nvptx64 -//@ ignore-nvptx64 // The following ABI tests are made with nvcc 11.6 does. // @@ -226,10 +225,11 @@ pub unsafe extern "ptx-kernel" fn f_byte_array_arg(_a: [u8; 5]) {} #[no_mangle] pub unsafe extern "ptx-kernel" fn f_float_array_arg(_a: [f32; 5]) {} -// CHECK: .visible .entry f_u128_array_arg( -// CHECK: .param .align 16 .b8 f_u128_array_arg_param_0[80] -#[no_mangle] -pub unsafe extern "ptx-kernel" fn f_u128_array_arg(_a: [u128; 5]) {} +// FIXME: u128 started to break compilation with disabled CI +// NO_CHECK: .visible .entry f_u128_array_arg( +// NO_CHECK: .param .align 16 .b8 f_u128_array_arg_param_0[80] +//#[no_mangle] +//pub unsafe extern "ptx-kernel" fn f_u128_array_arg(_a: [u128; 5]) {} // CHECK: .visible .entry f_u32_slice_arg( // CHECK: .param .u64 f_u32_slice_arg_param_0 @@ -247,7 +247,6 @@ pub unsafe extern "ptx-kernel" fn f_tuple_u8_u8_arg(_a: (u8, u8)) {} #[no_mangle] pub unsafe extern "ptx-kernel" fn f_tuple_u32_u32_arg(_a: (u32, u32)) {} - // CHECK: .visible .entry f_tuple_u8_u8_u32_arg( // CHECK: .param .align 4 .b8 f_tuple_u8_u8_u32_arg_param_0[8] #[no_mangle] diff --git a/tests/assembly/nvptx-safe-naming.rs b/tests/assembly/nvptx-safe-naming.rs index 59fd527be3c7e..d7b46aadd9cc1 100644 --- a/tests/assembly/nvptx-safe-naming.rs +++ b/tests/assembly/nvptx-safe-naming.rs @@ -1,7 +1,6 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib +//@ compile-flags: --crate-type cdylib -Z unstable-options -Clinker-flavor=llbc //@ only-nvptx64 -//@ ignore-nvptx64 #![feature(abi_ptx)] #![no_std] @@ -10,7 +9,7 @@ extern crate breakpoint_panic_handler; // Verify function name doesn't contain unacceaptable characters. -// CHECK: .func (.param .b32 func_retval0) [[IMPL_FN:[a-zA-Z0-9$_]+square[a-zA-Z0-9$_]+]]( +// CHECK: .func (.param .b32 func_retval0) [[IMPL_FN:[a-zA-Z0-9$_]+square[a-zA-Z0-9$_]+]] // CHECK-LABEL: .visible .entry top_kernel( #[no_mangle]