auto merge of #10812 : alexcrichton/rust/lto, r=pcwalton
The first commit was approved from another pull request, but I wanted to rebase LTO on top of it. LTO is not turned on by default at all, and it's hidden behind a `-Z` flag. I have added a few small tests for it, however.
This commit is contained in:
commit
29ca4350c8
22 changed files with 537 additions and 151 deletions
|
|
@ -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,
|
||||
|
|
@ -40,7 +42,8 @@ fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
|
|||
}
|
||||
let o = Process::new(ar, args.as_slice(), opts).finish_with_output();
|
||||
if !o.status.success() {
|
||||
sess.err(format!("{} failed with: {}", ar, o.status));
|
||||
sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
|
||||
o.status));
|
||||
sess.note(format!("stdout ---\n{}", str::from_utf8(o.output)));
|
||||
sess.note(format!("stderr ---\n{}", str::from_utf8(o.error)));
|
||||
sess.abort_if_errors();
|
||||
|
|
@ -81,17 +84,40 @@ 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);
|
||||
///
|
||||
/// This ignores adding the bytecode from the rlib, and if LTO is enabled
|
||||
/// then the object file also isn't added.
|
||||
pub fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool) {
|
||||
let object = format!("{}.o", name);
|
||||
let bytecode = format!("{}.bc", name);
|
||||
let mut ignore = ~[METADATA_FILENAME, bytecode.as_slice()];
|
||||
if lto {
|
||||
ignore.push(object.as_slice());
|
||||
}
|
||||
self.add_archive(rlib, name, ignore);
|
||||
}
|
||||
|
||||
fn add_archive(&mut self, archive: &Path, name: &str) {
|
||||
/// Adds an arbitrary file to this archive
|
||||
pub fn add_file(&mut self, file: &Path) {
|
||||
run_ar(self.sess, "r", None, [&self.dst, file]);
|
||||
}
|
||||
|
||||
/// Removes a file from this archive
|
||||
pub fn remove_file(&mut self, file: &str) {
|
||||
run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
|
||||
}
|
||||
|
||||
pub fn files(&self) -> ~[~str] {
|
||||
let output = run_ar(self.sess, "t", None, [&self.dst]);
|
||||
str::from_utf8(output.output).lines().map(|s| s.to_owned()).collect()
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -102,10 +128,17 @@ impl Archive {
|
|||
// The reason for this is that archives are keyed off the name of the
|
||||
// files, so if two files have the same name they will override one
|
||||
// another in the archive (bad).
|
||||
//
|
||||
// We skip any files explicitly desired for skipping, and we also skip
|
||||
// all SYMDEF files as these are just magical placeholders which get
|
||||
// re-created when we make a new archive anyway.
|
||||
let files = fs::readdir(loc.path());
|
||||
let mut inputs = ~[];
|
||||
for file in files.iter() {
|
||||
let filename = file.filename_str().unwrap();
|
||||
if skip.iter().any(|s| *s == filename) { continue }
|
||||
if filename.contains(".SYMDEF") { continue }
|
||||
|
||||
let filename = format!("r-{}-{}", name, filename);
|
||||
let new_filename = file.with_filename(filename);
|
||||
fs::rename(file, &new_filename);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -21,6 +22,7 @@ use metadata::{encoder, cstore, filesearch, csearch};
|
|||
use middle::trans::context::CrateContext;
|
||||
use middle::trans::common::gensym_name;
|
||||
use middle::ty;
|
||||
use util::common::time;
|
||||
use util::ppaux;
|
||||
|
||||
use std::c_str::ToCStr;
|
||||
|
|
@ -32,6 +34,7 @@ use std::ptr;
|
|||
use std::run;
|
||||
use std::str;
|
||||
use std::io::fs;
|
||||
use extra::tempfile::TempDir;
|
||||
use syntax::abi;
|
||||
use syntax::ast;
|
||||
use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
|
||||
|
|
@ -84,15 +87,18 @@ pub fn WriteOutputFile(
|
|||
|
||||
pub mod write {
|
||||
|
||||
use back::lto;
|
||||
use back::link::{WriteOutputFile, output_type};
|
||||
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, TargetMachineRef, PassManagerRef};
|
||||
use lib;
|
||||
use util::common::time;
|
||||
|
||||
use std::c_str::ToCStr;
|
||||
use std::libc::{c_uint, c_int};
|
||||
|
|
@ -101,10 +107,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();
|
||||
|
||||
|
|
@ -191,49 +198,106 @@ pub mod write {
|
|||
}
|
||||
|
||||
// Finally, run the actual optimization passes
|
||||
llvm::LLVMRustRunFunctionPassManager(fpm, llmod);
|
||||
llvm::LLVMRunPassManager(mpm, llmod);
|
||||
time(sess.time_passes(), "llvm function passes", (), |()|
|
||||
llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
|
||||
time(sess.time_passes(), "llvm module passes", (), |()|
|
||||
llvm::LLVMRunPassManager(mpm, llmod));
|
||||
|
||||
// Deallocate managers that we're now done with
|
||||
llvm::LLVMDisposePassManager(fpm);
|
||||
llvm::LLVMDisposePassManager(mpm);
|
||||
|
||||
if sess.opts.save_temps {
|
||||
// Emit the bytecode if we're either saving our temporaries or
|
||||
// emitting an rlib. Whenever an rlib is create, the bytecode is
|
||||
// inserted into the archive in order to allow LTO against it.
|
||||
if sess.opts.save_temps ||
|
||||
sess.outputs.iter().any(|&o| o == session::OutputRlib) {
|
||||
output.with_extension("bc").with_c_str(|buf| {
|
||||
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
||||
})
|
||||
}
|
||||
|
||||
// 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);
|
||||
if sess.lto() {
|
||||
time(sess.time_passes(), "all lto passes", (), |()|
|
||||
lto::run(sess, llmod, tm, trans.reachable));
|
||||
|
||||
match output_type {
|
||||
output_type_none => {}
|
||||
output_type_bitcode => {
|
||||
output.with_c_str(|buf| {
|
||||
if sess.opts.save_temps {
|
||||
output.with_extension("lto.bc").with_c_str(|buf| {
|
||||
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
||||
})
|
||||
}
|
||||
output_type_llvm_assembly => {
|
||||
output.with_c_str(|output| {
|
||||
llvm::LLVMRustPrintModule(cpm, llmod, output)
|
||||
})
|
||||
}
|
||||
output_type_assembly => {
|
||||
WriteOutputFile(sess, tm, cpm, llmod, output, lib::llvm::AssemblyFile);
|
||||
}
|
||||
output_type_exe | output_type_object => {
|
||||
WriteOutputFile(sess, tm, cpm, llmod, output, lib::llvm::ObjectFile);
|
||||
}
|
||||
|
||||
// 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|) {
|
||||
unsafe {
|
||||
let cpm = llvm::LLVMCreatePassManager();
|
||||
llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
|
||||
llvm::LLVMRustAddLibraryInfo(cpm, llmod);
|
||||
f(cpm);
|
||||
llvm::LLVMDisposePassManager(cpm);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::LLVMDisposePassManager(cpm);
|
||||
time(sess.time_passes(), "codegen passes", (), |()| {
|
||||
match output_type {
|
||||
output_type_none => {}
|
||||
output_type_bitcode => {
|
||||
output.with_c_str(|buf| {
|
||||
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
||||
})
|
||||
}
|
||||
output_type_llvm_assembly => {
|
||||
output.with_c_str(|output| {
|
||||
with_codegen(tm, llmod, |cpm| {
|
||||
llvm::LLVMRustPrintModule(cpm, llmod, output);
|
||||
})
|
||||
})
|
||||
}
|
||||
output_type_assembly => {
|
||||
with_codegen(tm, llmod, |cpm| {
|
||||
WriteOutputFile(sess, tm, cpm, llmod, output,
|
||||
lib::llvm::AssemblyFile);
|
||||
});
|
||||
|
||||
// If we're not using the LLVM assembler, this function
|
||||
// could be invoked specially with output_type_assembly,
|
||||
// so in this case we still want the metadata object
|
||||
// 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 => {
|
||||
with_codegen(tm, llmod, |cpm| {
|
||||
WriteOutputFile(sess, tm, cpm, llmod, output,
|
||||
lib::llvm::ObjectFile);
|
||||
});
|
||||
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);
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
llvm::LLVMRustDisposeTargetMachine(tm);
|
||||
llvm::LLVMDisposeModule(trans.metadata_module);
|
||||
llvm::LLVMDisposeModule(llmod);
|
||||
llvm::LLVMContextDispose(llcx);
|
||||
if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
|
||||
|
|
@ -782,43 +846,25 @@ 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) {
|
||||
// If we're generating a test executable, then ignore all other output
|
||||
// styles at all other locations
|
||||
let outputs = if sess.opts.test {
|
||||
// If we're generating a test executable, then ignore all other output
|
||||
// styles at all other locations
|
||||
~[session::OutputExecutable]
|
||||
} else {
|
||||
// Always generate whatever was specified on the command line, but also
|
||||
// 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() {
|
||||
if "bin" == *ty {
|
||||
outputs.push(session::OutputExecutable);
|
||||
} else if "dylib" == *ty || "lib" == *ty {
|
||||
outputs.push(session::OutputDylib);
|
||||
} else if "rlib" == *ty {
|
||||
outputs.push(session::OutputRlib);
|
||||
} else if "staticlib" == *ty {
|
||||
outputs.push(session::OutputStaticlib);
|
||||
}
|
||||
}
|
||||
if outputs.len() == 0 {
|
||||
outputs.push(session::OutputExecutable);
|
||||
}
|
||||
outputs
|
||||
(*sess.outputs).clone()
|
||||
};
|
||||
|
||||
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 +878,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 +920,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 +940,12 @@ 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,
|
||||
fn link_rlib(sess: Session,
|
||||
trans: Option<&CrateTranslation>, // None == no metadata/bytecode
|
||||
obj_filename: &Path,
|
||||
out_filename: &Path) -> Archive {
|
||||
let mut a = Archive::create(sess, out_filename, obj_filename);
|
||||
|
||||
for &(ref l, kind) in cstore::get_used_libraries(sess.cstore).iter() {
|
||||
match kind {
|
||||
cstore::NativeStatic => {
|
||||
|
|
@ -905,6 +954,48 @@ fn link_rlib(sess: Session, obj_filename: &Path,
|
|||
cstore::NativeFramework | cstore::NativeUnknown => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Note that it is important that we add all of our non-object "magical
|
||||
// files" *after* all of the object files in the archive. The reason for
|
||||
// this is as follows:
|
||||
//
|
||||
// * When performing LTO, this archive will be modified to remove
|
||||
// obj_filename from above. The reason for this is described below.
|
||||
//
|
||||
// * When the system linker looks at an archive, it will attempt to
|
||||
// determine the architecture of the archive in order to see whether its
|
||||
// linkable.
|
||||
//
|
||||
// The algorithm for this detections is: iterate over the files in the
|
||||
// archive. Skip magical SYMDEF names. Interpret the first file as an
|
||||
// object file. Read architecture from the object file.
|
||||
//
|
||||
// * As one can probably see, if "metadata" and "foo.bc" were placed
|
||||
// before all of the objects, then the architecture of this archive would
|
||||
// not be correctly inferred once 'foo.o' is removed.
|
||||
//
|
||||
// Basically, all this means is that this code should not move above the
|
||||
// code above.
|
||||
match trans {
|
||||
Some(trans) => {
|
||||
// Instead of putting the metadata in an object file section, rlibs
|
||||
// contain the metadata in a separate file.
|
||||
let metadata = obj_filename.with_filename(METADATA_FILENAME);
|
||||
fs::File::create(&metadata).write(trans.metadata);
|
||||
a.add_file(&metadata);
|
||||
fs::unlink(&metadata);
|
||||
|
||||
// For LTO purposes, the bytecode of this library is also inserted
|
||||
// into the archive.
|
||||
let bc = obj_filename.with_extension("bc");
|
||||
a.add_file(&bc);
|
||||
if !sess.opts.save_temps {
|
||||
fs::unlink(&bc);
|
||||
}
|
||||
}
|
||||
|
||||
None => {}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
|
@ -916,20 +1007,24 @@ 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);
|
||||
for &(cnum, ref path) in crates.iter() {
|
||||
let name = cstore::get_crate_data(sess.cstore, cnum).name;
|
||||
let p = match *path {
|
||||
Some(ref p) => p.clone(), None => {
|
||||
sess.err(format!("could not find rlib for: `{}`",
|
||||
cstore::get_crate_data(sess.cstore, cnum).name));
|
||||
sess.err(format!("could not find rlib for: `{}`", name));
|
||||
continue
|
||||
}
|
||||
};
|
||||
a.add_rlib(&p);
|
||||
a.add_rlib(&p, name, sess.lto());
|
||||
let native_libs = csearch::get_native_libraries(sess.cstore, cnum);
|
||||
for &(kind, ref lib) in native_libs.iter() {
|
||||
let name = match kind {
|
||||
|
|
@ -948,10 +1043,12 @@ fn link_staticlib(sess: Session, obj_filename: &Path, out_filename: &Path) {
|
|||
// links to all upstream files as well.
|
||||
fn link_natively(sess: Session, dylib: bool, obj_filename: &Path,
|
||||
out_filename: &Path) {
|
||||
let tmpdir = TempDir::new("rustc").expect("needs a temp dir");
|
||||
// The invocations of cc share some flags across platforms
|
||||
let cc_prog = get_cc_prog(sess);
|
||||
let mut cc_args = sess.targ_cfg.target_strs.cc_args.clone();
|
||||
cc_args.push_all_move(link_args(sess, dylib, obj_filename, out_filename));
|
||||
cc_args.push_all_move(link_args(sess, dylib, tmpdir.path(),
|
||||
obj_filename, out_filename));
|
||||
if (sess.opts.debugging_opts & session::print_link_args) != 0 {
|
||||
println!("{} link args: '{}'", cc_prog, cc_args.connect("' '"));
|
||||
}
|
||||
|
|
@ -961,7 +1058,8 @@ fn link_natively(sess: Session, dylib: bool, obj_filename: &Path,
|
|||
|
||||
// Invoke the system linker
|
||||
debug!("{} {}", cc_prog, cc_args.connect(" "));
|
||||
let prog = run::process_output(cc_prog, cc_args);
|
||||
let prog = time(sess.time_passes(), "running linker", (), |()|
|
||||
run::process_output(cc_prog, cc_args));
|
||||
|
||||
if !prog.status.success() {
|
||||
sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
|
||||
|
|
@ -982,6 +1080,7 @@ fn link_natively(sess: Session, dylib: bool, obj_filename: &Path,
|
|||
|
||||
fn link_args(sess: Session,
|
||||
dylib: bool,
|
||||
tmpdir: &Path,
|
||||
obj_filename: &Path,
|
||||
out_filename: &Path) -> ~[~str] {
|
||||
|
||||
|
|
@ -998,6 +1097,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
|
||||
|
|
@ -1015,7 +1122,7 @@ fn link_args(sess: Session,
|
|||
}
|
||||
|
||||
add_local_native_libraries(&mut args, sess);
|
||||
add_upstream_rust_crates(&mut args, sess, dylib);
|
||||
add_upstream_rust_crates(&mut args, sess, dylib, tmpdir);
|
||||
add_upstream_native_libraries(&mut args, sess);
|
||||
|
||||
// # Telling the linker what we're doing
|
||||
|
|
@ -1098,7 +1205,7 @@ fn add_local_native_libraries(args: &mut ~[~str], sess: Session) {
|
|||
// dependencies will be linked when producing the final output (instead of
|
||||
// the intermediate rlib version)
|
||||
fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session,
|
||||
dylib: bool) {
|
||||
dylib: bool, tmpdir: &Path) {
|
||||
// Converts a library file-stem into a cc -l argument
|
||||
fn unlib(config: @session::config, stem: &str) -> ~str {
|
||||
if stem.starts_with("lib") &&
|
||||
|
|
@ -1123,14 +1230,49 @@ fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session,
|
|||
// dynamic libraries.
|
||||
let crates = cstore::get_used_crates(cstore, cstore::RequireStatic);
|
||||
if crates.iter().all(|&(_, ref p)| p.is_some()) {
|
||||
for (_, path) in crates.move_iter() {
|
||||
let path = path.unwrap();
|
||||
args.push(path.as_str().unwrap().to_owned());
|
||||
for (cnum, path) in crates.move_iter() {
|
||||
let cratepath = path.unwrap();
|
||||
|
||||
// When performing LTO on an executable output, all of the
|
||||
// bytecode from the upstream libraries has already been
|
||||
// included in our object file output. We need to modify all of
|
||||
// the upstream archives to remove their corresponding object
|
||||
// file to make sure we don't pull the same code in twice.
|
||||
//
|
||||
// We must continue to link to the upstream archives to be sure
|
||||
// to pull in native static dependencies. As the final caveat,
|
||||
// on linux it is apparently illegal to link to a blank archive,
|
||||
// so if an archive no longer has any object files in it after
|
||||
// we remove `lib.o`, then don't link against it at all.
|
||||
//
|
||||
// If we're not doing LTO, then our job is simply to just link
|
||||
// against the archive.
|
||||
if sess.lto() {
|
||||
let name = cstore::get_crate_data(sess.cstore, cnum).name;
|
||||
time(sess.time_passes(), format!("altering {}.rlib", name),
|
||||
(), |()| {
|
||||
let dst = tmpdir.join(cratepath.filename().unwrap());
|
||||
fs::copy(&cratepath, &dst);
|
||||
let dst_str = dst.as_str().unwrap().to_owned();
|
||||
let mut archive = Archive::open(sess, dst);
|
||||
archive.remove_file(format!("{}.o", name));
|
||||
let files = archive.files();
|
||||
if files.iter().any(|s| s.ends_with(".o")) {
|
||||
args.push(dst_str);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
args.push(cratepath.as_str().unwrap().to_owned());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're performing LTO, then it should have been previously required
|
||||
// that all upstream rust depenencies were available in an rlib format.
|
||||
assert!(!sess.lto());
|
||||
|
||||
// This is a fallback of three different cases of linking:
|
||||
//
|
||||
// * When creating a dynamic library, all inputs are required to be dynamic
|
||||
|
|
|
|||
96
src/librustc/back/lto.rs
Normal file
96
src/librustc/back/lto.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
use back::archive::Archive;
|
||||
use back::link;
|
||||
use driver::session;
|
||||
use lib::llvm::{ModuleRef, TargetMachineRef, llvm, True, False};
|
||||
use metadata::cstore;
|
||||
use util::common::time;
|
||||
|
||||
use std::libc;
|
||||
use std::vec;
|
||||
|
||||
pub fn run(sess: session::Session, llmod: ModuleRef,
|
||||
tm: TargetMachineRef, reachable: &[~str]) {
|
||||
// Make sure we actually can run LTO
|
||||
for output in sess.outputs.iter() {
|
||||
match *output {
|
||||
session::OutputExecutable | session::OutputStaticlib => {}
|
||||
_ => {
|
||||
sess.fatal("lto can only be run for executables and \
|
||||
static library outputs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each of our upstream dependencies, find the corresponding rlib and
|
||||
// load the bitcode from the archive. Then merge it into the current LLVM
|
||||
// module that we've got.
|
||||
let crates = cstore::get_used_crates(sess.cstore, cstore::RequireStatic);
|
||||
for (cnum, path) in crates.move_iter() {
|
||||
let name = cstore::get_crate_data(sess.cstore, cnum).name;
|
||||
let path = match path {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
sess.fatal(format!("could not find rlib for: `{}`", name));
|
||||
}
|
||||
};
|
||||
|
||||
let archive = Archive::open(sess, path);
|
||||
debug!("reading {}", name);
|
||||
let bc = time(sess.time_passes(), format!("read {}.bc", name), (), |_|
|
||||
archive.read(format!("{}.bc", name)));
|
||||
let ptr = vec::raw::to_ptr(bc);
|
||||
debug!("linking {}", name);
|
||||
time(sess.time_passes(), format!("ll link {}", name), (), |()| unsafe {
|
||||
if !llvm::LLVMRustLinkInExternalBitcode(llmod,
|
||||
ptr as *libc::c_char,
|
||||
bc.len() as libc::size_t) {
|
||||
link::llvm_err(sess, format!("failed to load bc of `{}`", name));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Internalize everything but the reachable symbols of the current module
|
||||
let cstrs = reachable.map(|s| s.to_c_str());
|
||||
let arr = cstrs.map(|c| c.with_ref(|p| p));
|
||||
let ptr = vec::raw::to_ptr(arr);
|
||||
unsafe {
|
||||
llvm::LLVMRustRunRestrictionPass(llmod, ptr as **libc::c_char,
|
||||
arr.len() as libc::size_t);
|
||||
}
|
||||
|
||||
// Now we have one massive module inside of llmod. Time to run the
|
||||
// LTO-specific optimization passes that LLVM provides.
|
||||
//
|
||||
// This code is based off the code found in llvm's LTO code generator:
|
||||
// tools/lto/LTOCodeGenerator.cpp
|
||||
debug!("running the pass manager");
|
||||
unsafe {
|
||||
let pm = llvm::LLVMCreatePassManager();
|
||||
llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod);
|
||||
"verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s));
|
||||
|
||||
let builder = llvm::LLVMPassManagerBuilderCreate();
|
||||
llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm,
|
||||
/* Internalize = */ False,
|
||||
/* RunInliner = */ True);
|
||||
llvm::LLVMPassManagerBuilderDispose(builder);
|
||||
|
||||
"verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s));
|
||||
|
||||
time(sess.time_passes(), "LTO pases", (), |()|
|
||||
llvm::LLVMRunPassManager(pm, llmod));
|
||||
|
||||
llvm::LLVMDisposePassManager(pm);
|
||||
}
|
||||
debug!("lto done");
|
||||
}
|
||||
|
|
@ -165,10 +165,7 @@ pub fn phase_2_configure_and_expand(sess: Session,
|
|||
let time_passes = sess.time_passes();
|
||||
|
||||
*sess.building_library = session::building_library(sess.opts, &crate);
|
||||
let want_exe = sess.opts.outputs.iter().any(|&o| o == OutputExecutable);
|
||||
if *sess.building_library && want_exe {
|
||||
sess.err("cannot build both a library and an executable");
|
||||
}
|
||||
*sess.outputs = session::collect_outputs(sess.opts, &crate);
|
||||
|
||||
time(time_passes, "gated feature checking", (), |_|
|
||||
front::feature_gate::check_crate(sess, &crate));
|
||||
|
|
@ -335,8 +332,10 @@ pub fn phase_3_run_analysis_passes(sess: Session,
|
|||
pub struct CrateTranslation {
|
||||
context: ContextRef,
|
||||
module: ModuleRef,
|
||||
metadata_module: ModuleRef,
|
||||
link: LinkMeta,
|
||||
crate_types: ~[~str],
|
||||
metadata: ~[u8],
|
||||
reachable: ~[~str],
|
||||
}
|
||||
|
||||
/// Run the translation phase to LLVM, after which the AST and analysis can
|
||||
|
|
@ -362,8 +361,7 @@ pub fn phase_5_run_llvm_passes(sess: Session,
|
|||
|
||||
time(sess.time_passes(), "LLVM passes", (), |_|
|
||||
link::write::run_passes(sess,
|
||||
trans.context,
|
||||
trans.module,
|
||||
trans,
|
||||
output_type,
|
||||
&asm_filename));
|
||||
|
||||
|
|
@ -376,8 +374,7 @@ pub fn phase_5_run_llvm_passes(sess: Session,
|
|||
} else {
|
||||
time(sess.time_passes(), "LLVM passes", (), |_|
|
||||
link::write::run_passes(sess,
|
||||
trans.context,
|
||||
trans.module,
|
||||
trans,
|
||||
sess.opts.output_type,
|
||||
&outputs.obj_filename));
|
||||
}
|
||||
|
|
@ -390,10 +387,9 @@ pub fn phase_6_link_output(sess: Session,
|
|||
outputs: &OutputFilenames) {
|
||||
time(sess.time_passes(), "linking", (), |_|
|
||||
link::link_binary(sess,
|
||||
trans.crate_types,
|
||||
trans,
|
||||
&outputs.obj_filename,
|
||||
&outputs.out_filename,
|
||||
trans.link));
|
||||
&outputs.out_filename));
|
||||
}
|
||||
|
||||
pub fn stop_after_phase_3(sess: Session) -> bool {
|
||||
|
|
@ -838,7 +834,8 @@ pub fn build_session_(sopts: @session::options,
|
|||
building_library: @mut false,
|
||||
working_dir: os::getcwd(),
|
||||
lints: @mut HashMap::new(),
|
||||
node_id: @mut 1
|
||||
node_id: @mut 1,
|
||||
outputs: @mut ~[],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use metadata::filesearch;
|
|||
use metadata;
|
||||
use middle::lint;
|
||||
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
use syntax::ast::NodeId;
|
||||
use syntax::ast::{int_ty, uint_ty};
|
||||
use syntax::codemap::Span;
|
||||
|
|
@ -67,6 +68,7 @@ pub static use_softfp: uint = 1 << 26;
|
|||
pub static gen_crate_map: uint = 1 << 27;
|
||||
pub static prefer_dynamic: uint = 1 << 28;
|
||||
pub static no_integrated_as: uint = 1 << 29;
|
||||
pub static lto: uint = 1 << 30;
|
||||
|
||||
pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] {
|
||||
~[("verbose", "in general, enable more debug printouts", verbose),
|
||||
|
|
@ -120,6 +122,7 @@ pub fn debugging_opts_map() -> ~[(&'static str, &'static str, uint)] {
|
|||
("prefer-dynamic", "Prefer dynamic linking to static linking", prefer_dynamic),
|
||||
("no-integrated-as",
|
||||
"Use external assembler rather than LLVM's integrated one", no_integrated_as),
|
||||
("lto", "Perform LLVM link-time optimizations", lto),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +211,7 @@ pub struct Session_ {
|
|||
working_dir: Path,
|
||||
lints: @mut HashMap<ast::NodeId, ~[(lint::lint, codemap::Span, ~str)]>,
|
||||
node_id: @mut ast::NodeId,
|
||||
outputs: @mut ~[OutputStyle],
|
||||
}
|
||||
|
||||
pub type Session = @Session_;
|
||||
|
|
@ -341,6 +345,9 @@ impl Session_ {
|
|||
pub fn no_integrated_as(&self) -> bool {
|
||||
self.debugging_opt(no_integrated_as)
|
||||
}
|
||||
pub fn lto(&self) -> bool {
|
||||
self.debugging_opt(lto)
|
||||
}
|
||||
|
||||
// pointless function, now...
|
||||
pub fn str_of(&self, id: ast::Ident) -> @str {
|
||||
|
|
@ -408,6 +415,29 @@ pub fn building_library(options: &options, crate: &ast::Crate) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn collect_outputs(options: &options, crate: &ast::Crate) -> ~[OutputStyle] {
|
||||
let mut base = options.outputs.clone();
|
||||
let mut iter = crate.attrs.iter().filter_map(|a| {
|
||||
if "crate_type" == a.name() {
|
||||
match a.value_str() {
|
||||
Some(n) if "rlib" == n => Some(OutputRlib),
|
||||
Some(n) if "dylib" == n => Some(OutputDylib),
|
||||
Some(n) if "lib" == n => Some(OutputDylib),
|
||||
Some(n) if "staticlib" == n => Some(OutputStaticlib),
|
||||
Some(n) if "bin" == n => Some(OutputExecutable),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
base.extend(&mut iter);
|
||||
if base.len() == 0 {
|
||||
base.push(OutputExecutable);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
pub fn sess_os_to_meta_os(os: abi::Os) -> metadata::loader::Os {
|
||||
use metadata::loader;
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ pub mod back {
|
|||
pub mod x86_64;
|
||||
pub mod rpath;
|
||||
pub mod target_strs;
|
||||
pub mod lto;
|
||||
}
|
||||
|
||||
pub mod metadata;
|
||||
|
|
|
|||
|
|
@ -1403,6 +1403,11 @@ pub mod llvm {
|
|||
pub fn LLVMPassManagerBuilderPopulateFunctionPassManager(
|
||||
PMB: PassManagerBuilderRef,
|
||||
PM: PassManagerRef);
|
||||
pub fn LLVMPassManagerBuilderPopulateLTOPassManager(
|
||||
PMB: PassManagerBuilderRef,
|
||||
PM: PassManagerRef,
|
||||
Internalize: Bool,
|
||||
RunInliner: Bool);
|
||||
|
||||
/** Destroys a memory buffer. */
|
||||
pub fn LLVMDisposeMemoryBuffer(MemBuf: MemoryBufferRef);
|
||||
|
|
@ -1736,6 +1741,12 @@ pub mod llvm {
|
|||
pub fn LLVMRustSetNormalizedTarget(M: ModuleRef, triple: *c_char);
|
||||
pub fn LLVMRustAddAlwaysInlinePass(P: PassManagerBuilderRef,
|
||||
AddLifetimes: bool);
|
||||
pub fn LLVMRustLinkInExternalBitcode(M: ModuleRef,
|
||||
bc: *c_char,
|
||||
len: size_t) -> bool;
|
||||
pub fn LLVMRustRunRestrictionPass(M: ModuleRef,
|
||||
syms: **c_char,
|
||||
len: size_t);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,14 @@ use middle::ty;
|
|||
use middle::typeck;
|
||||
use middle;
|
||||
|
||||
use std::cast;
|
||||
use std::hashmap::{HashMap, HashSet};
|
||||
use std::io::{Writer, Seek, Decorator};
|
||||
use std::io::mem::MemWriter;
|
||||
use std::io::{Writer, Seek, Decorator};
|
||||
use std::str;
|
||||
use std::util;
|
||||
use std::vec;
|
||||
|
||||
use extra::flate;
|
||||
use extra::serialize::Encodable;
|
||||
use extra;
|
||||
|
||||
|
|
@ -47,8 +48,6 @@ use syntax::parse::token;
|
|||
use syntax;
|
||||
use writer = extra::ebml::writer;
|
||||
|
||||
use std::cast;
|
||||
|
||||
// used by astencode:
|
||||
type abbrev_map = @mut HashMap<ty::t, tyencode::ty_abbrev>;
|
||||
|
||||
|
|
@ -1871,10 +1870,9 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] {
|
|||
// remaining % 4 bytes.
|
||||
wr.write(&[0u8, 0u8, 0u8, 0u8]);
|
||||
|
||||
let writer_bytes: &mut ~[u8] = wr.inner_mut_ref();
|
||||
|
||||
metadata_encoding_version.to_owned() +
|
||||
flate::deflate_bytes(*writer_bytes)
|
||||
// This is a horrible thing to do to the outer MemWriter, but thankfully we
|
||||
// don't use it again so... it's ok right?
|
||||
return util::replace(wr.inner_mut_ref(), ~[]);
|
||||
}
|
||||
|
||||
// Get the encoded string for a type
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
//! Finds crate binaries and loads their metadata
|
||||
|
||||
use back::archive::Archive;
|
||||
use back::archive::{Archive, METADATA_FILENAME};
|
||||
use driver::session::Session;
|
||||
use lib::llvm::{False, llvm, ObjectFile, mk_section_iter};
|
||||
use metadata::decoder;
|
||||
|
|
@ -27,7 +27,6 @@ use syntax::attr::AttrMetaMethods;
|
|||
use std::c_str::ToCStr;
|
||||
use std::cast;
|
||||
use std::io;
|
||||
use std::libc;
|
||||
use std::num;
|
||||
use std::option;
|
||||
use std::os::consts::{macos, freebsd, linux, android, win32};
|
||||
|
|
@ -102,8 +101,7 @@ impl Context {
|
|||
if candidate && existing {
|
||||
FileMatches
|
||||
} else if candidate {
|
||||
match get_metadata_section(self.sess, self.os, path,
|
||||
crate_name) {
|
||||
match get_metadata_section(self.sess, self.os, path) {
|
||||
Some(cvec) =>
|
||||
if crate_matches(cvec, self.metas, self.hash) {
|
||||
debug!("found {} with matching metadata",
|
||||
|
|
@ -271,22 +269,15 @@ pub fn metadata_matches(extern_metas: &[@ast::MetaItem],
|
|||
local_metas.iter().all(|needed| attr::contains(extern_metas, *needed))
|
||||
}
|
||||
|
||||
fn get_metadata_section(sess: Session, os: Os, filename: &Path,
|
||||
crate_name: &str) -> Option<@~[u8]> {
|
||||
fn get_metadata_section(sess: Session, os: Os, filename: &Path) -> Option<@~[u8]> {
|
||||
if filename.filename_str().unwrap().ends_with(".rlib") {
|
||||
let archive = Archive::open(sess, filename.clone());
|
||||
return Some(@archive.read(METADATA_FILENAME));
|
||||
}
|
||||
unsafe {
|
||||
let mb = if filename.filename_str().unwrap().ends_with(".rlib") {
|
||||
let archive = Archive::open(sess, filename.clone());
|
||||
let contents = archive.read(crate_name + ".o");
|
||||
let ptr = vec::raw::to_ptr(contents);
|
||||
crate_name.with_c_str(|name| {
|
||||
llvm::LLVMCreateMemoryBufferWithMemoryRangeCopy(
|
||||
ptr as *i8, contents.len() as libc::size_t, name)
|
||||
})
|
||||
} else {
|
||||
filename.with_c_str(|buf| {
|
||||
llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
|
||||
})
|
||||
};
|
||||
let mb = filename.with_c_str(|buf| {
|
||||
llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
|
||||
});
|
||||
if mb as int == 0 { return None }
|
||||
let of = match ObjectFile::new(mb) {
|
||||
Some(of) => of,
|
||||
|
|
@ -356,12 +347,7 @@ pub fn list_file_metadata(sess: Session,
|
|||
os: Os,
|
||||
path: &Path,
|
||||
out: @mut io::Writer) {
|
||||
// guess the crate name from the pathname
|
||||
let crate_name = path.filename_str().unwrap();
|
||||
let crate_name = if crate_name.starts_with("lib") {
|
||||
crate_name.slice_from(3) } else { crate_name };
|
||||
let crate_name = crate_name.split('-').next().unwrap();
|
||||
match get_metadata_section(sess, os, path, crate_name) {
|
||||
match get_metadata_section(sess, os, path) {
|
||||
option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out),
|
||||
option::None => {
|
||||
write!(out, "could not find metadata in {}.\n", path.display())
|
||||
|
|
|
|||
|
|
@ -2929,7 +2929,7 @@ pub fn symname(sess: session::Session, name: &str,
|
|||
}
|
||||
|
||||
pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta,
|
||||
llmod: ModuleRef) -> ValueRef {
|
||||
llmod: ModuleRef) -> (~str, ValueRef) {
|
||||
let targ_cfg = sess.targ_cfg;
|
||||
let int_type = Type::int(targ_cfg.arch);
|
||||
let mut n_subcrates = 1;
|
||||
|
|
@ -2963,7 +2963,7 @@ pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta,
|
|||
lib::llvm::SetLinkage(map, lib::llvm::ExternalLinkage);
|
||||
}
|
||||
|
||||
return map;
|
||||
return (sym_name, map);
|
||||
}
|
||||
|
||||
pub fn fill_crate_map(ccx: @mut CrateContext, map: ValueRef) {
|
||||
|
|
@ -3044,19 +3044,26 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::encode_
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write_metadata(cx: &CrateContext, crate: &ast::Crate) {
|
||||
if !*cx.sess.building_library { return; }
|
||||
pub fn write_metadata(cx: &CrateContext, crate: &ast::Crate) -> ~[u8] {
|
||||
use extra::flate;
|
||||
|
||||
if !*cx.sess.building_library { return ~[]; }
|
||||
|
||||
let encode_inlined_item: encoder::encode_inlined_item =
|
||||
|ecx, ebml_w, path, ii|
|
||||
astencode::encode_inlined_item(ecx, ebml_w, path, ii, cx.maps);
|
||||
|
||||
let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item);
|
||||
let llmeta = C_bytes(encoder::encode_metadata(encode_parms, crate));
|
||||
let metadata = encoder::encode_metadata(encode_parms, crate);
|
||||
let compressed = encoder::metadata_encoding_version +
|
||||
flate::deflate_bytes(metadata);
|
||||
let llmeta = C_bytes(compressed);
|
||||
let llconst = C_struct([llmeta], false);
|
||||
let mut llglobal = "rust_metadata".with_c_str(|buf| {
|
||||
let name = format!("rust_metadata_{}_{}_{}", cx.link_meta.name,
|
||||
cx.link_meta.vers, cx.link_meta.extras_hash);
|
||||
let llglobal = name.with_c_str(|buf| {
|
||||
unsafe {
|
||||
llvm::LLVMAddGlobal(cx.llmod, val_ty(llconst).to_ref(), buf)
|
||||
llvm::LLVMAddGlobal(cx.metadata_llmod, val_ty(llconst).to_ref(), buf)
|
||||
}
|
||||
});
|
||||
unsafe {
|
||||
|
|
@ -3064,16 +3071,8 @@ pub fn write_metadata(cx: &CrateContext, crate: &ast::Crate) {
|
|||
cx.sess.targ_cfg.target_strs.meta_sect_name.with_c_str(|buf| {
|
||||
llvm::LLVMSetSection(llglobal, buf)
|
||||
});
|
||||
lib::llvm::SetLinkage(llglobal, lib::llvm::InternalLinkage);
|
||||
|
||||
let t_ptr_i8 = Type::i8p();
|
||||
llglobal = llvm::LLVMConstBitCast(llglobal, t_ptr_i8.to_ref());
|
||||
let llvm_used = "llvm.used".with_c_str(|buf| {
|
||||
llvm::LLVMAddGlobal(cx.llmod, Type::array(&t_ptr_i8, 1).to_ref(), buf)
|
||||
});
|
||||
lib::llvm::SetLinkage(llvm_used, lib::llvm::AppendingLinkage);
|
||||
llvm::LLVMSetInitializer(llvm_used, C_array(t_ptr_i8, [llglobal]));
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
pub fn trans_crate(sess: session::Session,
|
||||
|
|
@ -3140,7 +3139,7 @@ pub fn trans_crate(sess: session::Session,
|
|||
}
|
||||
|
||||
// Translate the metadata.
|
||||
write_metadata(ccx, &crate);
|
||||
let metadata = write_metadata(ccx, &crate);
|
||||
if ccx.sess.trans_stats() {
|
||||
println("--- trans stats ---");
|
||||
println!("n_static_tydescs: {}", ccx.stats.n_static_tydescs);
|
||||
|
|
@ -3174,18 +3173,27 @@ pub fn trans_crate(sess: session::Session,
|
|||
let llcx = ccx.llcx;
|
||||
let link_meta = ccx.link_meta;
|
||||
let llmod = ccx.llmod;
|
||||
let crate_types = crate.attrs.iter().filter_map(|a| {
|
||||
if "crate_type" == a.name() {
|
||||
a.value_str()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).map(|a| a.to_owned()).collect();
|
||||
let mut reachable = ccx.reachable.iter().filter_map(|id| {
|
||||
ccx.item_symbols.find(id).map(|s| s.to_owned())
|
||||
}).to_owned_vec();
|
||||
|
||||
// Make sure that some other crucial symbols are not eliminated from the
|
||||
// module. This includes the main function (main/amain elsewhere), the crate
|
||||
// map (used for debug log settings and I/O), and finally the curious
|
||||
// rust_stack_exhausted symbol. This symbol is required for use by the
|
||||
// libmorestack library that we link in, so we must ensure that this symbol
|
||||
// is not internalized (if defined in the crate).
|
||||
reachable.push(ccx.crate_map_name.to_owned());
|
||||
reachable.push(~"main");
|
||||
reachable.push(~"amain");
|
||||
reachable.push(~"rust_stack_exhausted");
|
||||
|
||||
return CrateTranslation {
|
||||
context: llcx,
|
||||
module: llmod,
|
||||
link: link_meta,
|
||||
crate_types: crate_types,
|
||||
metadata_module: ccx.metadata_llmod,
|
||||
metadata: metadata,
|
||||
reachable: reachable,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ pub struct CrateContext {
|
|||
sess: session::Session,
|
||||
llmod: ModuleRef,
|
||||
llcx: ContextRef,
|
||||
metadata_llmod: ModuleRef,
|
||||
td: TargetData,
|
||||
tn: TypeNames,
|
||||
externs: ExternMap,
|
||||
|
|
@ -110,6 +111,7 @@ pub struct CrateContext {
|
|||
opaque_vec_type: Type,
|
||||
builder: BuilderRef_res,
|
||||
crate_map: ValueRef,
|
||||
crate_map_name: ~str,
|
||||
// Set when at least one function uses GC. Needed so that
|
||||
// decl_gc_metadata knows whether to link to the module metadata, which
|
||||
// is not emitted by LLVM's GC pass when no functions use GC.
|
||||
|
|
@ -134,11 +136,18 @@ impl CrateContext {
|
|||
let llmod = name.with_c_str(|buf| {
|
||||
llvm::LLVMModuleCreateWithNameInContext(buf, llcx)
|
||||
});
|
||||
let metadata_llmod = format!("{}_metadata", name).with_c_str(|buf| {
|
||||
llvm::LLVMModuleCreateWithNameInContext(buf, llcx)
|
||||
});
|
||||
let data_layout: &str = sess.targ_cfg.target_strs.data_layout;
|
||||
let targ_triple: &str = sess.targ_cfg.target_strs.target_triple;
|
||||
data_layout.with_c_str(|buf| llvm::LLVMSetDataLayout(llmod, buf));
|
||||
data_layout.with_c_str(|buf| {
|
||||
llvm::LLVMSetDataLayout(llmod, buf);
|
||||
llvm::LLVMSetDataLayout(metadata_llmod, buf);
|
||||
});
|
||||
targ_triple.with_c_str(|buf| {
|
||||
llvm::LLVMRustSetNormalizedTarget(llmod, buf)
|
||||
llvm::LLVMRustSetNormalizedTarget(llmod, buf);
|
||||
llvm::LLVMRustSetNormalizedTarget(metadata_llmod, buf);
|
||||
});
|
||||
let targ_cfg = sess.targ_cfg;
|
||||
|
||||
|
|
@ -159,7 +168,8 @@ impl CrateContext {
|
|||
tn.associate_type("tydesc", &tydesc_type);
|
||||
tn.associate_type("str_slice", &str_slice_ty);
|
||||
|
||||
let crate_map = decl_crate_map(sess, link_meta, llmod);
|
||||
let (crate_map_name, crate_map) = decl_crate_map(sess, link_meta,
|
||||
llmod);
|
||||
let dbg_cx = if sess.opts.debuginfo {
|
||||
Some(debuginfo::CrateDebugContext::new(llmod, name.to_owned()))
|
||||
} else {
|
||||
|
|
@ -174,6 +184,7 @@ impl CrateContext {
|
|||
sess: sess,
|
||||
llmod: llmod,
|
||||
llcx: llcx,
|
||||
metadata_llmod: metadata_llmod,
|
||||
td: td,
|
||||
tn: tn,
|
||||
externs: HashMap::new(),
|
||||
|
|
@ -229,6 +240,7 @@ impl CrateContext {
|
|||
opaque_vec_type: opaque_vec_type,
|
||||
builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)),
|
||||
crate_map: crate_map,
|
||||
crate_map_name: crate_map_name,
|
||||
uses_gc: false,
|
||||
dbg_cx: dbg_cx,
|
||||
do_not_commit_warning_issued: false
|
||||
|
|
|
|||
|
|
@ -15,14 +15,23 @@ use syntax::visit;
|
|||
use syntax::visit::Visitor;
|
||||
|
||||
use std::hashmap::HashSet;
|
||||
use std::local_data;
|
||||
use extra;
|
||||
|
||||
pub fn time<T, U>(do_it: bool, what: &str, u: U, f: |U| -> T) -> T {
|
||||
local_data_key!(depth: uint);
|
||||
if !do_it { return f(u); }
|
||||
|
||||
let old = local_data::get(depth, |d| d.map(|a| *a).unwrap_or(0));
|
||||
local_data::set(depth, old + 1);
|
||||
|
||||
let start = extra::time::precise_time_s();
|
||||
let rv = f(u);
|
||||
let end = extra::time::precise_time_s();
|
||||
println!("time: {:3.3f} s\t{}", end - start, what);
|
||||
|
||||
println!("{}time: {:3.3f} s\t{}", " ".repeat(old), end - start, what);
|
||||
local_data::set(depth, old);
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,3 +211,11 @@ extern "C" void
|
|||
LLVMRustAddAlwaysInlinePass(LLVMPassManagerBuilderRef PMB, bool AddLifetimes) {
|
||||
unwrap(PMB)->Inliner = createAlwaysInlinerPass(AddLifetimes);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
LLVMRustRunRestrictionPass(LLVMModuleRef M, char **symbols, size_t len) {
|
||||
PassManager passes;
|
||||
ArrayRef<const char*> ref(symbols, len);
|
||||
passes.add(llvm::createInternalizePass(ref));
|
||||
passes.run(*unwrap(M));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -539,3 +539,22 @@ extern "C" char *LLVMTypeToString(LLVMTypeRef Type) {
|
|||
unwrap<llvm::Type>(Type)->print(os);
|
||||
return strdup(os.str().data());
|
||||
}
|
||||
|
||||
extern "C" bool
|
||||
LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) {
|
||||
Module *Dst = unwrap(dst);
|
||||
MemoryBuffer* buf = MemoryBuffer::getMemBufferCopy(StringRef(bc, len));
|
||||
std::string Err;
|
||||
Module *Src = llvm::getLazyBitcodeModule(buf, Dst->getContext(), &Err);
|
||||
if (Src == NULL) {
|
||||
LLVMRustError = Err.c_str();
|
||||
delete buf;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Linker::LinkModules(Dst, Src, Linker::DestroySource, &Err)) {
|
||||
LLVMRustError = Err.c_str();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -629,3 +629,6 @@ LLVMTypeToString
|
|||
LLVMAddColdAttribute
|
||||
LLVMCreateMemoryBufferWithMemoryRange
|
||||
LLVMCreateMemoryBufferWithMemoryRangeCopy
|
||||
LLVMPassManagerBuilderPopulateLTOPassManager
|
||||
LLVMRustLinkInExternalBitcode
|
||||
LLVMRustRunRestrictionPass
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/Analysis/Passes.h"
|
||||
#include "llvm/Analysis/Lint.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/Assembly/Parser.h"
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
#include "llvm/Transforms/Vectorize.h"
|
||||
#include "llvm/DebugInfo.h"
|
||||
#include "llvm/DIBuilder.h"
|
||||
#include "llvm/Bitcode/ReaderWriter.h"
|
||||
#include "llvm-c/Core.h"
|
||||
#include "llvm-c/BitReader.h"
|
||||
#include "llvm-c/ExecutionEngine.h"
|
||||
|
|
|
|||
11
src/test/run-make/lto-smoke-c/Makefile
Normal file
11
src/test/run-make/lto-smoke-c/Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
-include ../tools.mk
|
||||
|
||||
ifneq ($(shell uname),Darwin)
|
||||
EXTRAFLAGS := -lm -lrt -ldl -lpthread
|
||||
endif
|
||||
|
||||
all:
|
||||
$(RUSTC) foo.rs -Z gen-crate-map -Z lto
|
||||
ln -s $(call STATICLIB,foo-*) $(call STATICLIB,foo)
|
||||
$(CC) bar.c -lfoo -o $(call RUN,bar) $(EXTRAFLAGS) -lstdc++
|
||||
$(call RUN,bar)
|
||||
6
src/test/run-make/lto-smoke-c/bar.c
Normal file
6
src/test/run-make/lto-smoke-c/bar.c
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
void foo();
|
||||
|
||||
int main() {
|
||||
foo();
|
||||
return 0;
|
||||
}
|
||||
4
src/test/run-make/lto-smoke-c/foo.rs
Normal file
4
src/test/run-make/lto-smoke-c/foo.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#[crate_type = "staticlib"];
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn foo() {}
|
||||
6
src/test/run-make/lto-smoke/Makefile
Normal file
6
src/test/run-make/lto-smoke/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
-include ../tools.mk
|
||||
|
||||
all:
|
||||
$(RUSTC) lib.rs
|
||||
$(RUSTC) main.rs -Z lto
|
||||
$(call RUN,main)
|
||||
1
src/test/run-make/lto-smoke/lib.rs
Normal file
1
src/test/run-make/lto-smoke/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
#[crate_type = "rlib"];
|
||||
3
src/test/run-make/lto-smoke/main.rs
Normal file
3
src/test/run-make/lto-smoke/main.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
extern mod lib;
|
||||
|
||||
fn main() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue