diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index b76515763fbdb..586a362b5e3fe 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -45,7 +45,7 @@ impl Step for Std { let compiler = builder.compiler(0, builder.config.build); let mut cargo = builder.cargo(compiler, Mode::Std, target, cargo_subcommand(builder.kind)); - std_cargo(builder, target, &mut cargo); + std_cargo(builder, target, compiler.stage, &mut cargo); builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target)); run_cargo( diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index f44096af6dd53..c7ae73f9f8b91 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -86,7 +86,7 @@ impl Step for Std { target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter()); let mut cargo = builder.cargo(compiler, Mode::Std, target, "build"); - std_cargo(builder, target, &mut cargo); + std_cargo(builder, target, compiler.stage, &mut cargo); builder.info(&format!( "Building stage{} std artifacts ({} -> {})", @@ -164,7 +164,7 @@ fn copy_third_party_objects( /// Configure cargo to compile the standard library, adding appropriate env vars /// and such. -pub fn std_cargo(builder: &Builder<'_>, target: Interned, cargo: &mut Cargo) { +pub fn std_cargo(builder: &Builder<'_>, target: Interned, stage: u32, cargo: &mut Cargo) { if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { cargo.env("MACOSX_DEPLOYMENT_TARGET", target); } @@ -231,6 +231,16 @@ pub fn std_cargo(builder: &Builder<'_>, target: Interned, cargo: &mut Ca } } } + + // libstd must be built with embedded bitcode so that the produced + // artifacts can be used for both LTO builds (which use bitcode) and + // non-LTO builds (which use object code). + // + // But we don't bother for the stage 0 compiler because it's never used + // with LTO. + if stage >= 1 { + cargo.rustflag("-Cembed-bitcode=yes"); + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 04da3cc1015b8..fc217a707db94 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -394,7 +394,7 @@ impl Step for Std { let run_cargo_rustdoc_for = |package: &str| { let mut cargo = builder.cargo(compiler, Mode::Std, target, "rustdoc"); - compile::std_cargo(builder, target, &mut cargo); + compile::std_cargo(builder, target, compiler.stage, &mut cargo); // Keep a whitelist so we do not build internal stdlib crates, these will be // build by the rustc step later if enabled. diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 85c5d28bb8924..125563b7b6086 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1725,7 +1725,7 @@ impl Step for Crate { let mut cargo = builder.cargo(compiler, mode, target, test_kind.subcommand()); match mode { Mode::Std => { - compile::std_cargo(builder, target, &mut cargo); + compile::std_cargo(builder, target, compiler.stage, &mut cargo); } Mode::Rustc => { builder.ensure(compile::Rustc { compiler, target }); diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 8dc6257ce2e58..49750ba26604a 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -381,6 +381,31 @@ the linker. The default is `yes` if not specified. +## embed-bitcode + +This flag controls whether or not the compiler embeds LLVM bitcode into +generated object files. It takes one of the following values: + +* `y`, `yes`, `on`, or no value: enable bitcode embedding (the default). +* `n`, `no`, or `off`: disable bitcode embedding. + +LLVM bitcode embedding 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 embed-bitcode=no` can significantly improve compile times and +reduce generated file sizes. For these reasons, Cargo uses `-C +embed-bitcode=no` whenever appropriate to reduce compilation costs. Likewise, +if you are building directly with `rustc` we recommend using `-C +embed-bitcode=no` whenever you are not using LTO. + +If combined with `-C lto`, `-C embed-bitcode=no` will cause `rustc` to abort at +start-up, because the combination is invalid. + +If combined with `-C linker-plugin-lto`, `-C embed-bitcode` (with any value) +will be ignored, because in that case generated object files contain only LLVM +bitcode, and no object code. + [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 diff --git a/src/librustc_codegen_llvm/back/archive.rs b/src/librustc_codegen_llvm/back/archive.rs index f1fe40d919eeb..a115a1e95163e 100644 --- a/src/librustc_codegen_llvm/back/archive.rs +++ b/src/librustc_codegen_llvm/back/archive.rs @@ -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; } diff --git a/src/librustc_codegen_llvm/back/bytecode.rs b/src/librustc_codegen_llvm/back/bytecode.rs deleted file mode 100644 index 0c8ce39132abb..0000000000000 --- a/src/librustc_codegen_llvm/back/bytecode.rs +++ /dev/null @@ -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 { - 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, &'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 { - 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 - } -} diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 816329e06c7a5..506ca3105bd29 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -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,21 @@ 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 { + .filter(|&(name, _)| looks_like_rust_object_file(name)); + for (name, data) in obj_files { 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())); + info!("adding bitcode from {}", name); + match get_bitcode_slice_from_object_data(data.data()) { + Ok(bc) => { + let bc = SerializedModule::FromRlib(bc.to_vec()); + upstream_modules.push((bc, CString::new(name).unwrap())); + } + Err(e) => return Err(diag_handler.fatal(&e)), + } } } } @@ -134,6 +132,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( diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 5708cb4e65418..bb367b5efb6d4 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -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, @@ -14,9 +13,9 @@ use crate::type_::Type; use crate::LlvmCodegenBackend; use crate::ModuleLlvm; use log::debug; -use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; +use rustc_codegen_ssa::back::write::{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}; @@ -662,28 +661,13 @@ pub(crate) unsafe fn codegen( } } - if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { + if config.emit_obj == (EmitObj::ObjectCode { bitcode_section: true }) { let _timer = cgcx.prof.generic_activity_with_arg( "LLVM_module_codegen_embed_bitcode", &module.name[..], ); - embed_bitcode(cgcx, llcx, llmod, Some(data)); + embed_bitcode(cgcx, llcx, llmod, 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); } if config.emit_ir { @@ -742,7 +726,7 @@ pub(crate) unsafe fn codegen( // because that triggers various errors like invalid IR or broken // binaries. So we must clone the module to produce the asm output // if we are also producing object code. - let llmod = if let EmitObj::ObjectCode(_) = config.emit_obj { + let llmod = if let EmitObj::ObjectCode { .. } = config.emit_obj { llvm::LLVMCloneModule(llmod) } else { llmod @@ -753,7 +737,7 @@ pub(crate) unsafe fn codegen( } match config.emit_obj { - EmitObj::ObjectCode(_) => { + EmitObj::ObjectCode { .. } => { let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); @@ -792,36 +776,31 @@ 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, )) } /// Embed the bitcode of an LLVM module in the LLVM module itself. /// -/// This is done primarily for iOS where it appears to be standard to compile C -/// code at least with `-fembed-bitcode` which creates two sections in the -/// executable: +/// This is much like compiling C code with `-fembed-bitcode` which creates two +/// sections in the executable: /// /// * __LLVM,__bitcode /// * __LLVM,__cmdline /// -/// It appears *both* of these sections are necessary to get the linker to -/// recognize what's going on. For us though we just always throw in an empty -/// cmdline section. -/// -/// Furthermore debug/O1 builds don't actually embed bitcode but rather just -/// embed an empty section. +/// On iOS it appears *both* of these sections are necessary to get the linker +/// to recognize what's going on. For us though we just always throw in an +/// empty cmdline section. /// -/// Basically all of this is us attempting to follow in the footsteps of clang -/// on iOS. See #35968 for lots more info. +/// This started with us attempting to follow in the footsteps of clang on iOS +/// (see #35968 for lots more info) but it is now used for all targets. unsafe fn embed_bitcode( cgcx: &CodegenContext, llcx: &llvm::Context, llmod: &llvm::Module, - bitcode: Option<&[u8]>, + bitcode: &[u8], ) { - let llconst = common::bytes_in_context(llcx, bitcode.unwrap_or(&[])); + let llconst = common::bytes_in_context(llcx, bitcode); let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index 939f9e9c2a0c7..5d4c7109d6580 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -39,7 +39,6 @@ use std::sync::Arc; mod back { pub mod archive; - pub mod bytecode; pub mod lto; mod profiling; pub mod write; diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index aeb34e5c9c954..ceeb528430fff 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -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, diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 4c66d901e7a99..77cec347448a5 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -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 @@ -1842,7 +1824,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; } diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index b1fb1ef0e331c..7d161ef3cf122 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -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::*; @@ -24,7 +23,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; -use rustc_middle::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::middle::exported_symbols::SymbolExportLevel; use rustc_middle::ty::TyCtxt; @@ -61,21 +60,7 @@ pub enum EmitObj { Bitcode, // Object code, possibly augmented with a bitcode section. - ObjectCode(BitcodeSection), -} - -/// What kind of llvm bitcode section to embed in an object file. -#[derive(Clone, Copy, PartialEq)] -pub enum BitcodeSection { - // No bitcode section. - None, - - // An empty bitcode section (to placate tools such as the iOS linker that - // require this section even if they don't use it). - Marker, - - // A full, uncompressed bitcode section. - Full, + ObjectCode { bitcode_section: bool }, } /// Module-specific configuration for `optimize_and_codegen`. @@ -100,7 +85,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,15 +129,11 @@ impl ModuleConfig { || sess.opts.cg.linker_plugin_lto.enabled() { EmitObj::Bitcode - } else if sess.opts.debugging_opts.embed_bitcode { - match sess.opts.optimize { - config::OptLevel::No | config::OptLevel::Less => { - EmitObj::ObjectCode(BitcodeSection::Marker) - } - _ => EmitObj::ObjectCode(BitcodeSection::Full), - } + } else if sess.opts.cg.embed_bitcode.unwrap_or(true) { + // `unwrap_or(true)` because `-C embed-bitcode` defaults to true. + EmitObj::ObjectCode { bitcode_section: true } } else { - EmitObj::ObjectCode(BitcodeSection::None) + EmitObj::ObjectCode { bitcode_section: false } }; ModuleConfig { @@ -196,16 +176,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,9 +231,8 @@ 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) + || self.emit_obj == EmitObj::ObjectCode { bitcode_section: true } } } @@ -377,11 +346,6 @@ pub struct CompiledModules { pub allocator_module: Option, } -fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { - sess.crate_types.borrow().contains(&config::CrateType::Rlib) - && sess.opts.output_types.contains_key(&OutputType::Exe) -} - fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { if sess.opts.incremental.is_none() { return false; @@ -476,13 +440,7 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( let mut files = vec![]; if let Some(ref path) = module.object { - files.push((WorkProductFileKind::Object, path.clone())); - } - 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())); + files.push(path.clone()); } if let Some((id, product)) = @@ -819,29 +777,9 @@ fn execute_copy_from_cache_work_item( ) -> Result, FatalError> { 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 => { - let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)); - object = Some(path.clone()); - path - } - WorkProductFileKind::Bytecode => { - let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(&module.name)); - 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 - } - }; + for saved_file in &module.source.saved_files { + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)); + object = Some(obj_out.clone()); let source_file = in_incr_comp_dir(&incr_comp_session_dir, &saved_file); debug!( "copying pre-existing module `{}` from {:?} to {}", @@ -861,15 +799,12 @@ fn execute_copy_from_cache_work_item( } 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, + bytecode: None, })) } diff --git a/src/librustc_codegen_ssa/lib.rs b/src/librustc_codegen_ssa/lib.rs index bf8441562c55b..fbe0d29a9a936 100644 --- a/src/librustc_codegen_ssa/lib.rs +++ b/src/librustc_codegen_ssa/lib.rs @@ -54,31 +54,18 @@ pub struct ModuleCodegen { // 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 ModuleCodegen { 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 } } } @@ -88,7 +75,6 @@ pub struct CompiledModule { pub kind: ModuleKind, pub object: Option, pub bytecode: Option, - pub bytecode_compressed: Option, } pub struct CachedModuleCodegen { diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index b75a428c62a09..99c799950c063 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -134,7 +134,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { for swp in work_products { let mut all_files_exist = true; - for &(_, ref file_name) in swp.work_product.saved_files.iter() { + for file_name in swp.work_product.saved_files.iter() { let path = in_incr_comp_dir_sess(sess, file_name); if !path.exists() { all_files_exist = false; diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 6d4ba45c2e6ed..4db6297712c59 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -74,9 +74,9 @@ pub fn save_work_product_index( if !new_work_products.contains_key(id) { work_product::delete_workproduct_files(sess, wp); debug_assert!( - wp.saved_files.iter().all(|&(_, ref file_name)| { - !in_incr_comp_dir_sess(sess, file_name).exists() - }) + wp.saved_files + .iter() + .all(|file_name| { !in_incr_comp_dir_sess(sess, file_name).exists() }) ); } } @@ -85,7 +85,7 @@ pub fn save_work_product_index( debug_assert!({ new_work_products .iter() - .flat_map(|(_, wp)| wp.saved_files.iter().map(|&(_, ref name)| name)) + .flat_map(|(_, wp)| wp.saved_files.iter()) .map(|name| in_incr_comp_dir_sess(sess, name)) .all(|path| path.exists()) }); diff --git a/src/librustc_incremental/persist/work_product.rs b/src/librustc_incremental/persist/work_product.rs index 4dd81b1df5759..a15ee6d81dbbc 100644 --- a/src/librustc_incremental/persist/work_product.rs +++ b/src/librustc_incremental/persist/work_product.rs @@ -2,7 +2,7 @@ use crate::persist::fs::*; use rustc_fs_util::link_or_copy; -use rustc_middle::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; use std::fs as std_fs; use std::path::PathBuf; @@ -10,23 +10,18 @@ use std::path::PathBuf; pub fn copy_cgu_workproducts_to_incr_comp_cache_dir( sess: &Session, cgu_name: &str, - files: &[(WorkProductFileKind, PathBuf)], + files: &[PathBuf], ) -> Option<(WorkProductId, WorkProduct)> { debug!("copy_cgu_workproducts_to_incr_comp_cache_dir({:?},{:?})", cgu_name, files); sess.opts.incremental.as_ref()?; let saved_files = files .iter() - .map(|&(kind, ref path)| { - let extension = match kind { - WorkProductFileKind::Object => "o", - WorkProductFileKind::Bytecode => "bc", - WorkProductFileKind::BytecodeCompressed => "bc.z", - }; - let file_name = format!("{}.{}", cgu_name, extension); + .map(|path| { + let file_name = format!("{}.o", cgu_name); let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); match link_or_copy(path, &path_in_incr_dir) { - Ok(_) => Some((kind, file_name)), + Ok(_) => Some(file_name), Err(err) => { sess.warn(&format!( "error copying object file `{}` \ @@ -48,7 +43,7 @@ pub fn copy_cgu_workproducts_to_incr_comp_cache_dir( } pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - for &(_, ref file_name) in &work_product.saved_files { + for file_name in &work_product.saved_files { let path = in_incr_comp_dir_sess(sess, file_name); match std_fs::remove_file(&path) { Ok(()) => {} diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index c75f3b279a258..b2e5f6c54edc1 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -505,6 +505,10 @@ fn test_codegen_options_tracking_hash() { opts = reference.clone(); opts.cg.linker_plugin_lto = LinkerPluginLto::LinkerPluginAuto; assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.embed_bitcode = Some(true); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); } #[test] diff --git a/src/librustc_middle/dep_graph/mod.rs b/src/librustc_middle/dep_graph/mod.rs index f56df19bfb061..34add7f7a99eb 100644 --- a/src/librustc_middle/dep_graph/mod.rs +++ b/src/librustc_middle/dep_graph/mod.rs @@ -12,7 +12,7 @@ mod dep_node; pub(crate) use rustc_query_system::dep_graph::DepNodeParams; pub use rustc_query_system::dep_graph::{ debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, - WorkProduct, WorkProductFileKind, WorkProductId, + WorkProduct, WorkProductId, }; pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt}; diff --git a/src/librustc_query_system/dep_graph/graph.rs b/src/librustc_query_system/dep_graph/graph.rs index fa2b51058a378..5f14a09b24daa 100644 --- a/src/librustc_query_system/dep_graph/graph.rs +++ b/src/librustc_query_system/dep_graph/graph.rs @@ -861,14 +861,7 @@ impl DepGraph { pub struct WorkProduct { pub cgu_name: String, /// Saved files associated with this CGU. - pub saved_files: Vec<(WorkProductFileKind, String)>, -} - -#[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, PartialEq)] -pub enum WorkProductFileKind { - Object, - Bytecode, - BytecodeCompressed, + pub saved_files: Vec, } #[derive(Clone)] diff --git a/src/librustc_query_system/dep_graph/mod.rs b/src/librustc_query_system/dep_graph/mod.rs index fbc91575ede41..4acb70c7e803b 100644 --- a/src/librustc_query_system/dep_graph/mod.rs +++ b/src/librustc_query_system/dep_graph/mod.rs @@ -6,7 +6,6 @@ mod query; mod serialized; pub use dep_node::{DepNode, DepNodeParams, WorkProductId}; -pub use graph::WorkProductFileKind; pub use graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct}; pub use prev::PreviousDepGraph; pub use query::DepGraphQuery; diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs index aaf30c583e263..56c577b12c907 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -1680,6 +1680,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } + if let Some(false) = cg.embed_bitcode { + match cg.lto { + LtoCli::No | LtoCli::Unspecified => {} + LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( + error_format, + "options `-C embed-bitcode=no` and `-C lto` are incompatible", + ), + } + } + let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); let cg = cg; diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 8cd6ca86f4689..53b3065e22c60 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -727,6 +727,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "compile the program with profiling instrumentation"), profile_use: Option = (None, parse_opt_pathbuf, [TRACKED], "use the given `.profdata` file for profile-guided optimization"), + embed_bitcode: Option = (None, parse_opt_bool, [TRACKED], + "embed LLVM bitcode in object files (default: yes)"), } options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, @@ -926,8 +928,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "run `dsymutil` and delete intermediate object files"), ui_testing: Option = (None, parse_opt_bool, [UNTRACKED], "format compiler diagnostics in a way that's better suitable for UI testing"), - embed_bitcode: bool = (false, parse_bool, [TRACKED], - "embed LLVM bitcode in object files"), strip_debuginfo_if_disabled: Option = (None, parse_opt_bool, [TRACKED], "tell the linker to strip debuginfo when building without debuginfo enabled."), share_generics: Option = (None, parse_opt_bool, [TRACKED], diff --git a/src/llvm-project b/src/llvm-project index 9f9da27fbdb0b..3ba91917e52bd 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 9f9da27fbdb0ba7d887f8d2521e082f12b009417 +Subproject commit 3ba91917e52bd66ac37161ad4a1bc87d32aa2e18 diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 9e8614e3b6d34..5b955e6c949e2 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -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" @@ -1496,6 +1498,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 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 diff --git a/src/test/ui/lto-duplicate-symbols.stderr b/src/test/ui/lto-duplicate-symbols.stderr index 02204830120a5..713b79bae32e6 100644 --- a/src/test/ui/lto-duplicate-symbols.stderr +++ b/src/test/ui/lto-duplicate-symbols.stderr @@ -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