auto merge of #16367 : epdtry/rust/parallel-codegen, r=alexcrichton

This branch adds support for running LLVM optimization and codegen on different parts of a crate in parallel.  Instead of translating the crate into a single LLVM compilation unit, `rustc` now distributes items in the crate among several compilation units, and spawns worker threads to optimize and codegen each compilation unit independently.  This improves compile times on multicore machines, at the cost of worse performance in the compiled code.  The intent is to speed up build times during development without sacrificing too much optimization.

On the machine I tested this on, `librustc` build time with `-O` went from 265 seconds (master branch, single-threaded) to 115s (this branch, with 4 threads), a speedup of 2.3x.  For comparison, the build time without `-O` was 90s (single-threaded).  Bootstrapping `rustc` using 4 threads gets a 1.6x speedup over the default settings (870s vs. 1380s), and building `librustc` with the resulting stage2 compiler takes 1.3x as long as the master branch (44s vs.  55s, single threaded, ignoring time spent in LLVM codegen).

The user-visible changes from this branch are two new codegen flags:

 * `-C codegen-units=N`: Distribute items across `N` compilation units.
 * `-C codegen-threads=N`: Spawn `N` worker threads for running optimization and codegen.  (It is possible to set `codegen-threads` larger than `codegen-units`, but this is not very useful.)

Internal changes to the compiler are described in detail on the individual commit messages.

Note: The first commit on this branch is copied from #16359, which this branch depends on.

r? @nick29581
This commit is contained in:
bors 2014-09-06 06:06:35 +00:00
commit 4bea7b3ed0
56 changed files with 2966 additions and 1176 deletions

View File

@ -1577,10 +1577,6 @@ fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) {
// codegen tests (vs. clang)
fn make_o_name(config: &Config, testfile: &Path) -> Path {
output_base_name(config, testfile).with_extension("o")
}
fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path {
if suffix.len() == 0 {
(*p).clone()
@ -1596,14 +1592,13 @@ fn compile_test_and_save_bitcode(config: &Config, props: &TestProps,
// FIXME (#9639): This needs to handle non-utf8 paths
let link_args = vec!("-L".to_string(),
aux_dir.as_str().unwrap().to_string());
let llvm_args = vec!("--emit=obj".to_string(),
"--crate-type=lib".to_string(),
"-C".to_string(),
"save-temps".to_string());
let llvm_args = vec!("--emit=bc,obj".to_string(),
"--crate-type=lib".to_string());
let args = make_compile_args(config,
props,
link_args.append(llvm_args.as_slice()),
|a, b| ThisFile(make_o_name(a, b)), testfile);
|a, b| ThisDirectory(output_base_name(a, b).dir_path()),
testfile);
compose_and_run_compiler(config, props, testfile, args, None)
}

View File

@ -13,12 +13,11 @@ use super::archive;
use super::rpath;
use super::rpath::RPathConfig;
use super::svh::Svh;
use super::write::{OutputTypeBitcode, OutputTypeExe, OutputTypeObject};
use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput};
use driver::config::NoDebugInfo;
use driver::session::Session;
use driver::config;
use llvm;
use llvm::ModuleRef;
use metadata::common::LinkMeta;
use metadata::{encoder, cstore, filesearch, csearch, loader, creader};
use middle::trans::context::CrateContext;
@ -28,13 +27,11 @@ use util::common::time;
use util::ppaux;
use util::sha2::{Digest, Sha256};
use std::c_str::{ToCStr, CString};
use std::char;
use std::collections::HashSet;
use std::io::{fs, TempDir, Command};
use std::io;
use std::mem;
use std::ptr;
use std::str;
use std::string::String;
use flate;
@ -77,476 +74,6 @@ pub static RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: uint =
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::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
* carefully.
@ -715,14 +242,14 @@ fn symbol_hash(tcx: &ty::ctxt,
}
fn get_symbol_hash(ccx: &CrateContext, t: ty::t) -> String {
match ccx.type_hashcodes.borrow().find(&t) {
match ccx.type_hashcodes().borrow().find(&t) {
Some(h) => return h.to_string(),
None => {}
}
let mut symbol_hasher = ccx.symbol_hasher.borrow_mut();
let hash = symbol_hash(ccx.tcx(), &mut *symbol_hasher, t, &ccx.link_meta);
ccx.type_hashcodes.borrow_mut().insert(t, hash.clone());
let mut symbol_hasher = ccx.symbol_hasher().borrow_mut();
let hash = symbol_hash(ccx.tcx(), &mut *symbol_hasher, t, ccx.link_meta());
ccx.type_hashcodes().borrow_mut().insert(t, hash.clone());
hash
}
@ -877,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) {
Ok(..) => {}
Err(e) => {
@ -1135,51 +662,56 @@ fn link_rlib<'a>(sess: &'a Session,
ab.add_file(&metadata).unwrap();
remove(sess, &metadata);
// For LTO purposes, the bytecode of this library is also inserted
// into the archive.
//
// Note that we make sure that the bytecode filename in the archive
// is never exactly 16 bytes long by adding a 16 byte extension to
// it. This is to work around a bug in LLDB that would cause it to
// crash if the name of a file in an archive was exactly 16 bytes.
let bc_filename = obj_filename.with_extension("bc");
let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate");
if sess.opts.cg.codegen_units == 1 {
// For LTO purposes, the bytecode of this library is also
// inserted into the archive. We currently do this only when
// codegen_units == 1, so we don't have to deal with multiple
// bitcode files per crate.
//
// Note that we make sure that the bytecode filename in the
// archive is never exactly 16 bytes long by adding a 16 byte
// extension to it. This is to work around a bug in LLDB that
// would cause it to crash if the name of a file in an archive
// was exactly 16 bytes.
let bc_filename = obj_filename.with_extension("bc");
let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate");
let bc_data = match fs::File::open(&bc_filename).read_to_end() {
Ok(buffer) => buffer,
Err(e) => sess.fatal(format!("failed to read bytecode: {}",
e).as_slice())
};
let bc_data = match fs::File::open(&bc_filename).read_to_end() {
Ok(buffer) => buffer,
Err(e) => sess.fatal(format!("failed to read bytecode: {}",
e).as_slice())
};
let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) {
Some(compressed) => compressed,
None => sess.fatal(format!("failed to compress bytecode from {}",
bc_filename.display()).as_slice())
};
let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) {
Some(compressed) => compressed,
None => sess.fatal(format!("failed to compress bytecode from {}",
bc_filename.display()).as_slice())
};
let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) {
Ok(file) => file,
Err(e) => {
sess.fatal(format!("failed to create compressed bytecode \
file: {}", e).as_slice())
let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) {
Ok(file) => file,
Err(e) => {
sess.fatal(format!("failed to create compressed bytecode \
file: {}", e).as_slice())
}
};
match write_rlib_bytecode_object_v1(&mut bc_file_deflated,
bc_data_deflated.as_slice()) {
Ok(()) => {}
Err(e) => {
sess.err(format!("failed to write compressed bytecode: \
{}", e).as_slice());
sess.abort_if_errors()
}
};
ab.add_file(&bc_deflated_filename).unwrap();
remove(sess, &bc_deflated_filename);
if !sess.opts.cg.save_temps &&
!sess.opts.output_types.contains(&OutputTypeBitcode) {
remove(sess, &bc_filename);
}
};
match write_rlib_bytecode_object_v1(&mut bc_file_deflated,
bc_data_deflated.as_slice()) {
Ok(()) => {}
Err(e) => {
sess.err(format!("failed to write compressed bytecode: \
{}", e).as_slice());
sess.abort_if_errors()
}
};
ab.add_file(&bc_deflated_filename).unwrap();
remove(sess, &bc_deflated_filename);
if !sess.opts.cg.save_temps &&
!sess.opts.output_types.contains(&OutputTypeBitcode) {
remove(sess, &bc_filename);
}
}

View File

@ -9,6 +9,7 @@
// except according to those terms.
use super::link;
use super::write;
use driver::session;
use driver::config;
use llvm;
@ -66,7 +67,14 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
archive.read(format!("{}.bytecode.deflate",
file).as_slice())
});
let bc_encoded = bc_encoded.expect("missing compressed bytecode in archive!");
let bc_encoded = match bc_encoded {
Some(data) => data,
None => {
sess.fatal(format!("missing compressed bytecode in {} \
(perhaps it was compiled with -C codegen-units > 1)",
path.display()).as_slice());
},
};
let bc_extractor = if is_versioned_bytecode_format(bc_encoded) {
|_| {
// Read the version
@ -119,9 +127,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
if !llvm::LLVMRustLinkInExternalBitcode(llmod,
ptr as *const libc::c_char,
bc_decoded.len() as libc::size_t) {
link::llvm_err(sess,
format!("failed to load bc of `{}`",
name.as_slice()));
write::llvm_err(sess.diagnostic().handler(),
format!("failed to load bc of `{}`",
name.as_slice()));
}
});
}

962
src/librustc/back/write.rs Normal file
View File

@ -0,0 +1,962 @@
// 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, remove};
use driver::driver::{CrateTranslation, ModuleTranslation, 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 syntax::codemap;
use syntax::diagnostic;
use syntax::diagnostic::{Emitter, Handler, Level, mk_handler};
use std::c_str::{ToCStr, CString};
use std::io::Command;
use std::io::fs;
use std::iter::Unfold;
use std::ptr;
use std::str;
use std::sync::{Arc, Mutex};
use std::task::TaskBuilder;
use libc::{c_uint, c_int};
#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum OutputType {
OutputTypeBitcode,
OutputTypeAssembly,
OutputTypeLlvmAssembly,
OutputTypeObject,
OutputTypeExe,
}
pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! {
unsafe {
let cstr = llvm::LLVMRustGetLastError();
if cstr == ptr::null() {
handler.fatal(msg.as_slice());
} else {
let err = CString::new(cstr, true);
let err = String::from_utf8_lossy(err.as_bytes());
handler.fatal(format!("{}: {}",
msg.as_slice(),
err.as_slice()).as_slice());
}
}
}
pub fn write_output_file(
handler: &diagnostic::Handler,
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(handler, "could not write output".to_string());
}
})
}
}
struct Diagnostic {
msg: String,
code: Option<String>,
lvl: Level,
}
// We use an Arc instead of just returning a list of diagnostics from the
// child task because we need to make sure that the messages are seen even
// if the child task fails (for example, when `fatal` is called).
#[deriving(Clone)]
struct SharedEmitter {
buffer: Arc<Mutex<Vec<Diagnostic>>>,
}
impl SharedEmitter {
fn new() -> SharedEmitter {
SharedEmitter {
buffer: Arc::new(Mutex::new(Vec::new())),
}
}
fn dump(&mut self, handler: &Handler) {
let mut buffer = self.buffer.lock();
for diag in buffer.iter() {
match diag.code {
Some(ref code) => {
handler.emit_with_code(None,
diag.msg.as_slice(),
code.as_slice(),
diag.lvl);
},
None => {
handler.emit(None,
diag.msg.as_slice(),
diag.lvl);
},
}
}
buffer.clear();
}
}
impl Emitter for SharedEmitter {
fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
msg: &str, code: Option<&str>, lvl: Level) {
assert!(cmsp.is_none(), "SharedEmitter doesn't support spans");
self.buffer.lock().push(Diagnostic {
msg: msg.to_string(),
code: code.map(|s| s.to_string()),
lvl: lvl,
});
}
fn custom_emit(&mut self, _cm: &codemap::CodeMap,
_sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) {
fail!("SharedEmitter doesn't support custom_emit");
}
}
// 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()
}
}
fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
match optimize {
config::No => llvm::CodeGenLevelNone,
config::Less => llvm::CodeGenLevelLess,
config::Default => llvm::CodeGenLevelDefault,
config::Aggressive => llvm::CodeGenLevelAggressive,
}
}
fn create_target_machine(sess: &Session) -> TargetMachineRef {
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();
unreachable!();
}
};
let opt_level = get_llvm_opt_level(sess.opts.optimize);
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 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();
unreachable!();
}
};
unsafe {
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,
)
})
})
})
}
}
/// Module-specific configuration for `optimize_and_codegen`.
#[deriving(Clone)]
struct ModuleConfig {
/// LLVM TargetMachine to use for codegen.
tm: TargetMachineRef,
/// Names of additional optimization passes to run.
passes: Vec<String>,
/// Some(level) to optimize at a certain level, or None to run
/// absolutely no optimizations (used for the metadata module).
opt_level: Option<llvm::CodeGenOptLevel>,
// Flags indicating which outputs to produce.
emit_no_opt_bc: bool,
emit_bc: bool,
emit_lto_bc: bool,
emit_ir: bool,
emit_asm: bool,
emit_obj: bool,
// Miscellaneous flags. These are mostly copied from command-line
// options.
no_verify: bool,
no_prepopulate_passes: bool,
no_builtins: bool,
time_passes: bool,
}
impl ModuleConfig {
fn new(tm: TargetMachineRef, passes: Vec<String>) -> ModuleConfig {
ModuleConfig {
tm: tm,
passes: passes,
opt_level: None,
emit_no_opt_bc: false,
emit_bc: false,
emit_lto_bc: false,
emit_ir: false,
emit_asm: false,
emit_obj: false,
no_verify: false,
no_prepopulate_passes: false,
no_builtins: false,
time_passes: false,
}
}
fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) {
self.no_verify = sess.no_verify();
self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
self.no_builtins = trans.no_builtins;
self.time_passes = sess.time_passes();
}
}
/// Additional resources used by optimize_and_codegen (not module specific)
struct CodegenContext<'a> {
// Extra resources used for LTO: (sess, reachable). This will be `None`
// when running in a worker thread.
lto_ctxt: Option<(&'a Session, &'a [String])>,
// Handler to use for diagnostics produced during codegen.
handler: &'a Handler,
}
impl<'a> CodegenContext<'a> {
fn new(handler: &'a Handler) -> CodegenContext<'a> {
CodegenContext {
lto_ctxt: None,
handler: handler,
}
}
fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
CodegenContext {
lto_ctxt: Some((sess, reachable)),
handler: sess.diagnostic().handler(),
}
}
}
// Unsafe due to LLVM calls.
unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
mtrans: ModuleTranslation,
config: ModuleConfig,
name_extra: String,
output_names: OutputFilenames) {
let ModuleTranslation { llmod, llcx } = mtrans;
let tm = config.tm;
if config.emit_no_opt_bc {
let ext = format!("{}.no-opt.bc", name_extra);
output_names.with_extension(ext.as_slice()).with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
}
match config.opt_level {
Some(opt_level) => {
// 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 !config.no_verify { assert!(addpass("verify")); }
if !config.no_prepopulate_passes {
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
populate_llvm_passes(fpm, mpm, llmod, opt_level,
config.no_builtins);
}
for pass in config.passes.iter() {
pass.as_slice().with_c_str(|s| {
if !llvm::LLVMRustAddPass(mpm, s) {
cgcx.handler.warn(format!("unknown pass {}, ignoring",
*pass).as_slice());
}
})
}
// Finally, run the actual optimization passes
time(config.time_passes, "llvm function passes", (), |()|
llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
time(config.time_passes, "llvm module passes", (), |()|
llvm::LLVMRunPassManager(mpm, llmod));
// Deallocate managers that we're now done with
llvm::LLVMDisposePassManager(fpm);
llvm::LLVMDisposePassManager(mpm);
match cgcx.lto_ctxt {
Some((sess, reachable)) if sess.lto() => {
time(sess.time_passes(), "all lto passes", (), |()|
lto::run(sess, llmod, tm, reachable));
if config.emit_lto_bc {
let name = format!("{}.lto.bc", name_extra);
output_names.with_extension(name.as_slice()).with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
}
},
_ => {},
}
},
None => {},
}
// 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.
unsafe fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef,
no_builtins: bool, f: |PassManagerRef|) {
let cpm = llvm::LLVMCreatePassManager();
llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
f(cpm);
llvm::LLVMDisposePassManager(cpm);
}
if config.emit_bc {
let ext = format!("{}.bc", name_extra);
output_names.with_extension(ext.as_slice()).with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
}
time(config.time_passes, "codegen passes", (), |()| {
if config.emit_ir {
let ext = format!("{}.ll", name_extra);
output_names.with_extension(ext.as_slice()).with_c_str(|output| {
with_codegen(tm, llmod, config.no_builtins, |cpm| {
llvm::LLVMRustPrintModule(cpm, llmod, output);
})
})
}
if config.emit_asm {
let path = output_names.with_extension(format!("{}.s", name_extra).as_slice());
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFile);
});
}
if config.emit_obj {
let path = output_names.with_extension(format!("{}.o", name_extra).as_slice());
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFile);
});
}
});
llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
llvm::LLVMRustDisposeTargetMachine(tm);
}
pub fn run_passes(sess: &Session,
trans: &CrateTranslation,
output_types: &[OutputType],
crate_output: &OutputFilenames) {
// It's possible that we have `codegen_units > 1` but only one item in
// `trans.modules`. We could theoretically proceed and do LTO in that
// case, but it would be confusing to have the validity of
// `-Z lto -C codegen-units=2` depend on details of the crate being
// compiled, so we complain regardless.
if sess.lto() && sess.opts.cg.codegen_units > 1 {
// This case is impossible to handle because LTO expects to be able
// to combine the entire crate and all its dependencies into a
// single compilation unit, but each codegen unit is in a separate
// LLVM context, so they can't easily be combined.
sess.fatal("can't perform LTO when using multiple codegen units");
}
// Sanity check
assert!(trans.modules.len() == sess.opts.cg.codegen_units);
unsafe {
configure_llvm(sess);
}
let tm = create_target_machine(sess);
// Figure out what we actually need to build.
let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone());
let mut metadata_config = ModuleConfig::new(tm, vec!());
modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize));
// Save all versions of the bytecode if we're saving our temporaries.
if sess.opts.cg.save_temps {
modules_config.emit_no_opt_bc = true;
modules_config.emit_bc = true;
modules_config.emit_lto_bc = true;
metadata_config.emit_bc = true;
}
// Emit a bitcode file for the crate if we're emitting an rlib.
// Whenever an rlib is created, the bitcode is inserted into the
// archive in order to allow LTO against it.
let needs_crate_bitcode =
sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
sess.opts.output_types.contains(&OutputTypeExe) &&
sess.opts.cg.codegen_units == 1;
if needs_crate_bitcode {
modules_config.emit_bc = true;
}
for output_type in output_types.iter() {
match *output_type {
OutputTypeBitcode => { modules_config.emit_bc = true; },
OutputTypeLlvmAssembly => { modules_config.emit_ir = true; },
OutputTypeAssembly => {
modules_config.emit_asm = true;
// 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_types.contains(&OutputTypeAssembly) {
metadata_config.emit_obj = true;
}
},
OutputTypeObject => { modules_config.emit_obj = true; },
OutputTypeExe => {
modules_config.emit_obj = true;
metadata_config.emit_obj = true;
},
}
}
modules_config.set_flags(sess, trans);
metadata_config.set_flags(sess, trans);
// Populate a buffer with a list of codegen tasks. Items are processed in
// LIFO order, just because it's a tiny bit simpler that way. (The order
// doesn't actually matter.)
let mut work_items = Vec::with_capacity(1 + trans.modules.len());
{
let work = build_work_item(sess,
trans.metadata_module,
metadata_config.clone(),
crate_output.clone(),
"metadata".to_string());
work_items.push(work);
}
for (index, mtrans) in trans.modules.iter().enumerate() {
let work = build_work_item(sess,
*mtrans,
modules_config.clone(),
crate_output.clone(),
format!("{}", index));
work_items.push(work);
}
// Process the work items, optionally using worker threads.
if sess.opts.cg.codegen_units == 1 {
run_work_singlethreaded(sess, trans.reachable.as_slice(), work_items);
if needs_crate_bitcode {
// The only bitcode file produced (aside from metadata) was
// "crate.0.bc". Rename to "crate.bc" since that's what
// `link_rlib` expects to find.
fs::copy(&crate_output.with_extension("0.bc"),
&crate_output.temp_path(OutputTypeBitcode)).unwrap();
}
} else {
run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units);
assert!(!needs_crate_bitcode,
"can't produce a crate bitcode file from multiple compilation units");
}
// All codegen is finished.
unsafe {
llvm::LLVMRustDisposeTargetMachine(tm);
}
// Produce final compile outputs.
let copy_if_one_unit = |ext: &str, output_type: OutputType| {
// Three cases:
if sess.opts.cg.codegen_units == 1 {
// 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
fs::copy(&crate_output.with_extension(ext),
&crate_output.path(output_type)).unwrap();
if !sess.opts.cg.save_temps {
// The user just wants `foo.x`, not `foo.0.x`.
remove(sess, &crate_output.with_extension(ext));
}
} else {
if crate_output.single_output_file.is_some() {
// 2) Multiple codegen units, with `-o some_name`. We have
// no good solution for this case, so warn the user.
sess.warn(format!("ignoring -o because multiple .{} files were produced",
ext).as_slice());
} else {
// 3) Multiple codegen units, but no `-o some_name`. We
// just leave the `foo.0.x` files in place.
// (We don't have to do any work in this case.)
}
}
};
let link_obj = |output_path: &Path| {
// Running `ld -r` on a single input is kind of pointless.
if sess.opts.cg.codegen_units == 1 {
fs::copy(&crate_output.with_extension("0.o"),
output_path).unwrap();
// Leave the .0.o file around, to mimic the behavior of the normal
// code path.
return;
}
// Some builds of MinGW GCC will pass --force-exe-suffix to ld, which
// will automatically add a .exe extension if the extension is not
// already .exe or .dll. To ensure consistent behavior on Windows, we
// add the .exe suffix explicitly and then rename the output file to
// the desired path. This will give the correct behavior whether or
// not GCC adds --force-exe-suffix.
let windows_output_path =
if sess.targ_cfg.os == abi::OsWindows {
Some(output_path.with_extension("o.exe"))
} else {
None
};
let pname = get_cc_prog(sess);
let mut cmd = Command::new(pname.as_slice());
cmd.args(sess.targ_cfg.target_strs.cc_args.as_slice());
cmd.arg("-nostdlib");
for index in range(0, trans.modules.len()) {
cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice()));
}
cmd.arg("-r")
.arg("-o")
.arg(windows_output_path.as_ref().unwrap_or(output_path));
if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 {
println!("{}", &cmd);
}
cmd.stdin(::std::io::process::Ignored)
.stdout(::std::io::process::InheritFd(1))
.stderr(::std::io::process::InheritFd(2));
match cmd.status() {
Ok(_) => {},
Err(e) => {
sess.err(format!("could not exec the linker `{}`: {}",
pname,
e).as_slice());
sess.abort_if_errors();
},
}
match windows_output_path {
Some(ref windows_path) => {
fs::rename(windows_path, output_path).unwrap();
},
None => {
// The file is already named according to `output_path`.
}
}
};
// Flag to indicate whether the user explicitly requested bitcode.
// Otherwise, we produced it only as a temporary output, and will need
// to get rid of it.
// FIXME: Since we don't support LTO anyway, maybe we can avoid
// producing the temporary .0.bc's in the first place?
let mut save_bitcode = false;
for output_type in output_types.iter() {
match *output_type {
OutputTypeBitcode => {
save_bitcode = true;
copy_if_one_unit("0.bc", OutputTypeBitcode);
},
OutputTypeLlvmAssembly => { copy_if_one_unit("0.ll", OutputTypeLlvmAssembly); },
OutputTypeAssembly => { copy_if_one_unit("0.s", OutputTypeAssembly); },
OutputTypeObject => { link_obj(&crate_output.path(OutputTypeObject)); },
OutputTypeExe => {
// If OutputTypeObject is already in the list, then
// `crate.o` will be handled by the OutputTypeObject case.
// Otherwise, we need to create the temporary object so we
// can run the linker.
if !sess.opts.output_types.contains(&OutputTypeObject) {
link_obj(&crate_output.temp_path(OutputTypeObject));
}
},
}
}
let save_bitcode = save_bitcode;
// Clean up unwanted temporary files.
// We create the following files by default:
// - crate.0.bc
// - crate.0.o
// - crate.metadata.bc
// - crate.metadata.o
// - crate.o (linked from crate.##.o)
// - crate.bc (copied from crate.0.bc)
// We may create additional files if requested by the user (through
// `-C save-temps` or `--emit=` flags).
if !sess.opts.cg.save_temps {
// Remove the temporary .0.o objects. If the user didn't
// explicitly request bitcode (with --emit=bc), we must remove
// .0.bc as well. (We don't touch the crate.bc that may have been
// produced earlier.)
for i in range(0, trans.modules.len()) {
if modules_config.emit_obj {
let ext = format!("{}.o", i);
remove(sess, &crate_output.with_extension(ext.as_slice()));
}
if modules_config.emit_bc && !save_bitcode {
let ext = format!("{}.bc", i);
remove(sess, &crate_output.with_extension(ext.as_slice()));
}
}
if metadata_config.emit_bc && !save_bitcode {
remove(sess, &crate_output.with_extension("metadata.bc"));
}
}
// We leave the following files around by default:
// - crate.o
// - crate.metadata.o
// - crate.bc
// These are used in linking steps and will be cleaned up afterward.
// FIXME: time_llvm_passes support - does this use a global context or
// something?
//if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
}
type WorkItem = proc(&CodegenContext):Send;
fn build_work_item(sess: &Session,
mtrans: ModuleTranslation,
config: ModuleConfig,
output_names: OutputFilenames,
name_extra: String) -> WorkItem {
let mut config = config;
config.tm = create_target_machine(sess);
proc(cgcx) unsafe {
optimize_and_codegen(cgcx, mtrans, config, name_extra, output_names);
}
}
fn run_work_singlethreaded(sess: &Session,
reachable: &[String],
work_items: Vec<WorkItem>) {
let cgcx = CodegenContext::new_with_session(sess, reachable);
let mut work_items = work_items;
// Since we're running single-threaded, we can pass the session to
// the proc, allowing `optimize_and_codegen` to perform LTO.
for work in Unfold::new((), |_| work_items.pop()) {
work(&cgcx);
}
}
fn run_work_multithreaded(sess: &Session,
work_items: Vec<WorkItem>,
num_workers: uint) {
// Run some workers to process the work items.
let work_items_arc = Arc::new(Mutex::new(work_items));
let mut diag_emitter = SharedEmitter::new();
let mut futures = Vec::with_capacity(num_workers);
for i in range(0, num_workers) {
let work_items_arc = work_items_arc.clone();
let diag_emitter = diag_emitter.clone();
let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() {
let diag_handler = mk_handler(box diag_emitter);
// Must construct cgcx inside the proc because it has non-Send
// fields.
let cgcx = CodegenContext::new(&diag_handler);
loop {
// Avoid holding the lock for the entire duration of the match.
let maybe_work = work_items_arc.lock().pop();
match maybe_work {
Some(work) => {
work(&cgcx);
// Make sure to fail the worker so the main thread can
// tell that there were errors.
cgcx.handler.abort_if_errors();
}
None => break,
}
}
});
futures.push(future);
}
let mut failed = false;
for future in futures.move_iter() {
match future.unwrap() {
Ok(()) => {},
Err(_) => {
failed = true;
},
}
// Display any new diagnostics.
diag_emitter.dump(sess.diagnostic().handler());
}
if failed {
sess.fatal("aborting due to worker thread failure");
}
}
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));
}
_ => {}
};
}

