Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit d7f9d77

Browse files
committed
Auto merge of rust-lang#136840 - Flakebi:linker-plugin-lto-fat, r=<try>
Fix linker-plugin-lto only doing thin lto When rust provides LLVM bitcode files to lld and the bitcode contains function summaries as used for thin lto, lld defaults to using thin lto. This prevents some optimizations that are only applied for fat lto. Set the `ThinLTO=0` module flag to signal lld to do fat lto. The analogous code in clang that sets this flag is here: https://github.com/llvm/llvm-project/blob/560149b5e3c891c64899e9912e29467a69dc3a4c/clang/lib/CodeGen/BackendUtil.cpp#L1150 The code in LLVM that queries the flag and defaults to thin lto if not set is here: https://github.com/llvm/llvm-project/blob/e258bca9505f35e0a22cb213a305eea9b76d11ea/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp#L4441-L4446 r? `@workingjubilee,` as you’ve been reviewing most other amdgpu patches, not sure if there should be other reviewers for lto.
2 parents 75530e9 + b692fa0 commit d7f9d77

File tree

9 files changed

+133
-2
lines changed

9 files changed

+133
-2
lines changed

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ use rustc_middle::ty::layout::{
2121
};
2222
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
2323
use rustc_middle::{bug, span_bug};
24-
use rustc_session::Session;
2524
use rustc_session::config::{
2625
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet,
2726
};
27+
use rustc_session::{Session, config};
2828
use rustc_span::source_map::Spanned;
2929
use rustc_span::{DUMMY_SP, Span};
3030
use rustc_symbol_mangling::mangle_internal_symbol;
@@ -311,6 +311,11 @@ pub(crate) unsafe fn create_module<'ll>(
311311
);
312312
}
313313

