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:
bors 2013-12-09 16:11:28 -08:00
commit 29ca4350c8
22 changed files with 537 additions and 151 deletions

View file

@ -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);

View file

@ -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
View 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");
}

View file

@ -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 ~[],
}
}

View file

@ -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;

View file

@ -99,6 +99,7 @@ pub mod back {
pub mod x86_64;
pub mod rpath;
pub mod target_strs;
pub mod lto;
}
pub mod metadata;

View file

@ -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);
}
}

View file

@ -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

View file

@ -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())

View file

@ -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,
};
}

View file

@ -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

View file

@ -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
}

View file

@ -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));
}

View file

@ -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;
}

View file

@ -629,3 +629,6 @@ LLVMTypeToString
LLVMAddColdAttribute
LLVMCreateMemoryBufferWithMemoryRange
LLVMCreateMemoryBufferWithMemoryRangeCopy
LLVMPassManagerBuilderPopulateLTOPassManager
LLVMRustLinkInExternalBitcode
LLVMRustRunRestrictionPass

View file

@ -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"

View 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)

View file

@ -0,0 +1,6 @@
void foo();
int main() {
foo();
return 0;
}

View file

@ -0,0 +1,4 @@
#[crate_type = "staticlib"];
#[no_mangle]
pub extern "C" fn foo() {}

View file

@ -0,0 +1,6 @@
-include ../tools.mk
all:
$(RUSTC) lib.rs
$(RUSTC) main.rs -Z lto
$(call RUN,main)

View file

@ -0,0 +1 @@
#[crate_type = "rlib"];

View file

@ -0,0 +1,3 @@
extern mod lib;
fn main() {}