Auto merge of #52109 - michaelwoerister:ir-objs, r=alexcrichton

When doing linker-plugin based LTO, write LLVM bitcode obj-files instead of embedding the bitcode into the regular object file.

This PR makes the compiler emit LLVM bitcode object files instead of regular object files with the IR embed when compiling for linker-plugin-based LTO. The reasoning for switching the strategy is this:
- Embedding bitcode in a section of the object file actually makes us save bitcode twice in rlibs and Rust dylibs, once for linker-based LTO and once for rustc-based LTO. That's a waste of space.
- When compiling for plugin-based LTO, one usually has no use for the machine code also present in the object file. Generating it is a waste of time.
- When compiling for plugin-based LTO, `rustc` will skip running ThinLTO because the linker will do that anyway. This has the side effect of then generating poorly optimized machine code, which makes it even less useful (and may lead to users not knowing why their code is slow instead of getting an error).
- Not having machine code available makes it impossible for the linker to silently fall back to not inlining stuff across language boundaries.
- This is what Clang does and according to [the documentation](https://llvm.org/docs/BitCodeFormat.html#native-object-file-wrapper-format) is the better supported option.
- The current behavior (minus the runtime performance problems) is still available via `-Z embed-bitcode` (we might want to do this for `libstd` at some point).

r? @alexcrichton
This commit is contained in:
bors 2018-07-07 14:09:49 +00:00
commit e44906e80c
4 changed files with 28 additions and 36 deletions

View File

@ -99,16 +99,14 @@ pub enum Lto {
pub enum CrossLangLto {
LinkerPlugin(PathBuf),
LinkerPluginAuto,
NoLink,
Disabled
}
impl CrossLangLto {
pub fn embed_bitcode(&self) -> bool {
pub fn enabled(&self) -> bool {
match *self {
CrossLangLto::LinkerPlugin(_) |
CrossLangLto::LinkerPluginAuto |
CrossLangLto::NoLink => true,
CrossLangLto::LinkerPluginAuto => true,
CrossLangLto::Disabled => false,
}
}
@ -1031,8 +1029,7 @@ macro_rules! options {
}
*slot = match v {
None |
Some("no-link") => CrossLangLto::NoLink,
None => CrossLangLto::LinkerPluginAuto,
Some(path) => CrossLangLto::LinkerPlugin(PathBuf::from(path)),
};
true
@ -3165,7 +3162,7 @@ mod tests {
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
opts = reference.clone();
opts.debugging_opts.cross_lang_lto = CrossLangLto::NoLink;
opts.debugging_opts.cross_lang_lto = CrossLangLto::LinkerPluginAuto;
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
}

View File

@ -471,8 +471,7 @@ impl<'a> Linker for GccLinker<'a> {
fn cross_lang_lto(&mut self) {
match self.sess.opts.debugging_opts.cross_lang_lto {
CrossLangLto::Disabled |
CrossLangLto::NoLink => {
CrossLangLto::Disabled => {
// Nothing to do
}
CrossLangLto::LinkerPluginAuto => {

View File

@ -288,10 +288,10 @@ impl ModuleConfig {
self.no_builtins = no_builtins || sess.target.target.options.no_builtins;
self.time_passes = sess.time_passes();
self.inline_threshold = sess.opts.cg.inline_threshold;
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode ||
sess.opts.debugging_opts.cross_lang_lto.enabled();
let embed_bitcode = sess.target.target.options.embed_bitcode ||
sess.opts.debugging_opts.embed_bitcode ||
sess.opts.debugging_opts.cross_lang_lto.embed_bitcode();
sess.opts.debugging_opts.embed_bitcode;
if embed_bitcode {
match sess.opts.optimize {
config::OptLevel::No |
@ -1365,7 +1365,7 @@ fn execute_work_item(cgcx: &CodegenContext,
// Don't run LTO passes when cross-lang LTO is enabled. The linker
// will do that for us in this case.
let needs_lto = needs_lto &&
!cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode();
!cgcx.opts.debugging_opts.cross_lang_lto.enabled();
if needs_lto {
Ok(WorkItemResult::NeedsLTO(module))

View File

@ -1,53 +1,49 @@
# min-llvm-version 4.0
# ignore-mingw
# ignore-msvc
-include ../tools.mk
# This test makes sure that the expected .llvmbc sections for use by
# linker-based LTO are available in object files when compiling with
# -Z cross-lang-lto
# This test makes sure that the object files we generate are actually
# LLVM bitcode files (as used by linker LTO plugins) when compiling with
# -Z cross-lang-lto.
LLVMBC_SECTION_NAME=\\.llvmbc
ASSERT_IS_BITCODE_OBJ=llvm-bcanalyzer # this only succeeds for bitcode files
EXTRACT_OBJS=(cd $(TMPDIR); rm -f ./*.o; llvm-ar x $(1))
ifeq ($(UNAME),Darwin)
LLVMBC_SECTION_NAME=__bitcode
endif
OBJDUMP=llvm-objdump
SECTION_HEADERS=$(OBJDUMP) -section-headers
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto=no-link -Ccodegen-units=1
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto=no-link -Ccodegen-units=1 --emit=obj
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto=on -Ccodegen-units=1
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto=on -Ccodegen-units=1 --emit=obj
all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib
staticlib: lib.rs
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib.a
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
$(call EXTRACT_OBJS, liblib.a)
$(ASSERT_IS_BITCODE_OBJ) $(TMPDIR)/liblib.lib0.rcgu.o
staticlib-fat-lto: lib.rs
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-fat-lto.a -Clto=fat
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib-fat-lto.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
$(call EXTRACT_OBJS, liblib-fat-lto.a)
$(ASSERT_IS_BITCODE_OBJ) $(TMPDIR)/liblib-fat-lto.lib0.rcgu.o
staticlib-thin-lto: lib.rs
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-thin-lto.a -Clto=thin
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib-thin-lto.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
$(call EXTRACT_OBJS, liblib-thin-lto.a)
$(ASSERT_IS_BITCODE_OBJ) $(TMPDIR)/liblib-thin-lto.lib0.rcgu.o
rlib: lib.rs
$(BUILD_LIB) --crate-type=rlib -o $(TMPDIR)/liblib.rlib
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib.rlib | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
$(call EXTRACT_OBJS, liblib.rlib)
$(ASSERT_IS_BITCODE_OBJ) $(TMPDIR)/liblib.lib0.rcgu.o
cdylib: lib.rs
$(BUILD_LIB) --crate-type=cdylib --emit=obj -o $(TMPDIR)/cdylib.o
[ "$$($(SECTION_HEADERS) $(TMPDIR)/cdylib.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
$(ASSERT_IS_BITCODE_OBJ) $(TMPDIR)/cdylib.o
rdylib: lib.rs
$(BUILD_LIB) --crate-type=dylib --emit=obj -o $(TMPDIR)/rdylib.o
[ "$$($(SECTION_HEADERS) $(TMPDIR)/rdylib.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
$(ASSERT_IS_BITCODE_OBJ) $(TMPDIR)/rdylib.o
exe: lib.rs
$(BUILD_EXE) -o $(TMPDIR)/exe.o
[ "$$($(SECTION_HEADERS) $(TMPDIR)/exe.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
$(ASSERT_IS_BITCODE_OBJ) $(TMPDIR)/exe.o