Skip to content
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

Set target-abi module flag for RISC-V targets #123612

Merged
merged 3 commits into from
Apr 10, 2024
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
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
46 changes: 31 additions & 15 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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 _,
Expand All @@ -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 _,
Expand All @@ -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(),
Expand All @@ -273,15 +273,15 @@ 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(),
1,
)
}
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(),
Expand All @@ -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(),
Expand All @@ -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 _,
Expand All @@ -326,6 +326,22 @@ pub unsafe fn create_module<'ll>(
llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1),
);

// Emit RISC-V specific target-abi metadata
kxxt marked this conversation as resolved.
Show resolved Hide resolved
// 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");
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,15 @@ 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(),
dwarf_version,
);
} else {
// Indicate that we want CodeView debug information on MSVC
llvm::LLVMRustAddModuleFlag(
llvm::LLVMRustAddModuleFlagU32(
self.llmod,
llvm::LLVMModFlagBehavior::Warning,
c"CodeView".as_ptr().cast(),
Expand All @@ -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(),
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can keep its name so that the change can be smaller; consider only riscv needs to set a string flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it doesn't hurt because the current naming scheme is more consistent(method name with a type suffix. And we don't have to care about patch size.

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<'_>;
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -817,14 +817,24 @@ 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,
uint32_t Value) {
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;
Expand Down
1 change: 1 addition & 0 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 12 additions & 0 deletions src/tools/run-make-support/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}"));
Expand Down
20 changes: 20 additions & 0 deletions tests/codegen/riscv-target-abi.rs
Original file line number Diff line number Diff line change
@@ -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 {}
5 changes: 5 additions & 0 deletions tests/run-make/cross-lang-lto-riscv-abi/cstart.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extern void hello();

void _start() {
hello();
}
9 changes: 9 additions & 0 deletions tests/run-make/cross-lang-lto-riscv-abi/riscv-xlto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![allow(internal_features)]
#![feature(no_core, lang_items)]
#![no_core]

#[lang = "sized"]
trait Sized {}

#[no_mangle]
pub fn hello() {}
74 changes: 74 additions & 0 deletions tests/run-make/cross-lang-lto-riscv-abi/rmake.rs
Original file line number Diff line number Diff line change
@@ -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();
jieyouxu marked this conversation as resolved.
Show resolved Hide resolved
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"));
jieyouxu marked this conversation as resolved.
Show resolved Hide resolved
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);
}
Loading