support LTO against libraries built with codegen-units > 1
This commit is contained in:
parent
ad9ed40e7f
commit
ed476b02a6
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -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);
|
||||
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);
|
||||
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,
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue