diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 06a681c24e6..e61af863dc0 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -608,7 +608,7 @@ pub(crate) fn run_pass_manager( "LTOPostLink".as_ptr().cast(), 11, ) { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( module.module_llvm.llmod(), llvm::LLVMModFlagBehavior::Error, c"LTOPostLink".as_ptr().cast(), diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index df9f066e58a..d32baa6dc02 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -180,13 +180,13 @@ pub unsafe fn create_module<'ll>( // to ensure intrinsic calls don't use it. if !sess.needs_plt() { let avoid_plt = c"RtLibUseGOT".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); } // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.) if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() { let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, canonical_jump_tables, @@ -197,7 +197,7 @@ pub unsafe fn create_module<'ll>( // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.) if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, enable_split_lto_unit, @@ -208,7 +208,7 @@ pub unsafe fn create_module<'ll>( // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.) if sess.is_sanitizer_kcfi_enabled() { let kcfi = c"kcfi".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); } // Control Flow Guard is currently only supported by the MSVC linker on Windows. @@ -217,7 +217,7 @@ pub unsafe fn create_module<'ll>( CFGuard::Disabled => {} CFGuard::NoChecks => { // Set `cfguard=1` module flag to emit metadata only. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"cfguard".as_ptr() as *const _, @@ -226,7 +226,7 @@ pub unsafe fn create_module<'ll>( } CFGuard::Checks => { // Set `cfguard=2` module flag to emit metadata and checks. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"cfguard".as_ptr() as *const _, @@ -238,26 +238,26 @@ pub unsafe fn create_module<'ll>( if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { if sess.target.arch == "aarch64" { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"branch-target-enforcement".as_ptr().cast(), bti.into(), ); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address".as_ptr().cast(), pac_ret.is_some().into(), ); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address-all".as_ptr().cast(), pac_opts.leaf.into(), ); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address-with-bkey".as_ptr().cast(), @@ -273,7 +273,7 @@ pub unsafe fn create_module<'ll>( // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, c"cf-protection-branch".as_ptr().cast(), @@ -281,7 +281,7 @@ pub unsafe fn create_module<'ll>( ) } if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, c"cf-protection-return".as_ptr().cast(), @@ -290,7 +290,7 @@ pub unsafe fn create_module<'ll>( } if sess.opts.unstable_opts.virtual_function_elimination { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Error, c"Virtual Function Elim".as_ptr().cast(), @@ -300,7 +300,7 @@ pub unsafe fn create_module<'ll>( // Set module flag to enable Windows EHCont Guard (/guard:ehcont). if sess.opts.unstable_opts.ehcont_guard { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"ehcontguard".as_ptr() as *const _, @@ -326,6 +326,22 @@ pub unsafe fn create_module<'ll>( llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), ); + // Emit RISC-V specific target-abi metadata + // to workaround lld as the LTO plugin not + // correctly setting target-abi for the LTO object + // FIXME: https://github.com/llvm/llvm-project/issues/50591 + // If llvm_abiname is empty, emit nothing. + let llvm_abiname = &sess.target.options.llvm_abiname; + if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() { + llvm::LLVMRustAddModuleFlagString( + llmod, + llvm::LLVMModFlagBehavior::Error, + c"target-abi".as_ptr(), + llvm_abiname.as_ptr().cast(), + llvm_abiname.len(), + ); + } + // Add module flags specified via -Z llvm_module_flag for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag { let key = format!("{key}\0"); @@ -341,7 +357,7 @@ pub unsafe fn create_module<'ll>( // We already checked this during option parsing _ => unreachable!(), }; - llvm::LLVMRustAddModuleFlag(llmod, behavior, key.as_ptr().cast(), *value) + llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value) } llmod diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index d3a851b40c0..4fdaa59e0e5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -110,7 +110,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { .unstable_opts .dwarf_version .unwrap_or(sess.target.default_dwarf_version); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"Dwarf Version".as_ptr().cast(), @@ -118,7 +118,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { ); } else { // Indicate that we want CodeView debug information on MSVC - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"CodeView".as_ptr().cast(), @@ -127,7 +127,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } // Prevent bitcode readers from deleting the debug info. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"Debug Info Version".as_ptr().cast(), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 284bc74d5c4..5509baaa3e9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1801,12 +1801,21 @@ extern "C" { /// /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What /// "compatible" means depends on the merge behaviors involved. - pub fn LLVMRustAddModuleFlag( + pub fn LLVMRustAddModuleFlagU32( M: &Module, merge_behavior: LLVMModFlagBehavior, name: *const c_char, value: u32, ); + + pub fn LLVMRustAddModuleFlagString( + M: &Module, + merge_behavior: LLVMModFlagBehavior, + name: *const c_char, + value: *const c_char, + value_len: size_t, + ); + pub fn LLVMRustHasModuleFlag(M: &Module, name: *const c_char, len: size_t) -> bool; pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a6894a7e089..37c2da4c23a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -817,7 +817,7 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } -extern "C" void LLVMRustAddModuleFlag( +extern "C" void LLVMRustAddModuleFlagU32( LLVMModuleRef M, Module::ModFlagBehavior MergeBehavior, const char *Name, @@ -825,6 +825,16 @@ extern "C" void LLVMRustAddModuleFlag( unwrap(M)->addModuleFlag(MergeBehavior, Name, Value); } +extern "C" void LLVMRustAddModuleFlagString( + LLVMModuleRef M, + Module::ModFlagBehavior MergeBehavior, + const char *Name, + const char *Value, + size_t ValueLen) { + unwrap(M)->addModuleFlag(MergeBehavior, Name, + MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen))); +} + extern "C" bool LLVMRustHasModuleFlag(LLVMModuleRef M, const char *Name, size_t Len) { return unwrap(M)->getModuleFlag(StringRef(Name, Len)) != nullptr; diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 626841cb1bd..ec944cb7fb4 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -819,6 +819,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-dynamic-linking", "needs-git-hash", "needs-llvm-components", + "needs-matching-clang", "needs-profiler-support", "needs-relocation-model-pic", "needs-run-enabled", diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index f3844477ac3..5a5b008a7cb 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -86,6 +86,18 @@ impl Rustc { self } + /// This flag defers LTO optimizations to the linker. + pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self { + self.cmd.arg(format!("-Clinker-plugin-lto={option}")); + self + } + + /// Specify what happens when the code panics. + pub fn panic(&mut self, option: &str) -> &mut Self { + self.cmd.arg(format!("-Cpanic={option}")); + self + } + /// Specify number of codegen units pub fn codegen_units(&mut self, units: usize) -> &mut Self { self.cmd.arg(format!("-Ccodegen-units={units}")); diff --git a/tests/codegen/riscv-target-abi.rs b/tests/codegen/riscv-target-abi.rs new file mode 100644 index 00000000000..5d545af9c76 --- /dev/null +++ b/tests/codegen/riscv-target-abi.rs @@ -0,0 +1,20 @@ +//@ revisions:riscv64gc riscv32gc riscv32imac + +//@[riscv64gc] compile-flags: --target=riscv64gc-unknown-linux-gnu +//@[riscv64gc] needs-llvm-components: riscv +// riscv64gc: !{i32 1, !"target-abi", !"lp64d"} + +//@[riscv32gc] compile-flags: --target=riscv32gc-unknown-linux-musl +//@[riscv32gc] needs-llvm-components: riscv +// riscv32gc: !{i32 1, !"target-abi", !"ilp32d"} + +//@[riscv32imac] compile-flags: --target=riscv32imac-unknown-none-elf +//@[riscv32imac] needs-llvm-components: riscv +// riscv32imac-NOT: !"target-abi" + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +#[lang = "sized"] +trait Sized {} diff --git a/tests/run-make/cross-lang-lto-riscv-abi/cstart.c b/tests/run-make/cross-lang-lto-riscv-abi/cstart.c new file mode 100644 index 00000000000..660469b75a8 --- /dev/null +++ b/tests/run-make/cross-lang-lto-riscv-abi/cstart.c @@ -0,0 +1,5 @@ +extern void hello(); + +void _start() { + hello(); +} diff --git a/tests/run-make/cross-lang-lto-riscv-abi/riscv-xlto.rs b/tests/run-make/cross-lang-lto-riscv-abi/riscv-xlto.rs new file mode 100644 index 00000000000..c31cf27f9ae --- /dev/null +++ b/tests/run-make/cross-lang-lto-riscv-abi/riscv-xlto.rs @@ -0,0 +1,9 @@ +#![allow(internal_features)] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[no_mangle] +pub fn hello() {} diff --git a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs new file mode 100644 index 00000000000..2f13cf17169 --- /dev/null +++ b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs @@ -0,0 +1,74 @@ +//! Make sure that cross-language LTO works on riscv targets, +//! which requires extra abi metadata to be emitted. +//@ needs-matching-clang +//@ needs-llvm-components riscv +extern crate run_make_support; + +use run_make_support::{bin_name, rustc, tmp_dir}; +use std::{ + env, + path::PathBuf, + process::{Command, Output}, + str, +}; + +fn handle_failed_output(output: Output) { + eprintln!("output status: `{}`", output.status); + eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap()); + eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap()); + std::process::exit(1) +} + +fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float: bool) { + eprintln!("Checking target {target}"); + // Rust part + rustc() + .input("riscv-xlto.rs") + .crate_type("rlib") + .target(target) + .panic("abort") + .linker_plugin_lto("on") + .run(); + // C part + let clang = env::var("CLANG").unwrap(); + let mut cmd = Command::new(clang); + let executable = tmp_dir().join("riscv-xlto"); + cmd.arg("-target") + .arg(clang_target) + .arg(format!("-march={carch}")) + .arg(format!("-flto=thin")) + .arg(format!("-fuse-ld=lld")) + .arg("-nostdlib") + .arg("-o") + .arg(&executable) + .arg("cstart.c") + .arg(tmp_dir().join("libriscv_xlto.rlib")); + eprintln!("{cmd:?}"); + let output = cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(output); + } + // Check that the built binary has correct float abi + let llvm_readobj = + PathBuf::from(env::var("LLVM_BIN_DIR").unwrap()).join(bin_name("llvm-readobj")); + let mut cmd = Command::new(llvm_readobj); + cmd.arg("--file-header").arg(executable); + eprintln!("{cmd:?}"); + let output = cmd.output().unwrap(); + if output.status.success() { + assert!( + !(is_double_float + ^ dbg!(str::from_utf8(&output.stdout).unwrap()) + .contains("EF_RISCV_FLOAT_ABI_DOUBLE")) + ) + } else { + handle_failed_output(output); + } +} + +fn main() { + check_target("riscv64gc-unknown-linux-gnu", "riscv64-linux-gnu", "rv64gc", true); + check_target("riscv64imac-unknown-none-elf", "riscv64-unknown-elf", "rv64imac", false); + check_target("riscv32imac-unknown-none-elf", "riscv32-unknown-elf", "rv32imac", false); + check_target("riscv32gc-unknown-linux-gnu", "riscv32-linux-gnu", "rv32gc", true); +}