314+
// If fat lto is requested, lld still defaults to thin lto. Set ThinLTO=0 to force fat lto in lld.
315+
if sess.lto() == config::Lto::Fat {
316+
llvm::add_module_flag_u32(llmod, llvm::ModuleFlagMergeBehavior::Override, "ThinLTO", 0);
317+
}
318+
314319
// Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
315320
if sess.is_sanitizer_kcfi_enabled() {
316321
llvm::add_module_flag_u32(llmod, llvm::ModuleFlagMergeBehavior::Override, "kcfi", 1);

src/tools/run-make-support/src/external_deps/llvm.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ pub fn llvm_pdbutil() -> LlvmPdbutil {
6060
LlvmPdbutil::new()
6161
}
6262

63+
/// Construct a new `llvm-as` invocation. This assumes that `llvm-as` is available
64+
/// at `$LLVM_BIN_DIR/llvm-as`.
65+
pub fn llvm_as() -> LlvmAs {
66+
LlvmAs::new()
67+
}
68+
6369
/// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
6470
/// at `$LLVM_BIN_DIR/llvm-dis`.
6571
pub fn llvm_dis() -> LlvmDis {
@@ -135,6 +141,13 @@ pub struct LlvmPdbutil {
135141
cmd: Command,
136142
}
137143

144+
/// A `llvm-as` invocation builder.
145+
#[derive(Debug)]
146+
#[must_use]
147+
pub struct LlvmAs {
148+
cmd: Command,
149+
}
150+
138151
/// A `llvm-dis` invocation builder.
139152
#[derive(Debug)]
140153
#[must_use]
@@ -158,6 +171,7 @@ crate::macros::impl_common_helpers!(LlvmNm);
158171
crate::macros::impl_common_helpers!(LlvmBcanalyzer);
159172
crate::macros::impl_common_helpers!(LlvmDwarfdump);
160173
crate::macros::impl_common_helpers!(LlvmPdbutil);
174+
crate::macros::impl_common_helpers!(LlvmAs);
161175
crate::macros::impl_common_helpers!(LlvmDis);
162176
crate::macros::impl_common_helpers!(LlvmObjcopy);
163177

@@ -441,6 +455,22 @@ impl LlvmObjcopy {
441455
}
442456
}
443457

458+
impl LlvmAs {
459+
/// Construct a new `llvm-as` invocation. This assumes that `llvm-as` is available
460+
/// at `$LLVM_BIN_DIR/llvm-as`.
461+
pub fn new() -> Self {
462+
let llvm_as = llvm_bin_dir().join("llvm-as");
463+
let cmd = Command::new(llvm_as);
464+
Self { cmd }
465+
}
466+
467+
/// Provide an input file.
468+
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
469+
self.cmd.arg(path.as_ref());
470+
self
471+
}
472+
}
473+
444474
impl LlvmDis {
445475
/// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
446476
/// at `$LLVM_BIN_DIR/llvm-dis`.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub use cargo::cargo;
6363
pub use clang::{clang, Clang};
6464
pub use htmldocck::htmldocck;
6565
pub use llvm::{
66-
llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy,
66+
llvm_ar, llvm_bcanalyzer, llvm_as, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy,
6767
llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump,
6868
LlvmFilecheck, LlvmNm, LlvmObjcopy, LlvmObjdump, LlvmProfdata, LlvmReadobj,
6969
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![feature(no_core, lang_items)]
2+
#![no_core]
3+
#![crate_type = "rlib"]
4+
5+
#[lang = "sized"]
6+
trait Sized {}
7+
8+
pub fn foo() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![allow(internal_features)]
2+
#![feature(no_core, lang_items)]
3+
#![no_core]
4+
#![crate_type = "cdylib"]
5+
6+
extern crate lib;
7+
8+
#[unsafe(no_mangle)]
9+
pub fn bar() {
10+
lib::foo();
11+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Compile a library with lto=fat, then compile a binary with lto=thin
2+
// and check that lto is applied with the library.
3+
// The goal is to mimic the standard library being build with lto=fat
4+
// and allowing users to build with lto=thin.
5+
6+
//@ only-x86_64-unknown-linux-gnu
7+
8+
use run_make_support::{dynamic_lib_name, llvm_objdump, rustc};
9+
10+
fn main() {
11+
rustc().input("lib.rs").opt_level("3").arg("-Clto=fat").run();
12+
rustc().input("main.rs").panic("abort").opt_level("3").arg("-Clto=thin").run();
13+
14+
llvm_objdump()
15+
.input(dynamic_lib_name("main"))
16+
.arg("--disassemble-symbols=bar")
17+
.run()
18+
// The called function should be inlined.
19+
// Check that we have a ret (to detect tail
20+
// calls with a jmp) and no call.
21+
.assert_stdout_contains("bar")
22+
.assert_stdout_contains("ret")
23+
.assert_stdout_not_contains("foo")
24+
.assert_stdout_not_contains("call");
25+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
2+
target triple = "x86_64-unknown-linux-gnu"
3+
4+
define void @ir_callee() {
5+
ret void
6+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(no_core, lang_items)]
2+
#![no_core]
3+
#![crate_type = "cdylib"]
4+
5+
#[lang = "sized"]
6+
trait Sized {}
7+
8+
extern "C" {
9+
fn ir_callee();
10+
}
11+
12+
#[no_mangle]
13+
extern "C" fn rs_foo() {
14+
unsafe {
15+
ir_callee();
16+
}
17+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Check that -C lto=fat with -C linker-plugin-lto actually works and can inline functions.
2+
// A library is created from LLVM IR, defining a single function. Then a dylib is compiled,
3+
// linking to the library and calling the function from the library.
4+
// The function from the library should end up inlined and disappear from the output.
5+
6+
//@ only-x86_64-unknown-linux-gnu
7+
//@ needs-rust-lld
8+
9+
use run_make_support::{dynamic_lib_name, llvm_as, llvm_objdump, rustc};
10+
11+
fn main() {
12+
llvm_as().input("ir.ll").run();
13+
rustc()
14+
.input("main.rs")
15+
.opt_level("3")
16+
.args(&["-Clto=fat", "-Clinker-plugin-lto", "-Zlinker-features=+lld", "-Clink-arg=ir.bc"])
17+
.run();
18+
19+
llvm_objdump()
20+
.input(dynamic_lib_name("main"))
21+
.arg("--disassemble-symbols=rs_foo")
22+
.run()
23+
// The called function should be inlined.
24+
// Check that we have a ret (to detect tail
25+
// calls with a jmp) and no call.
26+
.assert_stdout_contains("foo")
27+
.assert_stdout_contains("ret")
28+
.assert_stdout_not_contains("call");
29+
}

0 commit comments

Comments
 (0)