Skip to content

support LTO against libraries built with codegen-units > 1 #17358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 20, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions src/librustc/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,19 +659,18 @@ fn link_rlib<'a>(sess: &'a Session,
ab.add_file(&metadata).unwrap();
remove(sess, &metadata);

if sess.opts.cg.codegen_units == 1 {
// For LTO purposes, the bytecode of this library is also
// inserted into the archive. We currently do this only when
// codegen_units == 1, so we don't have to deal with multiple
// bitcode files per crate.
//
// 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 i in range(0, sess.opts.cg.codegen_units) {
// 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 = obj_filename.with_extension("bc");
let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate");
let bc_filename = obj_filename.with_extension(format!("{}.bc", i).as_slice());
let bc_deflated_filename = obj_filename.with_extension(
format!("{}.bytecode.deflate", i).as_slice());

let bc_data = match fs::File::open(&bc_filename).read_to_end() {
Ok(buffer) => buffer,
Expand Down Expand Up @@ -705,8 +704,13 @@ fn link_rlib<'a>(sess: &'a Session,

ab.add_file(&bc_deflated_filename).unwrap();
remove(sess, &bc_deflated_filename);
if !sess.opts.cg.save_temps &&
!sess.opts.output_types.contains(&OutputTypeBitcode) {

// See the bottom of back::write::run_passes for an explanation
// of when we do and don't keep .0.bc files around.
let user_wants_numbered_bitcode =
sess.opts.output_types.contains(&OutputTypeBitcode) &&
sess.opts.cg.codegen_units > 1;
if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode {
remove(sess, &bc_filename);
}
}
Expand Down
137 changes: 72 additions & 65 deletions src/librustc/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use util::common::time;
use libc;
use flate;

use std::iter;
use std::mem;

pub fn run(sess: &session::Session, llmod: ModuleRef,
Expand Down Expand Up @@ -60,78 +61,84 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
let file = path.filename_str().unwrap();
let file = file.slice(3, file.len() - 5); // chop off lib/.rlib
debug!("reading {}", file);
let bc_encoded = time(sess.time_passes(),
format!("read {}.bytecode.deflate", name).as_slice(),
(),
|_| {
archive.read(format!("{}.bytecode.deflate",
file).as_slice())
});
let bc_encoded = match bc_encoded {
Some(data) => data,
None => {
sess.fatal(format!("missing compressed bytecode in {} \
(perhaps it was compiled with -C codegen-units > 1)",
path.display()).as_slice());
},
};
let bc_extractor = if is_versioned_bytecode_format(bc_encoded) {
|_| {
// Read the version
let version = extract_bytecode_format_version(bc_encoded);

if version == 1 {
// The only version existing so far
let data_size = extract_compressed_bytecode_size_v1(bc_encoded);
let compressed_data = bc_encoded.slice(
link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET,
link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as uint);

match flate::inflate_bytes(compressed_data) {
Some(inflated) => inflated,
for i in iter::count(0u, 1) {
let bc_encoded = time(sess.time_passes(),
format!("check for {}.{}.bytecode.deflate", name, i).as_slice(),
(),
|_| {
archive.read(format!("{}.{}.bytecode.deflate",
file, i).as_slice())
});
let bc_encoded = match bc_encoded {
Some(data) => data,
None => {
if i == 0 {
// No bitcode was found at all.
sess.fatal(format!("missing compressed bytecode in {}",
path.display()).as_slice());
}
// No more bitcode files to read.
break;
},
};
let bc_extractor = if is_versioned_bytecode_format(bc_encoded) {
|_| {
// Read the version
let version = extract_bytecode_format_version(bc_encoded);

if version == 1 {
// The only version existing so far
let data_size = extract_compressed_bytecode_size_v1(bc_encoded);
let compressed_data = bc_encoded.slice(
link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET,
link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as uint);

match flate::inflate_bytes(compressed_data) {
Some(inflated) => inflated,
None => {
sess.fatal(format!("failed to decompress bc of `{}`",
name).as_slice())
}
}
} else {
sess.fatal(format!("Unsupported bytecode format version {}",
version).as_slice())
}
}
} else {
// the object must be in the old, pre-versioning format, so simply
// inflate everything and let LLVM decide if it can make sense of it
|_| {
match flate::inflate_bytes(bc_encoded) {
Some(bc) => bc,
None => {
sess.fatal(format!("failed to decompress bc of `{}`",
name).as_slice())
}
}
} else {
sess.fatal(format!("Unsupported bytecode format version {}",
version).as_slice())
}
}
} else {
// the object must be in the old, pre-versioning format, so simply
// inflate everything and let LLVM decide if it can make sense of it
|_| {
match flate::inflate_bytes(bc_encoded) {
Some(bc) => bc,
None => {
sess.fatal(format!("failed to decompress bc of `{}`",
name).as_slice())
}
};

let bc_decoded = time(sess.time_passes(),
format!("decode {}.{}.bc", file, i).as_slice(),
(),
bc_extractor);

let ptr = bc_decoded.as_slice().as_ptr();
debug!("linking {}, part {}", name, i);
time(sess.time_passes(),
format!("ll link {}.{}", name, i).as_slice(),
(),
|()| unsafe {
if !llvm::LLVMRustLinkInExternalBitcode(llmod,
ptr as *const libc::c_char,
bc_decoded.len() as libc::size_t) {
write::llvm_err(sess.diagnostic().handler(),
format!("failed to load bc of `{}`",
name.as_slice()));
}
}
};

let bc_decoded = time(sess.time_passes(),
format!("decode {}.bc", file).as_slice(),
(),
bc_extractor);

let ptr = bc_decoded.as_slice().as_ptr();
debug!("linking {}", name);
time(sess.time_passes(),
format!("ll link {}", name).as_slice(),
(),
|()| unsafe {
if !llvm::LLVMRustLinkInExternalBitcode(llmod,
ptr as *const libc::c_char,
bc_decoded.len() as libc::size_t) {
write::llvm_err(sess.diagnostic().handler(),
format!("failed to load bc of `{}`",
name.as_slice()));
}
});
});
}
}

// Internalize everything but the reachable symbols of the current module
Expand Down
61 changes: 32 additions & 29 deletions src/librustc/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,13 +540,12 @@ pub fn run_passes(sess: &Session,
metadata_config.emit_bc = true;
}

// Emit a bitcode file for the crate if we're emitting an rlib.
// Emit 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.
let needs_crate_bitcode =
sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
sess.opts.output_types.contains(&OutputTypeExe) &&
sess.opts.cg.codegen_units == 1;
sess.opts.output_types.contains(&OutputTypeExe);
if needs_crate_bitcode {
modules_config.emit_bc = true;
}
Expand Down Expand Up @@ -602,19 +601,8 @@ pub fn run_passes(sess: &Session,
// Process the work items, optionally using worker threads.
if sess.opts.cg.codegen_units == 1 {
run_work_singlethreaded(sess, trans.reachable.as_slice(), work_items);

if needs_crate_bitcode {
// The only bitcode file produced (aside from metadata) was
// "crate.0.bc". Rename to "crate.bc" since that's what
// `link_rlib` expects to find.
fs::copy(&crate_output.with_extension("0.bc"),
&crate_output.temp_path(OutputTypeBitcode)).unwrap();
}
} else {
run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units);

assert!(!needs_crate_bitcode,
"can't produce a crate bitcode file from multiple compilation units");
}

// All codegen is finished.
Expand All @@ -624,14 +612,14 @@ pub fn run_passes(sess: &Session,

// Produce final compile outputs.

let copy_if_one_unit = |ext: &str, output_type: OutputType| {
let copy_if_one_unit = |ext: &str, output_type: OutputType, keep_numbered: bool| {
// Three cases:
if sess.opts.cg.codegen_units == 1 {
// 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
fs::copy(&crate_output.with_extension(ext),
&crate_output.path(output_type)).unwrap();
if !sess.opts.cg.save_temps {
if !sess.opts.cg.save_temps && !keep_numbered {
// The user just wants `foo.x`, not `foo.0.x`.
remove(sess, &crate_output.with_extension(ext));
}
Expand Down Expand Up @@ -716,17 +704,18 @@ pub fn run_passes(sess: &Session,
// Flag to indicate whether the user explicitly requested bitcode.
// Otherwise, we produced it only as a temporary output, and will need
// to get rid of it.
// FIXME: Since we don't support LTO anyway, maybe we can avoid
// producing the temporary .0.bc's in the first place?
let mut save_bitcode = false;
let mut user_wants_bitcode = false;
for output_type in output_types.iter() {
match *output_type {
OutputTypeBitcode => {
save_bitcode = true;
copy_if_one_unit("0.bc", OutputTypeBitcode);
user_wants_bitcode = true;
// Copy to .bc, but always keep the .0.bc. There is a later
// check to figure out if we should delete .0.bc files, or keep
// them for making an rlib.
copy_if_one_unit("0.bc", OutputTypeBitcode, true);
},
OutputTypeLlvmAssembly => { copy_if_one_unit("0.ll", OutputTypeLlvmAssembly); },
OutputTypeAssembly => { copy_if_one_unit("0.s", OutputTypeAssembly); },
OutputTypeLlvmAssembly => { copy_if_one_unit("0.ll", OutputTypeLlvmAssembly, false); },
OutputTypeAssembly => { copy_if_one_unit("0.s", OutputTypeAssembly, false); },
OutputTypeObject => { link_obj(&crate_output.path(OutputTypeObject)); },
OutputTypeExe => {
// If OutputTypeObject is already in the list, then
Expand All @@ -739,7 +728,7 @@ pub fn run_passes(sess: &Session,
},
}
}
let save_bitcode = save_bitcode;
let user_wants_bitcode = user_wants_bitcode;

// Clean up unwanted temporary files.

Expand All @@ -755,22 +744,36 @@ pub fn run_passes(sess: &Session,

if !sess.opts.cg.save_temps {
// Remove the temporary .0.o objects. If the user didn't
// explicitly request bitcode (with --emit=bc), we must remove
// .0.bc as well. (We don't touch the crate.bc that may have been
// produced earlier.)
// explicitly request bitcode (with --emit=bc), and the bitcode is not
// needed for building an rlib, then we must remove .0.bc as well.

// Specific rules for keeping .0.bc:
// - If we're building an rlib (`needs_crate_bitcode`), then keep
// it.
// - If the user requested bitcode (`user_wants_bitcode`), and
// codegen_units > 1, then keep it.
// - If the user requested bitcode but codegen_units == 1, then we
// can toss .0.bc because we copied it to .bc earlier.
// - If we're not building an rlib and the user didn't request
// bitcode, then delete .0.bc.
// If you change how this works, also update back::link::link_rlib,
// where .0.bc files are (maybe) deleted after making an rlib.
let keep_numbered_bitcode = needs_crate_bitcode ||
(user_wants_bitcode && sess.opts.cg.codegen_units > 1);

for i in range(0, trans.modules.len()) {
if modules_config.emit_obj {
let ext = format!("{}.o", i);
remove(sess, &crate_output.with_extension(ext.as_slice()));
}

if modules_config.emit_bc && !save_bitcode {
if modules_config.emit_bc && !keep_numbered_bitcode {
let ext = format!("{}.bc", i);
remove(sess, &crate_output.with_extension(ext.as_slice()));
}
}

if metadata_config.emit_bc && !save_bitcode {
if metadata_config.emit_bc && !user_wants_bitcode {
remove(sess, &crate_output.with_extension("metadata.bc"));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Make sure we give a sane error message when the user requests LTO with a
// library built with -C codegen-units > 1.
// Check that we can use `-Z lto` when linking against libraries that were
// separately compiled.

// aux-build:sepcomp_lib.rs
// compile-flags: -Z lto
// error-pattern:missing compressed bytecode
// no-prefer-dynamic

extern crate sepcomp_lib;
Expand Down