about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Pernsteiner <spernsteiner@mozilla.com>2014-09-17 16:18:12 -0700
committerStuart Pernsteiner <spernsteiner@mozilla.com>2014-09-17 16:58:20 -0700
commited476b02a6f6cdfae84b44974e1caf4fd9c81321 (patch)
treeae624069a1fc7805589e914c083e785c9a91dfed
parentad9ed40e7fec03158929ba3a2847870d54498d6d (diff)
downloadrust-ed476b02a6f6cdfae84b44974e1caf4fd9c81321.tar.gz
rust-ed476b02a6f6cdfae84b44974e1caf4fd9c81321.zip
support LTO against libraries built with codegen-units > 1
-rw-r--r--src/librustc/back/link.rs24
-rw-r--r--src/librustc/back/lto.rs137
-rw-r--r--src/librustc/back/write.rs61
-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;