Skip to content

Commit

Permalink
rustc: Move bytecode compression into codegen
Browse files Browse the repository at this point in the history
This commit moves compression of the bytecode from the `link` module to the
`write` module, namely allowing it to be (a) cached by incremental compilation
and (b) produced in parallel. The parallelization may show up as some nice wins
during normal compilation and the caching in incremental mode should be
beneficial for incremental compiles! (no more need to recompress the entire
crate's bitcode on all builds)
  • Loading branch information
alexcrichton committed Oct 21, 2017
1 parent ff8773d commit 8197a0b
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 175 deletions.
10 changes: 8 additions & 2 deletions src/librustc/dep_graph/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
StableHashingContextProvider};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use session::config::OutputType;
use std::cell::{Ref, RefCell};
use std::env;
use std::hash::Hash;
Expand Down Expand Up @@ -647,7 +646,14 @@ impl DepGraph {
pub struct WorkProduct {
pub cgu_name: String,
/// Saved files associated with this CGU
pub saved_files: Vec<(OutputType, String)>,
pub saved_files: Vec<(WorkProductFileKind, String)>,
}

#[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable)]
pub enum WorkProductFileKind {
Object,
Bytecode,
BytecodeCompressed,
}

pub(super) struct CurrentDepGraph {
Expand Down
1 change: 1 addition & 0 deletions src/librustc/dep_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod serialized;
pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig};
pub use self::dep_node::{DepNode, DepKind, DepConstructor, WorkProductId, label_strs};
pub use self::graph::{DepGraph, WorkProduct, DepNodeIndex, DepNodeColor};
pub use self::graph::WorkProductFileKind;
pub use self::prev::PreviousDepGraph;
pub use self::query::DepGraphQuery;
pub use self::safe::AssertDepGraphSafe;
Expand Down
14 changes: 9 additions & 5 deletions src/librustc_incremental/persist/work_product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,33 @@
//! This module contains files for saving intermediate work-products.
use persist::fs::*;
use rustc::dep_graph::{WorkProduct, WorkProductId, DepGraph};
use rustc::dep_graph::{WorkProduct, WorkProductId, DepGraph, WorkProductFileKind};
use rustc::session::Session;
use rustc::session::config::OutputType;
use rustc::util::fs::link_or_copy;
use std::path::PathBuf;
use std::fs as std_fs;

pub fn save_trans_partition(sess: &Session,
dep_graph: &DepGraph,
cgu_name: &str,
files: &[(OutputType, PathBuf)]) {
files: &[(WorkProductFileKind, PathBuf)]) {
debug!("save_trans_partition({:?},{:?})",
cgu_name,
files);
if sess.opts.incremental.is_none() {
return;
return
}
let work_product_id = WorkProductId::from_cgu_name(cgu_name);

let saved_files: Option<Vec<_>> =
files.iter()
.map(|&(kind, ref path)| {
let file_name = format!("cgu-{}.{}", cgu_name, kind.extension());
let extension = match kind {
WorkProductFileKind::Object => "o",
WorkProductFileKind::Bytecode => "bc",
WorkProductFileKind::BytecodeCompressed => "bc-compressed",
};
let file_name = format!("cgu-{}.{}", cgu_name, extension);
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)),
Expand Down
138 changes: 45 additions & 93 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// except according to those terms.

use super::archive::{ArchiveBuilder, ArchiveConfig};
use super::bytecode::{self, RLIB_BYTECODE_EXTENSION};
use super::bytecode::RLIB_BYTECODE_EXTENSION;
use super::linker::Linker;
use super::command::Command;
use super::rpath::RPathConfig;
Expand Down Expand Up @@ -37,7 +37,7 @@ use std::env;
use std::ffi::OsString;
use std::fmt;
use std::fs::{self, File};
use std::io::{self, Read, Write, BufWriter};
use std::io::{self, Write, BufWriter};
use std::path::{Path, PathBuf};
use std::process::{Output, Stdio};
use std::str;
Expand Down Expand Up @@ -126,14 +126,6 @@ fn command_path(sess: &Session) -> OsString {
env::join_paths(new_path).unwrap()
}

fn metadata_obj(outputs: &OutputFilenames) -> PathBuf {
outputs.temp_path(OutputType::Object, Some(METADATA_MODULE_NAME))
}

fn allocator_obj(outputs: &OutputFilenames) -> PathBuf {
outputs.temp_path(OutputType::Object, Some(ALLOCATOR_MODULE_NAME))
}

pub fn remove(sess: &Session, path: &Path) {
match fs::remove_file(path) {
Ok(..) => {}
Expand Down Expand Up @@ -175,13 +167,23 @@ pub fn link_binary(sess: &Session,
// Remove the temporary object file and metadata if we aren't saving temps
if !sess.opts.cg.save_temps {
if sess.opts.output_types.should_trans() {
for obj in trans.modules.iter() {
remove(sess, &obj.object);
for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) {
remove(sess, obj);
}
}
remove(sess, &metadata_obj(outputs));
if trans.allocator_module.is_some() {
remove(sess, &allocator_obj(outputs));
for obj in trans.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) {
remove(sess, obj);
}
if let Some(ref obj) = trans.metadata_module.object {
remove(sess, obj);
}
if let Some(ref allocator) = trans.allocator_module {
if let Some(ref obj) = allocator.object {
remove(sess, obj);
}
if let Some(ref bc) = allocator.bytecode_compressed {
remove(sess, bc);
}
}
}

Expand Down Expand Up @@ -256,8 +258,8 @@ fn link_binary_output(sess: &Session,
crate_type: config::CrateType,
outputs: &OutputFilenames,
crate_name: &str) -> Vec<PathBuf> {
for module in trans.modules.iter() {
check_file_is_writeable(&module.object, sess);
for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) {
check_file_is_writeable(obj, sess);
}

let tmpdir = match TempDir::new("rustc") {
Expand All @@ -280,20 +282,14 @@ fn link_binary_output(sess: &Session,
link_rlib(sess,
trans,
RlibFlavor::Normal,
outputs,
&out_filename,
tmpdir.path()).build();
}
config::CrateTypeStaticlib => {
link_staticlib(sess,
trans,
outputs,
&out_filename,
tmpdir.path());
link_staticlib(sess, trans, &out_filename, tmpdir.path());
}
_ => {
link_natively(sess, crate_type, &out_filename,
trans, outputs, tmpdir.path());
link_natively(sess, crate_type, &out_filename, trans, tmpdir.path());
}
}
out_filenames.push(out_filename);
Expand Down Expand Up @@ -349,14 +345,13 @@ enum RlibFlavor {
fn link_rlib<'a>(sess: &'a Session,
trans: &CrateTranslation,
flavor: RlibFlavor,
outputs: &OutputFilenames,
out_filename: &Path,
tmpdir: &Path) -> ArchiveBuilder<'a> {
info!("preparing rlib to {:?}", out_filename);
let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None));

for module in trans.modules.iter() {
ab.add_file(&module.object);
for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) {
ab.add_file(obj);
}

// Note that in this loop we are ignoring the value of `lib.cfg`. That is,
Expand Down Expand Up @@ -421,56 +416,9 @@ fn link_rlib<'a>(sess: &'a Session,
ab.add_file(&metadata);

// For LTO purposes, the bytecode of this library is also inserted
// into the archive. If codegen_units > 1, we insert each of the
// bitcode files.
for module in trans.modules.iter() {
// Note that we make sure that the bytecode filename in the
// archive is never exactly 16 bytes long by adding a 16 byte
// extension to it. This is to work around a bug in LLDB that
// would cause it to crash if the name of a file in an archive
// was exactly 16 bytes.
let bc_filename = module.object.with_extension("bc");
let bc_encoded_filename = tmpdir.join({
module.object.with_extension(RLIB_BYTECODE_EXTENSION).file_name().unwrap()
});

let mut bc_data = Vec::new();
match fs::File::open(&bc_filename).and_then(|mut f| {
f.read_to_end(&mut bc_data)
}) {
Ok(..) => {}
Err(e) => sess.fatal(&format!("failed to read bytecode: {}",
e))
}

let encoded = bytecode::encode(&module.llmod_id, &bc_data);

let mut bc_file_deflated = match fs::File::create(&bc_encoded_filename) {
Ok(file) => file,
Err(e) => {
sess.fatal(&format!("failed to create compressed \
bytecode file: {}", e))
}
};

match bc_file_deflated.write_all(&encoded) {
Ok(()) => {}
Err(e) => {
sess.fatal(&format!("failed to write compressed \
bytecode: {}", e));
}
};

ab.add_file(&bc_encoded_filename);

// See the bottom of back::write::run_passes for an explanation
// of when we do and don't keep .#module-name#.bc files around.
let user_wants_numbered_bitcode =
sess.opts.output_types.contains_key(&OutputType::Bitcode) &&
sess.codegen_units() > 1;
if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode {
remove(sess, &bc_filename);
}
// into the archive.
for bytecode in trans.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
Expand All @@ -482,8 +430,11 @@ fn link_rlib<'a>(sess: &'a Session,
}

RlibFlavor::StaticlibBase => {
if trans.allocator_module.is_some() {
ab.add_file(&allocator_obj(outputs));
let obj = trans.allocator_module
.as_ref()
.and_then(|m| m.object.as_ref());
if let Some(obj) = obj {
ab.add_file(obj);
}
}
}
Expand All @@ -505,13 +456,11 @@ fn link_rlib<'a>(sess: &'a Session,
// metadata file).
fn link_staticlib(sess: &Session,
trans: &CrateTranslation,
outputs: &OutputFilenames,
out_filename: &Path,
tempdir: &Path) {
let mut ab = link_rlib(sess,
trans,
RlibFlavor::StaticlibBase,
outputs,
out_filename,
tempdir);
let mut all_native_libs = vec![];
Expand Down Expand Up @@ -616,7 +565,6 @@ fn link_natively(sess: &Session,
crate_type: config::CrateType,
out_filename: &Path,
trans: &CrateTranslation,
outputs: &OutputFilenames,
tmpdir: &Path) {
info!("preparing {:?} to {:?}", crate_type, out_filename);
let flavor = sess.linker_flavor();
Expand Down Expand Up @@ -656,7 +604,7 @@ fn link_natively(sess: &Session,
{
let mut linker = trans.linker_info.to_linker(cmd, &sess);
link_args(&mut *linker, sess, crate_type, tmpdir,
out_filename, outputs, trans);
out_filename, trans);
cmd = linker.finalize();
}
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
Expand Down Expand Up @@ -878,7 +826,6 @@ fn link_args(cmd: &mut Linker,
crate_type: config::CrateType,
tmpdir: &Path,
out_filename: &Path,
outputs: &OutputFilenames,
trans: &CrateTranslation) {

// The default library location, we need this to find the runtime.
Expand All @@ -889,8 +836,8 @@ fn link_args(cmd: &mut Linker,
let t = &sess.target.target;

cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
for module in trans.modules.iter() {
cmd.add_object(&module.object);
for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) {
cmd.add_object(obj);
}
cmd.output_filename(out_filename);

Expand All @@ -913,11 +860,16 @@ fn link_args(cmd: &mut Linker,
// object file, so we link that in here.
if crate_type == config::CrateTypeDylib ||
crate_type == config::CrateTypeProcMacro {
cmd.add_object(&metadata_obj(outputs));
if let Some(obj) = trans.metadata_module.object.as_ref() {
cmd.add_object(obj);
}
}

if trans.allocator_module.is_some() {
cmd.add_object(&allocator_obj(outputs));
let obj = trans.allocator_module
.as_ref()
.and_then(|m| m.object.as_ref());
if let Some(obj) = obj {
cmd.add_object(obj);
}

// Try to strip as much out of the generated object by removing unused
Expand Down Expand Up @@ -1185,9 +1137,9 @@ fn add_upstream_rust_crates(cmd: &mut Linker,

for f in archive.src_files() {
if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
archive.remove_file(&f);
continue
}
archive.remove_file(&f);
continue
}
}

archive.build();
Expand Down
18 changes: 12 additions & 6 deletions src/librustc_trans/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,7 @@ fn thin_lto(diag_handler: &Handler,
info!("local module: {} - {}", i, module.llmod_id);
let llvm = module.llvm().expect("can't lto pretranslated module");
let name = CString::new(module.llmod_id.clone()).unwrap();
let buffer = llvm::LLVMRustThinLTOBufferCreate(llvm.llmod);
let buffer = ThinBuffer(buffer);
let buffer = ThinBuffer::new(llvm.llmod);
thin_modules.push(llvm::ThinLTOModule {
identifier: name.as_ptr(),
data: buffer.data().as_ptr(),
Expand Down Expand Up @@ -499,13 +498,13 @@ unsafe impl Send for ModuleBuffer {}
unsafe impl Sync for ModuleBuffer {}

impl ModuleBuffer {
fn new(m: ModuleRef) -> ModuleBuffer {
pub fn new(m: ModuleRef) -> ModuleBuffer {
ModuleBuffer(unsafe {
llvm::LLVMRustModuleBufferCreate(m)
})
}

fn data(&self) -> &[u8] {
pub fn data(&self) -> &[u8] {
unsafe {
let ptr = llvm::LLVMRustModuleBufferPtr(self.0);
let len = llvm::LLVMRustModuleBufferLen(self.0);
Expand Down Expand Up @@ -545,13 +544,20 @@ impl Drop for ThinData {
}
}

struct ThinBuffer(*mut llvm::ThinLTOBuffer);
pub struct ThinBuffer(*mut llvm::ThinLTOBuffer);

unsafe impl Send for ThinBuffer {}
unsafe impl Sync for ThinBuffer {}

impl ThinBuffer {
fn data(&self) -> &[u8] {
pub fn new(m: ModuleRef) -> ThinBuffer {
unsafe {
let buffer = llvm::LLVMRustThinLTOBufferCreate(m);
ThinBuffer(buffer)
}
}

pub fn data(&self) -> &[u8] {
unsafe {
let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _;
let len = llvm::LLVMRustThinLTOBufferLen(self.0);
Expand Down
Loading

0 comments on commit 8197a0b

Please sign in to comment.