move back:🔗:write into a separate file
This commit is contained in:
parent
0ab27b1d5b
commit
e29aa1430b
7 changed files with 511 additions and 506 deletions
|
@ -13,12 +13,11 @@ use super::archive;
|
||||||
use super::rpath;
|
use super::rpath;
|
||||||
use super::rpath::RPathConfig;
|
use super::rpath::RPathConfig;
|
||||||
use super::svh::Svh;
|
use super::svh::Svh;
|
||||||
|
use super::write::{OutputTypeBitcode, OutputTypeExe, OutputTypeObject};
|
||||||
use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput};
|
use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput};
|
||||||
use driver::config::NoDebugInfo;
|
use driver::config::NoDebugInfo;
|
||||||
use driver::session::Session;
|
use driver::session::Session;
|
||||||
use driver::config;
|
use driver::config;
|
||||||
use llvm;
|
|
||||||
use llvm::ModuleRef;
|
|
||||||
use metadata::common::LinkMeta;
|
use metadata::common::LinkMeta;
|
||||||
use metadata::{encoder, cstore, filesearch, csearch, loader, creader};
|
use metadata::{encoder, cstore, filesearch, csearch, loader, creader};
|
||||||
use middle::trans::context::CrateContext;
|
use middle::trans::context::CrateContext;
|
||||||
|
@ -28,13 +27,11 @@ use util::common::time;
|
||||||
use util::ppaux;
|
use util::ppaux;
|
||||||
use util::sha2::{Digest, Sha256};
|
use util::sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use std::c_str::{ToCStr, CString};
|
|
||||||
use std::char;
|
use std::char;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::{fs, TempDir, Command};
|
use std::io::{fs, TempDir, Command};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use flate;
|
use flate;
|
||||||
|
@ -77,477 +74,6 @@ pub static RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: uint =
|
||||||
RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8;
|
RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8;
|
||||||
|
|
||||||
|
|
||||||
#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)]
|
|
||||||
pub enum OutputType {
|
|
||||||
OutputTypeBitcode,
|
|
||||||
OutputTypeAssembly,
|
|
||||||
OutputTypeLlvmAssembly,
|
|
||||||
OutputTypeObject,
|
|
||||||
OutputTypeExe,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn llvm_err(sess: &Session, msg: String) -> ! {
|
|
||||||
unsafe {
|
|
||||||
let cstr = llvm::LLVMRustGetLastError();
|
|
||||||
if cstr == ptr::null() {
|
|
||||||
sess.fatal(msg.as_slice());
|
|
||||||
} else {
|
|
||||||
let err = CString::new(cstr, true);
|
|
||||||
let err = String::from_utf8_lossy(err.as_bytes());
|
|
||||||
sess.fatal(format!("{}: {}",
|
|
||||||
msg.as_slice(),
|
|
||||||
err.as_slice()).as_slice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_output_file(
|
|
||||||
sess: &Session,
|
|
||||||
target: llvm::TargetMachineRef,
|
|
||||||
pm: llvm::PassManagerRef,
|
|
||||||
m: ModuleRef,
|
|
||||||
output: &Path,
|
|
||||||
file_type: llvm::FileType) {
|
|
||||||
unsafe {
|
|
||||||
output.with_c_str(|output| {
|
|
||||||
let result = llvm::LLVMRustWriteOutputFile(
|
|
||||||
target, pm, m, output, file_type);
|
|
||||||
if !result {
|
|
||||||
llvm_err(sess, "could not write output".to_string());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod write {
|
|
||||||
|
|
||||||
use super::super::lto;
|
|
||||||
use super::{write_output_file, OutputType};
|
|
||||||
use super::{OutputTypeAssembly, OutputTypeBitcode};
|
|
||||||
use super::{OutputTypeExe, OutputTypeLlvmAssembly};
|
|
||||||
use super::{OutputTypeObject};
|
|
||||||
use driver::driver::{CrateTranslation, OutputFilenames};
|
|
||||||
use driver::config::NoDebugInfo;
|
|
||||||
use driver::session::Session;
|
|
||||||
use driver::config;
|
|
||||||
use llvm;
|
|
||||||
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
|
|
||||||
use util::common::time;
|
|
||||||
use syntax::abi;
|
|
||||||
|
|
||||||
use std::c_str::ToCStr;
|
|
||||||
use std::io::{Command};
|
|
||||||
use libc::{c_uint, c_int};
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
// On android, we by default compile for armv7 processors. This enables
|
|
||||||
// things like double word CAS instructions (rather than emulating them)
|
|
||||||
// which are *far* more efficient. This is obviously undesirable in some
|
|
||||||
// cases, so if any sort of target feature is specified we don't append v7
|
|
||||||
// to the feature list.
|
|
||||||
//
|
|
||||||
// On iOS only armv7 and newer are supported. So it is useful to
|
|
||||||
// get all hardware potential via VFP3 (hardware floating point)
|
|
||||||
// and NEON (SIMD) instructions supported by LLVM.
|
|
||||||
// Note that without those flags various linking errors might
|
|
||||||
// arise as some of intrinsics are converted into function calls
|
|
||||||
// and nobody provides implementations those functions
|
|
||||||
fn target_feature<'a>(sess: &'a Session) -> &'a str {
|
|
||||||
match sess.targ_cfg.os {
|
|
||||||
abi::OsAndroid => {
|
|
||||||
if "" == sess.opts.cg.target_feature.as_slice() {
|
|
||||||
"+v7"
|
|
||||||
} else {
|
|
||||||
sess.opts.cg.target_feature.as_slice()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
abi::OsiOS if sess.targ_cfg.arch == abi::Arm => {
|
|
||||||
"+v7,+thumb2,+vfp3,+neon"
|
|
||||||
},
|
|
||||||
_ => sess.opts.cg.target_feature.as_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_passes(sess: &Session,
|
|
||||||
trans: &CrateTranslation,
|
|
||||||
output_types: &[OutputType],
|
|
||||||
output: &OutputFilenames) {
|
|
||||||
let llmod = trans.module;
|
|
||||||
let llcx = trans.context;
|
|
||||||
unsafe {
|
|
||||||
configure_llvm(sess);
|
|
||||||
|
|
||||||
if sess.opts.cg.save_temps {
|
|
||||||
output.with_extension("no-opt.bc").with_c_str(|buf| {
|
|
||||||
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let opt_level = match sess.opts.optimize {
|
|
||||||
config::No => llvm::CodeGenLevelNone,
|
|
||||||
config::Less => llvm::CodeGenLevelLess,
|
|
||||||
config::Default => llvm::CodeGenLevelDefault,
|
|
||||||
config::Aggressive => llvm::CodeGenLevelAggressive,
|
|
||||||
};
|
|
||||||
let use_softfp = sess.opts.cg.soft_float;
|
|
||||||
|
|
||||||
// FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter.
|
|
||||||
// FIXME: #11954: mac64 unwinding may not work with fp elim
|
|
||||||
let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) ||
|
|
||||||
(sess.targ_cfg.os == abi::OsMacos &&
|
|
||||||
sess.targ_cfg.arch == abi::X86_64);
|
|
||||||
|
|
||||||
// OSX has -dead_strip, which doesn't rely on ffunction_sections
|
|
||||||
// FIXME(#13846) this should be enabled for windows
|
|
||||||
let ffunction_sections = sess.targ_cfg.os != abi::OsMacos &&
|
|
||||||
sess.targ_cfg.os != abi::OsWindows;
|
|
||||||
let fdata_sections = ffunction_sections;
|
|
||||||
|
|
||||||
let reloc_model = match sess.opts.cg.relocation_model.as_slice() {
|
|
||||||
"pic" => llvm::RelocPIC,
|
|
||||||
"static" => llvm::RelocStatic,
|
|
||||||
"default" => llvm::RelocDefault,
|
|
||||||
"dynamic-no-pic" => llvm::RelocDynamicNoPic,
|
|
||||||
_ => {
|
|
||||||
sess.err(format!("{} is not a valid relocation mode",
|
|
||||||
sess.opts
|
|
||||||
.cg
|
|
||||||
.relocation_model).as_slice());
|
|
||||||
sess.abort_if_errors();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let code_model = match sess.opts.cg.code_model.as_slice() {
|
|
||||||
"default" => llvm::CodeModelDefault,
|
|
||||||
"small" => llvm::CodeModelSmall,
|
|
||||||
"kernel" => llvm::CodeModelKernel,
|
|
||||||
"medium" => llvm::CodeModelMedium,
|
|
||||||
"large" => llvm::CodeModelLarge,
|
|
||||||
_ => {
|
|
||||||
sess.err(format!("{} is not a valid code model",
|
|
||||||
sess.opts
|
|
||||||
.cg
|
|
||||||
.code_model).as_slice());
|
|
||||||
sess.abort_if_errors();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let tm = sess.targ_cfg
|
|
||||||
.target_strs
|
|
||||||
.target_triple
|
|
||||||
.as_slice()
|
|
||||||
.with_c_str(|t| {
|
|
||||||
sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| {
|
|
||||||
target_feature(sess).with_c_str(|features| {
|
|
||||||
llvm::LLVMRustCreateTargetMachine(
|
|
||||||
t, cpu, features,
|
|
||||||
code_model,
|
|
||||||
reloc_model,
|
|
||||||
opt_level,
|
|
||||||
true /* EnableSegstk */,
|
|
||||||
use_softfp,
|
|
||||||
no_fp_elim,
|
|
||||||
ffunction_sections,
|
|
||||||
fdata_sections,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the two optimizing pass managers. These mirror what clang
|
|
||||||
// does, and are by populated by LLVM's default PassManagerBuilder.
|
|
||||||
// Each manager has a different set of passes, but they also share
|
|
||||||
// some common passes.
|
|
||||||
let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod);
|
|
||||||
let mpm = llvm::LLVMCreatePassManager();
|
|
||||||
|
|
||||||
// If we're verifying or linting, add them to the function pass
|
|
||||||
// manager.
|
|
||||||
let addpass = |pass: &str| {
|
|
||||||
pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s))
|
|
||||||
};
|
|
||||||
if !sess.no_verify() { assert!(addpass("verify")); }
|
|
||||||
|
|
||||||
if !sess.opts.cg.no_prepopulate_passes {
|
|
||||||
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
|
|
||||||
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
|
|
||||||
populate_llvm_passes(fpm, mpm, llmod, opt_level,
|
|
||||||
trans.no_builtins);
|
|
||||||
}
|
|
||||||
|
|
||||||
for pass in sess.opts.cg.passes.iter() {
|
|
||||||
pass.as_slice().with_c_str(|s| {
|
|
||||||
if !llvm::LLVMRustAddPass(mpm, s) {
|
|
||||||
sess.warn(format!("unknown pass {}, ignoring",
|
|
||||||
*pass).as_slice());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, run the actual optimization passes
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Emit the bytecode if we're either saving our temporaries or
|
|
||||||
// emitting an rlib. Whenever an rlib is created, the bytecode is
|
|
||||||
// inserted into the archive in order to allow LTO against it.
|
|
||||||
if sess.opts.cg.save_temps ||
|
|
||||||
(sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
|
|
||||||
sess.opts.output_types.contains(&OutputTypeExe)) {
|
|
||||||
output.temp_path(OutputTypeBitcode).with_c_str(|buf| {
|
|
||||||
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if sess.lto() {
|
|
||||||
time(sess.time_passes(), "all lto passes", (), |()|
|
|
||||||
lto::run(sess, llmod, tm, trans.reachable.as_slice()));
|
|
||||||
|
|
||||||
if sess.opts.cg.save_temps {
|
|
||||||
output.with_extension("lto.bc").with_c_str(|buf| {
|
|
||||||
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
no_builtins: bool, f: |PassManagerRef|) {
|
|
||||||
unsafe {
|
|
||||||
let cpm = llvm::LLVMCreatePassManager();
|
|
||||||
llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
|
|
||||||
llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
|
|
||||||
f(cpm);
|
|
||||||
llvm::LLVMDisposePassManager(cpm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut object_file = None;
|
|
||||||
let mut needs_metadata = false;
|
|
||||||
for output_type in output_types.iter() {
|
|
||||||
let path = output.path(*output_type);
|
|
||||||
match *output_type {
|
|
||||||
OutputTypeBitcode => {
|
|
||||||
path.with_c_str(|buf| {
|
|
||||||
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
OutputTypeLlvmAssembly => {
|
|
||||||
path.with_c_str(|output| {
|
|
||||||
with_codegen(tm, llmod, trans.no_builtins, |cpm| {
|
|
||||||
llvm::LLVMRustPrintModule(cpm, llmod, output);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
OutputTypeAssembly => {
|
|
||||||
// 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.
|
|
||||||
let ty = OutputTypeAssembly;
|
|
||||||
let path = if sess.opts.output_types.contains(&ty) {
|
|
||||||
path
|
|
||||||
} else {
|
|
||||||
needs_metadata = true;
|
|
||||||
output.temp_path(OutputTypeAssembly)
|
|
||||||
};
|
|
||||||
with_codegen(tm, llmod, trans.no_builtins, |cpm| {
|
|
||||||
write_output_file(sess, tm, cpm, llmod, &path,
|
|
||||||
llvm::AssemblyFile);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
OutputTypeObject => {
|
|
||||||
object_file = Some(path);
|
|
||||||
}
|
|
||||||
OutputTypeExe => {
|
|
||||||
object_file = Some(output.temp_path(OutputTypeObject));
|
|
||||||
needs_metadata = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time(sess.time_passes(), "codegen passes", (), |()| {
|
|
||||||
match object_file {
|
|
||||||
Some(ref path) => {
|
|
||||||
with_codegen(tm, llmod, trans.no_builtins, |cpm| {
|
|
||||||
write_output_file(sess, tm, cpm, llmod, path,
|
|
||||||
llvm::ObjectFile);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
if needs_metadata {
|
|
||||||
with_codegen(tm, trans.metadata_module,
|
|
||||||
trans.no_builtins, |cpm| {
|
|
||||||
let out = output.temp_path(OutputTypeObject)
|
|
||||||
.with_extension("metadata.o");
|
|
||||||
write_output_file(sess, tm, cpm,
|
|
||||||
trans.metadata_module, &out,
|
|
||||||
llvm::ObjectFile);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
llvm::LLVMRustDisposeTargetMachine(tm);
|
|
||||||
llvm::LLVMDisposeModule(trans.metadata_module);
|
|
||||||
llvm::LLVMContextDispose(trans.metadata_context);
|
|
||||||
llvm::LLVMDisposeModule(llmod);
|
|
||||||
llvm::LLVMContextDispose(llcx);
|
|
||||||
if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
|
|
||||||
let pname = super::get_cc_prog(sess);
|
|
||||||
let mut cmd = Command::new(pname.as_slice());
|
|
||||||
|
|
||||||
cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject))
|
|
||||||
.arg(outputs.temp_path(OutputTypeAssembly));
|
|
||||||
debug!("{}", &cmd);
|
|
||||||
|
|
||||||
match cmd.output() {
|
|
||||||
Ok(prog) => {
|
|
||||||
if !prog.status.success() {
|
|
||||||
sess.err(format!("linking with `{}` failed: {}",
|
|
||||||
pname,
|
|
||||||
prog.status).as_slice());
|
|
||||||
sess.note(format!("{}", &cmd).as_slice());
|
|
||||||
let mut note = prog.error.clone();
|
|
||||||
note.push_all(prog.output.as_slice());
|
|
||||||
sess.note(str::from_utf8(note.as_slice()).unwrap());
|
|
||||||
sess.abort_if_errors();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
sess.err(format!("could not exec the linker `{}`: {}",
|
|
||||||
pname,
|
|
||||||
e).as_slice());
|
|
||||||
sess.abort_if_errors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn configure_llvm(sess: &Session) {
|
|
||||||
use std::sync::{Once, ONCE_INIT};
|
|
||||||
static mut INIT: Once = ONCE_INIT;
|
|
||||||
|
|
||||||
// Copy what clang does by turning on loop vectorization at O2 and
|
|
||||||
// slp vectorization at O3
|
|
||||||
let vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
|
|
||||||
(sess.opts.optimize == config::Default ||
|
|
||||||
sess.opts.optimize == config::Aggressive);
|
|
||||||
let vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
|
|
||||||
sess.opts.optimize == config::Aggressive;
|
|
||||||
|
|
||||||
let mut llvm_c_strs = Vec::new();
|
|
||||||
let mut llvm_args = Vec::new();
|
|
||||||
{
|
|
||||||
let add = |arg: &str| {
|
|
||||||
let s = arg.to_c_str();
|
|
||||||
llvm_args.push(s.as_ptr());
|
|
||||||
llvm_c_strs.push(s);
|
|
||||||
};
|
|
||||||
add("rustc"); // fake program name
|
|
||||||
if vectorize_loop { add("-vectorize-loops"); }
|
|
||||||
if vectorize_slp { add("-vectorize-slp"); }
|
|
||||||
if sess.time_llvm_passes() { add("-time-passes"); }
|
|
||||||
if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
|
|
||||||
|
|
||||||
for arg in sess.opts.cg.llvm_args.iter() {
|
|
||||||
add((*arg).as_slice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
INIT.doit(|| {
|
|
||||||
llvm::LLVMInitializePasses();
|
|
||||||
|
|
||||||
// Only initialize the platforms supported by Rust here, because
|
|
||||||
// using --llvm-root will have multiple platforms that rustllvm
|
|
||||||
// doesn't actually link to and it's pointless to put target info
|
|
||||||
// into the registry that Rust cannot generate machine code for.
|
|
||||||
llvm::LLVMInitializeX86TargetInfo();
|
|
||||||
llvm::LLVMInitializeX86Target();
|
|
||||||
llvm::LLVMInitializeX86TargetMC();
|
|
||||||
llvm::LLVMInitializeX86AsmPrinter();
|
|
||||||
llvm::LLVMInitializeX86AsmParser();
|
|
||||||
|
|
||||||
llvm::LLVMInitializeARMTargetInfo();
|
|
||||||
llvm::LLVMInitializeARMTarget();
|
|
||||||
llvm::LLVMInitializeARMTargetMC();
|
|
||||||
llvm::LLVMInitializeARMAsmPrinter();
|
|
||||||
llvm::LLVMInitializeARMAsmParser();
|
|
||||||
|
|
||||||
llvm::LLVMInitializeMipsTargetInfo();
|
|
||||||
llvm::LLVMInitializeMipsTarget();
|
|
||||||
llvm::LLVMInitializeMipsTargetMC();
|
|
||||||
llvm::LLVMInitializeMipsAsmPrinter();
|
|
||||||
llvm::LLVMInitializeMipsAsmParser();
|
|
||||||
|
|
||||||
llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
|
|
||||||
llvm_args.as_ptr());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef,
|
|
||||||
mpm: llvm::PassManagerRef,
|
|
||||||
llmod: ModuleRef,
|
|
||||||
opt: llvm::CodeGenOptLevel,
|
|
||||||
no_builtins: bool) {
|
|
||||||
// Create the PassManagerBuilder for LLVM. We configure it with
|
|
||||||
// reasonable defaults and prepare it to actually populate the pass
|
|
||||||
// manager.
|
|
||||||
let builder = llvm::LLVMPassManagerBuilderCreate();
|
|
||||||
match opt {
|
|
||||||
llvm::CodeGenLevelNone => {
|
|
||||||
// Don't add lifetime intrinsics at O0
|
|
||||||
llvm::LLVMRustAddAlwaysInlinePass(builder, false);
|
|
||||||
}
|
|
||||||
llvm::CodeGenLevelLess => {
|
|
||||||
llvm::LLVMRustAddAlwaysInlinePass(builder, true);
|
|
||||||
}
|
|
||||||
// numeric values copied from clang
|
|
||||||
llvm::CodeGenLevelDefault => {
|
|
||||||
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
|
|
||||||
225);
|
|
||||||
}
|
|
||||||
llvm::CodeGenLevelAggressive => {
|
|
||||||
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
|
|
||||||
275);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint);
|
|
||||||
llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins);
|
|
||||||
|
|
||||||
// Use the builder to populate the function/module pass managers.
|
|
||||||
llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm);
|
|
||||||
llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm);
|
|
||||||
llvm::LLVMPassManagerBuilderDispose(builder);
|
|
||||||
|
|
||||||
match opt {
|
|
||||||
llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => {
|
|
||||||
"mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Name mangling and its relationship to metadata. This is complex. Read
|
* Name mangling and its relationship to metadata. This is complex. Read
|
||||||
* carefully.
|
* carefully.
|
||||||
|
@ -878,7 +404,7 @@ pub fn get_ar_prog(sess: &Session) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(sess: &Session, path: &Path) {
|
pub fn remove(sess: &Session, path: &Path) {
|
||||||
match fs::unlink(path) {
|
match fs::unlink(path) {
|
||||||
Ok(..) => {}
|
Ok(..) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use super::link;
|
use super::link;
|
||||||
|
use super::write;
|
||||||
use driver::session;
|
use driver::session;
|
||||||
use driver::config;
|
use driver::config;
|
||||||
use llvm;
|
use llvm;
|
||||||
|
@ -119,7 +120,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
|
||||||
if !llvm::LLVMRustLinkInExternalBitcode(llmod,
|
if !llvm::LLVMRustLinkInExternalBitcode(llmod,
|
||||||
ptr as *const libc::c_char,
|
ptr as *const libc::c_char,
|
||||||
bc_decoded.len() as libc::size_t) {
|
bc_decoded.len() as libc::size_t) {
|
||||||
link::llvm_err(sess,
|
write::llvm_err(sess,
|
||||||
format!("failed to load bc of `{}`",
|
format!("failed to load bc of `{}`",
|
||||||
name.as_slice()));
|
name.as_slice()));
|
||||||
}
|
}
|
||||||
|
|
476
src/librustc/back/write.rs
Normal file
476
src/librustc/back/write.rs
Normal file
|
@ -0,0 +1,476 @@
|
||||||
|
// Copyright 2013-2014 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::lto;
|
||||||
|
use back::link::get_cc_prog;
|
||||||
|
use driver::driver::{CrateTranslation, OutputFilenames};
|
||||||
|
use driver::config::NoDebugInfo;
|
||||||
|
use driver::session::Session;
|
||||||
|
use driver::config;
|
||||||
|
use llvm;
|
||||||
|
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
|
||||||
|
use util::common::time;
|
||||||
|
use syntax::abi;
|
||||||
|
|
||||||
|
use std::c_str::{ToCStr, CString};
|
||||||
|
use std::io::Command;
|
||||||
|
use std::ptr;
|
||||||
|
use std::str;
|
||||||
|
use libc::{c_uint, c_int};
|
||||||
|
|
||||||
|
|
||||||
|
#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||||
|
pub enum OutputType {
|
||||||
|
OutputTypeBitcode,
|
||||||
|
OutputTypeAssembly,
|
||||||
|
OutputTypeLlvmAssembly,
|
||||||
|
OutputTypeObject,
|
||||||
|
OutputTypeExe,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn llvm_err(sess: &Session, msg: String) -> ! {
|
||||||
|
unsafe {
|
||||||
|
let cstr = llvm::LLVMRustGetLastError();
|
||||||
|
if cstr == ptr::null() {
|
||||||
|
sess.fatal(msg.as_slice());
|
||||||
|
} else {
|
||||||
|
let err = CString::new(cstr, true);
|
||||||
|
let err = String::from_utf8_lossy(err.as_bytes());
|
||||||
|
sess.fatal(format!("{}: {}",
|
||||||
|
msg.as_slice(),
|
||||||
|
err.as_slice()).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_output_file(
|
||||||
|
sess: &Session,
|
||||||
|
target: llvm::TargetMachineRef,
|
||||||
|
pm: llvm::PassManagerRef,
|
||||||
|
m: ModuleRef,
|
||||||
|
output: &Path,
|
||||||
|
file_type: llvm::FileType) {
|
||||||
|
unsafe {
|
||||||
|
output.with_c_str(|output| {
|
||||||
|
let result = llvm::LLVMRustWriteOutputFile(
|
||||||
|
target, pm, m, output, file_type);
|
||||||
|
if !result {
|
||||||
|
llvm_err(sess, "could not write output".to_string());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// On android, we by default compile for armv7 processors. This enables
|
||||||
|
// things like double word CAS instructions (rather than emulating them)
|
||||||
|
// which are *far* more efficient. This is obviously undesirable in some
|
||||||
|
// cases, so if any sort of target feature is specified we don't append v7
|
||||||
|
// to the feature list.
|
||||||
|
//
|
||||||
|
// On iOS only armv7 and newer are supported. So it is useful to
|
||||||
|
// get all hardware potential via VFP3 (hardware floating point)
|
||||||
|
// and NEON (SIMD) instructions supported by LLVM.
|
||||||
|
// Note that without those flags various linking errors might
|
||||||
|
// arise as some of intrinsics are converted into function calls
|
||||||
|
// and nobody provides implementations those functions
|
||||||
|
fn target_feature<'a>(sess: &'a Session) -> &'a str {
|
||||||
|
match sess.targ_cfg.os {
|
||||||
|
abi::OsAndroid => {
|
||||||
|
if "" == sess.opts.cg.target_feature.as_slice() {
|
||||||
|
"+v7"
|
||||||
|
} else {
|
||||||
|
sess.opts.cg.target_feature.as_slice()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
abi::OsiOS if sess.targ_cfg.arch == abi::Arm => {
|
||||||
|
"+v7,+thumb2,+vfp3,+neon"
|
||||||
|
},
|
||||||
|
_ => sess.opts.cg.target_feature.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_passes(sess: &Session,
|
||||||
|
trans: &CrateTranslation,
|
||||||
|
output_types: &[OutputType],
|
||||||
|
output: &OutputFilenames) {
|
||||||
|
let llmod = trans.module;
|
||||||
|
let llcx = trans.context;
|
||||||
|
unsafe {
|
||||||
|
configure_llvm(sess);
|
||||||
|
|
||||||
|
if sess.opts.cg.save_temps {
|
||||||
|
output.with_extension("no-opt.bc").with_c_str(|buf| {
|
||||||
|
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt_level = match sess.opts.optimize {
|
||||||
|
config::No => llvm::CodeGenLevelNone,
|
||||||
|
config::Less => llvm::CodeGenLevelLess,
|
||||||
|
config::Default => llvm::CodeGenLevelDefault,
|
||||||
|
config::Aggressive => llvm::CodeGenLevelAggressive,
|
||||||
|
};
|
||||||
|
let use_softfp = sess.opts.cg.soft_float;
|
||||||
|
|
||||||
|
// FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter.
|
||||||
|
// FIXME: #11954: mac64 unwinding may not work with fp elim
|
||||||
|
let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) ||
|
||||||
|
(sess.targ_cfg.os == abi::OsMacos &&
|
||||||
|
sess.targ_cfg.arch == abi::X86_64);
|
||||||
|
|
||||||
|
// OSX has -dead_strip, which doesn't rely on ffunction_sections
|
||||||
|
// FIXME(#13846) this should be enabled for windows
|
||||||
|
let ffunction_sections = sess.targ_cfg.os != abi::OsMacos &&
|
||||||
|
sess.targ_cfg.os != abi::OsWindows;
|
||||||
|
let fdata_sections = ffunction_sections;
|
||||||
|
|
||||||
|
let reloc_model = match sess.opts.cg.relocation_model.as_slice() {
|
||||||
|
"pic" => llvm::RelocPIC,
|
||||||
|
"static" => llvm::RelocStatic,
|
||||||
|
"default" => llvm::RelocDefault,
|
||||||
|
"dynamic-no-pic" => llvm::RelocDynamicNoPic,
|
||||||
|
_ => {
|
||||||
|
sess.err(format!("{} is not a valid relocation mode",
|
||||||
|
sess.opts
|
||||||
|
.cg
|
||||||
|
.relocation_model).as_slice());
|
||||||
|
sess.abort_if_errors();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let code_model = match sess.opts.cg.code_model.as_slice() {
|
||||||
|
"default" => llvm::CodeModelDefault,
|
||||||
|
"small" => llvm::CodeModelSmall,
|
||||||
|
"kernel" => llvm::CodeModelKernel,
|
||||||
|
"medium" => llvm::CodeModelMedium,
|
||||||
|
"large" => llvm::CodeModelLarge,
|
||||||
|
_ => {
|
||||||
|
sess.err(format!("{} is not a valid code model",
|
||||||
|
sess.opts
|
||||||
|
.cg
|
||||||
|
.code_model).as_slice());
|
||||||
|
sess.abort_if_errors();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let tm = sess.targ_cfg
|
||||||
|
.target_strs
|
||||||
|
.target_triple
|
||||||
|
.as_slice()
|
||||||
|
.with_c_str(|t| {
|
||||||
|
sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| {
|
||||||
|
target_feature(sess).with_c_str(|features| {
|
||||||
|
llvm::LLVMRustCreateTargetMachine(
|
||||||
|
t, cpu, features,
|
||||||
|
code_model,
|
||||||
|
reloc_model,
|
||||||
|
opt_level,
|
||||||
|
true /* EnableSegstk */,
|
||||||
|
use_softfp,
|
||||||
|
no_fp_elim,
|
||||||
|
ffunction_sections,
|
||||||
|
fdata_sections,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create the two optimizing pass managers. These mirror what clang
|
||||||
|
// does, and are by populated by LLVM's default PassManagerBuilder.
|
||||||
|
// Each manager has a different set of passes, but they also share
|
||||||
|
// some common passes.
|
||||||
|
let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod);
|
||||||
|
let mpm = llvm::LLVMCreatePassManager();
|
||||||
|
|
||||||
|
// If we're verifying or linting, add them to the function pass
|
||||||
|
// manager.
|
||||||
|
let addpass = |pass: &str| {
|
||||||
|
pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s))
|
||||||
|
};
|
||||||
|
if !sess.no_verify() { assert!(addpass("verify")); }
|
||||||
|
|
||||||
|
if !sess.opts.cg.no_prepopulate_passes {
|
||||||
|
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
|
||||||
|
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
|
||||||
|
populate_llvm_passes(fpm, mpm, llmod, opt_level,
|
||||||
|
trans.no_builtins);
|
||||||
|
}
|
||||||
|
|
||||||
|
for pass in sess.opts.cg.passes.iter() {
|
||||||
|
pass.as_slice().with_c_str(|s| {
|
||||||
|
if !llvm::LLVMRustAddPass(mpm, s) {
|
||||||
|
sess.warn(format!("unknown pass {}, ignoring",
|
||||||
|
*pass).as_slice());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, run the actual optimization passes
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Emit the bytecode if we're either saving our temporaries or
|
||||||
|
// emitting an rlib. Whenever an rlib is created, the bytecode is
|
||||||
|
// inserted into the archive in order to allow LTO against it.
|
||||||
|
if sess.opts.cg.save_temps ||
|
||||||
|
(sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
|
||||||
|
sess.opts.output_types.contains(&OutputTypeExe)) {
|
||||||
|
output.temp_path(OutputTypeBitcode).with_c_str(|buf| {
|
||||||
|
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if sess.lto() {
|
||||||
|
time(sess.time_passes(), "all lto passes", (), |()|
|
||||||
|
lto::run(sess, llmod, tm, trans.reachable.as_slice()));
|
||||||
|
|
||||||
|
if sess.opts.cg.save_temps {
|
||||||
|
output.with_extension("lto.bc").with_c_str(|buf| {
|
||||||
|
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
no_builtins: bool, f: |PassManagerRef|) {
|
||||||
|
unsafe {
|
||||||
|
let cpm = llvm::LLVMCreatePassManager();
|
||||||
|
llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
|
||||||
|
llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
|
||||||
|
f(cpm);
|
||||||
|
llvm::LLVMDisposePassManager(cpm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut object_file = None;
|
||||||
|
let mut needs_metadata = false;
|
||||||
|
for output_type in output_types.iter() {
|
||||||
|
let path = output.path(*output_type);
|
||||||
|
match *output_type {
|
||||||
|
OutputTypeBitcode => {
|
||||||
|
path.with_c_str(|buf| {
|
||||||
|
llvm::LLVMWriteBitcodeToFile(llmod, buf);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
OutputTypeLlvmAssembly => {
|
||||||
|
path.with_c_str(|output| {
|
||||||
|
with_codegen(tm, llmod, trans.no_builtins, |cpm| {
|
||||||
|
llvm::LLVMRustPrintModule(cpm, llmod, output);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
OutputTypeAssembly => {
|
||||||
|
// 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.
|
||||||
|
let ty = OutputTypeAssembly;
|
||||||
|
let path = if sess.opts.output_types.contains(&ty) {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
needs_metadata = true;
|
||||||
|
output.temp_path(OutputTypeAssembly)
|
||||||
|
};
|
||||||
|
with_codegen(tm, llmod, trans.no_builtins, |cpm| {
|
||||||
|
write_output_file(sess, tm, cpm, llmod, &path,
|
||||||
|
llvm::AssemblyFile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
OutputTypeObject => {
|
||||||
|
object_file = Some(path);
|
||||||
|
}
|
||||||
|
OutputTypeExe => {
|
||||||
|
object_file = Some(output.temp_path(OutputTypeObject));
|
||||||
|
needs_metadata = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time(sess.time_passes(), "codegen passes", (), |()| {
|
||||||
|
match object_file {
|
||||||
|
Some(ref path) => {
|
||||||
|
with_codegen(tm, llmod, trans.no_builtins, |cpm| {
|
||||||
|
write_output_file(sess, tm, cpm, llmod, path,
|
||||||
|
llvm::ObjectFile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
if needs_metadata {
|
||||||
|
with_codegen(tm, trans.metadata_module,
|
||||||
|
trans.no_builtins, |cpm| {
|
||||||
|
let out = output.temp_path(OutputTypeObject)
|
||||||
|
.with_extension("metadata.o");
|
||||||
|
write_output_file(sess, tm, cpm,
|
||||||
|
trans.metadata_module, &out,
|
||||||
|
llvm::ObjectFile);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
llvm::LLVMRustDisposeTargetMachine(tm);
|
||||||
|
llvm::LLVMDisposeModule(trans.metadata_module);
|
||||||
|
llvm::LLVMDisposeModule(llmod);
|
||||||
|
llvm::LLVMContextDispose(llcx);
|
||||||
|
if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
|
||||||
|
let pname = get_cc_prog(sess);
|
||||||
|
let mut cmd = Command::new(pname.as_slice());
|
||||||
|
|
||||||
|
cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject))
|
||||||
|
.arg(outputs.temp_path(OutputTypeAssembly));
|
||||||
|
debug!("{}", &cmd);
|
||||||
|
|
||||||
|
match cmd.output() {
|
||||||
|
Ok(prog) => {
|
||||||
|
if !prog.status.success() {
|
||||||
|
sess.err(format!("linking with `{}` failed: {}",
|
||||||
|
pname,
|
||||||
|
prog.status).as_slice());
|
||||||
|
sess.note(format!("{}", &cmd).as_slice());
|
||||||
|
let mut note = prog.error.clone();
|
||||||
|
note.push_all(prog.output.as_slice());
|
||||||
|
sess.note(str::from_utf8(note.as_slice()).unwrap());
|
||||||
|
sess.abort_if_errors();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
sess.err(format!("could not exec the linker `{}`: {}",
|
||||||
|
pname,
|
||||||
|
e).as_slice());
|
||||||
|
sess.abort_if_errors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn configure_llvm(sess: &Session) {
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
static mut INIT: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
// Copy what clang does by turning on loop vectorization at O2 and
|
||||||
|
// slp vectorization at O3
|
||||||
|
let vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
|
||||||
|
(sess.opts.optimize == config::Default ||
|
||||||
|
sess.opts.optimize == config::Aggressive);
|
||||||
|
let vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
|
||||||
|
sess.opts.optimize == config::Aggressive;
|
||||||
|
|
||||||
|
let mut llvm_c_strs = Vec::new();
|
||||||
|
let mut llvm_args = Vec::new();
|
||||||
|
{
|
||||||
|
let add = |arg: &str| {
|
||||||
|
let s = arg.to_c_str();
|
||||||
|
llvm_args.push(s.as_ptr());
|
||||||
|
llvm_c_strs.push(s);
|
||||||
|
};
|
||||||
|
add("rustc"); // fake program name
|
||||||
|
if vectorize_loop { add("-vectorize-loops"); }
|
||||||
|
if vectorize_slp { add("-vectorize-slp"); }
|
||||||
|
if sess.time_llvm_passes() { add("-time-passes"); }
|
||||||
|
if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
|
||||||
|
|
||||||
|
for arg in sess.opts.cg.llvm_args.iter() {
|
||||||
|
add((*arg).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT.doit(|| {
|
||||||
|
llvm::LLVMInitializePasses();
|
||||||
|
|
||||||
|
// Only initialize the platforms supported by Rust here, because
|
||||||
|
// using --llvm-root will have multiple platforms that rustllvm
|
||||||
|
// doesn't actually link to and it's pointless to put target info
|
||||||
|
// into the registry that Rust cannot generate machine code for.
|
||||||
|
llvm::LLVMInitializeX86TargetInfo();
|
||||||
|
llvm::LLVMInitializeX86Target();
|
||||||
|
llvm::LLVMInitializeX86TargetMC();
|
||||||
|
llvm::LLVMInitializeX86AsmPrinter();
|
||||||
|
llvm::LLVMInitializeX86AsmParser();
|
||||||
|
|
||||||
|
llvm::LLVMInitializeARMTargetInfo();
|
||||||
|
llvm::LLVMInitializeARMTarget();
|
||||||
|
llvm::LLVMInitializeARMTargetMC();
|
||||||
|
llvm::LLVMInitializeARMAsmPrinter();
|
||||||
|
llvm::LLVMInitializeARMAsmParser();
|
||||||
|
|
||||||
|
llvm::LLVMInitializeMipsTargetInfo();
|
||||||
|
llvm::LLVMInitializeMipsTarget();
|
||||||
|
llvm::LLVMInitializeMipsTargetMC();
|
||||||
|
llvm::LLVMInitializeMipsAsmPrinter();
|
||||||
|
llvm::LLVMInitializeMipsAsmParser();
|
||||||
|
|
||||||
|
llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
|
||||||
|
llvm_args.as_ptr());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef,
|
||||||
|
mpm: llvm::PassManagerRef,
|
||||||
|
llmod: ModuleRef,
|
||||||
|
opt: llvm::CodeGenOptLevel,
|
||||||
|
no_builtins: bool) {
|
||||||
|
// Create the PassManagerBuilder for LLVM. We configure it with
|
||||||
|
// reasonable defaults and prepare it to actually populate the pass
|
||||||
|
// manager.
|
||||||
|
let builder = llvm::LLVMPassManagerBuilderCreate();
|
||||||
|
match opt {
|
||||||
|
llvm::CodeGenLevelNone => {
|
||||||
|
// Don't add lifetime intrinsics at O0
|
||||||
|
llvm::LLVMRustAddAlwaysInlinePass(builder, false);
|
||||||
|
}
|
||||||
|
llvm::CodeGenLevelLess => {
|
||||||
|
llvm::LLVMRustAddAlwaysInlinePass(builder, true);
|
||||||
|
}
|
||||||
|
// numeric values copied from clang
|
||||||
|
llvm::CodeGenLevelDefault => {
|
||||||
|
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
|
||||||
|
225);
|
||||||
|
}
|
||||||
|
llvm::CodeGenLevelAggressive => {
|
||||||
|
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
|
||||||
|
275);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint);
|
||||||
|
llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins);
|
||||||
|
|
||||||
|
// Use the builder to populate the function/module pass managers.
|
||||||
|
llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm);
|
||||||
|
llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm);
|
||||||
|
llvm::LLVMPassManagerBuilderDispose(builder);
|
||||||
|
|
||||||
|
match opt {
|
||||||
|
llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => {
|
||||||
|
"mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ use driver::driver;
|
||||||
use driver::session::Session;
|
use driver::session::Session;
|
||||||
|
|
||||||
use back;
|
use back;
|
||||||
use back::link;
|
use back::write;
|
||||||
use back::target_strs;
|
use back::target_strs;
|
||||||
use back::{arm, x86, x86_64, mips, mipsel};
|
use back::{arm, x86, x86_64, mips, mipsel};
|
||||||
use lint;
|
use lint;
|
||||||
|
@ -72,7 +72,7 @@ pub struct Options {
|
||||||
pub debuginfo: DebugInfoLevel,
|
pub debuginfo: DebugInfoLevel,
|
||||||
pub lint_opts: Vec<(String, lint::Level)>,
|
pub lint_opts: Vec<(String, lint::Level)>,
|
||||||
pub describe_lints: bool,
|
pub describe_lints: bool,
|
||||||
pub output_types: Vec<back::link::OutputType> ,
|
pub output_types: Vec<back::write::OutputType> ,
|
||||||
// This was mutable for rustpkg, which updates search paths based on the
|
// This was mutable for rustpkg, which updates search paths based on the
|
||||||
// parsed code. It remains mutable in case its replacements wants to use
|
// parsed code. It remains mutable in case its replacements wants to use
|
||||||
// this.
|
// this.
|
||||||
|
@ -646,11 +646,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
for unparsed_output_type in unparsed_output_types.iter() {
|
for unparsed_output_type in unparsed_output_types.iter() {
|
||||||
for part in unparsed_output_type.as_slice().split(',') {
|
for part in unparsed_output_type.as_slice().split(',') {
|
||||||
let output_type = match part.as_slice() {
|
let output_type = match part.as_slice() {
|
||||||
"asm" => link::OutputTypeAssembly,
|
"asm" => write::OutputTypeAssembly,
|
||||||
"ir" => link::OutputTypeLlvmAssembly,
|
"ir" => write::OutputTypeLlvmAssembly,
|
||||||
"bc" => link::OutputTypeBitcode,
|
"bc" => write::OutputTypeBitcode,
|
||||||
"obj" => link::OutputTypeObject,
|
"obj" => write::OutputTypeObject,
|
||||||
"link" => link::OutputTypeExe,
|
"link" => write::OutputTypeExe,
|
||||||
_ => {
|
_ => {
|
||||||
early_error(format!("unknown emission type: `{}`",
|
early_error(format!("unknown emission type: `{}`",
|
||||||
part).as_slice())
|
part).as_slice())
|
||||||
|
@ -663,7 +663,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
output_types.as_mut_slice().sort();
|
output_types.as_mut_slice().sort();
|
||||||
output_types.dedup();
|
output_types.dedup();
|
||||||
if output_types.len() == 0 {
|
if output_types.len() == 0 {
|
||||||
output_types.push(link::OutputTypeExe);
|
output_types.push(write::OutputTypeExe);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
|
let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
|
|
||||||
use back::link;
|
use back::link;
|
||||||
|
use back::write;
|
||||||
use driver::session::Session;
|
use driver::session::Session;
|
||||||
use driver::config;
|
use driver::config;
|
||||||
use front;
|
use front;
|
||||||
|
@ -473,20 +474,20 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
|
||||||
trans: &CrateTranslation,
|
trans: &CrateTranslation,
|
||||||
outputs: &OutputFilenames) {
|
outputs: &OutputFilenames) {
|
||||||
if sess.opts.cg.no_integrated_as {
|
if sess.opts.cg.no_integrated_as {
|
||||||
let output_type = link::OutputTypeAssembly;
|
let output_type = write::OutputTypeAssembly;
|
||||||
|
|
||||||
time(sess.time_passes(), "LLVM passes", (), |_|
|
time(sess.time_passes(), "LLVM passes", (), |_|
|
||||||
link::write::run_passes(sess, trans, [output_type], outputs));
|
write::run_passes(sess, trans, [output_type], outputs));
|
||||||
|
|
||||||
link::write::run_assembler(sess, outputs);
|
write::run_assembler(sess, outputs);
|
||||||
|
|
||||||
// Remove assembly source, unless --save-temps was specified
|
// Remove assembly source, unless --save-temps was specified
|
||||||
if !sess.opts.cg.save_temps {
|
if !sess.opts.cg.save_temps {
|
||||||
fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
|
fs::unlink(&outputs.temp_path(write::OutputTypeAssembly)).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
time(sess.time_passes(), "LLVM passes", (), |_|
|
time(sess.time_passes(), "LLVM passes", (), |_|
|
||||||
link::write::run_passes(sess,
|
write::run_passes(sess,
|
||||||
trans,
|
trans,
|
||||||
sess.opts.output_types.as_slice(),
|
sess.opts.output_types.as_slice(),
|
||||||
outputs));
|
outputs));
|
||||||
|
@ -533,7 +534,7 @@ pub fn stop_after_phase_2(sess: &Session) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_after_phase_5(sess: &Session) -> bool {
|
pub fn stop_after_phase_5(sess: &Session) -> bool {
|
||||||
if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
|
if !sess.opts.output_types.iter().any(|&i| i == write::OutputTypeExe) {
|
||||||
debug!("not building executable, returning early from compile_input");
|
debug!("not building executable, returning early from compile_input");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -549,7 +550,7 @@ fn write_out_deps(sess: &Session,
|
||||||
for output_type in sess.opts.output_types.iter() {
|
for output_type in sess.opts.output_types.iter() {
|
||||||
let file = outputs.path(*output_type);
|
let file = outputs.path(*output_type);
|
||||||
match *output_type {
|
match *output_type {
|
||||||
link::OutputTypeExe => {
|
write::OutputTypeExe => {
|
||||||
for output in sess.crate_types.borrow().iter() {
|
for output in sess.crate_types.borrow().iter() {
|
||||||
let p = link::filename_for_input(sess, *output,
|
let p = link::filename_for_input(sess, *output,
|
||||||
id, &file);
|
id, &file);
|
||||||
|
@ -688,7 +689,7 @@ pub struct OutputFilenames {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputFilenames {
|
impl OutputFilenames {
|
||||||
pub fn path(&self, flavor: link::OutputType) -> Path {
|
pub fn path(&self, flavor: write::OutputType) -> Path {
|
||||||
match self.single_output_file {
|
match self.single_output_file {
|
||||||
Some(ref path) => return path.clone(),
|
Some(ref path) => return path.clone(),
|
||||||
None => {}
|
None => {}
|
||||||
|
@ -696,14 +697,14 @@ impl OutputFilenames {
|
||||||
self.temp_path(flavor)
|
self.temp_path(flavor)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn temp_path(&self, flavor: link::OutputType) -> Path {
|
pub fn temp_path(&self, flavor: write::OutputType) -> Path {
|
||||||
let base = self.out_directory.join(self.filestem());
|
let base = self.out_directory.join(self.filestem());
|
||||||
match flavor {
|
match flavor {
|
||||||
link::OutputTypeBitcode => base.with_extension("bc"),
|
write::OutputTypeBitcode => base.with_extension("bc"),
|
||||||
link::OutputTypeAssembly => base.with_extension("s"),
|
write::OutputTypeAssembly => base.with_extension("s"),
|
||||||
link::OutputTypeLlvmAssembly => base.with_extension("ll"),
|
write::OutputTypeLlvmAssembly => base.with_extension("ll"),
|
||||||
link::OutputTypeObject => base.with_extension("o"),
|
write::OutputTypeObject => base.with_extension("o"),
|
||||||
link::OutputTypeExe => base,
|
write::OutputTypeExe => base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ pub mod back {
|
||||||
|
|
||||||
pub mod link;
|
pub mod link;
|
||||||
pub mod lto;
|
pub mod lto;
|
||||||
|
pub mod write;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use std::string::String;
|
||||||
|
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
use testing;
|
use testing;
|
||||||
use rustc::back::link;
|
use rustc::back::write;
|
||||||
use rustc::driver::config;
|
use rustc::driver::config;
|
||||||
use rustc::driver::driver;
|
use rustc::driver::driver;
|
||||||
use rustc::driver::session;
|
use rustc::driver::session;
|
||||||
|
@ -120,7 +120,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, externs: core::Exte
|
||||||
maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
|
maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
|
||||||
addl_lib_search_paths: RefCell::new(libs),
|
addl_lib_search_paths: RefCell::new(libs),
|
||||||
crate_types: vec!(config::CrateTypeExecutable),
|
crate_types: vec!(config::CrateTypeExecutable),
|
||||||
output_types: vec!(link::OutputTypeExe),
|
output_types: vec!(write::OutputTypeExe),
|
||||||
no_trans: no_run,
|
no_trans: no_run,
|
||||||
externs: externs,
|
externs: externs,
|
||||||
cg: config::CodegenOptions {
|
cg: config::CodegenOptions {
|
||||||
|
|
Loading…
Reference in a new issue