]> git.lizzy.rs Git - rust.git/commitdiff
support LTO against libraries built with codegen-units > 1
authorStuart Pernsteiner <spernsteiner@mozilla.com>
Wed, 17 Sep 2014 23:18:12 +0000 (16:18 -0700)
committerStuart Pernsteiner <spernsteiner@mozilla.com>
Wed, 17 Sep 2014 23:58:20 +0000 (16:58 -0700)
src/librustc/back/link.rs
src/librustc/back/lto.rs
src/librustc/back/write.rs
src/test/compile-fail/sepcomp-lib-lto.rs [deleted file]
src/test/run-pass/sepcomp-lib-lto.rs [new file with mode: 0644]

index de1eef1dce3be1554796d513c88e4db0fbacb66b..ddaafab7b383e72d54e419d8df4b7fc58e360956 100644 (file)
@@ -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);
                 }
             }
index 250557d0af1ac9f11fd8791b110a93835a5f8ce2..cd425b5fec170fafad637280fd6c4ffe5c23faed 100644 (file)
@@ -21,6 +21,7 @@
 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
index cff5ac5375dcd77fd892a685e8cf6dae491aec40..184f8497abe60015258a1b8a27da1385fffe2581 100644 (file)
@@ -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/compile-fail/sepcomp-lib-lto.rs
deleted file mode 100644 (file)
index 59706e2..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// 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.
-
-// aux-build:sepcomp_lib.rs
-// compile-flags: -Z lto
-// error-pattern:missing compressed bytecode
-// no-prefer-dynamic
-
-extern crate sepcomp_lib;
-use sepcomp_lib::a::one;
-use sepcomp_lib::b::two;
-use sepcomp_lib::c::three;
-
-fn main() {
-    assert_eq!(one(), 1);
-    assert_eq!(two(), 2);
-    assert_eq!(three(), 3);
-}
diff --git a/src/test/run-pass/sepcomp-lib-lto.rs b/src/test/run-pass/sepcomp-lib-lto.rs
new file mode 100644 (file)
index 0000000..51fd83a
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that we can use `-Z lto` when linking against libraries that were
+// separately compiled.
+
+// aux-build:sepcomp_lib.rs
+// compile-flags: -Z lto
+// no-prefer-dynamic
+
+extern crate sepcomp_lib;
+use sepcomp_lib::a::one;
+use sepcomp_lib::b::two;
+use sepcomp_lib::c::three;
+
+fn main() {
+    assert_eq!(one(), 1);
+    assert_eq!(two(), 2);
+    assert_eq!(three(), 3);
+}