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

Fix linker-plugin-lto only doing thin lto #136840

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
7 changes: 6 additions & 1 deletion compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ use rustc_middle::ty::layout::{
};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::Session;
use rustc_session::config::{
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet,
};
use rustc_session::{Session, config};
use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span};
use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel};
Expand Down Expand Up @@ -290,6 +290,11 @@ pub(crate) unsafe fn create_module<'ll>(
);
}

// If fat lto is requested, lld still defaults to thin lto. Set ThinLTO=0 to force fat lto in lld.
if sess.lto() == config::Lto::Fat {
llvm::add_module_flag_u32(llmod, llvm::ModuleFlagMergeBehavior::Override, "ThinLTO", 0);
}
Copy link
Member

Choose a reason for hiding this comment

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

What if a dependency is built with lto=true (aka lto=fat), but then the user wants to use thinLTO? I'm pretty sure the standard library is built with lto=true for example, but that shouldn't prevent thinLTO from ever working.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question, it seems to change somewhat, but still work in general. I added a test for this.
What changes: Without this change, the test passes when
lib is compiled with O0 and main with O3 and

  1. lib uses lto=thin and main uses lto=thin
  2. lib uses lto=thin and main uses lto=fat
  3. lib uses lto=fat and main uses lto=thin
  4. lib uses lto=fat and main uses lto=fat

With this change, all of these keep passing except for case 3 (lib uses lto=fat and main uses lto=thin).
When lib is compiled with O1, O2 or O3, case 3 passes as well.
I assume this is the important case, as the standard library is compiled with optimizations.
(And lto with O0 is kinda questionable, except maybe for nvptx and amdgpu, but they require lto=fat anyway.)


// Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.)
if sess.is_sanitizer_kcfi_enabled() {
llvm::add_module_flag_u32(llmod, llvm::ModuleFlagMergeBehavior::Override, "kcfi", 1);
Expand Down
30 changes: 30 additions & 0 deletions src/tools/run-make-support/src/external_deps/llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ pub fn llvm_pdbutil() -> LlvmPdbutil {
LlvmPdbutil::new()
}

/// Construct a new `llvm-as` invocation. This assumes that `llvm-as` is available
/// at `$LLVM_BIN_DIR/llvm-as`.
pub fn llvm_as() -> LlvmAs {
LlvmAs::new()
}

/// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
/// at `$LLVM_BIN_DIR/llvm-dis`.
pub fn llvm_dis() -> LlvmDis {
Expand Down Expand Up @@ -135,6 +141,13 @@ pub struct LlvmPdbutil {
cmd: Command,
}

/// A `llvm-as` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmAs {
cmd: Command,
}

/// A `llvm-dis` invocation builder.
#[derive(Debug)]
#[must_use]
Expand All @@ -158,6 +171,7 @@ crate::macros::impl_common_helpers!(LlvmNm);
crate::macros::impl_common_helpers!(LlvmBcanalyzer);
crate::macros::impl_common_helpers!(LlvmDwarfdump);
crate::macros::impl_common_helpers!(LlvmPdbutil);
crate::macros::impl_common_helpers!(LlvmAs);
crate::macros::impl_common_helpers!(LlvmDis);
crate::macros::impl_common_helpers!(LlvmObjcopy);

Expand Down Expand Up @@ -441,6 +455,22 @@ impl LlvmObjcopy {
}
}

impl LlvmAs {
/// Construct a new `llvm-as` invocation. This assumes that `llvm-as` is available
/// at `$LLVM_BIN_DIR/llvm-as`.
pub fn new() -> Self {
let llvm_as = llvm_bin_dir().join("llvm-as");
let cmd = Command::new(llvm_as);
Self { cmd }
}

/// Provide an input file.
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg(path.as_ref());
self
}
}

impl LlvmDis {
/// Construct a new `llvm-dis` invocation. This assumes that `llvm-dis` is available
/// at `$LLVM_BIN_DIR/llvm-dis`.
Expand Down
2 changes: 1 addition & 1 deletion src/tools/run-make-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub use cargo::cargo;
pub use clang::{clang, Clang};
pub use htmldocck::htmldocck;
pub use llvm::{
llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy,
llvm_ar, llvm_bcanalyzer, llvm_as, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy,
llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump,
LlvmFilecheck, LlvmNm, LlvmObjcopy, LlvmObjdump, LlvmProfdata, LlvmReadobj,
};
Expand Down
8 changes: 8 additions & 0 deletions tests/run-make/fat-then-thin-lto/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![feature(no_core, lang_items)]
#![no_core]
#![crate_type = "rlib"]

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

pub fn foo() {}
11 changes: 11 additions & 0 deletions tests/run-make/fat-then-thin-lto/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![allow(internal_features)]
#![feature(no_core, lang_items)]
#![no_core]
#![crate_type = "cdylib"]

extern crate lib;

#[unsafe(no_mangle)]
pub fn bar() {
lib::foo();
}
25 changes: 25 additions & 0 deletions tests/run-make/fat-then-thin-lto/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Compile a library with lto=fat, then compile a binary with lto=thin
// and check that lto is applied with the library.
// The goal is to mimic the standard library being build with lto=fat
// and allowing users to build with lto=thin.

//@ only-x86_64-unknown-linux-gnu

use run_make_support::{dynamic_lib_name, llvm_objdump, rustc};

fn main() {
rustc().input("lib.rs").opt_level("3").arg("-Clto=fat").run();
rustc().input("main.rs").panic("abort").opt_level("3").arg("-Clto=thin").run();

llvm_objdump()
.input(dynamic_lib_name("main"))
.arg("--disassemble-symbols=bar")
.run()
// The called function should be inlined.
// Check that we have a ret (to detect tail
// calls with a jmp) and no call.
.assert_stdout_contains("bar")
.assert_stdout_contains("ret")
.assert_stdout_not_contains("foo")
.assert_stdout_not_contains("call");
}
6 changes: 6 additions & 0 deletions tests/run-make/linker-plugin-lto-fat/ir.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define void @ir_callee() {
ret void
}
17 changes: 17 additions & 0 deletions tests/run-make/linker-plugin-lto-fat/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#![feature(no_core, lang_items)]
#![no_core]
#![crate_type = "cdylib"]

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

extern "C" {
fn ir_callee();
}

#[no_mangle]
extern "C" fn rs_foo() {
unsafe {
ir_callee();
}
}
29 changes: 29 additions & 0 deletions tests/run-make/linker-plugin-lto-fat/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Check that -C lto=fat with -C linker-plugin-lto actually works and can inline functions.
// A library is created from LLVM IR, defining a single function. Then a dylib is compiled,
// linking to the library and calling the function from the library.
// The function from the library should end up inlined and disappear from the output.

//@ only-x86_64-unknown-linux-gnu
//@ needs-rust-lld

use run_make_support::{dynamic_lib_name, llvm_as, llvm_objdump, rustc};

fn main() {
llvm_as().input("ir.ll").run();
rustc()
.input("main.rs")
.opt_level("3")
.args(&["-Clto=fat", "-Clinker-plugin-lto", "-Zlinker-features=+lld", "-Clink-arg=ir.bc"])
.run();

llvm_objdump()
.input(dynamic_lib_name("main"))
.arg("--disassemble-symbols=rs_foo")
.run()
// The called function should be inlined.
// Check that we have a ret (to detect tail
// calls with a jmp) and no call.
.assert_stdout_contains("foo")
.assert_stdout_contains("ret")
.assert_stdout_not_contains("call");
}
Loading