diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2013-12-03 17:41:01 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2013-12-09 08:25:58 -0800 |
| commit | 52b835c5e7a68b32a8f0532f178c150d09be200d (patch) | |
| tree | 8bb348671eb0bc046cfeb3d233e54003c155b6e8 /src/librustc/back | |
| parent | 4e0cb316fc980f00e1b74f3fdb7a842b540be280 (diff) | |
| download | rust-52b835c5e7a68b32a8f0532f178c150d09be200d.tar.gz rust-52b835c5e7a68b32a8f0532f178c150d09be200d.zip | |
Store metadata separately in rlib files
Right now whenever an rlib file is linked against, all of the metadata from the rlib is pulled in to the final staticlib or binary. The reason for this is that the metadata is currently stored in a section of the object file. Note that this is intentional for dynamic libraries in order to distribute metadata bundled with static libraries. This commit alters the situation for rlib libraries to instead store the metadata in a separate file in the archive. In doing so, when the archive is passed to the linker, none of the metadata will get pulled into the result executable. Furthermore, the metadata file is skipped when assembling rlibs into an archive. The snag in this implementation comes with multiple output formats. When generating a dylib, the metadata needs to be in the object file, but when generating an rlib this needs to be separate. In order to accomplish this, the metadata variable is inserted into an entirely separate LLVM Module which is then codegen'd into a different location (foo.metadata.o). This is then linked into dynamic libraries and silently ignored for rlib files. While changing how metadata is inserted into archives, I have also stopped compressing metadata when inserted into rlib files. We have wanted to stop compressing metadata, but the sections it creates in object file sections are apparently too large. Thankfully if it's just an arbitrary file it doesn't matter how large it is. I have seen massive reductions in executable sizes, as well as staticlib output sizes (to confirm that this is all working).
Diffstat (limited to 'src/librustc/back')
| -rw-r--r-- | src/librustc/back/archive.rs | 14 | ||||
| -rw-r--r-- | src/librustc/back/link.rs | 123 |
2 files changed, 107 insertions, 30 deletions
diff --git a/src/librustc/back/archive.rs b/src/librustc/back/archive.rs index 2dd53f7d514..d0225866cbe 100644 --- a/src/librustc/back/archive.rs +++ b/src/librustc/back/archive.rs @@ -20,6 +20,8 @@ use std::str; use extra::tempfile::TempDir; use syntax::abi; +pub static METADATA_FILENAME: &'static str = "metadata"; + pub struct Archive { priv sess: Session, priv dst: Path, @@ -81,17 +83,22 @@ impl Archive { /// search in the relevant locations for a library named `name`. pub fn add_native_library(&mut self, name: &str) { let location = self.find_library(name); - self.add_archive(&location, name); + self.add_archive(&location, name, []); } /// Adds all of the contents of the rlib at the specified path to this /// archive. pub fn add_rlib(&mut self, rlib: &Path) { let name = rlib.filename_str().unwrap().split('-').next().unwrap(); - self.add_archive(rlib, name); + self.add_archive(rlib, name, [METADATA_FILENAME]); + } + + /// Adds an arbitrary file to this archive + pub fn add_file(&mut self, file: &Path) { + run_ar(self.sess, "r", None, [&self.dst, file]); } - fn add_archive(&mut self, archive: &Path, name: &str) { + fn add_archive(&mut self, archive: &Path, name: &str, skip: &[&str]) { let loc = TempDir::new("rsar").unwrap(); // First, extract the contents of the archive to a temporary directory @@ -106,6 +113,7 @@ impl Archive { let mut inputs = ~[]; for file in files.iter() { let filename = file.filename_str().unwrap(); + if skip.iter().any(|s| *s == filename) { continue } let filename = format!("r-{}-{}", name, filename); let new_filename = file.with_filename(filename); fs::rename(file, &new_filename); diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 1cb10d59e59..cbe2dbebc06 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -9,8 +9,9 @@ // except according to those terms. -use back::archive::Archive; +use back::archive::{Archive, METADATA_FILENAME}; use back::rpath; +use driver::driver::CrateTranslation; use driver::session::Session; use driver::session; use lib::llvm::llvm; @@ -88,10 +89,11 @@ pub mod write { use back::link::{output_type_assembly, output_type_bitcode}; use back::link::{output_type_exe, output_type_llvm_assembly}; use back::link::{output_type_object}; + use driver::driver::CrateTranslation; use driver::session::Session; use driver::session; use lib::llvm::llvm; - use lib::llvm::{ModuleRef, ContextRef}; + use lib::llvm::ModuleRef; use lib; use std::c_str::ToCStr; @@ -101,10 +103,11 @@ pub mod write { use std::str; pub fn run_passes(sess: Session, - llcx: ContextRef, - llmod: ModuleRef, + trans: &CrateTranslation, output_type: output_type, output: &Path) { + let llmod = trans.module; + let llcx = trans.context; unsafe { llvm::LLVMInitializePasses(); @@ -204,12 +207,23 @@ pub mod write { }) } - // Create a codegen-specific pass manager to emit the actual - // assembly or object files. This may not end up getting used, - // but we make it anyway for good measure. - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); - llvm::LLVMRustAddLibraryInfo(cpm, llmod); + // A codegen-specific pass manager is used to generate object + // files for an LLVM module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, + f: |PassManagerRef|) { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod); + f(cpm); + llvm::LLVMDisposePassManager(cpm); + + } match output_type { output_type_none => {} @@ -220,20 +234,47 @@ pub mod write { } output_type_llvm_assembly => { output.with_c_str(|output| { - llvm::LLVMRustPrintModule(cpm, llmod, output) + with_codegen(tm, llmod, |cpm| { + llvm::LLVMRustPrintModule(cpm, llmod, output); + }) }) } output_type_assembly => { - WriteOutputFile(sess, tm, cpm, llmod, output, lib::llvm::AssemblyFile); + with_codegen(tm, llmod, |cpm| { + WriteOutputFile(sess, tm, cpm, llmod, output, + lib::llvm::AssemblyFile); + }); + + // windows will invoke this function with an assembly output + // type when it's actually generating an object file. This + // is because g++ is used to compile the assembly instead of + // having LLVM directly output an object file. Regardless, + // in this case, we're going to possibly need a metadata + // file. + if sess.opts.output_type != output_type_assembly { + with_codegen(tm, trans.metadata_module, |cpm| { + let out = output.with_extension("metadata.o"); + WriteOutputFile(sess, tm, cpm, + trans.metadata_module, &out, + lib::llvm::ObjectFile); + }) + } } output_type_exe | output_type_object => { - WriteOutputFile(sess, tm, cpm, llmod, output, lib::llvm::ObjectFile); + with_codegen(tm, llmod, |cpm| { + WriteOutputFile(sess, tm, cpm, llmod, output, + lib::llvm::ObjectFile); + }); + with_codegen(tm, trans.metadata_module, |cpm| { + WriteOutputFile(sess, tm, cpm, trans.metadata_module, + &output.with_extension("metadata.o"), + lib::llvm::ObjectFile); + }) } } - llvm::LLVMDisposePassManager(cpm); - llvm::LLVMRustDisposeTargetMachine(tm); + llvm::LLVMDisposeModule(trans.metadata_module); llvm::LLVMDisposeModule(llmod); llvm::LLVMContextDispose(llcx); if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } @@ -782,10 +823,9 @@ pub fn get_cc_prog(sess: Session) -> ~str { /// Perform the linkage portion of the compilation phase. This will generate all /// of the requested outputs for this compilation session. pub fn link_binary(sess: Session, - crate_types: &[~str], + trans: &CrateTranslation, obj_filename: &Path, - out_filename: &Path, - lm: LinkMeta) { + out_filename: &Path) { let outputs = if sess.opts.test { // If we're generating a test executable, then ignore all other output // styles at all other locations @@ -795,7 +835,7 @@ pub fn link_binary(sess: Session, // look at what was in the crate file itself for generating output // formats. let mut outputs = sess.opts.outputs.clone(); - for ty in crate_types.iter() { + for ty in trans.crate_types.iter() { if "bin" == *ty { outputs.push(session::OutputExecutable); } else if "dylib" == *ty || "lib" == *ty { @@ -813,12 +853,13 @@ pub fn link_binary(sess: Session, }; for output in outputs.move_iter() { - link_binary_output(sess, output, obj_filename, out_filename, lm); + link_binary_output(sess, trans, output, obj_filename, out_filename); } - // Remove the temporary object file if we aren't saving temps + // Remove the temporary object file and metadata if we aren't saving temps if !sess.opts.save_temps { fs::unlink(obj_filename); + fs::unlink(&obj_filename.with_extension("metadata.o")); } } @@ -832,11 +873,11 @@ fn is_writeable(p: &Path) -> bool { } fn link_binary_output(sess: Session, + trans: &CrateTranslation, output: session::OutputStyle, obj_filename: &Path, - out_filename: &Path, - lm: LinkMeta) { - let libname = output_lib_filename(lm); + out_filename: &Path) { + let libname = output_lib_filename(trans.link); let out_filename = match output { session::OutputRlib => { out_filename.with_filename(format!("lib{}.rlib", libname)) @@ -874,7 +915,7 @@ fn link_binary_output(sess: Session, match output { session::OutputRlib => { - link_rlib(sess, obj_filename, &out_filename); + link_rlib(sess, Some(trans), obj_filename, &out_filename); } session::OutputStaticlib => { link_staticlib(sess, obj_filename, &out_filename); @@ -894,9 +935,25 @@ fn link_binary_output(sess: Session, // rlib primarily contains the object file of the crate, but it also contains // all of the object files from native libraries. This is done by unzipping // native libraries and inserting all of the contents into this archive. -fn link_rlib(sess: Session, obj_filename: &Path, +// +// Instead of putting the metadata in an object file section, instead rlibs +// contain the metadata in a separate file. +fn link_rlib(sess: Session, + trans: Option<&CrateTranslation>, // None == no metadata + obj_filename: &Path, out_filename: &Path) -> Archive { let mut a = Archive::create(sess, out_filename, obj_filename); + + match trans { + Some(trans) => { + let metadata = obj_filename.with_filename(METADATA_FILENAME); + fs::File::create(&metadata).write(trans.metadata); + a.add_file(&metadata); + fs::unlink(&metadata); + } + None => {} + } + for &(ref l, kind) in cstore::get_used_libraries(sess.cstore).iter() { match kind { cstore::NativeStatic => { @@ -916,8 +973,12 @@ fn link_rlib(sess: Session, obj_filename: &Path, // // Additionally, there's no way for us to link dynamic libraries, so we warn // about all dynamic library dependencies that they're not linked in. +// +// There's no need to include metadata in a static archive, so ensure to not +// link in the metadata object file (and also don't prepare the archive with a +// metadata file). fn link_staticlib(sess: Session, obj_filename: &Path, out_filename: &Path) { - let mut a = link_rlib(sess, obj_filename, out_filename); + let mut a = link_rlib(sess, None, obj_filename, out_filename); a.add_native_library("morestack"); let crates = cstore::get_used_crates(sess.cstore, cstore::RequireStatic); @@ -998,6 +1059,14 @@ fn link_args(sess: Session, ~"-o", out_filename.as_str().unwrap().to_owned(), obj_filename.as_str().unwrap().to_owned()]); + // When linking a dynamic library, we put the metadata into a section of the + // executable. This metadata is in a separate object file from the main + // object file, so we link that in here. + if dylib { + let metadata = obj_filename.with_extension("metadata.o"); + args.push(metadata.as_str().unwrap().to_owned()); + } + if sess.targ_cfg.os == abi::OsLinux { // GNU-style linkers will use this to omit linking to libraries which // don't actually fulfill any relocations, but only for libraries which |