View File

@ -16,7 +16,7 @@ use driver::driver;
use driver::session::Session;
use back;
use back::link;
use back::write;
use back::target_strs;
use back::{arm, x86, x86_64, mips, mipsel};
use lint;
@ -72,7 +72,7 @@ pub struct Options {
pub debuginfo: DebugInfoLevel,
pub lint_opts: Vec<(String, lint::Level)>,
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
// parsed code. It remains mutable in case its replacements wants to use
// this.
@ -303,6 +303,13 @@ macro_rules! cgoptions(
}
}
fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool {
use std::from_str::FromStr;
match v.and_then(FromStr::from_str) {
Some(i) => { *slot = i; true },
None => false
}
}
}
) )
@ -347,6 +354,8 @@ cgoptions!(
"metadata to mangle symbol names with"),
extra_filename: String = ("".to_string(), parse_string,
"extra data to put in each output filename"),
codegen_units: uint = (1, parse_uint,
"divide crate into N units to optimize in parallel"),
)
pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
@ -646,11 +655,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
for unparsed_output_type in unparsed_output_types.iter() {
for part in unparsed_output_type.as_slice().split(',') {
let output_type = match part.as_slice() {
"asm" => link::OutputTypeAssembly,
"ir" => link::OutputTypeLlvmAssembly,
"bc" => link::OutputTypeBitcode,
"obj" => link::OutputTypeObject,
"link" => link::OutputTypeExe,
"asm" => write::OutputTypeAssembly,
"ir" => write::OutputTypeLlvmAssembly,
"bc" => write::OutputTypeBitcode,
"obj" => write::OutputTypeObject,
"link" => write::OutputTypeExe,
_ => {
early_error(format!("unknown emission type: `{}`",
part).as_slice())
@ -663,7 +672,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
output_types.as_mut_slice().sort();
output_types.dedup();
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));

View File

@ -10,6 +10,7 @@
use back::link;
use back::write;
use driver::session::Session;
use driver::config;
use front;
@ -441,10 +442,14 @@ pub fn phase_save_analysis(sess: &Session,
middle::save::process_crate(sess, krate, analysis, odir));
}
pub struct ModuleTranslation {
pub llcx: ContextRef,
pub llmod: ModuleRef,
}
pub struct CrateTranslation {
pub context: ContextRef,
pub module: ModuleRef,
pub metadata_module: ModuleRef,
pub modules: Vec<ModuleTranslation>,
pub metadata_module: ModuleTranslation,
pub link: LinkMeta,
pub metadata: Vec<u8>,
pub reachable: Vec<String>,
@ -472,23 +477,23 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
trans: &CrateTranslation,
outputs: &OutputFilenames) {
if sess.opts.cg.no_integrated_as {
let output_type = link::OutputTypeAssembly;
let output_type = write::OutputTypeAssembly;
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
if !sess.opts.cg.save_temps {
fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
fs::unlink(&outputs.temp_path(write::OutputTypeAssembly)).unwrap();
}
} else {
time(sess.time_passes(), "LLVM passes", (), |_|
link::write::run_passes(sess,
trans,
sess.opts.output_types.as_slice(),
outputs));
write::run_passes(sess,
trans,
sess.opts.output_types.as_slice(),
outputs));
}
}
@ -532,7 +537,7 @@ pub fn stop_after_phase_2(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");
return true;
}
@ -548,7 +553,7 @@ fn write_out_deps(sess: &Session,
for output_type in sess.opts.output_types.iter() {
let file = outputs.path(*output_type);
match *output_type {
link::OutputTypeExe => {
write::OutputTypeExe => {
for output in sess.crate_types.borrow().iter() {
let p = link::filename_for_input(sess, *output,
id, &file);
@ -679,6 +684,7 @@ pub fn collect_crate_metadata(session: &Session,
session.opts.cg.metadata.clone()
}
#[deriving(Clone)]
pub struct OutputFilenames {
pub out_directory: Path,
pub out_filestem: String,
@ -687,7 +693,7 @@ pub struct OutputFilenames {
}
impl OutputFilenames {
pub fn path(&self, flavor: link::OutputType) -> Path {
pub fn path(&self, flavor: write::OutputType) -> Path {
match self.single_output_file {
Some(ref path) => return path.clone(),
None => {}
@ -695,14 +701,14 @@ impl OutputFilenames {
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());
match flavor {
link::OutputTypeBitcode => base.with_extension("bc"),
link::OutputTypeAssembly => base.with_extension("s"),
link::OutputTypeLlvmAssembly => base.with_extension("ll"),
link::OutputTypeObject => base.with_extension("o"),
link::OutputTypeExe => base,
write::OutputTypeBitcode => base.with_extension("bc"),
write::OutputTypeAssembly => base.with_extension("s"),
write::OutputTypeLlvmAssembly => base.with_extension("ll"),
write::OutputTypeObject => base.with_extension("o"),
write::OutputTypeExe => base,
}
}

View File

@ -69,6 +69,7 @@ pub mod back {
pub mod link;
pub mod lto;
pub mod write;
}

View File

@ -892,7 +892,8 @@ fn encode_info_for_method(ecx: &EncodeContext,
IITraitItemRef(local_def(parent_id),
RequiredInlinedTraitItemRef(
&*ast_method)));
} else {
}
if !any_types {
encode_symbol(ecx, rbml_w, m.def_id.node);
}
encode_method_argument_names(rbml_w, &*ast_method.pe_fn_decl());
@ -1047,7 +1048,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_attributes(rbml_w, item.attrs.as_slice());
if tps_len > 0u || should_inline(item.attrs.as_slice()) {
encode_inlined_item(ecx, rbml_w, IIItemRef(item));
} else {
}
if tps_len == 0 {
encode_symbol(ecx, rbml_w, item.id);
}
encode_visibility(rbml_w, vis);
@ -1411,9 +1413,8 @@ fn encode_info_for_foreign_item(ecx: &EncodeContext,
encode_name(rbml_w, nitem.ident.name);
if abi == abi::RustIntrinsic {
encode_inlined_item(ecx, rbml_w, IIForeignRef(nitem));
} else {
encode_symbol(ecx, rbml_w, nitem.id);
}
encode_symbol(ecx, rbml_w, nitem.id);
}
ForeignItemStatic(_, mutbl) => {
if mutbl {

View File

@ -563,7 +563,7 @@ fn get_branches<'a>(bcx: &'a Block, m: &[Match], col: uint) -> Vec<Opt<'a>> {
}
ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => {
// This is either an enum variant or a variable binding.
let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id);
let opt_def = ccx.tcx().def_map.borrow().find_copy(&cur.id);
match opt_def {
Some(def::DefVariant(enum_id, var_id, _)) => {
let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);

View File

@ -150,14 +150,14 @@ pub fn represent_node(bcx: &Block, node: ast::NodeId) -> Rc<Repr> {
/// Decides how to represent a given type.
pub fn represent_type(cx: &CrateContext, t: ty::t) -> Rc<Repr> {
debug!("Representing: {}", ty_to_string(cx.tcx(), t));
match cx.adt_reprs.borrow().find(&t) {
match cx.adt_reprs().borrow().find(&t) {
Some(repr) => return repr.clone(),
None => {}
}
let repr = Rc::new(represent_type_uncached(cx, t));
debug!("Represented as: {:?}", repr)
cx.adt_reprs.borrow_mut().insert(t, repr.clone());
cx.adt_reprs().borrow_mut().insert(t, repr.clone());
repr
}
@ -423,7 +423,7 @@ fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntTyp
attempts = choose_shortest;
},
attr::ReprPacked => {
cx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
cx.tcx().sess.bug("range_to_inttype: found ReprPacked on an enum");
}
}
for &ity in attempts.iter() {

View File

@ -29,7 +29,7 @@ use back::link::{mangle_exported_name};
use back::{link, abi};
use driver::config;
use driver::config::{NoDebugInfo, FullDebugInfo};
use driver::driver::{CrateAnalysis, CrateTranslation};
use driver::driver::{CrateAnalysis, CrateTranslation, ModuleTranslation};
use driver::session::Session;
use lint;
use llvm::{BasicBlockRef, ModuleRef, ValueRef, Vector, get_param};
@ -47,8 +47,8 @@ use middle::trans::builder::{Builder, noname};
use middle::trans::callee;
use middle::trans::cleanup::{CleanupMethods, ScopeId};
use middle::trans::cleanup;
use middle::trans::common::{Block, C_bool, C_bytes, C_i32, C_integral, C_nil};
use middle::trans::common::{C_null, C_struct, C_u64, C_u8, C_uint, C_undef};
use middle::trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_integral, C_nil};
use middle::trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_uint, C_undef};
use middle::trans::common::{CrateContext, ExternMap, FunctionContext};
use middle::trans::common::{NodeInfo, Result, SubstP, monomorphize_type};
use middle::trans::common::{node_id_type, param_substs, return_type_is_void};
@ -56,6 +56,7 @@ use middle::trans::common::{tydesc_info, type_is_immediate};
use middle::trans::common::{type_is_zero_size, val_ty};
use middle::trans::common;
use middle::trans::consts;
use middle::trans::context::SharedCrateContext;
use middle::trans::controlflow;
use middle::trans::datum;
use middle::trans::debuginfo;
@ -84,6 +85,7 @@ use arena::TypedArena;
use libc::{c_uint, uint64_t};
use std::c_str::ToCStr;
use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::rc::Rc;
use std::{i8, i16, i32, i64};
use syntax::abi::{X86, X86_64, Arm, Mips, Mipsel, Rust, RustCall};
@ -136,7 +138,7 @@ pub fn push_ctxt(s: &'static str) -> _InsnCtxt {
}
pub struct StatRecorder<'a> {
ccx: &'a CrateContext,
ccx: &'a CrateContext<'a>,
name: Option<String>,
start: u64,
istart: uint,
@ -149,7 +151,7 @@ impl<'a> StatRecorder<'a> {
} else {
0
};
let istart = ccx.stats.n_llvm_insns.get();
let istart = ccx.stats().n_llvm_insns.get();
StatRecorder {
ccx: ccx,
name: Some(name),
@ -165,13 +167,13 @@ impl<'a> Drop for StatRecorder<'a> {
if self.ccx.sess().trans_stats() {
let end = time::precise_time_ns();
let elapsed = ((end - self.start) / 1_000_000) as uint;
let iend = self.ccx.stats.n_llvm_insns.get();
self.ccx.stats.fn_stats.borrow_mut().push((self.name.take().unwrap(),
let iend = self.ccx.stats().n_llvm_insns.get();
self.ccx.stats().fn_stats.borrow_mut().push((self.name.take().unwrap(),
elapsed,
iend - self.istart));
self.ccx.stats.n_fns.set(self.ccx.stats.n_fns.get() + 1);
self.ccx.stats().n_fns.set(self.ccx.stats().n_fns.get() + 1);
// Reset LLVM insn count to avoid compound costs.
self.ccx.stats.n_llvm_insns.set(self.istart);
self.ccx.stats().n_llvm_insns.set(self.istart);
}
}
}
@ -182,7 +184,7 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
let llfn: ValueRef = name.with_c_str(|buf| {
unsafe {
llvm::LLVMGetOrInsertFunction(ccx.llmod, buf, ty.to_ref())
llvm::LLVMGetOrInsertFunction(ccx.llmod(), buf, ty.to_ref())
}
});
@ -198,7 +200,7 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
_ => {}
}
if ccx.tcx.sess.opts.cg.no_redzone {
if ccx.tcx().sess.opts.cg.no_redzone {
unsafe {
llvm::LLVMAddFunctionAttribute(llfn,
llvm::FunctionIndex as c_uint,
@ -243,7 +245,7 @@ pub fn get_extern_fn(ccx: &CrateContext,
}
fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::DefId) -> ValueRef {
match ccx.externs.borrow().find_equiv(&name) {
match ccx.externs().borrow().find_equiv(&name) {
Some(n) => return *n,
None => ()
}
@ -254,7 +256,7 @@ fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::De
set_llvm_fn_attrs(attrs.as_slice(), f)
});
ccx.externs.borrow_mut().insert(name.to_string(), f);
ccx.externs().borrow_mut().insert(name.to_string(), f);
f
}
@ -264,14 +266,14 @@ pub fn self_type_for_unboxed_closure(ccx: &CrateContext,
let unboxed_closure_type = ty::mk_unboxed_closure(ccx.tcx(),
closure_id,
ty::ReStatic);
let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
let unboxed_closure = unboxed_closures.get(&closure_id);
match unboxed_closure.kind {
ty::FnUnboxedClosureKind => {
ty::mk_imm_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type)
ty::mk_imm_rptr(ccx.tcx(), ty::ReStatic, unboxed_closure_type)
}
ty::FnMutUnboxedClosureKind => {
ty::mk_mut_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type)
ty::mk_mut_rptr(ccx.tcx(), ty::ReStatic, unboxed_closure_type)
}
ty::FnOnceUnboxedClosureKind => unboxed_closure_type,
}
@ -279,7 +281,7 @@ pub fn self_type_for_unboxed_closure(ccx: &CrateContext,
pub fn kind_for_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId)
-> ty::UnboxedClosureKind {
let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
unboxed_closures.get(&closure_id).kind
}
@ -292,7 +294,7 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
(f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx)))
}
ty::ty_unboxed_closure(closure_did, _) => {
let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
let unboxed_closure = unboxed_closures.get(&closure_did);
let function_type = unboxed_closure.closure_type.clone();
let self_type = self_type_for_unboxed_closure(ccx, closure_did);
@ -308,7 +310,7 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
let llfty = type_of_rust_fn(ccx, env, inputs.as_slice(), output, abi);
debug!("decl_rust_fn(input count={},type={})",
inputs.len(),
ccx.tn.type_to_string(llfty));
ccx.tn().type_to_string(llfty));
let llfn = decl_fn(ccx, name, llvm::CCallConv, llfty, output);
let attrs = get_fn_llvm_attributes(ccx, fn_ty);
@ -413,15 +415,15 @@ pub fn malloc_raw_dyn_managed<'a>(
// Type descriptor and type glue stuff
pub fn get_tydesc(ccx: &CrateContext, t: ty::t) -> Rc<tydesc_info> {
match ccx.tydescs.borrow().find(&t) {
match ccx.tydescs().borrow().find(&t) {
Some(inf) => return inf.clone(),
_ => { }
}
ccx.stats.n_static_tydescs.set(ccx.stats.n_static_tydescs.get() + 1u);
ccx.stats().n_static_tydescs.set(ccx.stats().n_static_tydescs.get() + 1u);
let inf = Rc::new(glue::declare_tydesc(ccx, t));
ccx.tydescs.borrow_mut().insert(t, inf.clone());
ccx.tydescs().borrow_mut().insert(t, inf.clone());
inf
}
@ -492,10 +494,10 @@ pub fn unset_split_stack(f: ValueRef) {
// Double-check that we never ask LLVM to declare the same symbol twice. It
// silently mangles such symbols, breaking our linkage model.
pub fn note_unique_llvm_symbol(ccx: &CrateContext, sym: String) {
if ccx.all_llvm_symbols.borrow().contains(&sym) {
if ccx.all_llvm_symbols().borrow().contains(&sym) {
ccx.sess().bug(format!("duplicate LLVM symbol: {}", sym).as_slice());
}
ccx.all_llvm_symbols.borrow_mut().insert(sym);
ccx.all_llvm_symbols().borrow_mut().insert(sym);
}
@ -532,7 +534,7 @@ pub fn get_res_dtor(ccx: &CrateContext,
let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID,
[glue::get_drop_glue_type(ccx, t)], ty::mk_nil());
get_extern_fn(ccx,
&mut *ccx.externs.borrow_mut(),
&mut *ccx.externs().borrow_mut(),
name.as_slice(),
llvm::CCallConv,
llty,
@ -961,8 +963,8 @@ pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> Val
}
_ => {
let llty = type_of(ccx, t);
get_extern_const(&mut *ccx.externs.borrow_mut(),
ccx.llmod,
get_extern_const(&mut *ccx.externs().borrow_mut(),
ccx.llmod(),
name.as_slice(),
llty)
}
@ -1165,7 +1167,7 @@ pub fn call_memcpy(cx: &Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef,
let memcpy = ccx.get_intrinsic(&key);
let src_ptr = PointerCast(cx, src, Type::i8p(ccx));
let dst_ptr = PointerCast(cx, dst, Type::i8p(ccx));
let size = IntCast(cx, n_bytes, ccx.int_type);
let size = IntCast(cx, n_bytes, ccx.int_type());
let align = C_i32(ccx, align as i32);
let volatile = C_bool(ccx, false);
Call(cx, memcpy, [dst_ptr, src_ptr, size, align, volatile], None);
@ -1426,7 +1428,7 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
if id == -1 {
"".to_string()
} else {
ccx.tcx.map.path_to_string(id).to_string()
ccx.tcx().map.path_to_string(id).to_string()
},
id, param_substs.repr(ccx.tcx()));
@ -1786,7 +1788,7 @@ pub fn trans_closure(ccx: &CrateContext,
is_unboxed_closure: IsUnboxedClosureFlag,
maybe_load_env: <'a>|&'a Block<'a>, ScopeId|
-> &'a Block<'a>) {
ccx.stats.n_closures.set(ccx.stats.n_closures.get() + 1);
ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
let _icx = push_ctxt("trans_closure");
set_uwtable(llfndecl);
@ -1820,7 +1822,7 @@ pub fn trans_closure(ccx: &CrateContext,
ty_to_string(ccx.tcx(), *monomorphized_arg_type));
}
debug!("trans_closure: function lltype: {}",
bcx.fcx.ccx.tn.val_to_string(bcx.fcx.llfn));
bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn));
let arg_datums = if abi != RustCall {
create_datums_for_fn_args(&fcx,
@ -1912,7 +1914,7 @@ pub fn trans_fn(ccx: &CrateContext,
param_substs: &param_substs,
id: ast::NodeId,
attrs: &[ast::Attribute]) {
let _s = StatRecorder::new(ccx, ccx.tcx.map.path_to_string(id).to_string());
let _s = StatRecorder::new(ccx, ccx.tcx().map.path_to_string(id).to_string());
debug!("trans_fn(param_substs={})", param_substs.repr(ccx.tcx()));
let _icx = push_ctxt("trans_fn");
let fn_ty = ty::node_id_to_type(ccx.tcx(), id);
@ -1958,7 +1960,7 @@ pub fn trans_named_tuple_constructor<'a>(mut bcx: &'a Block<'a>,
dest: expr::Dest) -> Result<'a> {
let ccx = bcx.fcx.ccx;
let tcx = &ccx.tcx;
let tcx = ccx.tcx();
let result_ty = match ty::get(ctor_ty).sty {
ty::ty_bare_fn(ref bft) => bft.sig.output,
@ -2064,7 +2066,7 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) {
let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully
let levels = ccx.tcx.node_lint_levels.borrow();
let levels = ccx.tcx().node_lint_levels.borrow();
let lint_id = lint::LintId::of(lint::builtin::VARIANT_SIZE_DIFFERENCE);
let lvlsrc = match levels.find(&(id, lint_id)) {
None | Some(&(lint::Allow, _)) => return,
@ -2114,7 +2116,7 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span,
}
pub struct TransItemVisitor<'a> {
pub ccx: &'a CrateContext,
pub ccx: &'a CrateContext<'a>,
}
impl<'a> Visitor<()> for TransItemVisitor<'a> {
@ -2123,29 +2125,95 @@ impl<'a> Visitor<()> for TransItemVisitor<'a> {
}
}
/// Enum describing the origin of an LLVM `Value`, for linkage purposes.
pub enum ValueOrigin {
/// The LLVM `Value` is in this context because the corresponding item was
/// assigned to the current compilation unit.
OriginalTranslation,
/// The `Value`'s corresponding item was assigned to some other compilation
/// unit, but the `Value` was translated in this context anyway because the
/// item is marked `#[inline]`.
InlinedCopy,
}
/// Set the appropriate linkage for an LLVM `ValueRef` (function or global).
/// If the `llval` is the direct translation of a specific Rust item, `id`
/// should be set to the `NodeId` of that item. (This mapping should be
/// 1-to-1, so monomorphizations and drop/visit glue should have `id` set to
/// `None`.) `llval_origin` indicates whether `llval` is the translation of an
/// item assigned to `ccx`'s compilation unit or an inlined copy of an item
/// assigned to a different compilation unit.
pub fn update_linkage(ccx: &CrateContext,
llval: ValueRef,
id: Option<ast::NodeId>,
llval_origin: ValueOrigin) {
match llval_origin {
InlinedCopy => {
// `llval` is a translation of an item defined in a separate
// compilation unit. This only makes sense if there are at least
// two compilation units.
assert!(ccx.sess().opts.cg.codegen_units > 1);
// `llval` is a copy of something defined elsewhere, so use
// `AvailableExternallyLinkage` to avoid duplicating code in the
// output.
llvm::SetLinkage(llval, llvm::AvailableExternallyLinkage);
return;
},
OriginalTranslation => {},
}
match id {
Some(id) if ccx.reachable().contains(&id) => {
llvm::SetLinkage(llval, llvm::ExternalLinkage);
},
_ => {
// `id` does not refer to an item in `ccx.reachable`.
if ccx.sess().opts.cg.codegen_units > 1 {
llvm::SetLinkage(llval, llvm::ExternalLinkage);
} else {
llvm::SetLinkage(llval, llvm::InternalLinkage);
}
},
}
}
pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
let _icx = push_ctxt("trans_item");
let from_external = ccx.external_srcs().borrow().contains_key(&item.id);
match item.node {
ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => {
if !generics.is_type_parameterized() {
let llfn = get_item_val(ccx, item.id);
if abi != Rust {
foreign::trans_rust_fn_with_foreign_abi(ccx,
&**decl,
&**body,
item.attrs.as_slice(),
llfn,
&param_substs::empty(),
item.id,
None);
} else {
trans_fn(ccx,
&**decl,
&**body,
llfn,
&param_substs::empty(),
item.id,
item.attrs.as_slice());
let trans_everywhere = attr::requests_inline(item.attrs.as_slice());
// Ignore `trans_everywhere` for cross-crate inlined items
// (`from_external`). `trans_item` will be called once for each
// compilation unit that references the item, so it will still get
// translated everywhere it's needed.
for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
let llfn = get_item_val(ccx, item.id);
if abi != Rust {
foreign::trans_rust_fn_with_foreign_abi(ccx,
&**decl,
&**body,
item.attrs.as_slice(),
llfn,
&param_substs::empty(),
item.id,
None);
} else {
trans_fn(ccx,
&**decl,
&**body,
llfn,
&param_substs::empty(),
item.id,
item.attrs.as_slice());
}
update_linkage(ccx,
llfn,
Some(item.id),
if is_origin { OriginalTranslation } else { InlinedCopy });
}
}
@ -2162,7 +2230,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
item.id);
}
ast::ItemMod(ref m) => {
trans_mod(ccx, m);
trans_mod(&ccx.rotate(), m);
}
ast::ItemEnum(ref enum_definition, _) => {
enum_variant_size_lint(ccx, enum_definition, item.span, item.id);
@ -2171,7 +2239,18 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
// Recurse on the expression to catch items in blocks
let mut v = TransItemVisitor{ ccx: ccx };
v.visit_expr(&**expr, ());
consts::trans_const(ccx, m, item.id);
let trans_everywhere = attr::requests_inline(item.attrs.as_slice());
for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
consts::trans_const(ccx, m, item.id);
let g = get_item_val(ccx, item.id);
update_linkage(ccx,
g,
Some(item.id),
if is_origin { OriginalTranslation } else { InlinedCopy });
}
// Do static_assert checking. It can't really be done much earlier
// because we need to get the value of the bool out of LLVM
if attr::contains_name(item.attrs.as_slice(), "static_assert") {
@ -2181,7 +2260,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
static");
}
let v = ccx.const_values.borrow().get_copy(&item.id);
let v = ccx.const_values().borrow().get_copy(&item.id);
unsafe {
if !(llvm::LLVMConstIntGetZExtValue(v) != 0) {
ccx.sess().span_fatal(expr.span, "static assertion failed");
@ -2218,21 +2297,17 @@ pub fn trans_mod(ccx: &CrateContext, m: &ast::Mod) {
fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::NodeId,
llfn: ValueRef) {
ccx.item_symbols.borrow_mut().insert(node_id, sym);
if !ccx.reachable.contains(&node_id) {
llvm::SetLinkage(llfn, llvm::InternalLinkage);
}
ccx.item_symbols().borrow_mut().insert(node_id, sym);
// The stack exhaustion lang item shouldn't have a split stack because
// otherwise it would continue to be exhausted (bad), and both it and the
// eh_personality functions need to be externally linkable.
let def = ast_util::local_def(node_id);
if ccx.tcx.lang_items.stack_exhausted() == Some(def) {
if ccx.tcx().lang_items.stack_exhausted() == Some(def) {
unset_split_stack(llfn);
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
}
if ccx.tcx.lang_items.eh_personality() == Some(def) {
if ccx.tcx().lang_items.eh_personality() == Some(def) {
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
}
@ -2268,7 +2343,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
ty::ty_closure(ref f) => (f.sig.clone(), f.abi, true),
ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false),
ty::ty_unboxed_closure(closure_did, _) => {
let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
let ref function_type = unboxed_closures.get(&closure_did)
.closure_type;
@ -2485,8 +2560,8 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
fn create_entry_fn(ccx: &CrateContext,
rust_main: ValueRef,
use_start_lang_item: bool) {
let llfty = Type::func([ccx.int_type, Type::i8p(ccx).ptr_to()],
&ccx.int_type);
let llfty = Type::func([ccx.int_type(), Type::i8p(ccx).ptr_to()],
&ccx.int_type());
let llfn = decl_cdecl_fn(ccx, "main", llfty, ty::mk_nil());
@ -2498,15 +2573,15 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
let llbb = "top".with_c_str(|buf| {
unsafe {
llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llfn, buf)
llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, buf)
}
});
let bld = ccx.builder.b;
let bld = ccx.raw_builder();
unsafe {
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
let (start_fn, args) = if use_start_lang_item {
let start_def_id = match ccx.tcx.lang_items.require(StartFnLangItem) {
let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) {
Ok(id) => id,
Err(s) => { ccx.sess().fatal(s.as_slice()); }
};
@ -2553,11 +2628,20 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
fn exported_name(ccx: &CrateContext, id: ast::NodeId,
ty: ty::t, attrs: &[ast::Attribute]) -> String {
match ccx.external_srcs().borrow().find(&id) {
Some(&did) => {
let sym = csearch::get_symbol(&ccx.sess().cstore, did);
debug!("found item {} in other crate...", sym);
return sym;
}
None => {}
}
match attr::first_attr_value_str_by_name(attrs, "export_name") {
// Use provided name
Some(name) => name.get().to_string(),
_ => ccx.tcx.map.with_path(id, |mut path| {
_ => ccx.tcx().map.with_path(id, |mut path| {
if attr::contains_name(attrs, "no_mangle") {
// Don't mangle
path.last().unwrap().to_string()
@ -2577,13 +2661,12 @@ fn exported_name(ccx: &CrateContext, id: ast::NodeId,
pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
debug!("get_item_val(id=`{:?}`)", id);
match ccx.item_vals.borrow().find_copy(&id) {
match ccx.item_vals().borrow().find_copy(&id) {
Some(v) => return v,
None => {}
}
let mut foreign = false;
let item = ccx.tcx.map.get(id);
let item = ccx.tcx().map.get(id);
let val = match item {
ast_map::NodeItem(i) => {
let ty = ty::node_id_to_type(ccx.tcx(), i.id);
@ -2596,33 +2679,20 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
// using the current crate's name/version
// information in the hash of the symbol
debug!("making {}", sym);
let (sym, is_local) = {
match ccx.external_srcs.borrow().find(&i.id) {
Some(&did) => {
debug!("but found in other crate...");
(csearch::get_symbol(&ccx.sess().cstore,
did), false)
}
None => (sym, true)
}
};
let is_local = !ccx.external_srcs().borrow().contains_key(&id);
// We need the translated value here, because for enums the
// LLVM type is not fully determined by the Rust type.
let (v, inlineable, _) = consts::const_expr(ccx, &**expr, is_local);
ccx.const_values.borrow_mut().insert(id, v);
ccx.const_values().borrow_mut().insert(id, v);
let mut inlineable = inlineable;
unsafe {
let llty = llvm::LLVMTypeOf(v);
let g = sym.as_slice().with_c_str(|buf| {
llvm::LLVMAddGlobal(ccx.llmod, llty, buf)
llvm::LLVMAddGlobal(ccx.llmod(), llty, buf)
});
if !ccx.reachable.contains(&id) {
llvm::SetLinkage(g, llvm::InternalLinkage);
}
// Apply the `unnamed_addr` attribute if
// requested
if !ast_util::static_has_significant_address(
@ -2655,11 +2725,11 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
if !inlineable {
debug!("{} not inlined", sym);
ccx.non_inlineable_statics.borrow_mut()
ccx.non_inlineable_statics().borrow_mut()
.insert(id);
}
ccx.item_symbols.borrow_mut().insert(i.id, sym);
ccx.item_symbols().borrow_mut().insert(i.id, sym);
g
}
}
@ -2713,11 +2783,9 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
}
ast_map::NodeForeignItem(ni) => {
foreign = true;
match ni.node {
ast::ForeignItemFn(..) => {
let abi = ccx.tcx.map.get_foreign_abi(id);
let abi = ccx.tcx().map.get_foreign_abi(id);
let ty = ty::node_id_to_type(ccx.tcx(), ni.id);
let name = foreign::link_name(&*ni);
foreign::register_foreign_item_fn(ccx, abi, ty,
@ -2740,8 +2808,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
};
assert!(args.len() != 0u);
let ty = ty::node_id_to_type(ccx.tcx(), id);
let parent = ccx.tcx.map.get_parent(id);
let enm = ccx.tcx.map.expect_item(parent);
let parent = ccx.tcx().map.get_parent(id);
let enm = ccx.tcx().map.expect_item(parent);
let sym = exported_name(ccx,
id,
ty,
@ -2766,8 +2834,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
}
Some(ctor_id) => ctor_id,
};
let parent = ccx.tcx.map.get_parent(id);
let struct_item = ccx.tcx.map.expect_item(parent);
let parent = ccx.tcx().map.get_parent(id);
let struct_item = ccx.tcx().map.expect_item(parent);
let ty = ty::node_id_to_type(ccx.tcx(), ctor_id);
let sym = exported_name(ccx,
id,
@ -2786,14 +2854,16 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
}
};
// foreign items (extern fns and extern statics) don't have internal
// linkage b/c that doesn't quite make sense. Otherwise items can
// have internal linkage if they're not reachable.
if !foreign && !ccx.reachable.contains(&id) {
llvm::SetLinkage(val, llvm::InternalLinkage);
}
// All LLVM globals and functions are initially created as external-linkage
// declarations. If `trans_item`/`trans_fn` later turns the declaration
// into a definition, it adjusts the linkage then (using `update_linkage`).
//
// The exception is foreign items, which have their linkage set inside the
// call to `foreign::register_*` above. We don't touch the linkage after
// that (`foreign::trans_foreign_mod` doesn't adjust the linkage like the
// other item translation functions do).
ccx.item_vals.borrow_mut().insert(id, val);
ccx.item_vals().borrow_mut().insert(id, val);
val
}
@ -2810,26 +2880,27 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId,
pub fn p2i(ccx: &CrateContext, v: ValueRef) -> ValueRef {
unsafe {
return llvm::LLVMConstPtrToInt(v, ccx.int_type.to_ref());
return llvm::LLVMConstPtrToInt(v, ccx.int_type().to_ref());
}
}
pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeInlinedItem<'r>)
pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r SharedCrateContext,
ie: encoder::EncodeInlinedItem<'r>)
-> encoder::EncodeParams<'r> {
encoder::EncodeParams {
diag: cx.sess().diagnostic(),
tcx: cx.tcx(),
reexports2: &cx.exp_map2,
item_symbols: &cx.item_symbols,
non_inlineable_statics: &cx.non_inlineable_statics,
link_meta: &cx.link_meta,
reexports2: cx.exp_map2(),
item_symbols: cx.item_symbols(),
non_inlineable_statics: cx.non_inlineable_statics(),
link_meta: cx.link_meta(),
cstore: &cx.sess().cstore,
encode_inlined_item: ie,
reachable: &cx.reachable,
reachable: cx.reachable(),
}
}
pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
use flate;
let any_library = cx.sess().crate_types.borrow().iter().any(|ty| {
@ -2851,14 +2922,14 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
cx.sess().fatal("failed to compress metadata")
}
}.as_slice());
let llmeta = C_bytes(cx, compressed.as_slice());
let llconst = C_struct(cx, [llmeta], false);
let llmeta = C_bytes_in_context(cx.metadata_llcx(), compressed.as_slice());
let llconst = C_struct_in_context(cx.metadata_llcx(), [llmeta], false);
let name = format!("rust_metadata_{}_{}",
cx.link_meta.crate_name,
cx.link_meta.crate_hash);
cx.link_meta().crate_name,
cx.link_meta().crate_hash);
let llglobal = name.with_c_str(|buf| {
unsafe {
llvm::LLVMAddGlobal(cx.metadata_llmod, val_ty(llconst).to_ref(), buf)
llvm::LLVMAddGlobal(cx.metadata_llmod(), val_ty(llconst).to_ref(), buf)
}
});
unsafe {
@ -2871,6 +2942,84 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
return metadata;
}
/// Find any symbols that are defined in one compilation unit, but not declared
/// in any other compilation unit. Give these symbols internal linkage.
fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<String>) {
use std::c_str::CString;
unsafe {
let mut declared = HashSet::new();
let iter_globals = |llmod| {
ValueIter {
cur: llvm::LLVMGetFirstGlobal(llmod),
step: llvm::LLVMGetNextGlobal,
}
};
let iter_functions = |llmod| {
ValueIter {
cur: llvm::LLVMGetFirstFunction(llmod),
step: llvm::LLVMGetNextFunction,
}
};
// Collect all external declarations in all compilation units.
for ccx in cx.iter() {
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
let linkage = llvm::LLVMGetLinkage(val);
// We only care about external declarations (not definitions)
// and available_externally definitions.
if !(linkage == llvm::ExternalLinkage as c_uint &&
llvm::LLVMIsDeclaration(val) != 0) &&
!(linkage == llvm::AvailableExternallyLinkage as c_uint) {
continue
}
let name = CString::new(llvm::LLVMGetValueName(val), false);
declared.insert(name);
}
}
// Examine each external definition. If the definition is not used in
// any other compilation unit, and is not reachable from other crates,
// then give it internal linkage.
for ccx in cx.iter() {
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
// We only care about external definitions.
if !(llvm::LLVMGetLinkage(val) == llvm::ExternalLinkage as c_uint &&
llvm::LLVMIsDeclaration(val) == 0) {
continue
}
let name = CString::new(llvm::LLVMGetValueName(val), false);
if !declared.contains(&name) &&
!reachable.contains_equiv(&name.as_str().unwrap()) {
llvm::SetLinkage(val, llvm::InternalLinkage);
}
}
}
}
struct ValueIter {
cur: ValueRef,
step: unsafe extern "C" fn(ValueRef) -> ValueRef,
}
impl Iterator<ValueRef> for ValueIter {
fn next(&mut self) -> Option<ValueRef> {
let old = self.cur;
if !old.is_null() {
self.cur = unsafe { (self.step)(old) };
Some(old)
} else {
None
}
}
}
}
pub fn trans_crate(krate: ast::Crate,
analysis: CrateAnalysis) -> (ty::ctxt, CrateTranslation) {
let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis;
@ -2895,52 +3044,55 @@ pub fn trans_crate(krate: ast::Crate,
let link_meta = link::build_link_meta(&tcx.sess, &krate, name);
// Append ".rs" to crate name as LLVM module identifier.
//
// LLVM code generator emits a ".file filename" directive
// for ELF backends. Value of the "filename" is set as the
// LLVM module identifier. Due to a LLVM MC bug[1], LLVM
// crashes if the module identifier is same as other symbols
// such as a function name in the module.
// 1. http://llvm.org/bugs/show_bug.cgi?id=11479
let mut llmod_id = link_meta.crate_name.clone();
llmod_id.push_str(".rs");
let codegen_units = tcx.sess.opts.cg.codegen_units;
let shared_ccx = SharedCrateContext::new(link_meta.crate_name.as_slice(),
codegen_units,
tcx,
exp_map2,
Sha256::new(),
link_meta.clone(),
reachable);
let ccx = CrateContext::new(llmod_id.as_slice(), tcx, exp_map2,
Sha256::new(), link_meta, reachable);
// First, verify intrinsics.
intrinsic::check_intrinsics(&ccx);
// Next, translate the module.
{
let _icx = push_ctxt("text");
trans_mod(&ccx, &krate.module);
let ccx = shared_ccx.get_ccx(0);
// First, verify intrinsics.
intrinsic::check_intrinsics(&ccx);
// Next, translate the module.
{
let _icx = push_ctxt("text");
trans_mod(&ccx, &krate.module);
}
}
glue::emit_tydescs(&ccx);
if ccx.sess().opts.debuginfo != NoDebugInfo {
debuginfo::finalize(&ccx);
for ccx in shared_ccx.iter() {
glue::emit_tydescs(&ccx);
if ccx.sess().opts.debuginfo != NoDebugInfo {
debuginfo::finalize(&ccx);
}
}
// Translate the metadata.
let metadata = write_metadata(&ccx, &krate);
if ccx.sess().trans_stats() {
println!("--- trans stats ---");
println!("n_static_tydescs: {}", ccx.stats.n_static_tydescs.get());
println!("n_glues_created: {}", ccx.stats.n_glues_created.get());
println!("n_null_glues: {}", ccx.stats.n_null_glues.get());
println!("n_real_glues: {}", ccx.stats.n_real_glues.get());
let metadata = write_metadata(&shared_ccx, &krate);
println!("n_fns: {}", ccx.stats.n_fns.get());
println!("n_monos: {}", ccx.stats.n_monos.get());
println!("n_inlines: {}", ccx.stats.n_inlines.get());
println!("n_closures: {}", ccx.stats.n_closures.get());
if shared_ccx.sess().trans_stats() {
let stats = shared_ccx.stats();
println!("--- trans stats ---");
println!("n_static_tydescs: {}", stats.n_static_tydescs.get());
println!("n_glues_created: {}", stats.n_glues_created.get());
println!("n_null_glues: {}", stats.n_null_glues.get());
println!("n_real_glues: {}", stats.n_real_glues.get());
println!("n_fns: {}", stats.n_fns.get());
println!("n_monos: {}", stats.n_monos.get());
println!("n_inlines: {}", stats.n_inlines.get());
println!("n_closures: {}", stats.n_closures.get());
println!("fn stats:");
ccx.stats.fn_stats.borrow_mut().sort_by(|&(_, _, insns_a), &(_, _, insns_b)| {
stats.fn_stats.borrow_mut().sort_by(|&(_, _, insns_a), &(_, _, insns_b)| {
insns_b.cmp(&insns_a)
});
for tuple in ccx.stats.fn_stats.borrow().iter() {
for tuple in stats.fn_stats.borrow().iter() {
match *tuple {
(ref name, ms, insns) => {
println!("{} insns, {} ms, {}", insns, ms, *name);
@ -2948,27 +3100,27 @@ pub fn trans_crate(krate: ast::Crate,
}
}
}
if ccx.sess().count_llvm_insns() {
for (k, v) in ccx.stats.llvm_insns.borrow().iter() {
if shared_ccx.sess().count_llvm_insns() {
for (k, v) in shared_ccx.stats().llvm_insns.borrow().iter() {
println!("{:7u} {}", *v, *k);
}
}
let llcx = ccx.llcx;
let link_meta = ccx.link_meta.clone();
let llmod = ccx.llmod;
let modules = shared_ccx.iter()
.map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() })
.collect();
let mut reachable: Vec<String> = ccx.reachable.iter().filter_map(|id| {
ccx.item_symbols.borrow().find(id).map(|s| s.to_string())
let mut reachable: Vec<String> = shared_ccx.reachable().iter().filter_map(|id| {
shared_ccx.item_symbols().borrow().find(id).map(|s| s.to_string())
}).collect();
// For the purposes of LTO, we add to the reachable set all of the upstream
// reachable extern fns. These functions are all part of the public ABI of
// the final product, so LTO needs to preserve them.
ccx.sess().cstore.iter_crate_data(|cnum, _| {
let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum);
shared_ccx.sess().cstore.iter_crate_data(|cnum, _| {
let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum);
reachable.extend(syms.move_iter().map(|did| {
csearch::get_symbol(&ccx.sess().cstore, did)
csearch::get_symbol(&shared_ccx.sess().cstore, did)
}));
});
@ -2986,18 +3138,26 @@ pub fn trans_crate(krate: ast::Crate,
// referenced from rt/rust_try.ll
reachable.push("rust_eh_personality_catch".to_string());
let metadata_module = ccx.metadata_llmod;
let formats = ccx.tcx.dependency_formats.borrow().clone();
if codegen_units > 1 {
internalize_symbols(&shared_ccx, &reachable.iter().map(|x| x.clone()).collect());
}
let metadata_module = ModuleTranslation {
llcx: shared_ccx.metadata_llcx(),
llmod: shared_ccx.metadata_llmod(),
};
let formats = shared_ccx.tcx().dependency_formats.borrow().clone();
let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins");
(ccx.tcx, CrateTranslation {
context: llcx,
module: llmod,
link: link_meta,
let translation = CrateTranslation {
modules: modules,
metadata_module: metadata_module,
link: link_meta,
metadata: metadata,
reachable: reachable,
crate_formats: formats,
no_builtins: no_builtins,
})
};
(shared_ccx.take_tcx(), translation)
}

View File

@ -352,7 +352,7 @@ pub fn Load(cx: &Block, pointer_val: ValueRef) -> ValueRef {
let eltty = if ty.kind() == llvm::Array {
ty.element_type()
} else {
ccx.int_type
ccx.int_type()
};
return llvm::LLVMGetUndef(eltty.to_ref());
}
@ -373,7 +373,7 @@ pub fn AtomicLoad(cx: &Block, pointer_val: ValueRef, order: AtomicOrdering) -> V
unsafe {
let ccx = cx.fcx.ccx;
if cx.unreachable.get() {
return llvm::LLVMGetUndef(ccx.int_type.to_ref());
return llvm::LLVMGetUndef(ccx.int_type().to_ref());
}
B(cx).atomic_load(pointer_val, order)
}
@ -388,7 +388,7 @@ pub fn LoadRangeAssert(cx: &Block, pointer_val: ValueRef, lo: c_ulonglong,
let eltty = if ty.kind() == llvm::Array {
ty.element_type()
} else {
ccx.int_type
ccx.int_type()
};
unsafe {
llvm::LLVMGetUndef(eltty.to_ref())
@ -658,7 +658,7 @@ pub fn _UndefReturn(cx: &Block, fn_: ValueRef) -> ValueRef {
let retty = if ty.kind() == llvm::Integer {
ty.return_type()
} else {
ccx.int_type
ccx.int_type()
};
B(cx).count_insn("ret_undef");
llvm::LLVMGetUndef(retty.to_ref())
@ -786,7 +786,7 @@ pub fn IsNotNull(cx: &Block, val: ValueRef) -> ValueRef {
pub fn PtrDiff(cx: &Block, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
unsafe {
let ccx = cx.fcx.ccx;
if cx.unreachable.get() { return llvm::LLVMGetUndef(ccx.int_type.to_ref()); }
if cx.unreachable.get() { return llvm::LLVMGetUndef(ccx.int_type().to_ref()); }
B(cx).ptrdiff(lhs, rhs)
}
}

View File

@ -25,7 +25,7 @@ use syntax::codemap::Span;
pub struct Builder<'a> {
pub llbuilder: BuilderRef,
pub ccx: &'a CrateContext,
pub ccx: &'a CrateContext<'a>,
}
// This is a really awful way to get a zero-length c-string, but better (and a
@ -38,21 +38,22 @@ pub fn noname() -> *const c_char {
impl<'a> Builder<'a> {
pub fn new(ccx: &'a CrateContext) -> Builder<'a> {
Builder {
llbuilder: ccx.builder.b,
llbuilder: ccx.raw_builder(),
ccx: ccx,
}
}
pub fn count_insn(&self, category: &str) {
if self.ccx.sess().trans_stats() {
self.ccx.stats.n_llvm_insns.set(self.ccx
.stats
self.ccx.stats().n_llvm_insns.set(self.ccx
.stats()
.n_llvm_insns
.get() + 1);
}
self.ccx.count_llvm_insn();
if self.ccx.sess().count_llvm_insns() {
base::with_insn_ctxt(|v| {
let mut h = self.ccx.stats.llvm_insns.borrow_mut();
let mut h = self.ccx.stats().llvm_insns.borrow_mut();
// Build version of path with cycles removed.
@ -160,9 +161,9 @@ impl<'a> Builder<'a> {
self.count_insn("invoke");
debug!("Invoke {} with args ({})",
self.ccx.tn.val_to_string(llfn),
self.ccx.tn().val_to_string(llfn),
args.iter()
.map(|&v| self.ccx.tn.val_to_string(v))
.map(|&v| self.ccx.tn().val_to_string(v))
.collect::<Vec<String>>()
.connect(", "));
@ -488,7 +489,7 @@ impl<'a> Builder<'a> {
let v = [min, max];
llvm::LLVMSetMetadata(value, llvm::MD_range as c_uint,
llvm::LLVMMDNodeInContext(self.ccx.llcx,
llvm::LLVMMDNodeInContext(self.ccx.llcx(),
v.as_ptr(), v.len() as c_uint));
}
@ -497,8 +498,8 @@ impl<'a> Builder<'a> {
pub fn store(&self, val: ValueRef, ptr: ValueRef) {
debug!("Store {} -> {}",
self.ccx.tn.val_to_string(val),
self.ccx.tn.val_to_string(ptr));
self.ccx.tn().val_to_string(val),
self.ccx.tn().val_to_string(ptr));
assert!(self.llbuilder.is_not_null());
self.count_insn("store");
unsafe {
@ -508,8 +509,8 @@ impl<'a> Builder<'a> {
pub fn volatile_store(&self, val: ValueRef, ptr: ValueRef) {
debug!("Store {} -> {}",
self.ccx.tn.val_to_string(val),
self.ccx.tn.val_to_string(ptr));
self.ccx.tn().val_to_string(val),
self.ccx.tn().val_to_string(ptr));
assert!(self.llbuilder.is_not_null());
self.count_insn("store.volatile");
unsafe {
@ -520,8 +521,8 @@ impl<'a> Builder<'a> {
pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) {
debug!("Store {} -> {}",
self.ccx.tn.val_to_string(val),
self.ccx.tn.val_to_string(ptr));
self.ccx.tn().val_to_string(val),
self.ccx.tn().val_to_string(ptr));
self.count_insn("store.atomic");
unsafe {
let ty = Type::from_ref(llvm::LLVMTypeOf(ptr));
@ -794,11 +795,11 @@ impl<'a> Builder<'a> {
else { llvm::False };
let argtys = inputs.iter().map(|v| {
debug!("Asm Input Type: {:?}", self.ccx.tn.val_to_string(*v));
debug!("Asm Input Type: {:?}", self.ccx.tn().val_to_string(*v));
val_ty(*v)
}).collect::<Vec<_>>();
debug!("Asm Output Type: {:?}", self.ccx.tn.type_to_string(output));
debug!("Asm Output Type: {:?}", self.ccx.tn().type_to_string(output));
let fty = Type::func(argtys.as_slice(), &output);
unsafe {
let v = llvm::LLVMInlineAsm(
@ -812,9 +813,9 @@ impl<'a> Builder<'a> {
self.count_insn("call");
debug!("Call {} with args ({})",
self.ccx.tn.val_to_string(llfn),
self.ccx.tn().val_to_string(llfn),
args.iter()
.map(|&v| self.ccx.tn.val_to_string(v))
.map(|&v| self.ccx.tn().val_to_string(v))
.collect::<Vec<String>>()
.connect(", "));

View File

@ -146,7 +146,7 @@ fn coerce_to_int(ccx: &CrateContext, size: uint) -> Vec<Type> {
let r = size % 32;
if r > 0 {
unsafe {
args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx, r as c_uint)));
args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint)));
}
}

View File

@ -87,7 +87,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
*/
debug!("push_ast_cleanup_scope({})",
self.ccx.tcx.map.node_to_string(id));
self.ccx.tcx().map.node_to_string(id));
// FIXME(#2202) -- currently closure bodies have a parent
// region, which messes up the assertion below, since there
@ -101,7 +101,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
// this new AST scope had better be its immediate child.
let top_scope = self.top_ast_scope();
if top_scope.is_some() {
assert_eq!(self.ccx.tcx.region_maps.opt_encl_scope(id), top_scope);
assert_eq!(self.ccx.tcx().region_maps.opt_encl_scope(id), top_scope);
}
self.push_scope(CleanupScope::new(AstScopeKind(id)));
@ -111,7 +111,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
id: ast::NodeId,
exits: [&'a Block<'a>, ..EXIT_MAX]) {
debug!("push_loop_cleanup_scope({})",
self.ccx.tcx.map.node_to_string(id));
self.ccx.tcx().map.node_to_string(id));
assert_eq!(Some(id), self.top_ast_scope());
self.push_scope(CleanupScope::new(LoopScopeKind(id, exits)));
@ -135,7 +135,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
*/
debug!("pop_and_trans_ast_cleanup_scope({})",
self.ccx.tcx.map.node_to_string(cleanup_scope));
self.ccx.tcx().map.node_to_string(cleanup_scope));
assert!(self.top_scope(|s| s.kind.is_ast_with_id(cleanup_scope)));
@ -154,7 +154,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
*/
debug!("pop_loop_cleanup_scope({})",
self.ccx.tcx.map.node_to_string(cleanup_scope));
self.ccx.tcx().map.node_to_string(cleanup_scope));
assert!(self.top_scope(|s| s.kind.is_loop_with_id(cleanup_scope)));
@ -237,7 +237,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
debug!("schedule_lifetime_end({:?}, val={})",
cleanup_scope,
self.ccx.tn.val_to_string(val));
self.ccx.tn().val_to_string(val));
self.schedule_clean(cleanup_scope, drop as CleanupObj);
}
@ -262,7 +262,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
debug!("schedule_drop_mem({:?}, val={}, ty={})",
cleanup_scope,
self.ccx.tn.val_to_string(val),
self.ccx.tn().val_to_string(val),
ty.repr(self.ccx.tcx()));
self.schedule_clean(cleanup_scope, drop as CleanupObj);
@ -288,7 +288,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
debug!("schedule_drop_and_zero_mem({:?}, val={}, ty={}, zero={})",
cleanup_scope,
self.ccx.tn.val_to_string(val),
self.ccx.tn().val_to_string(val),
ty.repr(self.ccx.tcx()),
true);
@ -314,7 +314,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
debug!("schedule_drop_immediate({:?}, val={}, ty={})",
cleanup_scope,
self.ccx.tn.val_to_string(val),
self.ccx.tn().val_to_string(val),
ty.repr(self.ccx.tcx()));
self.schedule_clean(cleanup_scope, drop as CleanupObj);
@ -334,7 +334,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
debug!("schedule_free_value({:?}, val={}, heap={:?})",
cleanup_scope,
self.ccx.tn.val_to_string(val),
self.ccx.tn().val_to_string(val),
heap);
self.schedule_clean(cleanup_scope, drop as CleanupObj);
@ -374,7 +374,7 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
self.ccx.sess().bug(
format!("no cleanup scope {} found",
self.ccx.tcx.map.node_to_string(cleanup_scope)).as_slice());
self.ccx.tcx().map.node_to_string(cleanup_scope)).as_slice());
}
fn schedule_clean_in_custom_scope(&self,
@ -720,7 +720,7 @@ impl<'a> CleanupHelperMethods<'a> for FunctionContext<'a> {
let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() {
Some(def_id) => callee::trans_fn_ref(pad_bcx, def_id, ExprId(0)),
None => {
let mut personality = self.ccx.eh_personality.borrow_mut();
let mut personality = self.ccx.eh_personality().borrow_mut();
match *personality {
Some(llpersonality) => llpersonality,
None => {

View File

@ -427,12 +427,12 @@ pub fn trans_expr_fn<'a>(
pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext,
closure_id: ast::DefId)
-> Option<ValueRef> {
if !ccx.tcx.unboxed_closures.borrow().contains_key(&closure_id) {
if !ccx.tcx().unboxed_closures.borrow().contains_key(&closure_id) {
// Not an unboxed closure.
return None
}
match ccx.unboxed_closure_vals.borrow().find(&closure_id) {
match ccx.unboxed_closure_vals().borrow().find(&closure_id) {
Some(llfn) => {
debug!("get_or_create_declaration_if_unboxed_closure(): found \
closure");
@ -441,10 +441,10 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext,
None => {}
}
let function_type = ty::mk_unboxed_closure(&ccx.tcx,
let function_type = ty::mk_unboxed_closure(ccx.tcx(),
closure_id,
ty::ReStatic);
let symbol = ccx.tcx.map.with_path(closure_id.node, |path| {
let symbol = ccx.tcx().map.with_path(closure_id.node, |path| {
mangle_internal_name_by_path_and_seq(path, "unboxed_closure")
});
@ -456,8 +456,8 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext,
debug!("get_or_create_declaration_if_unboxed_closure(): inserting new \
closure {} (type {})",
closure_id,
ccx.tn.type_to_string(val_ty(llfn)));
ccx.unboxed_closure_vals.borrow_mut().insert(closure_id, llfn);
ccx.tn().type_to_string(val_ty(llfn)));
ccx.unboxed_closure_vals().borrow_mut().insert(closure_id, llfn);
Some(llfn)
}
@ -554,7 +554,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
}
};
match ccx.closure_bare_wrapper_cache.borrow().find(&fn_ptr) {
match ccx.closure_bare_wrapper_cache().borrow().find(&fn_ptr) {
Some(&llval) => return llval,
None => {}
}
@ -581,7 +581,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
decl_rust_fn(ccx, closure_ty, name.as_slice())
};
ccx.closure_bare_wrapper_cache.borrow_mut().insert(fn_ptr, llfn);
ccx.closure_bare_wrapper_cache().borrow_mut().insert(fn_ptr, llfn);
// This is only used by statics inlined from a different crate.
if !is_local {

View File

@ -14,7 +14,7 @@
use driver::session::Session;
use llvm;
use llvm::{ValueRef, BasicBlockRef, BuilderRef};
use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef};
use llvm::{True, False, Bool};
use middle::def;
use middle::freevars;
@ -82,7 +82,7 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool {
ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) |
ty::ty_unboxed_closure(..) => {
let llty = sizing_type_of(ccx, ty);
llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type)
llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type())
}
_ => type_is_zero_size(ccx, ty)
}
@ -297,7 +297,7 @@ pub struct FunctionContext<'a> {
pub block_arena: &'a TypedArena<Block<'a>>,
// This function's enclosing crate context.
pub ccx: &'a CrateContext,
pub ccx: &'a CrateContext<'a>,
// Used and maintained by the debuginfo module.
pub debug_context: debuginfo::FunctionDebugContext,
@ -342,7 +342,7 @@ impl<'a> FunctionContext<'a> {
self.llreturn.set(Some(unsafe {
"return".with_c_str(|buf| {
llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx, self.llfn, buf)
llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), self.llfn, buf)
})
}))
}
@ -365,7 +365,7 @@ impl<'a> FunctionContext<'a> {
-> &'a Block<'a> {
unsafe {
let llbb = name.with_c_str(|buf| {
llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx,
llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(),
self.llfn,
buf)
});
@ -449,9 +449,9 @@ impl<'a> Block<'a> {
})
}
pub fn ccx(&self) -> &'a CrateContext { self.fcx.ccx }
pub fn ccx(&self) -> &'a CrateContext<'a> { self.fcx.ccx }
pub fn tcx(&self) -> &'a ty::ctxt {
&self.fcx.ccx.tcx
self.fcx.ccx.tcx()
}
pub fn sess(&self) -> &'a Session { self.fcx.ccx.sess() }
@ -478,11 +478,11 @@ impl<'a> Block<'a> {
}
pub fn val_to_string(&self, val: ValueRef) -> String {
self.ccx().tn.val_to_string(val)
self.ccx().tn().val_to_string(val)
}
pub fn llty_str(&self, ty: Type) -> String {
self.ccx().tn.type_to_string(ty)
self.ccx().tn().type_to_string(ty)
}
pub fn ty_to_string(&self, t: ty::t) -> String {
@ -601,11 +601,11 @@ pub fn C_u64(ccx: &CrateContext, i: u64) -> ValueRef {
}
pub fn C_int(ccx: &CrateContext, i: int) -> ValueRef {
C_integral(ccx.int_type, i as u64, true)
C_integral(ccx.int_type(), i as u64, true)
}
pub fn C_uint(ccx: &CrateContext, i: uint) -> ValueRef {
C_integral(ccx.int_type, i as u64, false)
C_integral(ccx.int_type(), i as u64, false)
}
pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef {
@ -617,25 +617,25 @@ pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef {
// our boxed-and-length-annotated strings.
pub fn C_cstr(cx: &CrateContext, s: InternedString, null_terminated: bool) -> ValueRef {
unsafe {
match cx.const_cstr_cache.borrow().find(&s) {
match cx.const_cstr_cache().borrow().find(&s) {
Some(&llval) => return llval,
None => ()
}
let sc = llvm::LLVMConstStringInContext(cx.llcx,
let sc = llvm::LLVMConstStringInContext(cx.llcx(),
s.get().as_ptr() as *const c_char,
s.get().len() as c_uint,
!null_terminated as Bool);
let gsym = token::gensym("str");
let g = format!("str{}", gsym.uint()).with_c_str(|buf| {
llvm::LLVMAddGlobal(cx.llmod, val_ty(sc).to_ref(), buf)
llvm::LLVMAddGlobal(cx.llmod(), val_ty(sc).to_ref(), buf)
});
llvm::LLVMSetInitializer(g, sc);
llvm::LLVMSetGlobalConstant(g, True);
llvm::SetLinkage(g, llvm::InternalLinkage);
cx.const_cstr_cache.borrow_mut().insert(s, g);
cx.const_cstr_cache().borrow_mut().insert(s, g);
g
}
}
@ -647,7 +647,7 @@ pub fn C_str_slice(cx: &CrateContext, s: InternedString) -> ValueRef {
let len = s.get().len();
let cs = llvm::LLVMConstPointerCast(C_cstr(cx, s, false),
Type::i8p(cx).to_ref());
C_named_struct(cx.tn.find_type("str_slice").unwrap(), [cs, C_uint(cx, len)])
C_named_struct(cx.tn().find_type("str_slice").unwrap(), [cs, C_uint(cx, len)])
}
}
@ -658,7 +658,7 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef {
let gsym = token::gensym("binary");
let g = format!("binary{}", gsym.uint()).with_c_str(|buf| {
llvm::LLVMAddGlobal(cx.llmod, val_ty(lldata).to_ref(), buf)
llvm::LLVMAddGlobal(cx.llmod(), val_ty(lldata).to_ref(), buf)
});
llvm::LLVMSetInitializer(g, lldata);
llvm::LLVMSetGlobalConstant(g, True);
@ -669,9 +669,13 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef {
}
}
pub fn C_struct(ccx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef {
pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef {
C_struct_in_context(cx.llcx(), elts, packed)
}
pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef {
unsafe {
llvm::LLVMConstStructInContext(ccx.llcx,
llvm::LLVMConstStructInContext(llcx,
elts.as_ptr(), elts.len() as c_uint,
packed as Bool)
}
@ -689,10 +693,14 @@ pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef {
}
}
pub fn C_bytes(ccx: &CrateContext, bytes: &[u8]) -> ValueRef {
pub fn C_bytes(cx: &CrateContext, bytes: &[u8]) -> ValueRef {
C_bytes_in_context(cx.llcx(), bytes)
}
pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef {
unsafe {
let ptr = bytes.as_ptr() as *const c_char;
return llvm::LLVMConstStringInContext(ccx.llcx, ptr, bytes.len() as c_uint, True);
return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True);
}
}
@ -702,7 +710,7 @@ pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint])
let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint);
debug!("const_get_elt(v={}, us={:?}, r={})",
cx.tn.val_to_string(v), us, cx.tn.val_to_string(r));
cx.tn().val_to_string(v), us, cx.tn().val_to_string(r));
return r;
}

View File

@ -91,7 +91,7 @@ pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: ast::Lit)
pub fn const_ptrcast(cx: &CrateContext, a: ValueRef, t: Type) -> ValueRef {
unsafe {
let b = llvm::LLVMConstPointerCast(a, t.ptr_to().to_ref());
assert!(cx.const_globals.borrow_mut().insert(b as int, a));
assert!(cx.const_globals().borrow_mut().insert(b as int, a));
b
}
}
@ -119,7 +119,7 @@ fn const_vec(cx: &CrateContext, e: &ast::Expr,
pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> ValueRef {
unsafe {
let gv = "const".with_c_str(|name| {
llvm::LLVMAddGlobal(cx.llmod, val_ty(cv).to_ref(), name)
llvm::LLVMAddGlobal(cx.llmod(), val_ty(cv).to_ref(), name)
});
llvm::LLVMSetInitializer(gv, cv);
llvm::LLVMSetGlobalConstant(gv,
@ -130,7 +130,7 @@ pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) ->
}
fn const_deref_ptr(cx: &CrateContext, v: ValueRef) -> ValueRef {
let v = match cx.const_globals.borrow().find(&(v as int)) {
let v = match cx.const_globals().borrow().find(&(v as int)) {
Some(&v) => v,
None => v
};
@ -178,13 +178,13 @@ fn const_deref(cx: &CrateContext, v: ValueRef, t: ty::t, explicit: bool)
pub fn get_const_val(cx: &CrateContext,
mut def_id: ast::DefId) -> (ValueRef, bool) {
let contains_key = cx.const_values.borrow().contains_key(&def_id.node);
let contains_key = cx.const_values().borrow().contains_key(&def_id.node);
if !ast_util::is_local(def_id) || !contains_key {
if !ast_util::is_local(def_id) {
def_id = inline::maybe_instantiate_inline(cx, def_id);
}
match cx.tcx.map.expect_item(def_id.node).node {
match cx.tcx().map.expect_item(def_id.node).node {
ast::ItemStatic(_, ast::MutImmutable, _) => {
trans_const(cx, ast::MutImmutable, def_id.node);
}
@ -192,8 +192,8 @@ pub fn get_const_val(cx: &CrateContext,
}
}
(cx.const_values.borrow().get_copy(&def_id.node),
!cx.non_inlineable_statics.borrow().contains(&def_id.node))
(cx.const_values().borrow().get_copy(&def_id.node),
!cx.non_inlineable_statics().borrow().contains(&def_id.node))
}
pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef, bool, ty::t) {
@ -202,7 +202,7 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef
let mut inlineable = inlineable;
let ety = ty::expr_ty(cx.tcx(), e);
let mut ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e);
let opt_adj = cx.tcx.adjustments.borrow().find_copy(&e.id);
let opt_adj = cx.tcx().adjustments.borrow().find_copy(&e.id);
match opt_adj {
None => { }
Some(adj) => {
@ -523,7 +523,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
(expr::cast_enum, expr::cast_integral) => {
let repr = adt::represent_type(cx, basety);
let discr = adt::const_get_discrim(cx, &*repr, v);
let iv = C_integral(cx.int_type, discr, false);
let iv = C_integral(cx.int_type(), discr, false);
let ety_cast = expr::cast_type_kind(cx.tcx(), ety);
match ety_cast {
expr::cast_integral => {
@ -690,8 +690,17 @@ pub fn trans_const(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) {
let g = base::get_item_val(ccx, id);
// At this point, get_item_val has already translated the
// constant's initializer to determine its LLVM type.
let v = ccx.const_values.borrow().get_copy(&id);
let v = ccx.const_values().borrow().get_copy(&id);
llvm::LLVMSetInitializer(g, v);
// `get_item_val` left `g` with external linkage, but we just set an
// initializer for it. But we don't know yet if `g` should really be
// defined in this compilation unit, so we set its linkage to
// `AvailableExternallyLinkage`. (It's still a definition, but acts
// like a declaration for most purposes.) If `g` really should be
// declared here, then `trans_item` will fix up the linkage later on.
llvm::SetLinkage(g, llvm::AvailableExternallyLinkage);
if m != ast::MutMutable {
llvm::LLVMSetGlobalConstant(g, True);
}

View File

@ -11,7 +11,7 @@
use driver::config::NoDebugInfo;
use driver::session::Session;
use llvm;
use llvm::{ContextRef, ModuleRef, ValueRef};
use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef};
use llvm::{TargetData};
use llvm::mk_target_data;
use metadata::common::LinkMeta;
@ -51,39 +51,61 @@ pub struct Stats {
pub fn_stats: RefCell<Vec<(String, uint, uint)> >,
}
pub struct CrateContext {
pub llmod: ModuleRef,
pub llcx: ContextRef,
pub metadata_llmod: ModuleRef,
pub td: TargetData,
pub tn: TypeNames,
pub externs: RefCell<ExternMap>,
pub item_vals: RefCell<NodeMap<ValueRef>>,
pub exp_map2: resolve::ExportMap2,
pub reachable: NodeSet,
pub item_symbols: RefCell<NodeMap<String>>,
pub link_meta: LinkMeta,
pub drop_glues: RefCell<HashMap<ty::t, ValueRef>>,
pub tydescs: RefCell<HashMap<ty::t, Rc<tydesc_info>>>,
/// Set when running emit_tydescs to enforce that no more tydescs are
/// created.
pub finished_tydescs: Cell<bool>,
/// Track mapping of external ids to local items imported for inlining
pub external: RefCell<DefIdMap<Option<ast::NodeId>>>,
/// Backwards version of the `external` map (inlined items to where they
/// came from)
pub external_srcs: RefCell<NodeMap<ast::DefId>>,
/// The shared portion of a `CrateContext`. There is one `SharedCrateContext`
/// per crate. The data here is shared between all compilation units of the
/// crate, so it must not contain references to any LLVM data structures
/// (aside from metadata-related ones).
pub struct SharedCrateContext {
local_ccxs: Vec<LocalCrateContext>,
metadata_llmod: ModuleRef,
metadata_llcx: ContextRef,
exp_map2: resolve::ExportMap2,
reachable: NodeSet,
item_symbols: RefCell<NodeMap<String>>,
link_meta: LinkMeta,
/// A set of static items which cannot be inlined into other crates. This
/// will prevent in IIItem() structures from being encoded into the metadata
/// that is generated
pub non_inlineable_statics: RefCell<NodeSet>,
non_inlineable_statics: RefCell<NodeSet>,
symbol_hasher: RefCell<Sha256>,
tcx: ty::ctxt,
stats: Stats,
available_monomorphizations: RefCell<HashSet<String>>,
available_drop_glues: RefCell<HashMap<ty::t, String>>,
available_visit_glues: RefCell<HashMap<ty::t, String>>,
}
/// The local portion of a `CrateContext`. There is one `LocalCrateContext`
/// per compilation unit. Each one has its own LLVM `ContextRef` so that
/// several compilation units may be optimized in parallel. All other LLVM
/// data structures in the `LocalCrateContext` are tied to that `ContextRef`.
pub struct LocalCrateContext {
llmod: ModuleRef,
llcx: ContextRef,
td: TargetData,
tn: TypeNames,
externs: RefCell<ExternMap>,
item_vals: RefCell<NodeMap<ValueRef>>,
drop_glues: RefCell<HashMap<ty::t, ValueRef>>,
tydescs: RefCell<HashMap<ty::t, Rc<tydesc_info>>>,
/// Set when running emit_tydescs to enforce that no more tydescs are
/// created.
finished_tydescs: Cell<bool>,
/// Track mapping of external ids to local items imported for inlining
external: RefCell<DefIdMap<Option<ast::NodeId>>>,
/// Backwards version of the `external` map (inlined items to where they
/// came from)
external_srcs: RefCell<NodeMap<ast::DefId>>,
/// Cache instances of monomorphized functions
pub monomorphized: RefCell<HashMap<MonoId, ValueRef>>,
pub monomorphizing: RefCell<DefIdMap<uint>>,
monomorphized: RefCell<HashMap<MonoId, ValueRef>>,
monomorphizing: RefCell<DefIdMap<uint>>,
/// Cache generated vtables
pub vtables: RefCell<HashMap<(ty::t, MonoId), ValueRef>>,
vtables: RefCell<HashMap<(ty::t, MonoId), ValueRef>>,
/// Cache of constant strings,
pub const_cstr_cache: RefCell<HashMap<InternedString, ValueRef>>,
const_cstr_cache: RefCell<HashMap<InternedString, ValueRef>>,
/// Reverse-direction for const ptrs cast from globals.
/// Key is an int, cast from a ValueRef holding a *T,
@ -93,106 +115,295 @@ pub struct CrateContext {
/// when we ptrcast, and we have to ptrcast during translation
/// of a [T] const because we form a slice, a [*T,int] pair, not
/// a pointer to an LLVM array type.
pub const_globals: RefCell<HashMap<int, ValueRef>>,
const_globals: RefCell<HashMap<int, ValueRef>>,
/// Cache of emitted const values
pub const_values: RefCell<NodeMap<ValueRef>>,
const_values: RefCell<NodeMap<ValueRef>>,
/// Cache of external const values
pub extern_const_values: RefCell<DefIdMap<ValueRef>>,
extern_const_values: RefCell<DefIdMap<ValueRef>>,
pub impl_method_cache: RefCell<HashMap<(ast::DefId, ast::Name), ast::DefId>>,
impl_method_cache: RefCell<HashMap<(ast::DefId, ast::Name), ast::DefId>>,
/// Cache of closure wrappers for bare fn's.
pub closure_bare_wrapper_cache: RefCell<HashMap<ValueRef, ValueRef>>,
closure_bare_wrapper_cache: RefCell<HashMap<ValueRef, ValueRef>>,
pub lltypes: RefCell<HashMap<ty::t, Type>>,
pub llsizingtypes: RefCell<HashMap<ty::t, Type>>,
pub adt_reprs: RefCell<HashMap<ty::t, Rc<adt::Repr>>>,
pub symbol_hasher: RefCell<Sha256>,
pub type_hashcodes: RefCell<HashMap<ty::t, String>>,
pub all_llvm_symbols: RefCell<HashSet<String>>,
pub tcx: ty::ctxt,
pub stats: Stats,
pub int_type: Type,
pub opaque_vec_type: Type,
pub builder: BuilderRef_res,
lltypes: RefCell<HashMap<ty::t, Type>>,
llsizingtypes: RefCell<HashMap<ty::t, Type>>,
adt_reprs: RefCell<HashMap<ty::t, Rc<adt::Repr>>>,
type_hashcodes: RefCell<HashMap<ty::t, String>>,
all_llvm_symbols: RefCell<HashSet<String>>,
int_type: Type,
opaque_vec_type: Type,
builder: BuilderRef_res,
/// Holds the LLVM values for closure IDs.
pub unboxed_closure_vals: RefCell<DefIdMap<ValueRef>>,
unboxed_closure_vals: RefCell<DefIdMap<ValueRef>>,
pub dbg_cx: Option<debuginfo::CrateDebugContext>,
dbg_cx: Option<debuginfo::CrateDebugContext>,
pub eh_personality: RefCell<Option<ValueRef>>,
eh_personality: RefCell<Option<ValueRef>>,
intrinsics: RefCell<HashMap<&'static str, ValueRef>>,
/// Number of LLVM instructions translated into this `LocalCrateContext`.
/// This is used to perform some basic load-balancing to keep all LLVM
/// contexts around the same size.
n_llvm_insns: Cell<uint>,
}
impl CrateContext {
pub fn new(name: &str,
pub struct CrateContext<'a> {
shared: &'a SharedCrateContext,
local: &'a LocalCrateContext,
/// The index of `local` in `shared.local_ccxs`. This is used in
/// `maybe_iter(true)` to identify the original `LocalCrateContext`.
index: uint,
}
pub struct CrateContextIterator<'a> {
shared: &'a SharedCrateContext,
index: uint,
}
impl<'a> Iterator<CrateContext<'a>> for CrateContextIterator<'a> {
fn next(&mut self) -> Option<CrateContext<'a>> {
if self.index >= self.shared.local_ccxs.len() {
return None;
}
let index = self.index;
self.index += 1;
Some(CrateContext {
shared: self.shared,
local: &self.shared.local_ccxs[index],
index: index,
})
}
}
/// The iterator produced by `CrateContext::maybe_iter`.
pub struct CrateContextMaybeIterator<'a> {
shared: &'a SharedCrateContext,
index: uint,
single: bool,
origin: uint,
}
impl<'a> Iterator<(CrateContext<'a>, bool)> for CrateContextMaybeIterator<'a> {
fn next(&mut self) -> Option<(CrateContext<'a>, bool)> {
if self.index >= self.shared.local_ccxs.len() {
return None;
}
let index = self.index;
self.index += 1;
if self.single {
self.index = self.shared.local_ccxs.len();
}
let ccx = CrateContext {
shared: self.shared,
local: &self.shared.local_ccxs[index],
index: index,
};
Some((ccx, index == self.origin))
}
}
unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) {
let llcx = llvm::LLVMContextCreate();
let llmod = mod_name.with_c_str(|buf| {
llvm::LLVMModuleCreateWithNameInContext(buf, llcx)
});
sess.targ_cfg
.target_strs
.data_layout
.as_slice()
.with_c_str(|buf| {
llvm::LLVMSetDataLayout(llmod, buf);
});
sess.targ_cfg
.target_strs
.target_triple
.as_slice()
.with_c_str(|buf| {
llvm::LLVMRustSetNormalizedTarget(llmod, buf);
});
(llcx, llmod)
}
impl SharedCrateContext {
pub fn new(crate_name: &str,
local_count: uint,
tcx: ty::ctxt,
emap2: resolve::ExportMap2,
symbol_hasher: Sha256,
link_meta: LinkMeta,
reachable: NodeSet)
-> CrateContext {
-> SharedCrateContext {
let (metadata_llcx, metadata_llmod) = unsafe {
create_context_and_module(&tcx.sess, "metadata")
};
let mut shared_ccx = SharedCrateContext {
local_ccxs: Vec::with_capacity(local_count),
metadata_llmod: metadata_llmod,
metadata_llcx: metadata_llcx,
exp_map2: emap2,
reachable: reachable,
item_symbols: RefCell::new(NodeMap::new()),
link_meta: link_meta,
non_inlineable_statics: RefCell::new(NodeSet::new()),
symbol_hasher: RefCell::new(symbol_hasher),
tcx: tcx,
stats: Stats {
n_static_tydescs: Cell::new(0u),
n_glues_created: Cell::new(0u),
n_null_glues: Cell::new(0u),
n_real_glues: Cell::new(0u),
n_fns: Cell::new(0u),
n_monos: Cell::new(0u),
n_inlines: Cell::new(0u),
n_closures: Cell::new(0u),
n_llvm_insns: Cell::new(0u),
llvm_insns: RefCell::new(HashMap::new()),
fn_stats: RefCell::new(Vec::new()),
},
available_monomorphizations: RefCell::new(HashSet::new()),
available_drop_glues: RefCell::new(HashMap::new()),
available_visit_glues: RefCell::new(HashMap::new()),
};
for i in range(0, local_count) {
// Append ".rs" to crate name as LLVM module identifier.
//
// LLVM code generator emits a ".file filename" directive
// for ELF backends. Value of the "filename" is set as the
// LLVM module identifier. Due to a LLVM MC bug[1], LLVM
// crashes if the module identifier is same as other symbols
// such as a function name in the module.
// 1. http://llvm.org/bugs/show_bug.cgi?id=11479
let llmod_id = format!("{}.{}.rs", crate_name, i);
let local_ccx = LocalCrateContext::new(&shared_ccx, llmod_id.as_slice());
shared_ccx.local_ccxs.push(local_ccx);
}
shared_ccx
}
pub fn iter<'a>(&'a self) -> CrateContextIterator<'a> {
CrateContextIterator {
shared: self,
index: 0,
}
}
pub fn get_ccx<'a>(&'a self, index: uint) -> CrateContext<'a> {
CrateContext {
shared: self,
local: &self.local_ccxs[index],
index: index,
}
}
fn get_smallest_ccx<'a>(&'a self) -> CrateContext<'a> {
let (local_ccx, index) =
self.local_ccxs
.iter()
.zip(range(0, self.local_ccxs.len()))
.min_by(|&(local_ccx, _idx)| local_ccx.n_llvm_insns.get())
.unwrap();
CrateContext {
shared: self,
local: local_ccx,
index: index,
}
}
pub fn metadata_llmod(&self) -> ModuleRef {
self.metadata_llmod
}
pub fn metadata_llcx(&self) -> ContextRef {
self.metadata_llcx
}
pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 {
&self.exp_map2
}
pub fn reachable<'a>(&'a self) -> &'a NodeSet {
&self.reachable
}
pub fn item_symbols<'a>(&'a self) -> &'a RefCell<NodeMap<String>> {
&self.item_symbols
}
pub fn link_meta<'a>(&'a self) -> &'a LinkMeta {
&self.link_meta
}
pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell<NodeSet> {
&self.non_inlineable_statics
}
pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell<Sha256> {
&self.symbol_hasher
}
pub fn tcx<'a>(&'a self) -> &'a ty::ctxt {
&self.tcx
}
pub fn take_tcx(self) -> ty::ctxt {
self.tcx
}
pub fn sess<'a>(&'a self) -> &'a Session {
&self.tcx.sess
}
pub fn stats<'a>(&'a self) -> &'a Stats {
&self.stats
}
}
impl LocalCrateContext {
fn new(shared: &SharedCrateContext,
name: &str)
-> LocalCrateContext {
unsafe {
let llcx = llvm::LLVMContextCreate();
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)
});
tcx.sess
.targ_cfg
.target_strs
.data_layout
.as_slice()
.with_c_str(|buf| {
llvm::LLVMSetDataLayout(llmod, buf);
llvm::LLVMSetDataLayout(metadata_llmod, buf);
});
tcx.sess
.targ_cfg
.target_strs
.target_triple
.as_slice()
.with_c_str(|buf| {
llvm::LLVMRustSetNormalizedTarget(llmod, buf);
llvm::LLVMRustSetNormalizedTarget(metadata_llmod, buf);
});
let (llcx, llmod) = create_context_and_module(&shared.tcx.sess, name);
let td = mk_target_data(tcx.sess
.targ_cfg
.target_strs
.data_layout
.as_slice());
let td = mk_target_data(shared.tcx
.sess
.targ_cfg
.target_strs
.data_layout
.as_slice());
let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo {
let dbg_cx = if shared.tcx.sess.opts.debuginfo != NoDebugInfo {
Some(debuginfo::CrateDebugContext::new(llmod))
} else {
None
};
let mut ccx = CrateContext {
let mut local_ccx = LocalCrateContext {
llmod: llmod,
llcx: llcx,
metadata_llmod: metadata_llmod,
td: td,
tn: TypeNames::new(),
externs: RefCell::new(HashMap::new()),
item_vals: RefCell::new(NodeMap::new()),
exp_map2: emap2,
reachable: reachable,
item_symbols: RefCell::new(NodeMap::new()),
link_meta: link_meta,
drop_glues: RefCell::new(HashMap::new()),
tydescs: RefCell::new(HashMap::new()),
finished_tydescs: Cell::new(false),
external: RefCell::new(DefIdMap::new()),
external_srcs: RefCell::new(NodeMap::new()),
non_inlineable_statics: RefCell::new(NodeSet::new()),
monomorphized: RefCell::new(HashMap::new()),
monomorphizing: RefCell::new(DefIdMap::new()),
vtables: RefCell::new(HashMap::new()),
@ -205,23 +416,8 @@ impl CrateContext {
lltypes: RefCell::new(HashMap::new()),
llsizingtypes: RefCell::new(HashMap::new()),
adt_reprs: RefCell::new(HashMap::new()),
symbol_hasher: RefCell::new(symbol_hasher),
type_hashcodes: RefCell::new(HashMap::new()),
all_llvm_symbols: RefCell::new(HashSet::new()),
tcx: tcx,
stats: Stats {
n_static_tydescs: Cell::new(0u),
n_glues_created: Cell::new(0u),
n_null_glues: Cell::new(0u),
n_real_glues: Cell::new(0u),
n_fns: Cell::new(0u),
n_monos: Cell::new(0u),
n_inlines: Cell::new(0u),
n_closures: Cell::new(0u),
n_llvm_insns: Cell::new(0u),
llvm_insns: RefCell::new(HashMap::new()),
fn_stats: RefCell::new(Vec::new()),
},
int_type: Type::from_ref(ptr::mut_null()),
opaque_vec_type: Type::from_ref(ptr::mut_null()),
builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)),
@ -229,43 +425,103 @@ impl CrateContext {
dbg_cx: dbg_cx,
eh_personality: RefCell::new(None),
intrinsics: RefCell::new(HashMap::new()),
n_llvm_insns: Cell::new(0u),
};
ccx.int_type = Type::int(&ccx);
ccx.opaque_vec_type = Type::opaque_vec(&ccx);
local_ccx.int_type = Type::int(&local_ccx.dummy_ccx(shared));
local_ccx.opaque_vec_type = Type::opaque_vec(&local_ccx.dummy_ccx(shared));
let mut str_slice_ty = Type::named_struct(&ccx, "str_slice");
str_slice_ty.set_struct_body([Type::i8p(&ccx), ccx.int_type], false);
ccx.tn.associate_type("str_slice", &str_slice_ty);
// Done mutating local_ccx directly. (The rest of the
// initialization goes through RefCell.)
{
let ccx = local_ccx.dummy_ccx(shared);
ccx.tn.associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty));
let mut str_slice_ty = Type::named_struct(&ccx, "str_slice");
str_slice_ty.set_struct_body([Type::i8p(&ccx), ccx.int_type()], false);
ccx.tn().associate_type("str_slice", &str_slice_ty);
if ccx.sess().count_llvm_insns() {
base::init_insn_ctxt()
ccx.tn().associate_type("tydesc", &Type::tydesc(&ccx, str_slice_ty));
if ccx.sess().count_llvm_insns() {
base::init_insn_ctxt()
}
}
ccx
local_ccx
}
}
/// Create a dummy `CrateContext` from `self` and the provided
/// `SharedCrateContext`. This is somewhat dangerous because `self` may
/// not actually be an element of `shared.local_ccxs`, which can cause some
/// operations to `fail` unexpectedly.
///
/// This is used in the `LocalCrateContext` constructor to allow calling
/// functions that expect a complete `CrateContext`, even before the local
/// portion is fully initialized and attached to the `SharedCrateContext`.
fn dummy_ccx<'a>(&'a self, shared: &'a SharedCrateContext) -> CrateContext<'a> {
CrateContext {
shared: shared,
local: self,
index: -1 as uint,
}
}
}
impl<'b> CrateContext<'b> {
pub fn shared(&self) -> &'b SharedCrateContext {
self.shared
}
pub fn local(&self) -> &'b LocalCrateContext {
self.local
}
/// Get a (possibly) different `CrateContext` from the same
/// `SharedCrateContext`.
pub fn rotate(&self) -> CrateContext<'b> {
self.shared.get_smallest_ccx()
}
/// Either iterate over only `self`, or iterate over all `CrateContext`s in
/// the `SharedCrateContext`. The iterator produces `(ccx, is_origin)`
/// pairs, where `is_origin` is `true` if `ccx` is `self` and `false`
/// otherwise. This method is useful for avoiding code duplication in
/// cases where it may or may not be necessary to translate code into every
/// context.
pub fn maybe_iter(&self, iter_all: bool) -> CrateContextMaybeIterator<'b> {
CrateContextMaybeIterator {
shared: self.shared,
index: if iter_all { 0 } else { self.index },
single: !iter_all,
origin: self.index,
}
}
pub fn tcx<'a>(&'a self) -> &'a ty::ctxt {
&self.tcx
&self.shared.tcx
}
pub fn sess<'a>(&'a self) -> &'a Session {
&self.tcx.sess
&self.shared.tcx.sess
}
pub fn builder<'a>(&'a self) -> Builder<'a> {
Builder::new(self)
}
pub fn raw_builder<'a>(&'a self) -> BuilderRef {
self.local.builder.b
}
pub fn tydesc_type(&self) -> Type {
self.tn.find_type("tydesc").unwrap()
self.local.tn.find_type("tydesc").unwrap()
}
pub fn get_intrinsic(&self, key: & &'static str) -> ValueRef {
match self.intrinsics.borrow().find_copy(key) {
match self.intrinsics().borrow().find_copy(key) {
Some(v) => return v,
_ => {}
}
@ -286,6 +542,176 @@ impl CrateContext {
let ref cfg = self.sess().targ_cfg;
cfg.os != abi::OsiOS || cfg.arch != abi::Arm
}
pub fn llmod(&self) -> ModuleRef {
self.local.llmod
}
pub fn llcx(&self) -> ContextRef {
self.local.llcx
}
pub fn td<'a>(&'a self) -> &'a TargetData {
&self.local.td
}
pub fn tn<'a>(&'a self) -> &'a TypeNames {
&self.local.tn
}
pub fn externs<'a>(&'a self) -> &'a RefCell<ExternMap> {
&self.local.externs
}
pub fn item_vals<'a>(&'a self) -> &'a RefCell<NodeMap<ValueRef>> {
&self.local.item_vals
}
pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 {
&self.shared.exp_map2
}
pub fn reachable<'a>(&'a self) -> &'a NodeSet {
&self.shared.reachable
}
pub fn item_symbols<'a>(&'a self) -> &'a RefCell<NodeMap<String>> {
&self.shared.item_symbols
}
pub fn link_meta<'a>(&'a self) -> &'a LinkMeta {
&self.shared.link_meta
}
pub fn drop_glues<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, ValueRef>> {
&self.local.drop_glues
}
pub fn tydescs<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Rc<tydesc_info>>> {
&self.local.tydescs
}
pub fn finished_tydescs<'a>(&'a self) -> &'a Cell<bool> {
&self.local.finished_tydescs
}
pub fn external<'a>(&'a self) -> &'a RefCell<DefIdMap<Option<ast::NodeId>>> {
&self.local.external
}
pub fn external_srcs<'a>(&'a self) -> &'a RefCell<NodeMap<ast::DefId>> {
&self.local.external_srcs
}
pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell<NodeSet> {
&self.shared.non_inlineable_statics
}
pub fn monomorphized<'a>(&'a self) -> &'a RefCell<HashMap<MonoId, ValueRef>> {
&self.local.monomorphized
}
pub fn monomorphizing<'a>(&'a self) -> &'a RefCell<DefIdMap<uint>> {
&self.local.monomorphizing
}
pub fn vtables<'a>(&'a self) -> &'a RefCell<HashMap<(ty::t, MonoId), ValueRef>> {
&self.local.vtables
}
pub fn const_cstr_cache<'a>(&'a self) -> &'a RefCell<HashMap<InternedString, ValueRef>> {
&self.local.const_cstr_cache
}
pub fn const_globals<'a>(&'a self) -> &'a RefCell<HashMap<int, ValueRef>> {
&self.local.const_globals
}
pub fn const_values<'a>(&'a self) -> &'a RefCell<NodeMap<ValueRef>> {
&self.local.const_values
}
pub fn extern_const_values<'a>(&'a self) -> &'a RefCell<DefIdMap<ValueRef>> {
&self.local.extern_const_values
}
pub fn impl_method_cache<'a>(&'a self)
-> &'a RefCell<HashMap<(ast::DefId, ast::Name), ast::DefId>> {
&self.local.impl_method_cache
}
pub fn closure_bare_wrapper_cache<'a>(&'a self) -> &'a RefCell<HashMap<ValueRef, ValueRef>> {
&self.local.closure_bare_wrapper_cache
}
pub fn lltypes<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Type>> {
&self.local.lltypes
}
pub fn llsizingtypes<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Type>> {
&self.local.llsizingtypes
}
pub fn adt_reprs<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Rc<adt::Repr>>> {
&self.local.adt_reprs
}
pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell<Sha256> {
&self.shared.symbol_hasher
}
pub fn type_hashcodes<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, String>> {
&self.local.type_hashcodes
}
pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell<HashSet<String>> {
&self.local.all_llvm_symbols
}
pub fn stats<'a>(&'a self) -> &'a Stats {
&self.shared.stats
}
pub fn available_monomorphizations<'a>(&'a self) -> &'a RefCell<HashSet<String>> {
&self.shared.available_monomorphizations
}
pub fn available_drop_glues<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, String>> {
&self.shared.available_drop_glues
}
pub fn available_visit_glues<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, String>> {
&self.shared.available_visit_glues
}
pub fn int_type(&self) -> Type {
self.local.int_type
}
pub fn opaque_vec_type(&self) -> Type {
self.local.opaque_vec_type
}
pub fn unboxed_closure_vals<'a>(&'a self) -> &'a RefCell<DefIdMap<ValueRef>> {
&self.local.unboxed_closure_vals
}
pub fn dbg_cx<'a>(&'a self) -> &'a Option<debuginfo::CrateDebugContext> {
&self.local.dbg_cx
}
pub fn eh_personality<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
&self.local.eh_personality
}
fn intrinsics<'a>(&'a self) -> &'a RefCell<HashMap<&'static str, ValueRef>> {
&self.local.intrinsics
}
pub fn count_llvm_insn(&self) {
self.local.n_llvm_insns.set(self.local.n_llvm_insns.get() + 1);
}
}
fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef> {
@ -293,7 +719,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
($name:expr fn() -> $ret:expr) => (
if *key == $name {
let f = base::decl_cdecl_fn(ccx, $name, Type::func([], &$ret), ty::mk_nil());
ccx.intrinsics.borrow_mut().insert($name, f.clone());
ccx.intrinsics().borrow_mut().insert($name, f.clone());
return Some(f);
}
);
@ -301,7 +727,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
if *key == $name {
let f = base::decl_cdecl_fn(ccx, $name,
Type::func([$($arg),*], &$ret), ty::mk_nil());
ccx.intrinsics.borrow_mut().insert($name, f.clone());
ccx.intrinsics().borrow_mut().insert($name, f.clone());
return Some(f);
}
)
@ -437,7 +863,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
let f = base::decl_cdecl_fn(ccx, stringify!($cname),
Type::func([$($arg),*], &$ret),
ty::mk_nil());
ccx.intrinsics.borrow_mut().insert($name, f.clone());
ccx.intrinsics().borrow_mut().insert($name, f.clone());
return Some(f);
}
)

