Store LLVM bitcode in object files, not compressed

This commit is an attempted resurrection of #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 #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.
This commit is contained in:
Alex Crichton 2020-04-23 11:45:55 -07:00
parent 413a12909f
commit ef89cc8f04
18 changed files with 188 additions and 265 deletions

View File

@ -7,6 +7,32 @@ a version of this list for your exact compiler by running `rustc -C help`.
This option is deprecated and does nothing.
## bitcode-in-rlib
This flag controls whether or not the compiler puts LLVM bitcode into generated
rlibs. It takes one of the following values:
* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default).
* `n`, `no`, or `off`: omit bitcode from rlibs.
LLVM bitcode is only needed when link-time optimization (LTO) is being
performed, but it is enabled by default for backwards compatibility reasons.
The use of `-C bitcode-in-rlib=no` can significantly improve compile times and
reduce generated file sizes. For these reasons, Cargo uses `-C
bitcode-in-rlib=no` whenever possible. Likewise, if you are building directly
with `rustc` we recommend using `-C bitcode-in-rlib=no` whenever you are not
using LTO.
If combined with `-C lto`, `-C bitcode-in-rlib=no` will cause `rustc` to abort
at start-up, because the combination is invalid.
> **Note**: the implementation of this flag today is to enable the
> `-Zembed-bitcode` option. When bitcode is embedded into an rlib then all
> object files within the rlib will have a special section (typically named
> `.llvmbc`, depends on the platform though) which contains LLVM bytecode. This
> section of the object file will not appear in the final linked artifact.
## code-model
This option lets you choose which code model to use.
@ -387,26 +413,6 @@ This also supports the feature `+crt-static` and `-crt-static` to control
Each target and [`target-cpu`](#target-cpu) has a default set of enabled
features.
## bitcode-in-rlib
This flag controls whether or not the compiler puts compressed LLVM bitcode
into generated rlibs. It takes one of the following values:
* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default).
* `n`, `no`, or `off`: omit bitcode from rlibs.
LLVM bitcode is only needed when link-time optimization (LTO) is being
performed, but it is enabled by default for backwards compatibility reasons.
The use of `-C bitcode-in-rlib=no` can significantly improve compile times and
reduce generated file sizes. For these reasons, Cargo uses `-C
bitcode-in-rlib=no` whenever possible. Likewise, if you are building directly
with `rustc` we recommend using `-C bitcode-in-rlib=no` whenever you are not
using LTO.
If combined with `-C lto`, `-C bitcode-in-rlib=no` will cause `rustc` to abort
at start-up, because the combination is invalid.
[option-emit]: ../command-line-arguments.md#option-emit
[option-o-optimize]: ../command-line-arguments.md#option-o-optimize
[profile-guided optimization]: ../profile-guided-optimization.md

View File

