diff options
Diffstat (limited to 'src/librustc_codegen_llvm')
| -rw-r--r-- | src/librustc_codegen_llvm/back/archive.rs | 6 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/back/bytecode.rs | 141 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/back/lto.rs | 48 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/back/write.rs | 66 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/lib.rs | 1 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/llvm/ffi.rs | 5 |
6 files changed, 90 insertions, 177 deletions
diff --git a/src/librustc_codegen_llvm/back/archive.rs b/src/librustc_codegen_llvm/back/archive.rs index f1fe40d919e..a115a1e9516 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 0c8ce39132a..00000000000 --- 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<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 - } -} diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 7292492a0c0..e65bdbe171b 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,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( diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index b57ad102d63..5f4c122f02c 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, @@ -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( diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index 64158679740..5effde444af 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -40,7 +40,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 aeb34e5c9c9..ceeb528430f 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, |