View File

@ -627,7 +627,7 @@ impl<K:KindOps> Datum<K> {
#[allow(dead_code)] // useful for debugging
pub fn to_string(&self, ccx: &CrateContext) -> String {
format!("Datum({}, {}, {:?})",
ccx.tn.val_to_string(self.val),
ccx.tn().val_to_string(self.val),
ty_to_string(ccx.tcx(), self.ty),
self.kind)
}

View File

@ -538,7 +538,7 @@ impl TypeMap {
// First, find out the 'real' def_id of the type. Items inlined from
// other crates have to be mapped back to their source.
let source_def_id = if def_id.krate == ast::LOCAL_CRATE {
match cx.external_srcs.borrow().find_copy(&def_id.node) {
match cx.external_srcs().borrow().find_copy(&def_id.node) {
Some(source_def_id) => {
// The given def_id identifies the inlined copy of a
// type definition, let's take the source of the copy.
@ -552,7 +552,7 @@ impl TypeMap {
// Get the crate hash as first part of the identifier.
let crate_hash = if source_def_id.krate == ast::LOCAL_CRATE {
cx.link_meta.crate_hash.clone()
cx.link_meta().crate_hash.clone()
} else {
cx.sess().cstore.get_crate_hash(source_def_id.krate)
};
@ -721,7 +721,7 @@ enum VariableKind {
/// Create any deferred debug metadata nodes
pub fn finalize(cx: &CrateContext) {
if cx.dbg_cx.is_none() {
if cx.dbg_cx().is_none() {
return;
}
@ -738,18 +738,18 @@ pub fn finalize(cx: &CrateContext) {
if cx.sess().targ_cfg.os == abi::OsMacos ||
cx.sess().targ_cfg.os == abi::OsiOS {
"Dwarf Version".with_c_str(
|s| llvm::LLVMRustAddModuleFlag(cx.llmod, s, 2));
|s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, 2));
} else {
// FIXME(#13611) this is a kludge fix because the Linux bots have
// gdb 7.4 which doesn't understand dwarf4, we should
// do something more graceful here.
"Dwarf Version".with_c_str(
|s| llvm::LLVMRustAddModuleFlag(cx.llmod, s, 3));
|s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s, 3));
}
// Prevent bitcode readers from deleting the debug info.
"Debug Info Version".with_c_str(
|s| llvm::LLVMRustAddModuleFlag(cx.llmod, s,
|s| llvm::LLVMRustAddModuleFlag(cx.llmod(), s,
llvm::LLVMRustDebugMetadataVersion));
};
}
@ -760,7 +760,7 @@ pub fn finalize(cx: &CrateContext) {
pub fn create_global_var_metadata(cx: &CrateContext,
node_id: ast::NodeId,
global: ValueRef) {
if cx.dbg_cx.is_none() {
if cx.dbg_cx().is_none() {
return;
}
@ -768,11 +768,11 @@ pub fn create_global_var_metadata(cx: &CrateContext,
// crate should already contain debuginfo for it. More importantly, the
// global might not even exist in un-inlined form anywhere which would lead
// to a linker errors.
if cx.external_srcs.borrow().contains_key(&node_id) {
if cx.external_srcs().borrow().contains_key(&node_id) {
return;
}
let var_item = cx.tcx.map.get(node_id);
let var_item = cx.tcx().map.get(node_id);
let (ident, span) = match var_item {
ast_map::NodeItem(item) => {
@ -838,7 +838,7 @@ pub fn create_local_var_metadata(bcx: &Block, local: &ast::Local) {
}
let cx = bcx.ccx();
let def_map = &cx.tcx.def_map;
let def_map = &cx.tcx().def_map;
pat_util::pat_bindings(def_map, &*local.pat, |_, node_id, span, path1| {
let var_ident = path1.node;
@ -880,7 +880,7 @@ pub fn create_captured_var_metadata(bcx: &Block,
let cx = bcx.ccx();
let ast_item = cx.tcx.map.find(node_id);
let ast_item = cx.tcx().map.find(node_id);
let variable_ident = match ast_item {
None => {
@ -963,7 +963,7 @@ pub fn create_match_binding_metadata(bcx: &Block,
let scope_metadata = scope_metadata(bcx.fcx, binding.id, binding.span);
let aops = unsafe {
[llvm::LLVMDIBuilderCreateOpDeref(bcx.ccx().int_type.to_ref())]
[llvm::LLVMDIBuilderCreateOpDeref(bcx.ccx().int_type().to_ref())]
};
// Regardless of the actual type (`T`) we're always passed the stack slot (alloca)
// for the binding. For ByRef bindings that's a `T*` but for ByMove bindings we
@ -1002,7 +1002,7 @@ pub fn create_argument_metadata(bcx: &Block, arg: &ast::Arg) {
let fcx = bcx.fcx;
let cx = fcx.ccx;
let def_map = &cx.tcx.def_map;
let def_map = &cx.tcx().def_map;
let scope_metadata = bcx.fcx.debug_context.get_ref(cx, arg.pat.span).fn_metadata;
pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, span, path1| {
@ -1120,7 +1120,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
let empty_generics = ast_util::empty_generics();
let fnitem = cx.tcx.map.get(fn_ast_id);
let fnitem = cx.tcx().map.get(fn_ast_id);
let (ident, fn_decl, generics, top_level_block, span, has_path) = match fnitem {
ast_map::NodeItem(ref item) => {
@ -1447,7 +1447,7 @@ fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool
// externally visible or by being inlined into something externally visible).
// It might better to use the `exported_items` set from `driver::CrateAnalysis`
// in the future, but (atm) this set is not available in the translation pass.
!cx.reachable.contains(&node_id)
!cx.reachable().contains(&node_id)
}
#[allow(non_snake_case)]
@ -1514,7 +1514,7 @@ fn compile_unit_metadata(cx: &CrateContext) {
});
fn fallback_path(cx: &CrateContext) -> CString {
cx.link_meta.crate_name.as_slice().to_c_str()
cx.link_meta().crate_name.as_slice().to_c_str()
}
}
@ -1643,7 +1643,7 @@ fn scope_metadata(fcx: &FunctionContext,
match scope_map.borrow().find_copy(&node_id) {
Some(scope_metadata) => scope_metadata,
None => {
let node = fcx.ccx.tcx.map.get(node_id);
let node = fcx.ccx.tcx().map.get(node_id);
fcx.ccx.sess().span_bug(span,
format!("debuginfo: Could not find scope info for node {:?}",
@ -2440,9 +2440,9 @@ fn prepare_enum_metadata(cx: &CrateContext,
def_id: ast::DefId)
-> token::InternedString {
let name = if def_id.krate == ast::LOCAL_CRATE {
cx.tcx.map.get_path_elem(def_id.node).name()
cx.tcx().map.get_path_elem(def_id.node).name()
} else {
csearch::get_item_path(&cx.tcx, def_id).last().unwrap().name()
csearch::get_item_path(cx.tcx(), def_id).last().unwrap().name()
};
token::get_name(name)
@ -2685,7 +2685,7 @@ fn at_box_metadata(cx: &CrateContext,
content_llvm_type: Type)
-> bool {
member_llvm_types.len() == 5 &&
member_llvm_types[0] == cx.int_type &&
member_llvm_types[0] == cx.int_type() &&
member_llvm_types[1] == Type::generic_glue_fn(cx).ptr_to() &&
member_llvm_types[2] == Type::i8(cx).ptr_to() &&
member_llvm_types[3] == Type::i8(cx).ptr_to() &&
@ -2787,7 +2787,7 @@ fn vec_slice_metadata(cx: &CrateContext,
-> bool {
member_llvm_types.len() == 2 &&
member_llvm_types[0] == type_of::type_of(cx, element_type).ptr_to() &&
member_llvm_types[1] == cx.int_type
member_llvm_types[1] == cx.int_type()
}
}
@ -3090,7 +3090,7 @@ fn set_debug_location(cx: &CrateContext, debug_location: DebugLocation) {
};
unsafe {
llvm::LLVMSetCurrentDebugLocation(cx.builder.b, metadata_node);
llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node);
}
debug_context(cx).current_debug_location.set(debug_location);
@ -3125,14 +3125,14 @@ fn bytes_to_bits(bytes: u64) -> c_ulonglong {
#[inline]
fn debug_context<'a>(cx: &'a CrateContext) -> &'a CrateDebugContext {
let debug_context: &'a CrateDebugContext = cx.dbg_cx.get_ref();
let debug_context: &'a CrateDebugContext = cx.dbg_cx().get_ref();
debug_context
}
#[inline]
#[allow(non_snake_case)]
fn DIB(cx: &CrateContext) -> DIBuilderRef {
cx.dbg_cx.get_ref().builder
cx.dbg_cx().get_ref().builder
}
fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
@ -3143,7 +3143,7 @@ fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
}
fn assert_type_for_node_id(cx: &CrateContext, node_id: ast::NodeId, error_span: Span) {
if !cx.tcx.node_types.borrow().contains_key(&(node_id as uint)) {
if !cx.tcx().node_types.borrow().contains_key(&(node_id as uint)) {
cx.sess().span_bug(error_span, "debuginfo: Could not find type for node id!");
}
}
@ -3152,7 +3152,7 @@ fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId)
-> (DIScope, Span) {
let containing_scope = namespace_for_item(cx, def_id).scope;
let definition_span = if def_id.krate == ast::LOCAL_CRATE {
cx.tcx.map.span(def_id.node)
cx.tcx().map.span(def_id.node)
} else {
// For external items there is no span information
codemap::DUMMY_SP
@ -3173,7 +3173,7 @@ fn populate_scope_map(cx: &CrateContext,
fn_entry_block: &ast::Block,
fn_metadata: DISubprogram,
scope_map: &mut HashMap<ast::NodeId, DIScope>) {
let def_map = &cx.tcx.def_map;
let def_map = &cx.tcx().def_map;
struct ScopeStackEntry {
scope_metadata: DIScope,
@ -3290,7 +3290,7 @@ fn populate_scope_map(cx: &CrateContext,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut HashMap<ast::NodeId, DIScope>) {
let def_map = &cx.tcx.def_map;
let def_map = &cx.tcx().def_map;
// Unfortunately, we cannot just use pat_util::pat_bindings() or
// ast_util::walk_pat() here because we have to visit *all* nodes in
@ -3942,7 +3942,7 @@ impl NamespaceTreeNode {
}
fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str {
cx.link_meta.crate_name.as_slice()
cx.link_meta().crate_name.as_slice()
}
fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc<NamespaceTreeNode> {

View File

@ -94,7 +94,7 @@ pub enum Dest {
impl Dest {
pub fn to_string(&self, ccx: &CrateContext) -> String {
match *self {
SaveIn(v) => format!("SaveIn({})", ccx.tn.val_to_string(v)),
SaveIn(v) => format!("SaveIn({})", ccx.tn().val_to_string(v)),
Ignore => "Ignore".to_string()
}
}
@ -711,7 +711,7 @@ fn trans_index<'a>(bcx: &'a Block<'a>,
let mut bcx = bcx;
// Check for overloaded index.
let method_ty = ccx.tcx
let method_ty = ccx.tcx()
.method_map
.borrow()
.find(&method_call)
@ -758,14 +758,14 @@ fn trans_index<'a>(bcx: &'a Block<'a>,
let ix_size = machine::llbitsize_of_real(bcx.ccx(),
val_ty(ix_val));
let int_size = machine::llbitsize_of_real(bcx.ccx(),
ccx.int_type);
ccx.int_type());
let ix_val = {
if ix_size < int_size {
if ty::type_is_signed(expr_ty(bcx, idx)) {
SExt(bcx, ix_val, ccx.int_type)
} else { ZExt(bcx, ix_val, ccx.int_type) }
SExt(bcx, ix_val, ccx.int_type())
} else { ZExt(bcx, ix_val, ccx.int_type()) }
} else if ix_size > int_size {
Trunc(bcx, ix_val, ccx.int_type)
Trunc(bcx, ix_val, ccx.int_type())
} else {
ix_val
}
@ -817,13 +817,28 @@ fn trans_def<'a>(bcx: &'a Block<'a>,
trans_def_fn_unadjusted(bcx, ref_expr, def)
}
def::DefStatic(did, _) => {
// There are three things that may happen here:
// 1) If the static item is defined in this crate, it will be
// translated using `get_item_val`, and we return a pointer to
// the result.
// 2) If the static item is defined in another crate, but is
// marked inlineable, then it will be inlined into this crate
// and then translated with `get_item_val`. Again, we return a
// pointer to the result.
// 3) If the static item is defined in another crate and is not
// marked inlineable, then we add (or reuse) a declaration of
// an external global, and return a pointer to that.
let const_ty = expr_ty(bcx, ref_expr);
fn get_did(ccx: &CrateContext, did: ast::DefId)
-> ast::DefId {
if did.krate != ast::LOCAL_CRATE {
// Case 2 or 3. Which one we're in is determined by
// whether the DefId produced by `maybe_instantiate_inline`
// is in the LOCAL_CRATE or not.
inline::maybe_instantiate_inline(ccx, did)
} else {
// Case 1.
did
}
}
@ -832,6 +847,9 @@ fn trans_def<'a>(bcx: &'a Block<'a>,
-> ValueRef {
// For external constants, we don't inline.
if did.krate == ast::LOCAL_CRATE {
// Case 1 or 2. (The inlining in case 2 produces a new
// DefId in LOCAL_CRATE.)
// The LLVM global has the type of its initializer,
// which may not be equal to the enum's type for
// non-C-like enums.
@ -839,7 +857,8 @@ fn trans_def<'a>(bcx: &'a Block<'a>,
let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to();
PointerCast(bcx, val, pty)
} else {
match bcx.ccx().extern_const_values.borrow().find(&did) {
// Case 3.
match bcx.ccx().extern_const_values().borrow().find(&did) {
None => {} // Continue.
Some(llval) => {
return *llval;
@ -852,11 +871,11 @@ fn trans_def<'a>(bcx: &'a Block<'a>,
&bcx.ccx().sess().cstore,
did);
let llval = symbol.as_slice().with_c_str(|buf| {
llvm::LLVMAddGlobal(bcx.ccx().llmod,
llvm::LLVMAddGlobal(bcx.ccx().llmod(),
llty.to_ref(),
buf)
});
bcx.ccx().extern_const_values.borrow_mut()
bcx.ccx().extern_const_values().borrow_mut()
.insert(did, llval);
llval
}
@ -1439,7 +1458,7 @@ fn trans_unary<'a>(bcx: &'a Block<'a>,
// Otherwise, we should be in the RvalueDpsExpr path.
assert!(
op == ast::UnDeref ||
!ccx.tcx.method_map.borrow().contains_key(&method_call));
!ccx.tcx().method_map.borrow().contains_key(&method_call));
let un_ty = expr_ty(bcx, expr);
@ -1706,7 +1725,7 @@ fn trans_binary<'a>(bcx: &'a Block<'a>,
let ccx = bcx.ccx();
// if overloaded, would be RvalueDpsExpr
assert!(!ccx.tcx.method_map.borrow().contains_key(&MethodCall::expr(expr.id)));
assert!(!ccx.tcx().method_map.borrow().contains_key(&MethodCall::expr(expr.id)));
match op {
ast::BiAnd => {
@ -2050,7 +2069,7 @@ fn deref_once<'a>(bcx: &'a Block<'a>,
let mut bcx = bcx;
// Check for overloaded deref.
let method_ty = ccx.tcx.method_map.borrow()
let method_ty = ccx.tcx().method_map.borrow()
.find(&method_call).map(|method| method.ty);
let datum = match method_ty {
Some(method_ty) => {

View File

@ -159,15 +159,22 @@ pub fn register_static(ccx: &CrateContext,
}
};
unsafe {
// Declare a symbol `foo` with the desired linkage.
let g1 = ident.get().with_c_str(|buf| {
llvm::LLVMAddGlobal(ccx.llmod, llty2.to_ref(), buf)
llvm::LLVMAddGlobal(ccx.llmod(), llty2.to_ref(), buf)
});
llvm::SetLinkage(g1, linkage);
// Declare an internal global `extern_with_linkage_foo` which
// is initialized with the address of `foo`. If `foo` is
// discarded during linking (for example, if `foo` has weak
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
// zero.
let mut real_name = "_rust_extern_with_linkage_".to_string();
real_name.push_str(ident.get());
let g2 = real_name.with_c_str(|buf| {
llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf)
llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf)
});
llvm::SetLinkage(g2, llvm::InternalLinkage);
llvm::LLVMSetInitializer(g2, g1);
@ -175,8 +182,9 @@ pub fn register_static(ccx: &CrateContext,
}
}
None => unsafe {
// Generate an external declaration.
ident.get().with_c_str(|buf| {
llvm::LLVMAddGlobal(ccx.llmod, llty.to_ref(), buf)
llvm::LLVMAddGlobal(ccx.llmod(), llty.to_ref(), buf)
})
}
}
@ -229,7 +237,7 @@ pub fn register_foreign_item_fn(ccx: &CrateContext, abi: Abi, fty: ty::t,
let llfn_ty = lltype_for_fn_from_foreign_types(ccx, &tys);
let llfn = base::get_extern_fn(ccx,
&mut *ccx.externs.borrow_mut(),
&mut *ccx.externs().borrow_mut(),
name,
cc,
llfn_ty,
@ -271,8 +279,8 @@ pub fn trans_native_call<'a>(
llfn={}, \
llretptr={})",
callee_ty.repr(tcx),
ccx.tn.val_to_string(llfn),
ccx.tn.val_to_string(llretptr));
ccx.tn().val_to_string(llfn),
ccx.tn().val_to_string(llretptr));
let (fn_abi, fn_sig) = match ty::get(callee_ty).sty {
ty::ty_bare_fn(ref fn_ty) => (fn_ty.abi, fn_ty.sig.clone()),
@ -319,9 +327,9 @@ pub fn trans_native_call<'a>(
debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}",
i,
ccx.tn.val_to_string(llarg_rust),
ccx.tn().val_to_string(llarg_rust),
rust_indirect,
ccx.tn.type_to_string(arg_tys[i].ty));
ccx.tn().type_to_string(arg_tys[i].ty));
// Ensure that we always have the Rust value indirectly,
// because it makes bitcasting easier.
@ -335,7 +343,7 @@ pub fn trans_native_call<'a>(
}
debug!("llarg_rust={} (after indirection)",
ccx.tn.val_to_string(llarg_rust));
ccx.tn().val_to_string(llarg_rust));
// Check whether we need to do any casting
match arg_tys[i].cast {
@ -344,7 +352,7 @@ pub fn trans_native_call<'a>(
}
debug!("llarg_rust={} (after casting)",
ccx.tn.val_to_string(llarg_rust));
ccx.tn().val_to_string(llarg_rust));
// Finally, load the value if needed for the foreign ABI
let foreign_indirect = arg_tys[i].is_indirect();
@ -360,7 +368,7 @@ pub fn trans_native_call<'a>(
};
debug!("argument {}, llarg_foreign={}",
i, ccx.tn.val_to_string(llarg_foreign));
i, ccx.tn().val_to_string(llarg_foreign));
// fill padding with undef value
match arg_tys[i].pad {
@ -438,10 +446,10 @@ pub fn trans_native_call<'a>(
None => fn_type.ret_ty.ty
};
debug!("llretptr={}", ccx.tn.val_to_string(llretptr));
debug!("llforeign_retval={}", ccx.tn.val_to_string(llforeign_retval));
debug!("llrust_ret_ty={}", ccx.tn.type_to_string(llrust_ret_ty));
debug!("llforeign_ret_ty={}", ccx.tn.type_to_string(llforeign_ret_ty));
debug!("llretptr={}", ccx.tn().val_to_string(llretptr));
debug!("llforeign_retval={}", ccx.tn().val_to_string(llforeign_retval));
debug!("llrust_ret_ty={}", ccx.tn().type_to_string(llrust_ret_ty));
debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty));
if llrust_ret_ty == llforeign_ret_ty {
base::store_ty(bcx, llforeign_retval, llretptr, fn_sig.output)
@ -490,13 +498,17 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) {
register_foreign_item_fn(ccx, abi, ty,
lname.get().as_slice(),
Some(foreign_item.span));
// Unlike for other items, we shouldn't call
// `base::update_linkage` here. Foreign items have
// special linkage requirements, which are handled
// inside `foreign::register_*`.
}
}
}
_ => {}
}
ccx.item_symbols.borrow_mut().insert(foreign_item.id,
ccx.item_symbols().borrow_mut().insert(foreign_item.id,
lname.get().to_string());
}
}
@ -542,7 +554,7 @@ pub fn decl_rust_fn_with_foreign_abi(ccx: &CrateContext,
let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::mk_nil());
add_argument_attributes(&tys, llfn);
debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})",
ccx.tn.type_to_string(llfn_ty), ccx.tn.val_to_string(llfn));
ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn));
llfn
}
@ -566,7 +578,7 @@ pub fn register_rust_fn_with_foreign_abi(ccx: &CrateContext,
let llfn = base::register_fn_llvmty(ccx, sp, sym, node_id, cconv, llfn_ty);
add_argument_attributes(&tys, llfn);
debug!("register_rust_fn_with_foreign_abi(node_id={:?}, llfn_ty={}, llfn={})",
node_id, ccx.tn.type_to_string(llfn_ty), ccx.tn.val_to_string(llfn));
node_id, ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn));
llfn
}
@ -605,7 +617,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let t = ty::node_id_to_type(tcx, id).subst(
ccx.tcx(), &param_substs.substs);
let ps = ccx.tcx.map.with_path(id, |path| {
let ps = ccx.tcx().map.with_path(id, |path| {
let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name));
link::mangle(path.chain(abi.move_iter()), hash)
});
@ -619,13 +631,13 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
_ => {
ccx.sess().bug(format!("build_rust_fn: extern fn {} has ty {}, \
expected a bare fn ty",
ccx.tcx.map.path_to_string(id),
ccx.tcx().map.path_to_string(id),
t.repr(tcx)).as_slice());
}
};
debug!("build_rust_fn: path={} id={} t={}",
ccx.tcx.map.path_to_string(id),
ccx.tcx().map.path_to_string(id),
id, t.repr(tcx));
let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice());
@ -644,8 +656,8 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let tcx = ccx.tcx();
debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})",
ccx.tn.val_to_string(llrustfn),
ccx.tn.val_to_string(llwrapfn),
ccx.tn().val_to_string(llrustfn),
ccx.tn().val_to_string(llwrapfn),
t.repr(ccx.tcx()));
// Avoid all the Rust generation stuff and just generate raw
@ -661,7 +673,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let the_block =
"the block".with_c_str(
|s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llwrapfn, s));
|s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn, s));
let builder = ccx.builder();
builder.position_at_end(the_block);
@ -702,11 +714,11 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
match foreign_outptr {
Some(llforeign_outptr) => {
debug!("out pointer, foreign={}",
ccx.tn.val_to_string(llforeign_outptr));
ccx.tn().val_to_string(llforeign_outptr));
let llrust_retptr =
builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to());
debug!("out pointer, foreign={} (casted)",
ccx.tn.val_to_string(llrust_retptr));
ccx.tn().val_to_string(llrust_retptr));
llrust_args.push(llrust_retptr);
return_alloca = None;
}
@ -717,8 +729,8 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
allocad={}, \
llrust_ret_ty={}, \
return_ty={}",
ccx.tn.val_to_string(slot),
ccx.tn.type_to_string(llrust_ret_ty),
ccx.tn().val_to_string(slot),
ccx.tn().type_to_string(llrust_ret_ty),
tys.fn_sig.output.repr(tcx));
llrust_args.push(slot);
return_alloca = Some(slot);
@ -752,7 +764,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let mut llforeign_arg = get_param(llwrapfn, foreign_index);
debug!("llforeign_arg {}{}: {}", "#",
i, ccx.tn.val_to_string(llforeign_arg));
i, ccx.tn().val_to_string(llforeign_arg));
debug!("rust_indirect = {}, foreign_indirect = {}",
rust_indirect, foreign_indirect);
@ -791,12 +803,13 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
};
debug!("llrust_arg {}{}: {}", "#",
i, ccx.tn.val_to_string(llrust_arg));
i, ccx.tn().val_to_string(llrust_arg));
llrust_args.push(llrust_arg);
}
// Perform the call itself
debug!("calling llrustfn = {}, t = {}", ccx.tn.val_to_string(llrustfn), t.repr(ccx.tcx()));
debug!("calling llrustfn = {}, t = {}",
ccx.tn().val_to_string(llrustfn), t.repr(ccx.tcx()));
let attributes = base::get_fn_llvm_attributes(ccx, t);
let llrust_ret_val = builder.call(llrustfn, llrust_args.as_slice(), Some(attributes));
@ -915,10 +928,10 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext,
fn_ty={} -> {}, \
ret_def={}",
ty.repr(ccx.tcx()),
ccx.tn.types_to_str(llsig.llarg_tys.as_slice()),
ccx.tn.type_to_string(llsig.llret_ty),
ccx.tn.types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>().as_slice()),
ccx.tn.type_to_string(fn_ty.ret_ty.ty),
ccx.tn().types_to_str(llsig.llarg_tys.as_slice()),
ccx.tn().type_to_string(llsig.llret_ty),
ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>().as_slice()),
ccx.tn().type_to_string(fn_ty.ret_ty.ty),
ret_def);
ForeignTypes {

View File

@ -159,7 +159,7 @@ pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef {
debug!("make drop glue for {}", ppaux::ty_to_string(ccx.tcx(), t));
let t = get_drop_glue_type(ccx, t);
debug!("drop glue type {}", ppaux::ty_to_string(ccx.tcx(), t));
match ccx.drop_glues.borrow().find(&t) {
match ccx.drop_glues().borrow().find(&t) {
Some(&glue) => return glue,
_ => { }
}
@ -171,11 +171,30 @@ pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef {
};
let llfnty = Type::glue_fn(ccx, llty);
let glue = declare_generic_glue(ccx, t, llfnty, "drop");
ccx.drop_glues.borrow_mut().insert(t, glue);
let (glue, new_sym) = match ccx.available_drop_glues().borrow().find(&t) {
Some(old_sym) => {
let glue = decl_cdecl_fn(ccx, old_sym.as_slice(), llfnty, ty::mk_nil());
(glue, None)
},
None => {
let (sym, glue) = declare_generic_glue(ccx, t, llfnty, "drop");
(glue, Some(sym))
},
};
make_generic_glue(ccx, t, glue, make_drop_glue, "drop");
ccx.drop_glues().borrow_mut().insert(t, glue);
// To avoid infinite recursion, don't `make_drop_glue` until after we've
// added the entry to the `drop_glues` cache.
match new_sym {
Some(sym) => {
ccx.available_drop_glues().borrow_mut().insert(t, sym);
// We're creating a new drop glue, so also generate a body.
make_generic_glue(ccx, t, glue, make_drop_glue, "drop");
},
None => {},
}
glue
}
@ -189,9 +208,28 @@ pub fn lazily_emit_visit_glue(ccx: &CrateContext, ti: &tydesc_info) -> ValueRef
Some(visit_glue) => visit_glue,
None => {
debug!("+++ lazily_emit_tydesc_glue VISIT {}", ppaux::ty_to_string(ccx.tcx(), ti.ty));
let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, "visit");
let (glue_fn, new_sym) = match ccx.available_visit_glues().borrow().find(&ti.ty) {
Some(old_sym) => {
let glue_fn = decl_cdecl_fn(ccx, old_sym.as_slice(), llfnty, ty::mk_nil());
(glue_fn, None)
},
None => {
let (sym, glue_fn) = declare_generic_glue(ccx, ti.ty, llfnty, "visit");
(glue_fn, Some(sym))
},
};
ti.visit_glue.set(Some(glue_fn));
make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, "visit");
match new_sym {
Some(sym) => {
ccx.available_visit_glues().borrow_mut().insert(ti.ty, sym);
make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, "visit");
},
None => {},
}
debug!("--- lazily_emit_tydesc_glue VISIT {}", ppaux::ty_to_string(ccx.tcx(), ti.ty));
glue_fn
}
@ -566,7 +604,7 @@ fn incr_refcnt_of_boxed<'a>(bcx: &'a Block<'a>,
pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info {
// If emit_tydescs already ran, then we shouldn't be creating any new
// tydescs.
assert!(!ccx.finished_tydescs.get());
assert!(!ccx.finished_tydescs().get());
let llty = type_of(ccx, t);
@ -581,7 +619,7 @@ pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info {
debug!("+++ declare_tydesc {} {}", ppaux::ty_to_string(ccx.tcx(), t), name);
let gvar = name.as_slice().with_c_str(|buf| {
unsafe {
llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type().to_ref(), buf)
llvm::LLVMAddGlobal(ccx.llmod(), ccx.tydesc_type().to_ref(), buf)
}
});
note_unique_llvm_symbol(ccx, name);
@ -602,15 +640,15 @@ pub fn declare_tydesc(ccx: &CrateContext, t: ty::t) -> tydesc_info {
}
fn declare_generic_glue(ccx: &CrateContext, t: ty::t, llfnty: Type,
name: &str) -> ValueRef {
name: &str) -> (String, ValueRef) {
let _icx = push_ctxt("declare_generic_glue");
let fn_nm = mangle_internal_name_by_type_and_seq(
ccx,
t,
format!("glue_{}", name).as_slice());
let llfn = decl_cdecl_fn(ccx, fn_nm.as_slice(), llfnty, ty::mk_nil());
note_unique_llvm_symbol(ccx, fn_nm);
return llfn;
note_unique_llvm_symbol(ccx, fn_nm.clone());
return (fn_nm, llfn);
}
fn make_generic_glue(ccx: &CrateContext,
@ -631,8 +669,9 @@ fn make_generic_glue(ccx: &CrateContext,
let bcx = init_function(&fcx, false, ty::mk_nil());
llvm::SetLinkage(llfn, llvm::InternalLinkage);
ccx.stats.n_glues_created.set(ccx.stats.n_glues_created.get() + 1u);
update_linkage(ccx, llfn, None, OriginalTranslation);
ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1u);
// All glue functions take values passed *by alias*; this is a
// requirement since in many contexts glue is invoked indirectly and
// the caller has no idea if it's dealing with something that can be
@ -651,9 +690,9 @@ fn make_generic_glue(ccx: &CrateContext,
pub fn emit_tydescs(ccx: &CrateContext) {
let _icx = push_ctxt("emit_tydescs");
// As of this point, allow no more tydescs to be created.
ccx.finished_tydescs.set(true);
ccx.finished_tydescs().set(true);
let glue_fn_ty = Type::generic_glue_fn(ccx).ptr_to();
for (_, ti) in ccx.tydescs.borrow().iter() {
for (_, ti) in ccx.tydescs().borrow().iter() {
// Each of the glue functions needs to be cast to a generic type
// before being put into the tydesc because we only have a singleton
// tydesc type. Then we'll recast each function to its real type when
@ -661,17 +700,17 @@ pub fn emit_tydescs(ccx: &CrateContext) {
let drop_glue = unsafe {
llvm::LLVMConstPointerCast(get_drop_glue(ccx, ti.ty), glue_fn_ty.to_ref())
};
ccx.stats.n_real_glues.set(ccx.stats.n_real_glues.get() + 1);
ccx.stats().n_real_glues.set(ccx.stats().n_real_glues.get() + 1);
let visit_glue =
match ti.visit_glue.get() {
None => {
ccx.stats.n_null_glues.set(ccx.stats.n_null_glues.get() +
ccx.stats().n_null_glues.set(ccx.stats().n_null_glues.get() +
1u);
C_null(glue_fn_ty)
}
Some(v) => {
unsafe {
ccx.stats.n_real_glues.set(ccx.stats.n_real_glues.get() +
ccx.stats().n_real_glues.set(ccx.stats().n_real_glues.get() +
1);
llvm::LLVMConstPointerCast(v, glue_fn_ty.to_ref())
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use llvm::{AvailableExternallyLinkage, SetLinkage};
use llvm::{AvailableExternallyLinkage, InternalLinkage, SetLinkage};
use metadata::csearch;
use middle::astencode;
use middle::trans::base::{push_ctxt, trans_item, get_item_val, trans_fn};
@ -22,7 +22,7 @@ use syntax::ast_util;
pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
-> ast::DefId {
let _icx = push_ctxt("maybe_instantiate_inline");
match ccx.external.borrow().find(&fn_id) {
match ccx.external().borrow().find(&fn_id) {
Some(&Some(node_id)) => {
// Already inline
debug!("maybe_instantiate_inline({}): already inline as node id {}",
@ -43,48 +43,74 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
|a,b,c,d| astencode::decode_inlined_item(a, b, c, d));
return match csearch_result {
csearch::not_found => {
ccx.external.borrow_mut().insert(fn_id, None);
ccx.external().borrow_mut().insert(fn_id, None);
fn_id
}
csearch::found(ast::IIItem(item)) => {
ccx.external.borrow_mut().insert(fn_id, Some(item.id));
ccx.external_srcs.borrow_mut().insert(item.id, fn_id);
ccx.external().borrow_mut().insert(fn_id, Some(item.id));
ccx.external_srcs().borrow_mut().insert(item.id, fn_id);
ccx.stats.n_inlines.set(ccx.stats.n_inlines.get() + 1);
ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1);
trans_item(ccx, &*item);
// We're bringing an external global into this crate, but we don't
// want to create two copies of the global. If we do this, then if
// you take the address of the global in two separate crates you get
// two different addresses. This is bad for things like conditions,
// but it could possibly have other adverse side effects. We still
// want to achieve the optimizations related to this global,
// however, so we use the available_externally linkage which llvm
// provides
match item.node {
ast::ItemStatic(_, mutbl, _) => {
let g = get_item_val(ccx, item.id);
// see the comment in get_item_val() as to why this check is
// performed here.
if ast_util::static_has_significant_address(
mutbl,
item.attrs.as_slice()) {
SetLinkage(g, AvailableExternallyLinkage);
let linkage = match item.node {
ast::ItemFn(_, _, _, ref generics, _) => {
if generics.is_type_parameterized() {
// Generics have no symbol, so they can't be given any
// linkage.
None
} else {
if ccx.sess().opts.cg.codegen_units == 1 {
// We could use AvailableExternallyLinkage here,
// but InternalLinkage allows LLVM to optimize more
// aggressively (at the cost of sometimes
// duplicating code).
Some(InternalLinkage)
} else {
// With multiple compilation units, duplicated code
// is more of a problem. Also, `codegen_units > 1`
// means the user is okay with losing some
// performance.
Some(AvailableExternallyLinkage)
}
}
}
_ => {}
ast::ItemStatic(_, mutbl, _) => {
if !ast_util::static_has_significant_address(mutbl, item.attrs.as_slice()) {
// Inlined static items use internal linkage when
// possible, so that LLVM will coalesce globals with
// identical initializers. (It only does this for
// globals with unnamed_addr and either internal or
// private linkage.)
Some(InternalLinkage)
} else {
// The address is significant, so we can't create an
// internal copy of the static. (The copy would have a
// different address from the original.)
Some(AvailableExternallyLinkage)
}
}
_ => unreachable!(),
};
match linkage {
Some(linkage) => {
let g = get_item_val(ccx, item.id);
SetLinkage(g, linkage);
}
None => {}
}
local_def(item.id)
}
csearch::found(ast::IIForeign(item)) => {
ccx.external.borrow_mut().insert(fn_id, Some(item.id));
ccx.external_srcs.borrow_mut().insert(item.id, fn_id);
ccx.external().borrow_mut().insert(fn_id, Some(item.id));
ccx.external_srcs().borrow_mut().insert(item.id, fn_id);
local_def(item.id)
}
csearch::found_parent(parent_id, ast::IIItem(item)) => {
ccx.external.borrow_mut().insert(parent_id, Some(item.id));
ccx.external_srcs.borrow_mut().insert(item.id, parent_id);
ccx.external().borrow_mut().insert(parent_id, Some(item.id));
ccx.external_srcs().borrow_mut().insert(item.id, parent_id);
let mut my_id = 0;
match item.node {
@ -93,14 +119,14 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
let vs_there = ty::enum_variants(ccx.tcx(), parent_id);
for (here, there) in vs_here.iter().zip(vs_there.iter()) {
if there.id == fn_id { my_id = here.id.node; }
ccx.external.borrow_mut().insert(there.id, Some(here.id.node));
ccx.external().borrow_mut().insert(there.id, Some(here.id.node));
}
}
ast::ItemStruct(ref struct_def, _) => {
match struct_def.ctor_id {
None => {}
Some(ctor_id) => {
ccx.external.borrow_mut().insert(fn_id, Some(ctor_id));
ccx.external().borrow_mut().insert(fn_id, Some(ctor_id));
my_id = ctor_id;
}
}
@ -119,10 +145,10 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
match impl_item {
ast::ProvidedInlinedTraitItem(mth) |
ast::RequiredInlinedTraitItem(mth) => {
ccx.external.borrow_mut().insert(fn_id, Some(mth.id));
ccx.external_srcs.borrow_mut().insert(mth.id, fn_id);
ccx.external().borrow_mut().insert(fn_id, Some(mth.id));
ccx.external_srcs().borrow_mut().insert(mth.id, fn_id);
ccx.stats.n_inlines.set(ccx.stats.n_inlines.get() + 1);
ccx.stats().n_inlines.set(ccx.stats().n_inlines.get() + 1);
}
}
@ -147,6 +173,9 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
&param_substs::empty(),
mth.id,
[]);
// Use InternalLinkage so LLVM can optimize more
// aggressively.
SetLinkage(llfn, InternalLinkage);
}
local_def(mth.id)
}

View File

@ -89,7 +89,7 @@ pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Opti
/// Performs late verification that intrinsics are used correctly. At present,
/// the only intrinsic that needs such verification is `transmute`.
pub fn check_intrinsics(ccx: &CrateContext) {
for transmute_restriction in ccx.tcx
for transmute_restriction in ccx.tcx()
.transmute_restrictions
.borrow()
.iter() {
@ -276,7 +276,7 @@ pub fn trans_intrinsic_call<'a>(mut bcx: &'a Block<'a>, node: ast::NodeId,
let hash = ty::hash_crate_independent(
ccx.tcx(),
*substs.types.get(FnSpace, 0),
&ccx.link_meta.crate_hash);
&ccx.link_meta().crate_hash);
// NB: This needs to be kept in lockstep with the TypeId struct in
// the intrinsic module
C_named_struct(llret_ty, [C_u64(ccx, hash)])
@ -554,7 +554,7 @@ fn copy_intrinsic(bcx: &Block, allow_overlap: bool, volatile: bool,
let lltp_ty = type_of::type_of(ccx, tp_ty);
let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32);
let size = machine::llsize_of(ccx, lltp_ty);
let int_size = machine::llbitsize_of_real(ccx, ccx.int_type);
let int_size = machine::llbitsize_of_real(ccx, ccx.int_type());
let name = if allow_overlap {
if int_size == 32 {
"llvm.memmove.p0i8.p0i8.i32"
@ -583,7 +583,7 @@ fn memset_intrinsic(bcx: &Block, volatile: bool, tp_ty: ty::t,
let lltp_ty = type_of::type_of(ccx, tp_ty);
let align = C_i32(ccx, type_of::align_of(ccx, tp_ty) as i32);
let size = machine::llsize_of(ccx, lltp_ty);
let name = if machine::llbitsize_of_real(ccx, ccx.int_type) == 32 {
let name = if machine::llbitsize_of_real(ccx, ccx.int_type()) == 32 {
"llvm.memset.p0i8.i32"
} else {
"llvm.memset.p0i8.i64"

View File

@ -25,13 +25,13 @@ impl<'a, T:LlvmRepr> LlvmRepr for &'a [T] {
impl LlvmRepr for Type {
fn llrepr(&self, ccx: &CrateContext) -> String {
ccx.tn.type_to_string(*self)
ccx.tn().type_to_string(*self)
}
}
impl LlvmRepr for ValueRef {
fn llrepr(&self, ccx: &CrateContext) -> String {
ccx.tn.val_to_string(*self)
ccx.tn().val_to_string(*self)
}
}

View File

@ -23,7 +23,7 @@ use middle::trans::type_::Type;
// Returns the number of bytes clobbered by a Store to this type.
pub fn llsize_of_store(cx: &CrateContext, ty: Type) -> u64 {
unsafe {
return llvm::LLVMStoreSizeOfType(cx.td.lltd, ty.to_ref()) as u64;
return llvm::LLVMStoreSizeOfType(cx.td().lltd, ty.to_ref()) as u64;
}
}
@ -31,7 +31,7 @@ pub fn llsize_of_store(cx: &CrateContext, ty: Type) -> u64 {
// array of T. This is the "ABI" size. It includes any ABI-mandated padding.
pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> u64 {
unsafe {
return llvm::LLVMABISizeOfType(cx.td.lltd, ty.to_ref()) as u64;
return llvm::LLVMABISizeOfType(cx.td().lltd, ty.to_ref()) as u64;
}
}
@ -45,7 +45,7 @@ pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> u64 {
// below.
pub fn llsize_of_real(cx: &CrateContext, ty: Type) -> u64 {
unsafe {
let nbits = llvm::LLVMSizeOfTypeInBits(cx.td.lltd, ty.to_ref()) as u64;
let nbits = llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()) as u64;
if nbits & 7 != 0 {
// Not an even number of bytes, spills into "next" byte.
1 + (nbits >> 3)
@ -58,7 +58,7 @@ pub fn llsize_of_real(cx: &CrateContext, ty: Type) -> u64 {
/// Returns the "real" size of the type in bits.
pub fn llbitsize_of_real(cx: &CrateContext, ty: Type) -> u64 {
unsafe {
llvm::LLVMSizeOfTypeInBits(cx.td.lltd, ty.to_ref()) as u64
llvm::LLVMSizeOfTypeInBits(cx.td().lltd, ty.to_ref()) as u64
}
}
@ -79,7 +79,7 @@ pub fn llsize_of(cx: &CrateContext, ty: Type) -> ValueRef {
// space to be consumed.
pub fn nonzero_llsize_of(cx: &CrateContext, ty: Type) -> ValueRef {
if llbitsize_of_real(cx, ty) == 0 {
unsafe { llvm::LLVMConstInt(cx.int_type.to_ref(), 1, False) }
unsafe { llvm::LLVMConstInt(cx.int_type().to_ref(), 1, False) }
} else {
llsize_of(cx, ty)
}
@ -91,7 +91,7 @@ pub fn nonzero_llsize_of(cx: &CrateContext, ty: Type) -> ValueRef {
// allocations inside a stack frame, which LLVM has a free hand in.
pub fn llalign_of_pref(cx: &CrateContext, ty: Type) -> u64 {
unsafe {
return llvm::LLVMPreferredAlignmentOfType(cx.td.lltd, ty.to_ref()) as u64;
return llvm::LLVMPreferredAlignmentOfType(cx.td().lltd, ty.to_ref()) as u64;
}
}
@ -100,7 +100,7 @@ pub fn llalign_of_pref(cx: &CrateContext, ty: Type) -> u64 {
// and similar ABI-mandated things.
pub fn llalign_of_min(cx: &CrateContext, ty: Type) -> u64 {
unsafe {
return llvm::LLVMABIAlignmentOfType(cx.td.lltd, ty.to_ref()) as u64;
return llvm::LLVMABIAlignmentOfType(cx.td().lltd, ty.to_ref()) as u64;
}
}
@ -110,12 +110,12 @@ pub fn llalign_of_min(cx: &CrateContext, ty: Type) -> u64 {
pub fn llalign_of(cx: &CrateContext, ty: Type) -> ValueRef {
unsafe {
return llvm::LLVMConstIntCast(
llvm::LLVMAlignOf(ty.to_ref()), cx.int_type.to_ref(), False);
llvm::LLVMAlignOf(ty.to_ref()), cx.int_type().to_ref(), False);
}
}
pub fn llelement_offset(cx: &CrateContext, struct_ty: Type, element: uint) -> u64 {
unsafe {
return llvm::LLVMOffsetOfElement(cx.td.lltd, struct_ty.to_ref(), element as u32) as u64;
return llvm::LLVMOffsetOfElement(cx.td().lltd, struct_ty.to_ref(), element as u32) as u64;
}
}

View File

@ -38,7 +38,7 @@ use util::ppaux::Repr;
use std::c_str::ToCStr;
use syntax::abi::{Rust, RustCall};
use syntax::parse::token;
use syntax::{ast, ast_map, visit};
use syntax::{ast, ast_map, attr, visit};
use syntax::ast_util::PostExpansionMethod;
// drop_glue pointer, size, align.
@ -77,14 +77,21 @@ pub fn trans_impl(ccx: &CrateContext,
match *impl_item {
ast::MethodImplItem(method) => {
if method.pe_generics().ty_params.len() == 0u {
let llfn = get_item_val(ccx, method.id);
trans_fn(ccx,
&*method.pe_fn_decl(),
&*method.pe_body(),
llfn,
&param_substs::empty(),
method.id,
[]);
let trans_everywhere = attr::requests_inline(method.attrs.as_slice());
for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
let llfn = get_item_val(ccx, method.id);
trans_fn(ccx,
&*method.pe_fn_decl(),
&*method.pe_body(),
llfn,
&param_substs::empty(),
method.id,
[]);
update_linkage(ccx,
llfn,
Some(method.id),
if is_origin { OriginalTranslation } else { InlinedCopy });
}
}
let mut v = TransItemVisitor {
ccx: ccx,
@ -196,7 +203,7 @@ pub fn trans_static_method_callee(bcx: &Block,
let vtable_key = MethodCall::expr(expr_id);
let vtbls = resolve_vtables_in_fn_ctxt(
bcx.fcx,
ccx.tcx.vtable_map.borrow().get(&vtable_key));
ccx.tcx().vtable_map.borrow().get(&vtable_key));
match *vtbls.get_self().unwrap().get(0) {
typeck::vtable_static(impl_did, ref rcvr_substs, ref rcvr_origins) => {
@ -228,12 +235,12 @@ pub fn trans_static_method_callee(bcx: &Block,
fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name)
-> ast::DefId {
match ccx.impl_method_cache.borrow().find_copy(&(impl_id, name)) {
match ccx.impl_method_cache().borrow().find_copy(&(impl_id, name)) {
Some(m) => return m,
None => {}
}
let impl_items = ccx.tcx.impl_items.borrow();
let impl_items = ccx.tcx().impl_items.borrow();
let impl_items =
impl_items.find(&impl_id)
.expect("could not find impl while translating");
@ -241,7 +248,7 @@ fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name)
.find(|&did| {
match *did {
ty::MethodTraitItemId(did) => {
ty::impl_or_trait_item(&ccx.tcx,
ty::impl_or_trait_item(ccx.tcx(),
did).ident()
.name ==
name
@ -250,7 +257,7 @@ fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name)
}).expect("could not find method while \
translating");
ccx.impl_method_cache.borrow_mut().insert((impl_id, name),
ccx.impl_method_cache().borrow_mut().insert((impl_id, name),
meth_did.def_id());
meth_did.def_id()
}
@ -502,7 +509,7 @@ fn get_vtable(bcx: &Block,
// Check the cache.
let hash_id = (self_ty, monomorphize::make_vtable_id(ccx, origins.get(0)));
match ccx.vtables.borrow().find(&hash_id) {
match ccx.vtables().borrow().find(&hash_id) {
Some(&val) => { return val }
None => { }
}
@ -594,7 +601,7 @@ fn get_vtable(bcx: &Block,
let drop_glue = glue::get_drop_glue(ccx, self_ty);
let vtable = make_vtable(ccx, drop_glue, ll_size, ll_align, methods);
ccx.vtables.borrow_mut().insert(hash_id, vtable);
ccx.vtables().borrow_mut().insert(hash_id, vtable);
vtable
}
@ -614,7 +621,7 @@ pub fn make_vtable<I: Iterator<ValueRef>>(ccx: &CrateContext,
let tbl = C_struct(ccx, components.as_slice(), false);
let sym = token::gensym("vtable");
let vt_gvar = format!("vtable{}", sym.uint()).with_c_str(|buf| {
llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl).to_ref(), buf)
llvm::LLVMAddGlobal(ccx.llmod(), val_ty(tbl).to_ref(), buf)
});
llvm::LLVMSetInitializer(vt_gvar, tbl);
llvm::LLVMSetGlobalConstant(vt_gvar, llvm::True);
@ -684,9 +691,9 @@ pub fn vtable_ptr<'a>(bcx: &'a Block<'a>,
self_ty: ty::t) -> ValueRef {
let ccx = bcx.ccx();
let origins = {
let vtable_map = ccx.tcx.vtable_map.borrow();
let vtable_map = ccx.tcx().vtable_map.borrow();
// This trait cast might be because of implicit coercion
let adjs = ccx.tcx.adjustments.borrow();
let adjs = ccx.tcx().adjustments.borrow();
let adjust = adjs.find(&id);
let method_call = if adjust.is_some() && ty::adjust_is_object(adjust.unwrap()) {
MethodCall::autoobject(id)

View File

@ -11,6 +11,7 @@
use back::link::exported_name;
use driver::session;
use llvm::ValueRef;
use llvm;
use middle::subst;
use middle::subst::Subst;
use middle::trans::base::{set_llvm_fn_attrs, set_inline_hint};
@ -27,6 +28,7 @@ use syntax::abi;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{local_def, PostExpansionMethod};
use syntax::attr;
use std::hash::{sip, Hash};
pub fn monomorphic_fn(ccx: &CrateContext,
@ -56,7 +58,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
params: real_substs.types.clone()
};
match ccx.monomorphized.borrow().find(&hash_id) {
match ccx.monomorphized().borrow().find(&hash_id) {
Some(&val) => {
debug!("leaving monomorphic fn {}",
ty::item_path_str(ccx.tcx(), fn_id));
@ -83,7 +85,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
let map_node = session::expect(
ccx.sess(),
ccx.tcx.map.find(fn_id.node),
ccx.tcx().map.find(fn_id.node),
|| {
format!("while monomorphizing {:?}, couldn't find it in \
the item map (may have attempted to monomorphize \
@ -93,7 +95,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
match map_node {
ast_map::NodeForeignItem(_) => {
if ccx.tcx.map.get_foreign_abi(fn_id.node) != abi::RustIntrinsic {
if ccx.tcx().map.get_foreign_abi(fn_id.node) != abi::RustIntrinsic {
// Foreign externs don't have to be monomorphized.
return (get_item_val(ccx, fn_id.node), true);
}
@ -104,11 +106,11 @@ pub fn monomorphic_fn(ccx: &CrateContext,
debug!("monomorphic_fn about to subst into {}", llitem_ty.repr(ccx.tcx()));
let mono_ty = llitem_ty.subst(ccx.tcx(), real_substs);
ccx.stats.n_monos.set(ccx.stats.n_monos.get() + 1);
ccx.stats().n_monos.set(ccx.stats().n_monos.get() + 1);
let depth;
{
let mut monomorphizing = ccx.monomorphizing.borrow_mut();
let mut monomorphizing = ccx.monomorphizing().borrow_mut();
depth = match monomorphizing.find(&fn_id) {
Some(&d) => d, None => 0
};
@ -117,7 +119,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
// recursively more than thirty times can probably safely be assumed
// to be causing an infinite expansion.
if depth > ccx.sess().recursion_limit.get() {
ccx.sess().span_fatal(ccx.tcx.map.span(fn_id.node),
ccx.sess().span_fatal(ccx.tcx().map.span(fn_id.node),
"reached the recursion limit during monomorphization");
}
@ -131,7 +133,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
mono_ty.hash(&mut state);
hash = format!("h{}", state.result());
ccx.tcx.map.with_path(fn_id.node, |path| {
ccx.tcx().map.with_path(fn_id.node, |path| {
exported_name(path, hash.as_slice())
})
};
@ -147,9 +149,28 @@ pub fn monomorphic_fn(ccx: &CrateContext,
decl_internal_rust_fn(ccx, mono_ty, s.as_slice())
};
ccx.monomorphized.borrow_mut().insert(hash_id.take().unwrap(), lldecl);
ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl);
lldecl
};
let setup_lldecl = |lldecl, attrs: &[ast::Attribute]| {
base::update_linkage(ccx, lldecl, None, base::OriginalTranslation);
set_llvm_fn_attrs(attrs, lldecl);
let is_first = !ccx.available_monomorphizations().borrow().contains(&s);
if is_first {
ccx.available_monomorphizations().borrow_mut().insert(s.clone());
}
let trans_everywhere = attr::requests_inline(attrs);
if trans_everywhere && !is_first {
llvm::SetLinkage(lldecl, llvm::AvailableExternallyLinkage);
}
// If `true`, then `lldecl` should be given a function body.
// Otherwise, it should be left as a declaration of an external
// function, with no definition in the current compilation unit.
trans_everywhere || is_first
};
let lldecl = match map_node {
ast_map::NodeItem(i) => {
@ -159,14 +180,15 @@ pub fn monomorphic_fn(ccx: &CrateContext,
..
} => {
let d = mk_lldecl(abi);
set_llvm_fn_attrs(i.attrs.as_slice(), d);
if abi != abi::Rust {
foreign::trans_rust_fn_with_foreign_abi(
ccx, &**decl, &**body, [], d, &psubsts, fn_id.node,
Some(hash.as_slice()));
} else {
trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, []);
let needs_body = setup_lldecl(d, i.attrs.as_slice());
if needs_body {
if abi != abi::Rust {
foreign::trans_rust_fn_with_foreign_abi(
ccx, &**decl, &**body, [], d, &psubsts, fn_id.node,
Some(hash.as_slice()));
} else {
trans_fn(ccx, &**decl, &**body, d, &psubsts, fn_id.node, []);
}
}
d
@ -177,7 +199,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
}
}
ast_map::NodeVariant(v) => {
let parent = ccx.tcx.map.get_parent(fn_id.node);
let parent = ccx.tcx().map.get_parent(fn_id.node);
let tvs = ty::enum_variants(ccx.tcx(), local_def(parent));
let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap();
let d = mk_lldecl(abi::Rust);
@ -201,14 +223,16 @@ pub fn monomorphic_fn(ccx: &CrateContext,
match *ii {
ast::MethodImplItem(mth) => {
let d = mk_lldecl(abi::Rust);
set_llvm_fn_attrs(mth.attrs.as_slice(), d);
trans_fn(ccx,
&*mth.pe_fn_decl(),
&*mth.pe_body(),
d,
&psubsts,
mth.id,
[]);
let needs_body = setup_lldecl(d, mth.attrs.as_slice());
if needs_body {
trans_fn(ccx,
&*mth.pe_fn_decl(),
&*mth.pe_body(),
d,
&psubsts,
mth.id,
[]);
}
d
}
}
@ -217,9 +241,11 @@ pub fn monomorphic_fn(ccx: &CrateContext,
match *method {
ast::ProvidedMethod(mth) => {
let d = mk_lldecl(abi::Rust);
set_llvm_fn_attrs(mth.attrs.as_slice(), d);
trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d,
&psubsts, mth.id, []);
let needs_body = setup_lldecl(d, mth.attrs.as_slice());
if needs_body {
trans_fn(ccx, &*mth.pe_fn_decl(), &*mth.pe_body(), d,
&psubsts, mth.id, []);
}
d
}
_ => {
@ -254,7 +280,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
}
};
ccx.monomorphizing.borrow_mut().insert(fn_id, depth);
ccx.monomorphizing().borrow_mut().insert(fn_id, depth);
debug!("leaving monomorphic fn {}", ty::item_path_str(ccx.tcx(), fn_id));
(lldecl, true)

View File

@ -335,7 +335,7 @@ impl<'a, 'b> Reflector<'a, 'b> {
let sym = mangle_internal_name_by_path_and_seq(
ast_map::Values([].iter()).chain(None), "get_disr");
let fn_ty = ty::mk_ctor_fn(&ccx.tcx, ast::DUMMY_NODE_ID,
let fn_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID,
[opaqueptrty], ty::mk_u64());
let llfdecl = decl_internal_rust_fn(ccx,
fn_ty,

View File

@ -94,8 +94,8 @@ impl VecTypes {
format!("VecTypes {{unit_ty={}, llunit_ty={}, \
llunit_size={}, llunit_alloc_size={}}}",
ty_to_string(ccx.tcx(), self.unit_ty),
ccx.tn.type_to_string(self.llunit_ty),
ccx.tn.val_to_string(self.llunit_size),
ccx.tn().type_to_string(self.llunit_ty),
ccx.tn().val_to_string(self.llunit_size),
self.llunit_alloc_size)
}
}
@ -546,7 +546,7 @@ pub fn iter_vec_loop<'r,
let loop_counter = {
// i = 0
let i = alloca(loop_bcx, bcx.ccx().int_type, "__i");
let i = alloca(loop_bcx, bcx.ccx().int_type(), "__i");
Store(loop_bcx, C_uint(bcx.ccx(), 0), i);
Br(loop_bcx, cond_bcx.llbb);

View File

@ -53,7 +53,7 @@ impl Type {
}
pub fn void(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMVoidTypeInContext(ccx.llcx))
ty!(llvm::LLVMVoidTypeInContext(ccx.llcx()))
}
pub fn nil(ccx: &CrateContext) -> Type {
@ -61,35 +61,35 @@ impl Type {
}
pub fn metadata(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx))
ty!(llvm::LLVMMetadataTypeInContext(ccx.llcx()))
}
pub fn i1(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMInt1TypeInContext(ccx.llcx))
ty!(llvm::LLVMInt1TypeInContext(ccx.llcx()))
}
pub fn i8(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMInt8TypeInContext(ccx.llcx))
ty!(llvm::LLVMInt8TypeInContext(ccx.llcx()))
}
pub fn i16(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMInt16TypeInContext(ccx.llcx))
ty!(llvm::LLVMInt16TypeInContext(ccx.llcx()))
}
pub fn i32(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMInt32TypeInContext(ccx.llcx))
ty!(llvm::LLVMInt32TypeInContext(ccx.llcx()))
}
pub fn i64(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMInt64TypeInContext(ccx.llcx))
ty!(llvm::LLVMInt64TypeInContext(ccx.llcx()))
}
pub fn f32(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMFloatTypeInContext(ccx.llcx))
ty!(llvm::LLVMFloatTypeInContext(ccx.llcx()))
}
pub fn f64(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx))
ty!(llvm::LLVMDoubleTypeInContext(ccx.llcx()))
}
pub fn bool(ccx: &CrateContext) -> Type {
@ -105,7 +105,7 @@ impl Type {
}
pub fn int(ccx: &CrateContext) -> Type {
match ccx.tcx.sess.targ_cfg.arch {
match ccx.tcx().sess.targ_cfg.arch {
X86 | Arm | Mips | Mipsel => Type::i32(ccx),
X86_64 => Type::i64(ccx)
}
@ -113,7 +113,7 @@ impl Type {
pub fn int_from_ty(ccx: &CrateContext, t: ast::IntTy) -> Type {
match t {
ast::TyI => ccx.int_type,
ast::TyI => ccx.int_type(),
ast::TyI8 => Type::i8(ccx),
ast::TyI16 => Type::i16(ccx),
ast::TyI32 => Type::i32(ccx),
@ -123,7 +123,7 @@ impl Type {
pub fn uint_from_ty(ccx: &CrateContext, t: ast::UintTy) -> Type {
match t {
ast::TyU => ccx.int_type,
ast::TyU => ccx.int_type(),
ast::TyU8 => Type::i8(ccx),
ast::TyU16 => Type::i16(ccx),
ast::TyU32 => Type::i32(ccx),
@ -152,13 +152,13 @@ impl Type {
pub fn struct_(ccx: &CrateContext, els: &[Type], packed: bool) -> Type {
let els : &[TypeRef] = unsafe { mem::transmute(els) };
ty!(llvm::LLVMStructTypeInContext(ccx.llcx, els.as_ptr(),
ty!(llvm::LLVMStructTypeInContext(ccx.llcx(), els.as_ptr(),
els.len() as c_uint,
packed as Bool))
}
pub fn named_struct(ccx: &CrateContext, name: &str) -> Type {
ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ccx.llcx, s)))
ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ccx.llcx(), s)))
}
pub fn empty_struct(ccx: &CrateContext) -> Type {
@ -170,13 +170,13 @@ impl Type {
}
pub fn generic_glue_fn(cx: &CrateContext) -> Type {
match cx.tn.find_type("glue_fn") {
match cx.tn().find_type("glue_fn") {
Some(ty) => return ty,
None => ()
}
let ty = Type::glue_fn(cx, Type::i8p(cx));
cx.tn.associate_type("glue_fn", &ty);
cx.tn().associate_type("glue_fn", &ty);
ty
}
@ -226,7 +226,7 @@ impl Type {
// The box pointed to by @T.
pub fn at_box(ccx: &CrateContext, ty: Type) -> Type {
Type::struct_(ccx, [
ccx.int_type, Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(),
ccx.int_type(), Type::glue_fn(ccx, Type::i8p(ccx)).ptr_to(),
Type::i8p(ccx), Type::i8p(ccx), ty
], false)
}

View File

@ -156,7 +156,7 @@ pub fn type_of_fn_from_ty(cx: &CrateContext, fty: ty::t) -> Type {
// recursive types. For example, enum types rely on this behavior.
pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type {
match cx.llsizingtypes.borrow().find_copy(&t) {
match cx.llsizingtypes().borrow().find_copy(&t) {
Some(t) => return t,
None => ()
}
@ -217,7 +217,7 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type {
ty::ty_vec(_, None) | ty::ty_trait(..) | ty::ty_str => fail!("unreachable")
};
cx.llsizingtypes.borrow_mut().insert(t, llsizingty);
cx.llsizingtypes().borrow_mut().insert(t, llsizingty);
llsizingty
}
@ -249,7 +249,7 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
}
// Check the cache.
match cx.lltypes.borrow().find(&t) {
match cx.lltypes().borrow().find(&t) {
Some(&llty) => return llty,
None => ()
}
@ -270,8 +270,8 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
t,
t_norm.repr(cx.tcx()),
t_norm,
cx.tn.type_to_string(llty));
cx.lltypes.borrow_mut().insert(t, llty);
cx.tn().type_to_string(llty));
cx.lltypes().borrow_mut().insert(t, llty);
return llty;
}
@ -308,7 +308,7 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
ty::ty_str => {
// This means we get a nicer name in the output (str is always
// unsized).
cx.tn.find_type("str_slice").unwrap()
cx.tn().find_type("str_slice").unwrap()
}
ty::ty_trait(..) => Type::opaque_trait(cx),
_ if !ty::type_is_sized(cx.tcx(), ty) => {
@ -385,9 +385,9 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
debug!("--> mapped t={} {:?} to llty={}",
t.repr(cx.tcx()),
t,
cx.tn.type_to_string(llty));
cx.tn().type_to_string(llty));
cx.lltypes.borrow_mut().insert(t, llty);
cx.lltypes().borrow_mut().insert(t, llty);
// If this was an enum or struct, fill in the type now.
match ty::get(t).sty {

View File

@ -331,7 +331,7 @@ pub enum AsmDialect {
AD_Intel = 1
}
#[deriving(PartialEq)]
#[deriving(PartialEq, Clone)]
#[repr(C)]
pub enum CodeGenOptLevel {
CodeGenLevelNone = 0,

View File

@ -20,7 +20,7 @@ use std::string::String;
use std::collections::{HashSet, HashMap};
use testing;
use rustc::back::link;
use rustc::back::write;
use rustc::driver::config;
use rustc::driver::driver;
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()),
addl_lib_search_paths: RefCell::new(libs),
crate_types: vec!(config::CrateTypeExecutable),
output_types: vec!(link::OutputTypeExe),
output_types: vec!(write::OutputTypeExe),
no_trans: no_run,
externs: externs,
cg: config::CodegenOptions {

View File

@ -280,7 +280,7 @@ pub enum InlineAttr {
InlineNever,
}
/// True if something like #[inline] is found in the list of attrs.
/// Determine what `#[inline]` attribute is present in `attrs`, if any.
pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr {
// FIXME (#2809)---validate the usage of #[inline] and #[inline]
attrs.iter().fold(InlineNone, |ia,attr| {
@ -304,6 +304,14 @@ pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr {
})
}
/// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
pub fn requests_inline(attrs: &[Attribute]) -> bool {
match find_inline_attr(attrs) {
InlineHint | InlineAlways => true,
InlineNone | InlineNever => false,
}
}
/// Tests if any `cfg(...)` meta items in `metas` match `cfg`. e.g.
///
/// test_cfg(`[foo="a", bar]`, `[cfg(foo), cfg(bar)]`) == true

View File

@ -0,0 +1,14 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[no_mangle]
pub extern "C" fn foo() -> uint {
1234
}

View File

@ -0,0 +1,17 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[inline]
pub fn cci_fn() -> uint {
1200
}
#[inline]
pub static CCI_STATIC: uint = 34;

View File

@ -0,0 +1,31 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C codegen-units=3 --crate-type=rlib,dylib
pub mod a {
pub fn one() -> uint {
1
}
}
pub mod b {
pub fn two() -> uint {
2
}
}
pub mod c {
use a::one;
use b::two;
pub fn three() -> uint {
one() + two()
}
}

View File

@ -0,0 +1,28 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Make sure we give a sane error message when the user requests LTO with a
// library built with -C codegen-units > 1.
// aux-build:sepcomp_lib.rs
// compile-flags: -Z lto
// error-pattern:missing compressed bytecode
// no-prefer-dynamic
extern crate sepcomp_lib;
use sepcomp_lib::a::one;
use sepcomp_lib::b::two;
use sepcomp_lib::c::three;
fn main() {
assert_eq!(one(), 1);
assert_eq!(two(), 2);
assert_eq!(three(), 3);
}

View File

@ -5,40 +5,69 @@ all:
$(call REMOVE_RLIBS,bar)
$(call REMOVE_DYLIBS,bar)
rm $(TMPDIR)/$(call STATICLIB_GLOB,bar)
# Check that $(TMPDIR) is empty.
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=bin
rm $(TMPDIR)/$(call BIN,bar)
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=asm,ir,bc,obj,link
rm $(TMPDIR)/bar.ll
rm $(TMPDIR)/bar.bc
rm $(TMPDIR)/bar.s
rm $(TMPDIR)/bar.o
rm $(TMPDIR)/$(call BIN,bar)
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=asm -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=bc -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=ir -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=obj -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=link -o $(TMPDIR)/foo
rm $(TMPDIR)/$(call BIN,foo)
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=rlib -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=dylib -o $(TMPDIR)/foo
rm $(TMPDIR)/$(call BIN,foo) # FIXME 13794
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=staticlib -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=bin -o $(TMPDIR)/foo
rm $(TMPDIR)/$(call BIN,foo)
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=asm,ir,bc,obj,link --crate-type=staticlib
rm $(TMPDIR)/bar.ll
rm $(TMPDIR)/bar.s
rm $(TMPDIR)/bar.o
rm $(TMPDIR)/$(call STATICLIB_GLOB,bar)
$(RUSTC) foo.rs --emit=asm -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit=bc -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit=ir -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit=obj -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit=link -o $(TMPDIR)/foo
rm $(TMPDIR)/$(call BIN,foo)
$(RUSTC) foo.rs --crate-type=rlib -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --crate-type=dylib -o $(TMPDIR)/foo
rm $(TMPDIR)/$(call BIN,foo) # FIXME 13794
$(RUSTC) foo.rs --crate-type=staticlib -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --crate-type=bin -o $(TMPDIR)/foo
rm $(TMPDIR)/$(call BIN,foo)
mv $(TMPDIR)/bar.bc $(TMPDIR)/foo.bc
# Don't check that the $(TMPDIR) is empty - we left `foo.bc` for later
# comparison.
$(RUSTC) foo.rs --emit=bc,link --crate-type=rlib
cmp $(TMPDIR)/foo.bc $(TMPDIR)/bar.bc
rm $(TMPDIR)/bar.bc
rm $(TMPDIR)/foo.bc
$(call REMOVE_RLIBS,bar)
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]

View File

@ -0,0 +1,10 @@
-include ../tools.mk
# Check that cross-crate inlined items are inlined in all compilation units
# that refer to them, and not in any other compilation units.
all:
$(RUSTC) cci_lib.rs
$(RUSTC) foo.rs --emit=ir -C codegen-units=3
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*cci_fn)" -eq "2" ]
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c CCI_STATIC.*=.*constant)" -eq "2" ]

View File

@ -0,0 +1,19 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rlib"]
#[inline]
pub fn cci_fn() -> uint {
1234
}
#[inline]
pub static CCI_STATIC: uint = 2345;

View File

@ -0,0 +1,36 @@
// Copyright 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.
extern crate cci_lib;
use cci_lib::{cci_fn, CCI_STATIC};
fn call1() -> uint {
cci_fn() + CCI_STATIC
}
mod a {
use cci_lib::cci_fn;
pub fn call2() -> uint {
cci_fn()
}
}
mod b {
use cci_lib::CCI_STATIC;
pub fn call3() -> uint {
CCI_STATIC
}
}
fn main() {
call1();
a::call2();
b::call3();
}

View File

@ -0,0 +1,13 @@
-include ../tools.mk
# Test that #[inline(always)] functions still get inlined across compilation
# unit boundaries. Compilation should produce three IR files, with each one
# containing a definition of the inlined function. Also, the non-#[inline]
# function should be defined in only one compilation unit.
all:
$(RUSTC) foo.rs --emit=ir -C codegen-units=3
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*inlined)" -eq "1" ]
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ available_externally\ i32\ .*inlined)" -eq "2" ]
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*normal)" -eq "1" ]
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ i32\ .*normal)" -eq "2" ]

View File

@ -0,0 +1,35 @@
// Copyright 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.
#[inline]
fn inlined() -> u32 {
1234
}
fn normal() -> u32 {
2345
}
mod a {
pub fn f() -> u32 {
::inlined() + ::normal()
}
}
mod b {
pub fn f() -> u32 {
::inlined() + ::normal()
}
}
fn main() {
a::f();
b::f();
}

View File

@ -0,0 +1,9 @@
-include ../tools.mk
# Test that separate compilation actually puts code into separate compilation
# units. `foo.rs` defines `magic_fn` in three different modules, which should
# wind up in three different compilation units.
all:
$(RUSTC) foo.rs --emit=ir -C codegen-units=3
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*magic_fn)" -eq "3" ]

View File

@ -0,0 +1,27 @@
// Copyright 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.
fn magic_fn() -> uint {
1234
}
mod a {
pub fn magic_fn() -> uint {
2345
}
}
mod b {
pub fn magic_fn() -> uint {
3456
}
}
fn main() { }

View File

@ -0,0 +1,41 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C codegen-units=3
// aux-build:sepcomp_cci_lib.rs
// Test accessing cross-crate inlined items from multiple compilation units.
extern crate sepcomp_cci_lib;
use sepcomp_cci_lib::{cci_fn, CCI_STATIC};
fn call1() -> uint {
cci_fn() + CCI_STATIC
}
mod a {
use sepcomp_cci_lib::{cci_fn, CCI_STATIC};
pub fn call2() -> uint {
cci_fn() + CCI_STATIC
}
}
mod b {
use sepcomp_cci_lib::{cci_fn, CCI_STATIC};
pub fn call3() -> uint {
cci_fn() + CCI_STATIC
}
}
fn main() {
assert_eq!(call1(), 1234);
assert_eq!(a::call2(), 1234);
assert_eq!(b::call3(), 1234);
}

View File

@ -0,0 +1,42 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C codegen-units=3
// aux-build:sepcomp-extern-lib.rs
// Test accessing external items from multiple compilation units.
#[link(name = "sepcomp-extern-lib")]
extern {
#[allow(ctypes)]
fn foo() -> uint;
}
fn call1() -> uint {
unsafe { foo() }
}
mod a {
pub fn call2() -> uint {
unsafe { ::foo() }
}
}
mod b {
pub fn call3() -> uint {
unsafe { ::foo() }
}
}
fn main() {
assert_eq!(call1(), 1234);
assert_eq!(a::call2(), 1234);
assert_eq!(b::call3(), 1234);
}

View File

@ -0,0 +1,41 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C codegen-units=3
// Test references to items that haven't been translated yet.
// Generate some code in the first compilation unit before declaring any
// modules. This ensures that the first module doesn't go into the same
// compilation unit as the top-level module.
fn pad() -> uint { 0 }
mod b {
pub fn three() -> uint {
::one() + ::a::two()
}
}
mod a {
pub fn two() -> uint {
::one() + ::one()
}
}
fn one() -> uint {
1
}
fn main() {
assert_eq!(one(), 1);
assert_eq!(a::two(), 2);
assert_eq!(b::three(), 3);
}

View File

@ -0,0 +1,38 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C codegen-units=3
// Test basic separate compilation functionality. The functions should be able
// to call each other even though they will be placed in different compilation
// units.
// Generate some code in the first compilation unit before declaring any
// modules. This ensures that the first module doesn't go into the same
// compilation unit as the top-level module.
fn one() -> uint { 1 }
mod a {
pub fn two() -> uint {
::one() + ::one()
}
}
mod b {
pub fn three() -> uint {
::one() + ::a::two()
}
}
fn main() {
assert_eq!(one(), 1);
assert_eq!(a::two(), 2);
assert_eq!(b::three(), 3);
}

View File

@ -0,0 +1,24 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:sepcomp_lib.rs
// Test linking against a library built with -C codegen-units > 1
extern crate sepcomp_lib;
use sepcomp_lib::a::one;
use sepcomp_lib::b::two;
use sepcomp_lib::c::three;
fn main() {
assert_eq!(one(), 1);
assert_eq!(two(), 2);
assert_eq!(three(), 3);
}

View File

@ -0,0 +1,39 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C codegen-units=3
// Test references to static items across compilation units.
fn pad() -> uint { 0 }
static ONE: uint = 1;
mod b {
// Separate compilation always switches to the LLVM module with the fewest
// instructions. Make sure we have some instructions in this module so
// that `a` and `b` don't go into the same compilation unit.
fn pad() -> uint { 0 }
pub static THREE: uint = ::ONE + ::a::TWO;
}
mod a {
fn pad() -> uint { 0 }
pub static TWO: uint = ::ONE + ::ONE;
}
fn main() {
assert_eq!(ONE, 1);
assert_eq!(a::TWO, 2);
assert_eq!(b::THREE, 3);
}

View File

@ -0,0 +1,38 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -C codegen-units=3
// Test unwinding through multiple compilation units.
// According to acrichto, in the distant past `ld -r` (which is used during
// linking when codegen-units > 1) was known to produce object files with
// damaged unwinding tables. This may be related to GNU binutils bug #6893
// ("Partial linking results in corrupt .eh_frame_hdr"), but I'm not certain.
// In any case, this test should let us know if enabling parallel codegen ever
// breaks unwinding.
fn pad() -> uint { 0 }
mod a {
pub fn f() {
fail!();
}
}
mod b {
pub fn g() {
::a::f();
}
}
fn main() {
std::task::try(proc() { ::b::g() }).unwrap_err();
}