Skip to content

Commit 1357af3

Browse files
committed
Auto merge of rust-lang#71528 - alexcrichton:no-more-bitcode, r=nnethercote
Store LLVM bitcode in object files, not compressed This commit is an attempted resurrection of rust-lang#70458 where LLVM bitcode emitted by rustc into rlibs is stored into object file sections rather than in a separate file. The main rationale for doing this is that when rustc emits bitcode it will no longer use a custom compression scheme which makes it both easier to interoperate with existing tools and also cuts down on compile time since this compression isn't happening. The blocker for this in rust-lang#70458 turned out to be that native linkers didn't handle the new sections well, causing the sections to either trigger bugs in the linker or actually end up in the final linked artifact. This commit attempts to address these issues by ensuring that native linkers ignore the new sections by inserting custom flags with module-level inline assembly. Note that this does not currently change the API of the compiler at all. The pre-existing `-C bitcode-in-rlib` flag is co-opted to indicate whether the bitcode should be present in the object file or not. Finally, note that an important consequence of this commit, which is also one of its primary purposes, is to enable rustc's `-Clto` bitcode loading to load rlibs produced with `-Clinker-plugin-lto`. The goal here is that when you're building with LTO Cargo will tell rustc to skip codegen of all intermediate crates and only generate LLVM IR. Today rustc will generate both object code and LLVM IR, but the object code is later simply thrown away, wastefully.
2 parents fa51f81 + ef89cc8 commit 1357af3

File tree

18 files changed

+189
-266
lines changed

18 files changed

+189
-266
lines changed

src/doc/rustc/src/codegen-options/index.md

+26-20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,32 @@ a version of this list for your exact compiler by running `rustc -C help`.
77

88
This option is deprecated and does nothing.
99

10+
## bitcode-in-rlib
11+
12+
This flag controls whether or not the compiler puts LLVM bitcode into generated
13+
rlibs. It takes one of the following values:
14+
15+
* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default).
16+
* `n`, `no`, or `off`: omit bitcode from rlibs.
17+
18+
LLVM bitcode is only needed when link-time optimization (LTO) is being
19+
performed, but it is enabled by default for backwards compatibility reasons.
20+
21+
The use of `-C bitcode-in-rlib=no` can significantly improve compile times and
22+
reduce generated file sizes. For these reasons, Cargo uses `-C
23+
bitcode-in-rlib=no` whenever possible. Likewise, if you are building directly
24+
with `rustc` we recommend using `-C bitcode-in-rlib=no` whenever you are not
25+
using LTO.
26+
27+
If combined with `-C lto`, `-C bitcode-in-rlib=no` will cause `rustc` to abort
28+
at start-up, because the combination is invalid.
29+
30+
> **Note**: the implementation of this flag today is to enable the
31+
> `-Zembed-bitcode` option. When bitcode is embedded into an rlib then all
32+
> object files within the rlib will have a special section (typically named
33+
> `.llvmbc`, depends on the platform though) which contains LLVM bytecode. This
34+
> section of the object file will not appear in the final linked artifact.
35+
1036
## code-model
1137

1238
This option lets you choose which code model to use.
@@ -422,26 +448,6 @@ This also supports the feature `+crt-static` and `-crt-static` to control
422448
Each target and [`target-cpu`](#target-cpu) has a default set of enabled
423449
features.
424450

425-
## bitcode-in-rlib
426-
427-
This flag controls whether or not the compiler puts compressed LLVM bitcode
428-
into generated rlibs. It takes one of the following values:
429-
430-
* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default).
431-
* `n`, `no`, or `off`: omit bitcode from rlibs.
432-
433-
LLVM bitcode is only needed when link-time optimization (LTO) is being
434-
performed, but it is enabled by default for backwards compatibility reasons.
435-
436-
The use of `-C bitcode-in-rlib=no` can significantly improve compile times and
437-
reduce generated file sizes. For these reasons, Cargo uses `-C
438-
bitcode-in-rlib=no` whenever possible. Likewise, if you are building directly
439-
with `rustc` we recommend using `-C bitcode-in-rlib=no` whenever you are not
440-
using LTO.
441-
442-
If combined with `-C lto`, `-C bitcode-in-rlib=no` will cause `rustc` to abort
443-
at start-up, because the combination is invalid.
444-
445451
[option-emit]: ../command-line-arguments.md#option-emit
446452
[option-o-optimize]: ../command-line-arguments.md#option-o-optimize
447453
[profile-guided optimization]: ../profile-guided-optimization.md

src/librustc_codegen_llvm/back/archive.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::str;
1010
use crate::llvm::archive_ro::{ArchiveRO, Child};
1111
use crate::llvm::{self, ArchiveKind};
1212
use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
13-
use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME, RLIB_BYTECODE_EXTENSION};
13+
use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME};
1414
use rustc_session::Session;
1515
use rustc_span::symbol::Symbol;
1616

@@ -129,8 +129,8 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
129129
let obj_start = name.to_owned();
130130

131131
self.add_archive(rlib, move |fname: &str| {
132-
// Ignore bytecode/metadata files, no matter the name.
133-
if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
132+
// Ignore metadata files, no matter the name.
133+
if fname == METADATA_FILENAME {
134134
return true;
135135
}
136136

src/librustc_codegen_llvm/back/bytecode.rs

-141
This file was deleted.

src/librustc_codegen_llvm/back/lto.rs

+32-16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::back::bytecode::DecodedBytecode;
21
use crate::back::write::{
32
self, save_temp_bitcode, to_llvm_opt_settings, with_llvm_pmb, DiagnosticHandlers,
43
};
@@ -10,7 +9,7 @@ use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModul
109
use rustc_codegen_ssa::back::symbol_export;
1110
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig};
1211
use rustc_codegen_ssa::traits::*;
13-
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, RLIB_BYTECODE_EXTENSION};
12+
use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
1413
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1514
use rustc_errors::{FatalError, Handler};
1615
use rustc_hir::def_id::LOCAL_CRATE;
@@ -111,29 +110,46 @@ fn prepare_lto(
111110
}
112111

113112
let archive = ArchiveRO::open(&path).expect("wanted an rlib");
114-
let bytecodes = archive
113+
let obj_files = archive
115114
.iter()
116115
.filter_map(|child| child.ok().and_then(|c| c.name().map(|name| (name, c))))
117-
.filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION));
118-
for (name, data) in bytecodes {
119-
let _timer =
120-
cgcx.prof.generic_activity_with_arg("LLVM_lto_load_upstream_bitcode", name);
121-
info!("adding bytecode {}", name);
122-
let bc_encoded = data.data();
123-
124-
let (bc, id) = match DecodedBytecode::new(bc_encoded) {
125-
Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
126-
Err(e) => Err(diag_handler.fatal(&e)),
127-
}?;
128-
let bc = SerializedModule::FromRlib(bc);
129-
upstream_modules.push((bc, CString::new(id).unwrap()));
116+
.filter(|&(name, _)| looks_like_rust_object_file(name));
117+
for (name, child) in obj_files {
118+
info!("adding bitcode from {}", name);
119+
match get_bitcode_slice_from_object_data(child.data()) {
120+
Ok(data) => {
121+
let module = SerializedModule::FromRlib(data.to_vec());
122+
upstream_modules.push((module, CString::new(name).unwrap()));
123+
}
124+
Err(msg) => return Err(diag_handler.fatal(&msg)),
125+
}
130126
}
131127
}
132128
}
133129

134130
Ok((symbol_white_list, upstream_modules))
135131
}
136132