@ -10,7 +10,7 @@ use std::str;
use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind};
use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME, RLIB_BYTECODE_EXTENSION};
use rustc_codegen_ssa::{looks_like_rust_object_file, METADATA_FILENAME};
use rustc_session::Session;
use rustc_span::symbol::Symbol;
@ -129,8 +129,8 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
let obj_start = name.to_owned();
self.add_archive(rlib, move |fname: &str| {
// Ignore bytecode/metadata files, no matter the name.
if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
// Ignore metadata files, no matter the name.
if fname == METADATA_FILENAME {
return true;
}

View File

@ -1,141 +0,0 @@
//! Management of the encoding of LLVM bytecode into rlibs
//!
//! This module contains the management of encoding LLVM bytecode into rlibs,
//! primarily for the usage in LTO situations. Currently the compiler will
//! unconditionally encode LLVM-IR into rlibs regardless of what's happening
//! elsewhere, so we currently compress the bytecode via deflate to avoid taking
//! up too much space on disk.
//!
//! After compressing the bytecode we then have the rest of the format to
//! basically deal with various bugs in various archive implementations. The
//! format currently is:
//!
//! RLIB LLVM-BYTECODE OBJECT LAYOUT
//! Version 2
//! Bytes Data
//! 0..10 "RUST_OBJECT" encoded in ASCII
//! 11..14 format version as little-endian u32
//! 15..19 the length of the module identifier string
//! 20..n the module identifier string
//! n..n+8 size in bytes of deflate compressed LLVM bitcode as
//! little-endian u64
//! n+9.. compressed LLVM bitcode
//! ? maybe a byte to make this whole thing even length
use std::io::{Read, Write};
use std::ptr;
use std::str;
use flate2::read::DeflateDecoder;
use flate2::write::DeflateEncoder;
use flate2::Compression;
// This is the "magic number" expected at the beginning of a LLVM bytecode
// object in an rlib.
pub const RLIB_BYTECODE_OBJECT_MAGIC: &[u8] = b"RUST_OBJECT";
// The version number this compiler will write to bytecode objects in rlibs
pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2;
pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec<u8> {
let mut encoded = Vec::new();
// Start off with the magic string
encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC);
// Next up is the version
encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]);
// Next is the LLVM module identifier length + contents
let identifier_len = identifier.len();
encoded.extend_from_slice(&[
(identifier_len >> 0) as u8,
(identifier_len >> 8) as u8,
(identifier_len >> 16) as u8,
(identifier_len >> 24) as u8,
]);
encoded.extend_from_slice(identifier.as_bytes());
// Next is the LLVM module deflate compressed, prefixed with its length. We
// don't know its length yet, so fill in 0s
let deflated_size_pos = encoded.len();
encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
let before = encoded.len();
DeflateEncoder::new(&mut encoded, Compression::fast()).write_all(bytecode).unwrap();
let after = encoded.len();
// Fill in the length we reserved space for before
let bytecode_len = (after - before) as u64;
encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8;
encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8;
encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8;
encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8;
encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8;
encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8;
encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8;
encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8;
// If the number of bytes written to the object so far is odd, add a
// padding byte to make it even. This works around a crash bug in LLDB
// (see issue #15950)
if encoded.len() % 2 == 1 {
encoded.push(0);
}
encoded
}
pub struct DecodedBytecode<'a> {
identifier: &'a str,
encoded_bytecode: &'a [u8],
}
impl<'a> DecodedBytecode<'a> {
pub fn new(data: &'a [u8]) -> Result<DecodedBytecode<'a>, &'static str> {
if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) {
return Err("magic bytecode prefix not found");
}
let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..];
if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) {
return Err("wrong version prefix found in bytecode");
}
let data = &data[4..];
if data.len() < 4 {
return Err("bytecode corrupted");
}
let identifier_len =
unsafe { u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize };
let data = &data[4..];
if data.len() < identifier_len {
return Err("bytecode corrupted");
}
let identifier = match str::from_utf8(&data[..identifier_len]) {
Ok(s) => s,
Err(_) => return Err("bytecode corrupted"),
};
let data = &data[identifier_len..];
if data.len() < 8 {
return Err("bytecode corrupted");
}
let bytecode_len =
unsafe { u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize };
let data = &data[8..];
if data.len() < bytecode_len {
return Err("bytecode corrupted");
}
let encoded_bytecode = &data[..bytecode_len];
Ok(DecodedBytecode { identifier, encoded_bytecode })
}
pub fn bytecode(&self) -> Vec<u8> {
let mut data = Vec::new();
DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap();
data
}
pub fn identifier(&self) -> &'a str {
self.identifier
}
}

View File

