Skip to content

Commit 2ddf984

Browse files
authored
Rollup merge of #123612 - kxxt:riscv-target-abi, r=jieyouxu,nikic,DianQK
Set target-abi module flag for RISC-V targets Fixes cross-language LTO on RISC-V targets (Fixes #121924)
2 parents 4bc891a + 33db209 commit 2ddf984

File tree

11 files changed

+177
-21
lines changed

11 files changed

+177
-21
lines changed

compiler/rustc_codegen_llvm/src/back/lto.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ pub(crate) fn run_pass_manager(
608608
"LTOPostLink".as_ptr().cast(),
609609
11,
610610
) {
611-
llvm::LLVMRustAddModuleFlag(
611+
llvm::LLVMRustAddModuleFlagU32(
612612
module.module_llvm.llmod(),
613613
llvm::LLVMModFlagBehavior::Error,
614614
c"LTOPostLink".as_ptr().cast(),

compiler/rustc_codegen_llvm/src/context.rs

+31-15
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,13 @@ pub unsafe fn create_module<'ll>(
180180
// to ensure intrinsic calls don't use it.
181181
if !sess.needs_plt() {
182182
let avoid_plt = c"RtLibUseGOT".as_ptr().cast();
183-
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
183+
llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
184184
}
185185

186186
// Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.)
187187
if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() {
188188
let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr().cast();
189-
llvm::LLVMRustAddModuleFlag(
189+
llvm::LLVMRustAddModuleFlagU32(
190190
llmod,
191191
llvm::LLVMModFlagBehavior::Override,
192192
canonical_jump_tables,
@@ -197,7 +197,7 @@ pub unsafe fn create_module<'ll>(
197197
// Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.)
198198
if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() {
199199
let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr().cast();
200-
llvm::LLVMRustAddModuleFlag(
200+
llvm::LLVMRustAddModuleFlagU32(
201201
llmod,
202202
llvm::LLVMModFlagBehavior::Override,
203203
enable_split_lto_unit,
@@ -208,7 +208,7 @@ pub unsafe fn create_module<'ll>(
208208
// Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
209209
if sess.is_sanitizer_kcfi_enabled() {
210210
let kcfi = c"kcfi".as_ptr().cast();
211-
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
211+
llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
212212
}
213213

214214
// Control Flow Guard is currently only supported by the MSVC linker on Windows.
@@ -217,7 +217,7 @@ pub unsafe fn create_module<'ll>(
217217
CFGuard::Disabled => {}
218218
CFGuard::NoChecks => {
219219
// Set `cfguard=1` module flag to emit metadata only.
220-
llvm::LLVMRustAddModuleFlag(
220+
llvm::LLVMRustAddModuleFlagU32(
221221
llmod,
222222
llvm::LLVMModFlagBehavior::Warning,
223223
c"cfguard".as_ptr() as *const _,
@@ -226,7 +226,7 @@ pub unsafe fn create_module<'ll>(
226226
}
227227
CFGuard::Checks => {
228228
// Set `cfguard=2` module flag to emit metadata and checks.
229-
llvm::LLVMRustAddModuleFlag(
229+
llvm::LLVMRustAddModuleFlagU32(
230230
llmod,
231231
llvm::LLVMModFlagBehavior::Warning,
232232
c"cfguard".as_ptr() as *const _,
@@ -238,26 +238,26 @@ pub unsafe fn create_module<'ll>(
238238

239239
if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
240240
if sess.target.arch == "aarch64" {
241-
llvm::LLVMRustAddModuleFlag(
241+
llvm::LLVMRustAddModuleFlagU32(
242242
llmod,
243243
llvm::LLVMModFlagBehavior::Min,
244244
c"branch-target-enforcement".as_ptr().cast(),
245245
bti.into(),
246246
);
247-
llvm::LLVMRustAddModuleFlag(
247+
llvm::LLVMRustAddModuleFlagU32(
248248
llmod,
249249
llvm::LLVMModFlagBehavior::Min,
250250
c"sign-return-address".as_ptr().cast(),
251251
pac_ret.is_some().into(),
252252
);
253253
let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
254-
llvm::LLVMRustAddModuleFlag(
254+
llvm::LLVMRustAddModuleFlagU32(
255255
llmod,
256256
llvm::LLVMModFlagBehavior::Min,
257257
c"sign-return-address-all".as_ptr().cast(),
258258
pac_opts.leaf.into(),
259259
);
260-
llvm::LLVMRustAddModuleFlag(
260+
llvm::LLVMRustAddModuleFlagU32(
261261
llmod,
262262
llvm::LLVMModFlagBehavior::Min,
263263
c"sign-return-address-with-bkey".as_ptr().cast(),
@@ -273,15 +273,15 @@ pub unsafe fn create_module<'ll>(
273273

274274
// Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang).
275275
if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
276-
llvm::LLVMRustAddModuleFlag(
276+
llvm::LLVMRustAddModuleFlagU32(
277277
llmod,
278278
llvm::LLVMModFlagBehavior::Override,
279279
c"cf-protection-branch".as_ptr().cast(),
280280
1,
281281
)
282282
}
283283
if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection {
284-
llvm::LLVMRustAddModuleFlag(
284+
llvm::LLVMRustAddModuleFlagU32(
285285
llmod,
286286
llvm::LLVMModFlagBehavior::Override,
287287
c"cf-protection-return".as_ptr().cast(),
@@ -290,7 +290,7 @@ pub unsafe fn create_module<'ll>(
290290
}
291291

292292
if sess.opts.unstable_opts.virtual_function_elimination {
293-
llvm::LLVMRustAddModuleFlag(
293+
llvm::LLVMRustAddModuleFlagU32(
294294
llmod,
295295
llvm::LLVMModFlagBehavior::Error,
296296
c"Virtual Function Elim".as_ptr().cast(),
@@ -300,7 +300,7 @@ pub unsafe fn create_module<'ll>(
300300

301301
// Set module flag to enable Windows EHCont Guard (/guard:ehcont).
302302
if sess.opts.unstable_opts.ehcont_guard {
303-
llvm::LLVMRustAddModuleFlag(
303+
llvm::LLVMRustAddModuleFlagU32(
304304
llmod,
305305
llvm::LLVMModFlagBehavior::Warning,
306306
c"ehcontguard".as_ptr() as *const _,
@@ -326,6 +326,22 @@ pub unsafe fn create_module<'ll>(
326326
llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1),
327327
);
328328

329+
// Emit RISC-V specific target-abi metadata
330+
// to workaround lld as the LTO plugin not
331+
// correctly setting target-abi for the LTO object
332+
// FIXME: https://github.com/llvm/llvm-project/issues/50591
333+
// If llvm_abiname is empty, emit nothing.
334+
let llvm_abiname = &sess.target.options.llvm_abiname;
335+
if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() {
336+
llvm::LLVMRustAddModuleFlagString(
337+
llmod,
338+
llvm::LLVMModFlagBehavior::Error,
339+
c"target-abi".as_ptr(),
340+
llvm_abiname.as_ptr().cast(),
341+
llvm_abiname.len(),
342+
);
343+
}
344+
329345
// Add module flags specified via -Z llvm_module_flag
330346
for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag {
331347
let key = format!("{key}\0");
@@ -341,7 +357,7 @@ pub unsafe fn create_module<'ll>(
341357
// We already checked this during option parsing
342358
_ => unreachable!(),
343359
};
344-
llvm::LLVMRustAddModuleFlag(llmod, behavior, key.as_ptr().cast(), *value)
360+
llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value)
345361
}
346362

347363
llmod

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
110110
.unstable_opts
111111
.dwarf_version
112112
.unwrap_or(sess.target.default_dwarf_version);
113-
llvm::LLVMRustAddModuleFlag(
113+
llvm::LLVMRustAddModuleFlagU32(
114114
self.llmod,
115115
llvm::LLVMModFlagBehavior::Warning,
116116
c"Dwarf Version".as_ptr().cast(),
117117
dwarf_version,
118118
);
119119
} else {
120120
// Indicate that we want CodeView debug information on MSVC
121-
llvm::LLVMRustAddModuleFlag(
121+
llvm::LLVMRustAddModuleFlagU32(
122122
self.llmod,
123123
llvm::LLVMModFlagBehavior::Warning,
124124
c"CodeView".as_ptr().cast(),
@@ -127,7 +127,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
127127
}
128128

129129
// Prevent bitcode readers from deleting the debug info.
130-
llvm::LLVMRustAddModuleFlag(
130+
llvm::LLVMRustAddModuleFlagU32(
131131
self.llmod,
132132
llvm::LLVMModFlagBehavior::Warning,
133133
c"Debug Info Version".as_ptr().cast(),

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1801,12 +1801,21 @@ extern "C" {
18011801
///
18021802
/// In order for Rust-C LTO to work, module flags must be compatible with Clang. What
18031803
/// "compatible" means depends on the merge behaviors involved.
1804-
pub fn LLVMRustAddModuleFlag(
1804+
pub fn LLVMRustAddModuleFlagU32(
18051805
M: &Module,
18061806
merge_behavior: LLVMModFlagBehavior,
18071807
name: *const c_char,
18081808
value: u32,
18091809
);
1810+
1811+
pub fn LLVMRustAddModuleFlagString(
1812+
M: &Module,
1813+
merge_behavior: LLVMModFlagBehavior,
1814+
name: *const c_char,
1815+
value: *const c_char,
1816+
value_len: size_t,
1817+
);
1818+
18101819
pub fn LLVMRustHasModuleFlag(M: &Module, name: *const c_char, len: size_t) -> bool;
18111820

18121821
pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>;

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -817,14 +817,24 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }
817817

818818
extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
819819

820-
extern "C" void LLVMRustAddModuleFlag(
820+
extern "C" void LLVMRustAddModuleFlagU32(
821821
LLVMModuleRef M,
822822
Module::ModFlagBehavior MergeBehavior,
823823
const char *Name,
824824
uint32_t Value) {
825825
unwrap(M)->addModuleFlag(MergeBehavior, Name, Value);
826826
}
827827

828+
extern "C" void LLVMRustAddModuleFlagString(
829+
LLVMModuleRef M,
830+
Module::ModFlagBehavior MergeBehavior,
831+
const char *Name,
832+
const char *Value,
833+
size_t ValueLen) {
834+
unwrap(M)->addModuleFlag(MergeBehavior, Name,
835+
MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen)));
836+
}
837+
828838
extern "C" bool LLVMRustHasModuleFlag(LLVMModuleRef M, const char *Name,
829839
size_t Len) {
830840
return unwrap(M)->getModuleFlag(StringRef(Name, Len)) != nullptr;

src/tools/compiletest/src/header.rs

+1
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
819819
"needs-dynamic-linking",
820820
"needs-git-hash",
821821
"needs-llvm-components",
822+
"needs-matching-clang",
822823
"needs-profiler-support",
823824
"needs-relocation-model-pic",
824825
"needs-run-enabled",

src/tools/run-make-support/src/rustc.rs

+12
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ impl Rustc {
8686
self
8787
}
8888

89+
/// This flag defers LTO optimizations to the linker.
90+
pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self {
91+
self.cmd.arg(format!("-Clinker-plugin-lto={option}"));
92+
self
93+
}
94+
95+
/// Specify what happens when the code panics.
96+
pub fn panic(&mut self, option: &str) -> &mut Self {
97+
self.cmd.arg(format!("-Cpanic={option}"));
98+
self
99+
}
100+
89101
/// Specify number of codegen units
90102
pub fn codegen_units(&mut self, units: usize) -> &mut Self {
91103
self.cmd.arg(format!("-Ccodegen-units={units}"));

tests/codegen/riscv-target-abi.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ revisions:riscv64gc riscv32gc riscv32imac
2+
3+
//@[riscv64gc] compile-flags: --target=riscv64gc-unknown-linux-gnu
4+
//@[riscv64gc] needs-llvm-components: riscv
5+
// riscv64gc: !{i32 1, !"target-abi", !"lp64d"}
6+
7+
//@[riscv32gc] compile-flags: --target=riscv32gc-unknown-linux-musl
8+
//@[riscv32gc] needs-llvm-components: riscv
9+
// riscv32gc: !{i32 1, !"target-abi", !"ilp32d"}
10+
11+
//@[riscv32imac] compile-flags: --target=riscv32imac-unknown-none-elf
12+
//@[riscv32imac] needs-llvm-components: riscv
13+
// riscv32imac-NOT: !"target-abi"
14+
15+
#![feature(no_core, lang_items)]
16+
#![crate_type = "lib"]
17+
#![no_core]
18+
19+
#[lang = "sized"]
20+
trait Sized {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extern void hello();
2+
3+
void _start() {
4+
hello();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![allow(internal_features)]
2+
#![feature(no_core, lang_items)]
3+
#![no_core]
4+
5+
#[lang = "sized"]
6+
trait Sized {}
7+
8+
#[no_mangle]
9+
pub fn hello() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! Make sure that cross-language LTO works on riscv targets,
2+
//! which requires extra abi metadata to be emitted.
3+
//@ needs-matching-clang
4+
//@ needs-llvm-components riscv
5+
extern crate run_make_support;
6+
7+
use run_make_support::{bin_name, rustc, tmp_dir};
8+
use std::{
9+
env,
10+
path::PathBuf,
11+
process::{Command, Output},
12+
str,
13+
};
14+
15+
fn handle_failed_output(output: Output) {
16+
eprintln!("output status: `{}`", output.status);
17+
eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap());
18+
eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap());
19+
std::process::exit(1)
20+
}
21+
22+
fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float: bool) {
23+
eprintln!("Checking target {target}");
24+
// Rust part
25+
rustc()
26+
.input("riscv-xlto.rs")
27+
.crate_type("rlib")
28+
.target(target)
29+
.panic("abort")
30+
.linker_plugin_lto("on")
31+
.run();
32+
// C part
33+
let clang = env::var("CLANG").unwrap();
34+
let mut cmd = Command::new(clang);
35+
let executable = tmp_dir().join("riscv-xlto");
36+
cmd.arg("-target")
37+
.arg(clang_target)
38+
.arg(format!("-march={carch}"))
39+
.arg(format!("-flto=thin"))
40+
.arg(format!("-fuse-ld=lld"))
41+
.arg("-nostdlib")
42+
.arg("-o")
43+
.arg(&executable)
44+
.arg("cstart.c")
45+
.arg(tmp_dir().join("libriscv_xlto.rlib"));
46+
eprintln!("{cmd:?}");
47+
let output = cmd.output().unwrap();
48+
if !output.status.success() {
49+
handle_failed_output(output);
50+
}
51+
// Check that the built binary has correct float abi
52+
let llvm_readobj =
53+
PathBuf::from(env::var("LLVM_BIN_DIR").unwrap()).join(bin_name("llvm-readobj"));
54+
let mut cmd = Command::new(llvm_readobj);
55+
cmd.arg("--file-header").arg(executable);
56+
eprintln!("{cmd:?}");
57+
let output = cmd.output().unwrap();
58+
if output.status.success() {
59+
assert!(
60+
!(is_double_float
61+
^ dbg!(str::from_utf8(&output.stdout).unwrap())
62+
.contains("EF_RISCV_FLOAT_ABI_DOUBLE"))
63+
)
64+
} else {
65+
handle_failed_output(output);
66+
}
67+
}
68+
69+
fn main() {
70+
check_target("riscv64gc-unknown-linux-gnu", "riscv64-linux-gnu", "rv64gc", true);
71+
check_target("riscv64imac-unknown-none-elf", "riscv64-unknown-elf", "rv64imac", false);
72+
check_target("riscv32imac-unknown-none-elf", "riscv32-unknown-elf", "rv32imac", false);
73+
check_target("riscv32gc-unknown-linux-gnu", "riscv32-linux-gnu", "rv32gc", true);
74+
}

0 commit comments

Comments
 (0)