diff options
| author | Stuart Pernsteiner <spernsteiner@mozilla.com> | 2014-09-17 16:18:12 -0700 |
|---|---|---|
| committer | Stuart Pernsteiner <spernsteiner@mozilla.com> | 2014-09-17 16:58:20 -0700 |
| commit | ed476b02a6f6cdfae84b44974e1caf4fd9c81321 (patch) | |
| tree | ae624069a1fc7805589e914c083e785c9a91dfed | |
| parent | ad9ed40e7fec03158929ba3a2847870d54498d6d (diff) | |
| download | rust-ed476b02a6f6cdfae84b44974e1caf4fd9c81321.tar.gz rust-ed476b02a6f6cdfae84b44974e1caf4fd9c81321.zip | |
support LTO against libraries built with codegen-units > 1
| -rw-r--r-- | src/librustc/back/link.rs | 24 | ||||
| -rw-r--r-- | src/librustc/back/lto.rs | 137 | ||||
| -rw-r--r-- | src/librustc/back/write.rs | 61 | ||||
| -rw-r--r-- | src/test/run-pass/sepcomp-lib-lto.rs (renamed from src/test/compile-fail/sepcomp-lib-lto.rs) | 5 |
4 files changed, 120 insertions, 107 deletions
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index de1eef1dce3..ddaafab7b38 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -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); } } diff --git a/src/librustc/back/lto.rs b/src/librustc/back/lto.rs index 250557d0af1..cd425b5fec1 100644 --- a/src/librustc/back/lto.rs +++ b/src/librustc/back/lto.rs @@ -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); - - 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 diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs index cff5ac5375d..184f8497abe 100644 --- a/src/librustc/back/write.rs +++ b/src/librustc/back/write.rs @@ -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")); } } diff --git a/src/test/compile-fail/sepcomp-lib-lto.rs b/src/test/run-pass/sepcomp-lib-lto.rs index 59706e20bed..51fd83a54cc 100644 --- a/src/test/compile-fail/sepcomp-lib-lto.rs +++ b/src/test/run-pass/sepcomp-lib-lto.rs @@ -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; |