133+
fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], String> {
134+
let mut len = 0;
135+
let data =
136+
unsafe { llvm::LLVMRustGetBitcodeSliceFromObjectData(obj.as_ptr(), obj.len(), &mut len) };
137+
if !data.is_null() {
138+
assert!(len != 0);
139+
let bc = unsafe { slice::from_raw_parts(data, len) };
140+
141+
// `bc` must be a sub-slice of `obj`.
142+
assert!(obj.as_ptr() <= bc.as_ptr());
143+
assert!(bc[bc.len()..bc.len()].as_ptr() <= obj[obj.len()..obj.len()].as_ptr());
144+
145+
Ok(bc)
146+
} else {
147+
assert!(len == 0);
148+
let msg = llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string());
149+
Err(format!("failed to get bitcode from object file for LTO ({})", msg))
150+
}
151+
}
152+
137153
/// Performs fat LTO by merging all modules into a single one and returning it
138154
/// for further optimization.
139155
pub(crate) fn run_fat(

src/librustc_codegen_llvm/back/write.rs

+50-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::attributes;
2-
use crate::back::bytecode;
32
use crate::back::lto::ThinBuffer;
43
use crate::back::profiling::{
54
selfprofile_after_pass_callback, selfprofile_before_pass_callback, LlvmSelfProfiler,
@@ -16,7 +15,7 @@ use crate::ModuleLlvm;
1615
use log::debug;
1716
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
1817
use rustc_codegen_ssa::traits::*;
19-
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, RLIB_BYTECODE_EXTENSION};
18+
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
2019
use rustc_data_structures::small_c_str::SmallCStr;
2120
use rustc_errors::{FatalError, Handler};
2221
use rustc_fs_util::{link_or_copy, path_to_c_string};
@@ -654,19 +653,6 @@ pub(crate) unsafe fn codegen(
654653
);
655654
embed_bitcode(cgcx, llcx, llmod, Some(data));
656655
}
657-
658-
if config.emit_bc_compressed {
659-
let _timer = cgcx.prof.generic_activity_with_arg(
660-
"LLVM_module_codegen_emit_compressed_bitcode",
661-
&module.name[..],
662-
);
663-
let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
664-
let data = bytecode::encode(&module.name, data);
665-
if let Err(e) = fs::write(&dst, data) {
666-
let msg = format!("failed to write bytecode to {}: {}", dst.display(), e);
667-
diag_handler.err(&msg);
668-
}
669-
}
670656
} else if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Marker) {
671657
embed_bitcode(cgcx, llcx, llmod, None);
672658
}
@@ -777,7 +763,6 @@ pub(crate) unsafe fn codegen(
777763
Ok(module.into_compiled_module(
778764
config.emit_obj != EmitObj::None,
779765
config.emit_bc,
780-
config.emit_bc_compressed,
781766
&cgcx.output_filenames,
782767
))
783768
}
@@ -832,6 +817,55 @@ unsafe fn embed_bitcode(
832817
let section = if is_apple { "__LLVM,__cmdline\0" } else { ".llvmcmd\0" };
833818
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
834819
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
820+
821+
// We're adding custom sections to the output object file, but we definitely
822+
// do not want these custom sections to make their way into the final linked
823+
// executable. The purpose of these custom sections is for tooling
824+
// surrounding object files to work with the LLVM IR, if necessary. For
825+
// example rustc's own LTO will look for LLVM IR inside of the object file
826+
// in these sections by default.
827+
//
828+
// To handle this is a bit different depending on the object file format
829+
// used by the backend, broken down into a few different categories:
830+
//
831+
// * Mach-O - this is for macOS. Inspecting the source code for the native
832+
// linker here shows that the `.llvmbc` and `.llvmcmd` sections are
833+
// automatically skipped by the linker. In that case there's nothing extra
834+
// that we need to do here.
835+
//
836+
// * Wasm - the native LLD linker is hard-coded to skip `.llvmbc` and
837+
// `.llvmcmd` sections, so there's nothing extra we need to do.
838+
//
839+
// * COFF - if we don't do anything the linker will by default copy all
840+
// these sections to the output artifact, not what we want! To subvert
841+
// this we want to flag the sections we inserted here as
842+
// `IMAGE_SCN_LNK_REMOVE`. Unfortunately though LLVM has no native way to
843+
// do this. Thankfully though we can do this with some inline assembly,
844+
// which is easy enough to add via module-level global inline asm.
845+
//
846+
// * ELF - this is very similar to COFF above. One difference is that these
847+
// sections are removed from the output linked artifact when
848+
// `--gc-sections` is passed, which we pass by default. If that flag isn't
849+
// passed though then these sections will show up in the final output.
850+
// Additionally the flag that we need to set here is `SHF_EXCLUDE`.
851+
if is_apple
852+
|| cgcx.opts.target_triple.triple().starts_with("wasm")
853+
|| cgcx.opts.target_triple.triple().starts_with("asmjs")
854+
{
855+
// nothing to do here
856+
} else if cgcx.opts.target_triple.triple().contains("windows") {
857+
let asm = "
858+
.section .llvmbc,\"n\"
859+
.section .llvmcmd,\"n\"
860+
";
861+
llvm::LLVMRustAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len());
862+
} else {
863+
let asm = "
864+
.section .llvmbc,\"e\"
865+
.section .llvmcmd,\"e\"
866+
";
867+
llvm::LLVMRustAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len());
868+
}
835869
}
836870

837871
pub unsafe fn with_llvm_pmb(

0 commit comments

Comments
 (0)