@ -1,4 +1,3 @@
use crate::back::bytecode::DecodedBytecode;
use crate::back::write::{
self, save_temp_bitcode, to_llvm_opt_settings, with_llvm_pmb, DiagnosticHandlers,
};
@ -10,7 +9,7 @@ use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModul
use rustc_codegen_ssa::back::symbol_export;
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, RLIB_BYTECODE_EXTENSION};
use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{FatalError, Handler};
use rustc_hir::def_id::LOCAL_CRATE;
@ -111,22 +110,19 @@ fn prepare_lto(
}
let archive = ArchiveRO::open(&path).expect("wanted an rlib");
let bytecodes = archive
let obj_files = archive
.iter()
.filter_map(|child| child.ok().and_then(|c| c.name().map(|name| (name, c))))
.filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION));
for (name, data) in bytecodes {
let _timer =
cgcx.prof.generic_activity_with_arg("LLVM_lto_load_upstream_bitcode", name);
info!("adding bytecode {}", name);
let bc_encoded = data.data();
let (bc, id) = match DecodedBytecode::new(bc_encoded) {
Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
Err(e) => Err(diag_handler.fatal(&e)),
}?;
let bc = SerializedModule::FromRlib(bc);
upstream_modules.push((bc, CString::new(id).unwrap()));
.filter(|&(name, _)| looks_like_rust_object_file(name));
for (name, child) in obj_files {
info!("adding bitcode from {}", name);
match get_bitcode_slice_from_object_data(child.data()) {
Ok(data) => {
let module = SerializedModule::FromRlib(data.to_vec());
upstream_modules.push((module, CString::new(name).unwrap()));
}
Err(msg) => return Err(diag_handler.fatal(&msg)),
}
}
}
}
@ -134,6 +130,26 @@ fn prepare_lto(
Ok((symbol_white_list, upstream_modules))
}
fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], String> {
let mut len = 0;
let data =
unsafe { llvm::LLVMRustGetBitcodeSliceFromObjectData(obj.as_ptr(), obj.len(), &mut len) };
if !data.is_null() {
assert!(len != 0);
let bc = unsafe { slice::from_raw_parts(data, len) };
// `bc` must be a sub-slice of `obj`.
assert!(obj.as_ptr() <= bc.as_ptr());
assert!(bc[bc.len()..bc.len()].as_ptr() <= obj[obj.len()..obj.len()].as_ptr());
Ok(bc)
} else {
assert!(len == 0);
let msg = llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string());
Err(format!("failed to get bitcode from object file for LTO ({})", msg))
}
}
/// Performs fat LTO by merging all modules into a single one and returning it
/// for further optimization.
pub(crate) fn run_fat(

View File

@ -1,5 +1,4 @@
use crate::attributes;
use crate::back::bytecode;
use crate::back::lto::ThinBuffer;
use crate::back::profiling::{
selfprofile_after_pass_callback, selfprofile_before_pass_callback, LlvmSelfProfiler,
@ -16,7 +15,7 @@ use crate::ModuleLlvm;
use log::debug;
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, RLIB_BYTECODE_EXTENSION};
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_errors::{FatalError, Handler};
use rustc_fs_util::{link_or_copy, path_to_c_string};
@ -669,19 +668,6 @@ pub(crate) unsafe fn codegen(
);
embed_bitcode(cgcx, llcx, llmod, Some(data));
}
if config.emit_bc_compressed {
let _timer = cgcx.prof.generic_activity_with_arg(
"LLVM_module_codegen_emit_compressed_bitcode",
&module.name[..],
);
let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
let data = bytecode::encode(&module.name, data);
if let Err(e) = fs::write(&dst, data) {
let msg = format!("failed to write bytecode to {}: {}", dst.display(), e);
diag_handler.err(&msg);
}
}
} else if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Marker) {
embed_bitcode(cgcx, llcx, llmod, None);
}
@ -792,7 +778,6 @@ pub(crate) unsafe fn codegen(
Ok(module.into_compiled_module(
config.emit_obj != EmitObj::None,
config.emit_bc,
config.emit_bc_compressed,
&cgcx.output_filenames,
))
}
@ -847,6 +832,55 @@ unsafe fn embed_bitcode(
let section = if is_apple { "__LLVM,__cmdline\0" } else { ".llvmcmd\0" };
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
// We're adding custom sections to the output object file, but we definitely
// do not want these custom sections to make their way into the final linked
// executable. The purpose of these custom sections is for tooling
// surrounding object files to work with the LLVM IR, if necessary. For
// example rustc's own LTO will look for LLVM IR inside of the object file
// in these sections by default.
//
// To handle this is a bit different depending on the object file format
// used by the backend, broken down into a few different categories:
//
// * Mach-O - this is for macOS. Inspecting the source code for the native
// linker here shows that the `.llvmbc` and `.llvmcmd` sections are
// automatically skipped by the linker. In that case there's nothing extra
// that we need to do here.
//
// * Wasm - the native LLD linker is hard-coded to skip `.llvmbc` and
// `.llvmcmd` sections, so there's nothing extra we need to do.
//
// * COFF - if we don't do anything the linker will by default copy all
// these sections to the output artifact, not what we want! To subvert
// this we want to flag the sections we inserted here as
// `IMAGE_SCN_LNK_REMOVE`. Unfortunately though LLVM has no native way to
// do this. Thankfully though we can do this with some inline assembly,
// which is easy enough to add via module-level global inline asm.
//
// * ELF - this is very similar to COFF above. One difference is that these
// sections are removed from the output linked artifact when
// `--gc-sections` is passed, which we pass by default. If that flag isn't
// passed though then these sections will show up in the final output.
// Additionally the flag that we need to set here is `SHF_EXCLUDE`.
if is_apple
|| cgcx.opts.target_triple.triple().starts_with("wasm")
|| cgcx.opts.target_triple.triple().starts_with("asmjs")
{
// nothing to do here
} else if cgcx.opts.target_triple.triple().contains("windows") {
let asm = "
.section .llvmbc,\"n\"
.section .llvmcmd,\"n\"
";
llvm::LLVMRustAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len());
} else {
let asm = "
.section .llvmbc,\"e\"
.section .llvmcmd,\"e\"
";
llvm::LLVMRustAppendModuleInlineAsm(llmod, asm.as_ptr().cast(), asm.len());
}
}
pub unsafe fn with_llvm_pmb(

View File

@ -40,7 +40,6 @@ use std::sync::Arc;
mod back {
pub mod archive;
pub mod bytecode;
pub mod lto;
mod profiling;
pub mod write;

View File

@ -2138,6 +2138,11 @@ extern "C" {
len: usize,
Identifier: *const c_char,
) -> Option<&Module>;
pub fn LLVMRustGetBitcodeSliceFromObjectData(
Data: *const u8,
len: usize,
out_len: &mut usize,
) -> *const u8;
pub fn LLVMRustThinLTOGetDICompileUnit(
M: &Module,
CU1: &mut *mut c_void,

View File

@ -18,10 +18,7 @@ use super::archive::ArchiveBuilder;
use super::command::Command;
use super::linker::Linker;
use super::rpath::{self, RPathConfig};
use crate::{
looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME,
RLIB_BYTECODE_EXTENSION,
};
use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FILENAME};
use cc::windows_registry;
use tempfile::{Builder as TempFileBuilder, TempDir};
@ -130,10 +127,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
remove(sess, obj);
}
}
for obj in codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref())
{
remove(sess, obj);
}
if let Some(ref metadata_module) = codegen_results.metadata_module {
if let Some(ref obj) = metadata_module.object {
remove(sess, obj);
@ -143,9 +136,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
if let Some(ref obj) = allocator_module.object {
remove(sess, obj);
}
if let Some(ref bc) = allocator_module.bytecode_compressed {
remove(sess, bc);
}
}
}
});
@ -378,14 +368,6 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
// contain the metadata in a separate file.
ab.add_file(&emit_metadata(sess, &codegen_results.metadata, tmpdir));
// For LTO purposes, the bytecode of this library is also inserted
// into the archive.
for bytecode in
codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref())
{
ab.add_file(bytecode);
}
// After adding all files to the archive, we need to update the
// symbol table of the archive. This currently dies on macOS (see
// #11162), and isn't necessary there anyway
@ -1829,7 +1811,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
let mut any_objects = false;
for f in archive.src_files() {
if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
if f == METADATA_FILENAME {
archive.remove_file(&f);
continue;
}

View File

@ -5,7 +5,6 @@ use super::symbol_export::symbol_name_for_instance_in_crate;
use crate::{
CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind,
RLIB_BYTECODE_EXTENSION,
};
use crate::traits::*;
@ -100,7 +99,6 @@ pub struct ModuleConfig {
pub emit_pre_lto_bc: bool,
pub emit_no_opt_bc: bool,
pub emit_bc: bool,
pub emit_bc_compressed: bool,
pub emit_ir: bool,
pub emit_asm: bool,
pub emit_obj: EmitObj,
@ -145,9 +143,10 @@ impl ModuleConfig {
|| sess.opts.cg.linker_plugin_lto.enabled()
{
EmitObj::Bitcode
} else if sess.opts.debugging_opts.embed_bitcode {
} else if sess.opts.debugging_opts.embed_bitcode || need_crate_bitcode_for_rlib(sess) {
let force_full = need_crate_bitcode_for_rlib(sess);
match sess.opts.optimize {
config::OptLevel::No | config::OptLevel::Less => {
config::OptLevel::No | config::OptLevel::Less if !force_full => {
EmitObj::ObjectCode(BitcodeSection::Marker)
}
_ => EmitObj::ObjectCode(BitcodeSection::Full),
@ -196,16 +195,6 @@ impl ModuleConfig {
save_temps || sess.opts.output_types.contains_key(&OutputType::Bitcode),
save_temps
),
emit_bc_compressed: match kind {
ModuleKind::Regular | ModuleKind::Allocator => {
// Emit compressed bitcode files for the crate if we're
// emitting an rlib. Whenever an rlib is created, the
// bitcode is inserted into the archive in order to allow
// LTO against it.
need_crate_bitcode_for_rlib(sess)
}
ModuleKind::Metadata => false,
},
emit_ir: if_regular!(
sess.opts.output_types.contains_key(&OutputType::LlvmAssembly),
false
@ -261,7 +250,6 @@ impl ModuleConfig {
pub fn bitcode_needed(&self) -> bool {
self.emit_bc
|| self.emit_bc_compressed
|| self.emit_obj == EmitObj::Bitcode
|| self.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)
}
@ -482,9 +470,6 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
if let Some(ref path) = module.bytecode {
files.push((WorkProductFileKind::Bytecode, path.clone()));
}
if let Some(ref path) = module.bytecode_compressed {
files.push((WorkProductFileKind::BytecodeCompressed, path.clone()));
}
if let Some((id, product)) =
copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files)
@ -821,7 +806,6 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap();
let mut object = None;
let mut bytecode = None;
let mut bytecode_compressed = None;
for (kind, saved_file) in &module.source.saved_files {
let obj_out = match kind {
WorkProductFileKind::Object => {
@ -834,14 +818,6 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
bytecode = Some(path.clone());
path
}
WorkProductFileKind::BytecodeCompressed => {
let path = cgcx
.output_filenames
.temp_path(OutputType::Bitcode, Some(&module.name))
.with_extension(RLIB_BYTECODE_EXTENSION);
bytecode_compressed = Some(path.clone());
path
}
};
let source_file = in_incr_comp_dir(&incr_comp_session_dir, &saved_file);
debug!(
@ -863,14 +839,12 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
assert_eq!(object.is_some(), module_config.emit_obj != EmitObj::None);
assert_eq!(bytecode.is_some(), module_config.emit_bc);
assert_eq!(bytecode_compressed.is_some(), module_config.emit_bc_compressed);
Ok(WorkItemResult::Compiled(CompiledModule {
name: module.name,
kind: ModuleKind::Regular,
object,
bytecode,
bytecode_compressed,
}))
}

View File

@ -55,31 +55,18 @@ pub struct ModuleCodegen<M> {
// FIXME(eddyb) maybe include the crate name in this?
pub const METADATA_FILENAME: &str = "lib.rmeta";
pub const RLIB_BYTECODE_EXTENSION: &str = "bc.z";
impl<M> ModuleCodegen<M> {
pub fn into_compiled_module(
self,
emit_obj: bool,
emit_bc: bool,
emit_bc_compressed: bool,
outputs: &OutputFilenames,
) -> CompiledModule {
let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name)));
let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name)));
let bytecode_compressed = emit_bc_compressed.then(|| {
outputs
.temp_path(OutputType::Bitcode, Some(&self.name))
.with_extension(RLIB_BYTECODE_EXTENSION)
});
CompiledModule {
name: self.name.clone(),
kind: self.kind,
object,
bytecode,
bytecode_compressed,
}
CompiledModule { name: self.name.clone(), kind: self.kind, object, bytecode }
}
}
@ -89,7 +76,6 @@ pub struct CompiledModule {
pub kind: ModuleKind,
pub object: Option<PathBuf>,
pub bytecode: Option<PathBuf>,
pub bytecode_compressed: Option<PathBuf>,
}
pub struct CachedModuleCodegen {

View File

@ -21,7 +21,6 @@ pub fn copy_cgu_workproducts_to_incr_comp_cache_dir(
let extension = match kind {
WorkProductFileKind::Object => "o",
WorkProductFileKind::Bytecode => "bc",
WorkProductFileKind::BytecodeCompressed => "bc.z",
};
let file_name = format!("{}.{}", cgu_name, extension);
let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);

View File

@ -868,7 +868,6 @@ pub struct WorkProduct {
pub enum WorkProductFileKind {
Object,
Bytecode,
BytecodeCompressed,
}
#[derive(Clone)]

@ -1 +1 @@
Subproject commit 9f9da27fbdb0ba7d887f8d2521e082f12b009417
Subproject commit 3ba91917e52bd66ac37161ad4a1bc87d32aa2e18

View File

@ -13,6 +13,8 @@
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Passes/PassBuilder.h"
#if LLVM_VERSION_GE(9, 0)
#include "llvm/Passes/StandardInstrumentations.h"
@ -1478,6 +1480,32 @@ LLVMRustParseBitcodeForLTO(LLVMContextRef Context,
return wrap(std::move(*SrcOrError).release());
}
// Find the bitcode section in the object file data and return it as a slice.
// Fail if the bitcode section is present but empty.
//
// On success, the return value is the pointer to the start of the slice and
// `out_len` is filled with the (non-zero) length. On failure, the return value
// is `nullptr` and `out_len` is set to zero.
extern "C" const char*
LLVMRustGetBitcodeSliceFromObjectData(const char *data,
size_t len,
size_t *out_len) {
*out_len = 0;
StringRef Data(data, len);
MemoryBufferRef Buffer(Data, ""); // The id is unused.
Expected<MemoryBufferRef> BitcodeOrError =
object::IRObjectFile::findBitcodeInMemBuffer(Buffer);
if (!BitcodeOrError) {
LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str());
return nullptr;
}
*out_len = BitcodeOrError->getBufferSize();
return BitcodeOrError->getBufferStart();
}
// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
// the comment in `back/lto.rs` for why this exists.
extern "C" void

View File

@ -0,0 +1,6 @@
// compile-flags: -Clinker-plugin-lto
// no-prefer-dynamic
#![crate_type = "rlib"]
pub fn foo() {}

View File

@ -1,6 +1,6 @@
warning: Linking globals named 'foo': symbol multiply defined!
error: failed to load bc of "lto_duplicate_symbols2.3a1fbbbh-cgu.0":
error: failed to load bc of "lto-duplicate-symbols2.lto_duplicate_symbols2.3a1fbbbh-cgu.0.rcgu.o":
error: aborting due to previous error; 1 warning emitted

View File

@ -0,0 +1,17 @@
// compile-flags: -C lto
// aux-build:lto-rustc-loads-linker-plugin.rs
// run-pass
// no-prefer-dynamic
// This test ensures that if a dependency was compiled with
// `-Clinker-plugin-lto` then we can compile with `-Clto` and still link against
// that upstream rlib. This should work because LTO implies we're not actually
// linking against upstream rlibs since we're generating the object code
// locally. This test will fail if rustc can't find bytecode in rlibs compiled
// with `-Clinker-plugin-lto`.
extern crate lto_rustc_loads_linker_plugin;
fn main() {
lto_rustc_loads_linker_plugin::foo();
}

View File

@ -0,0 +1,13 @@
// compile-flags: -C lto=thin
// aux-build:lto-rustc-loads-linker-plugin.rs
// run-pass
// no-prefer-dynamic
// Same as the adjacent `lto-thin-rustc-loads-linker-plugin.rs` test, only with
// ThinLTO.
extern crate lto_rustc_loads_linker_plugin;
fn main() {
lto_rustc_loads_linker_plugin::foo();
}