about summary refs log tree commit diff
path: root/src/librustc_codegen_llvm
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2020-04-23 11:45:55 -0700
committerAlex Crichton <alex@alexcrichton.com>2020-04-29 11:57:26 -0700
commitef89cc8f042a980e72e9d0262c267bfffd9e75fe (patch)
tree82cc2d96faf8edf01975f5858876c4d16fb9b461 /src/librustc_codegen_llvm
parent413a12909f3b149af17d75268ed4a136afb82c36 (diff)
downloadrust-ef89cc8f042a980e72e9d0262c267bfffd9e75fe.tar.gz
rust-ef89cc8f042a980e72e9d0262c267bfffd9e75fe.zip
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.
Diffstat (limited to 'src/librustc_codegen_llvm')
-rw-r--r--src/librustc_codegen_llvm/back/archive.rs6
-rw-r--r--src/librustc_codegen_llvm/back/bytecode.rs141
-rw-r--r--src/librustc_codegen_llvm/back/lto.rs48
-rw-r--r--src/librustc_codegen_llvm/back/write.rs66
-rw-r--r--src/librustc_codegen_llvm/lib.rs1
-rw-r--r--src/librustc_codegen_llvm/llvm/ffi.rs5
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,