diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index ee787a5f0a9bf..32671722ba728 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -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) } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 4e4a28cc538ce..5adf8b653813d 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -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"); - - 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()) - }; + 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_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() - } - }; + 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); + 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); + } } } diff --git a/src/librustc/back/lto.rs b/src/librustc/back/lto.rs index 6c6a07f35029f..d7f183faa0192 100644 --- a/src/librustc/back/lto.rs +++ b/src/librustc/back/lto.rs @@ -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())); } }); } diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs new file mode 100644 index 0000000000000..627d455f06e11 --- /dev/null +++ b/src/librustc/back/write.rs @@ -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 or the MIT license +// , 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, + 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>>, +} + +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, + /// Some(level) to optimize at a certain level, or None to run + /// absolutely no optimizations (used for the metadata module). + opt_level: Option, + + // 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) -> 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) { + 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, + 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)); + } + _ => {} + }; +} diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index c402c256a3719..3e3a88ceffd67 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -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 , + pub output_types: Vec , // 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)); diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 3d0678aa0e7ac..09bf69bff4cdd 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -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, + pub metadata_module: ModuleTranslation, pub link: LinkMeta, pub metadata: Vec, pub reachable: Vec, @@ -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, } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 03dfcec18dbdb..e4eac80c4afd9 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -69,6 +69,7 @@ pub mod back { pub mod link; pub mod lto; + pub mod write; } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 1386e23b77dfc..da73f25b6d164 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -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 { diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 040577e7048e1..1b93e1974419c 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -563,7 +563,7 @@ fn get_branches<'a>(bcx: &'a Block, m: &[Match], col: uint) -> Vec> { } 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); diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs index 8a5581e592ae3..21673a67ea8ac 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -150,14 +150,14 @@ pub fn represent_node(bcx: &Block, node: ast::NodeId) -> Rc { /// Decides how to represent a given type. pub fn represent_type(cx: &CrateContext, t: ty::t) -> Rc { 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() { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 76e2266fcb92f..49e058333e523 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -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, 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 { - 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: ¶m_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, + 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, - ¶m_substs::empty(), - item.id, - None); - } else { - trans_fn(ccx, - &**decl, - &**body, - llfn, - ¶m_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, + ¶m_substs::empty(), + item.id, + None); + } else { + trans_fn(ccx, + &**decl, + &**body, + llfn, + ¶m_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 { +pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { 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 { 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 { 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) { + 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 for ValueIter { + fn next(&mut self) -> Option { + 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 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 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 _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() { + let metadata = write_metadata(&shared_ccx, &krate); + + if shared_ccx.sess().trans_stats() { + let stats = shared_ccx.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()); - - 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()); + 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 = ccx.reachable.iter().filter_map(|id| { - ccx.item_symbols.borrow().find(id).map(|s| s.to_string()) + let mut reachable: Vec = 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) } diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index 76059e8df4f8c..fd988eb7fc137 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -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) } } diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index b3192a405be59..322a6a3cc909e 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -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::>() .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::>(); - 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::>() .connect(", ")); diff --git a/src/librustc/middle/trans/cabi_mips.rs b/src/librustc/middle/trans/cabi_mips.rs index 77815285428fd..90bd1521705f0 100644 --- a/src/librustc/middle/trans/cabi_mips.rs +++ b/src/librustc/middle/trans/cabi_mips.rs @@ -146,7 +146,7 @@ fn coerce_to_int(ccx: &CrateContext, size: uint) -> Vec { 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))); } } diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs index 515413e03f060..15d296ac72371 100644 --- a/src/librustc/middle/trans/cleanup.rs +++ b/src/librustc/middle/trans/cleanup.rs @@ -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 => { diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index 17f1b6ca52669..4e9431f262911 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -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 { - 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 { diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index d92364b257010..4ea60a4e128ad 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -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>, // 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; } diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 10b6adad1e33d..bd5132ea42736 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -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); } diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index bb528790314da..5bdd5f6739d61 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -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 >, } -pub struct CrateContext { - pub llmod: ModuleRef, - pub llcx: ContextRef, - pub metadata_llmod: ModuleRef, - pub td: TargetData, - pub tn: TypeNames, - pub externs: RefCell, - pub item_vals: RefCell>, - pub exp_map2: resolve::ExportMap2, - pub reachable: NodeSet, - pub item_symbols: RefCell>, - pub link_meta: LinkMeta, - pub drop_glues: RefCell>, - pub tydescs: RefCell>>, +/// 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, + + metadata_llmod: ModuleRef, + metadata_llcx: ContextRef, + + exp_map2: resolve::ExportMap2, + reachable: NodeSet, + item_symbols: RefCell>, + 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 + non_inlineable_statics: RefCell, + symbol_hasher: RefCell, + tcx: ty::ctxt, + stats: Stats, + + available_monomorphizations: RefCell>, + available_drop_glues: RefCell>, + available_visit_glues: RefCell>, +} + +/// 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, + item_vals: RefCell>, + drop_glues: RefCell>, + tydescs: RefCell>>, /// Set when running emit_tydescs to enforce that no more tydescs are /// created. - pub finished_tydescs: Cell, + finished_tydescs: Cell, /// Track mapping of external ids to local items imported for inlining - pub external: RefCell>>, + external: RefCell>>, /// Backwards version of the `external` map (inlined items to where they /// came from) - pub external_srcs: RefCell>, - /// 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, + external_srcs: RefCell>, /// Cache instances of monomorphized functions - pub monomorphized: RefCell>, - pub monomorphizing: RefCell>, + monomorphized: RefCell>, + monomorphizing: RefCell>, /// Cache generated vtables - pub vtables: RefCell>, + vtables: RefCell>, /// Cache of constant strings, - pub const_cstr_cache: RefCell>, + const_cstr_cache: RefCell>, /// 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>, + const_globals: RefCell>, /// Cache of emitted const values - pub const_values: RefCell>, + const_values: RefCell>, /// Cache of external const values - pub extern_const_values: RefCell>, + extern_const_values: RefCell>, - pub impl_method_cache: RefCell>, + impl_method_cache: RefCell>, /// Cache of closure wrappers for bare fn's. - pub closure_bare_wrapper_cache: RefCell>, - - pub lltypes: RefCell>, - pub llsizingtypes: RefCell>, - pub adt_reprs: RefCell>>, - pub symbol_hasher: RefCell, - pub type_hashcodes: RefCell>, - pub all_llvm_symbols: RefCell>, - pub tcx: ty::ctxt, - pub stats: Stats, - pub int_type: Type, - pub opaque_vec_type: Type, - pub builder: BuilderRef_res, + closure_bare_wrapper_cache: RefCell>, + + lltypes: RefCell>, + llsizingtypes: RefCell>, + adt_reprs: RefCell>>, + type_hashcodes: RefCell>, + all_llvm_symbols: RefCell>, + int_type: Type, + opaque_vec_type: Type, + builder: BuilderRef_res, /// Holds the LLVM values for closure IDs. - pub unboxed_closure_vals: RefCell>, + unboxed_closure_vals: RefCell>, - pub dbg_cx: Option, + dbg_cx: Option, - pub eh_personality: RefCell>, + eh_personality: RefCell>, intrinsics: RefCell>, + + /// 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, +} + +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> for CrateContextIterator<'a> { + fn next(&mut self) -> Option> { + 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 CrateContext { - pub fn new(name: &str, +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> { + &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 { + &self.non_inlineable_statics + } + + pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { + &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 td = mk_target_data(tcx.sess - .targ_cfg - .target_strs - .data_layout - .as_slice()); - - let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo { + let (llcx, llmod) = create_context_and_module(&shared.tcx.sess, name); + + let td = mk_target_data(shared.tcx + .sess + .targ_cfg + .target_strs + .data_layout + .as_slice()); + + 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 { + &self.local.externs + } + + pub fn item_vals<'a>(&'a self) -> &'a RefCell> { + &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> { + &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> { + &self.local.drop_glues + } + + pub fn tydescs<'a>(&'a self) -> &'a RefCell>> { + &self.local.tydescs + } + + pub fn finished_tydescs<'a>(&'a self) -> &'a Cell { + &self.local.finished_tydescs + } + + pub fn external<'a>(&'a self) -> &'a RefCell>> { + &self.local.external + } + + pub fn external_srcs<'a>(&'a self) -> &'a RefCell> { + &self.local.external_srcs + } + + pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell { + &self.shared.non_inlineable_statics + } + + pub fn monomorphized<'a>(&'a self) -> &'a RefCell> { + &self.local.monomorphized + } + + pub fn monomorphizing<'a>(&'a self) -> &'a RefCell> { + &self.local.monomorphizing + } + + pub fn vtables<'a>(&'a self) -> &'a RefCell> { + &self.local.vtables + } + + pub fn const_cstr_cache<'a>(&'a self) -> &'a RefCell> { + &self.local.const_cstr_cache + } + + pub fn const_globals<'a>(&'a self) -> &'a RefCell> { + &self.local.const_globals + } + + pub fn const_values<'a>(&'a self) -> &'a RefCell> { + &self.local.const_values + } + + pub fn extern_const_values<'a>(&'a self) -> &'a RefCell> { + &self.local.extern_const_values + } + + pub fn impl_method_cache<'a>(&'a self) + -> &'a RefCell> { + &self.local.impl_method_cache + } + + pub fn closure_bare_wrapper_cache<'a>(&'a self) -> &'a RefCell> { + &self.local.closure_bare_wrapper_cache + } + + pub fn lltypes<'a>(&'a self) -> &'a RefCell> { + &self.local.lltypes + } + + pub fn llsizingtypes<'a>(&'a self) -> &'a RefCell> { + &self.local.llsizingtypes + } + + pub fn adt_reprs<'a>(&'a self) -> &'a RefCell>> { + &self.local.adt_reprs + } + + pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { + &self.shared.symbol_hasher + } + + pub fn type_hashcodes<'a>(&'a self) -> &'a RefCell> { + &self.local.type_hashcodes + } + + pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell> { + &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> { + &self.shared.available_monomorphizations + } + + pub fn available_drop_glues<'a>(&'a self) -> &'a RefCell> { + &self.shared.available_drop_glues + } + + pub fn available_visit_glues<'a>(&'a self) -> &'a RefCell> { + &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> { + &self.local.unboxed_closure_vals + } + + pub fn dbg_cx<'a>(&'a self) -> &'a Option { + &self.local.dbg_cx + } + + pub fn eh_personality<'a>(&'a self) -> &'a RefCell> { + &self.local.eh_personality + } + + fn intrinsics<'a>(&'a self) -> &'a RefCell> { + &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 { @@ -293,7 +719,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option $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 Option Datum { #[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) } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 8403e84f7b655..01633bea9565c 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -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) { - 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 , scope_map: &mut HashMap) { - 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 { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 4fe687da4b194..61c27292a3767 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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) => { diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index fe9b593c11c7e..8ed45f89c29e4 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -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(), ¶m_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::>().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::>().as_slice()), + ccx.tn().type_to_string(fn_ty.ret_ty.ty), ret_def); ForeignTypes { diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 51cd9a115cd7c..c8a47532a923a 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -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()) } diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index 4b1f37fcdc22a..0713b2b535c07 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -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 { + 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, _) => { - 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); + 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) ¶m_substs::empty(), mth.id, []); + // Use InternalLinkage so LLVM can optimize more + // aggressively. + SetLinkage(llfn, InternalLinkage); } local_def(mth.id) } diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 3c61708fb7b83..f10df00ca9184 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -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" diff --git a/src/librustc/middle/trans/llrepr.rs b/src/librustc/middle/trans/llrepr.rs index 2740e5695be10..5aec1cfbf268a 100644 --- a/src/librustc/middle/trans/llrepr.rs +++ b/src/librustc/middle/trans/llrepr.rs @@ -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) } } diff --git a/src/librustc/middle/trans/machine.rs b/src/librustc/middle/trans/machine.rs index 15bbc5ae84589..938cbc2176468 100644 --- a/src/librustc/middle/trans/machine.rs +++ b/src/librustc/middle/trans/machine.rs @@ -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; } } diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 83bdcc9dead64..c002f3e72c89f 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -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, - ¶m_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, + ¶m_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>(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) diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index f2a7f1dc4f8ee..1cf3e55967d4e 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -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) diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index a49dc1b915028..1fcf4c189129b 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -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, diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs index a6ba758d5f810..3701d83d6a122 100644 --- a/src/librustc/middle/trans/tvec.rs +++ b/src/librustc/middle/trans/tvec.rs @@ -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); diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs index 7b98d65a3105b..3df1ce32fc7d7 100644 --- a/src/librustc/middle/trans/type_.rs +++ b/src/librustc/middle/trans/type_.rs @@ -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) } diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index ab7e71c41d368..54f24516867f0 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -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 { diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 12d10ec66302a..ceb29ddcf8fe5 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -331,7 +331,7 @@ pub enum AsmDialect { AD_Intel = 1 } -#[deriving(PartialEq)] +#[deriving(PartialEq, Clone)] #[repr(C)] pub enum CodeGenOptLevel { CodeGenLevelNone = 0, diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 9df748e74e8ba..d2345614b25c2 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -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, 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 { diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index c234bea0a331e..dd422d021493f 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -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 diff --git a/src/test/auxiliary/sepcomp-extern-lib.rs b/src/test/auxiliary/sepcomp-extern-lib.rs new file mode 100644 index 0000000000000..8f5d3b5768a1a --- /dev/null +++ b/src/test/auxiliary/sepcomp-extern-lib.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/auxiliary/sepcomp_cci_lib.rs b/src/test/auxiliary/sepcomp_cci_lib.rs new file mode 100644 index 0000000000000..1cb7ead2cff05 --- /dev/null +++ b/src/test/auxiliary/sepcomp_cci_lib.rs @@ -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 or the MIT license +// , 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; diff --git a/src/test/auxiliary/sepcomp_lib.rs b/src/test/auxiliary/sepcomp_lib.rs new file mode 100644 index 0000000000000..d1d9e3b8ff3ac --- /dev/null +++ b/src/test/auxiliary/sepcomp_lib.rs @@ -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 or the MIT license +// , 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() + } +} diff --git a/src/test/compile-fail/sepcomp-lib-lto.rs b/src/test/compile-fail/sepcomp-lib-lto.rs new file mode 100644 index 0000000000000..59706e20bede2 --- /dev/null +++ b/src/test/compile-fail/sepcomp-lib-lto.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-make/output-type-permutations/Makefile b/src/test/run-make/output-type-permutations/Makefile index c163a5bec086f..fed071d1a43c2 100644 --- a/src/test/run-make/output-type-permutations/Makefile +++ b/src/test/run-make/output-type-permutations/Makefile @@ -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) - $(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) + [ "$$(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) 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" ] diff --git a/src/test/run-make/sepcomp-cci-copies/Makefile b/src/test/run-make/sepcomp-cci-copies/Makefile new file mode 100644 index 0000000000000..fdb39f851970f --- /dev/null +++ b/src/test/run-make/sepcomp-cci-copies/Makefile @@ -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" ] diff --git a/src/test/run-make/sepcomp-cci-copies/cci_lib.rs b/src/test/run-make/sepcomp-cci-copies/cci_lib.rs new file mode 100644 index 0000000000000..099101d6f2679 --- /dev/null +++ b/src/test/run-make/sepcomp-cci-copies/cci_lib.rs @@ -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 or the MIT license +// , 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; diff --git a/src/test/run-make/sepcomp-cci-copies/foo.rs b/src/test/run-make/sepcomp-cci-copies/foo.rs new file mode 100644 index 0000000000000..c702e578c0965 --- /dev/null +++ b/src/test/run-make/sepcomp-cci-copies/foo.rs @@ -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 or the MIT license +// , 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(); +} diff --git a/src/test/run-make/sepcomp-inlining/Makefile b/src/test/run-make/sepcomp-inlining/Makefile new file mode 100644 index 0000000000000..6cb9f9a3f31bc --- /dev/null +++ b/src/test/run-make/sepcomp-inlining/Makefile @@ -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" ] diff --git a/src/test/run-make/sepcomp-inlining/foo.rs b/src/test/run-make/sepcomp-inlining/foo.rs new file mode 100644 index 0000000000000..20fd18b829562 --- /dev/null +++ b/src/test/run-make/sepcomp-inlining/foo.rs @@ -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 or the MIT license +// , 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(); +} diff --git a/src/test/run-make/sepcomp-separate/Makefile b/src/test/run-make/sepcomp-separate/Makefile new file mode 100644 index 0000000000000..265bd68bd2e82 --- /dev/null +++ b/src/test/run-make/sepcomp-separate/Makefile @@ -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" ] diff --git a/src/test/run-make/sepcomp-separate/foo.rs b/src/test/run-make/sepcomp-separate/foo.rs new file mode 100644 index 0000000000000..fe6a7b5a18f27 --- /dev/null +++ b/src/test/run-make/sepcomp-separate/foo.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/run-pass/sepcomp-cci.rs b/src/test/run-pass/sepcomp-cci.rs new file mode 100644 index 0000000000000..0178b5e786d65 --- /dev/null +++ b/src/test/run-pass/sepcomp-cci.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/sepcomp-extern.rs b/src/test/run-pass/sepcomp-extern.rs new file mode 100644 index 0000000000000..a5506e3fc761d --- /dev/null +++ b/src/test/run-pass/sepcomp-extern.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/sepcomp-fns-backwards.rs b/src/test/run-pass/sepcomp-fns-backwards.rs new file mode 100644 index 0000000000000..61f008ad854eb --- /dev/null +++ b/src/test/run-pass/sepcomp-fns-backwards.rs @@ -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 or the MIT license +// , 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); +} + diff --git a/src/test/run-pass/sepcomp-fns.rs b/src/test/run-pass/sepcomp-fns.rs new file mode 100644 index 0000000000000..09f2a4281be08 --- /dev/null +++ b/src/test/run-pass/sepcomp-fns.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/sepcomp-lib.rs b/src/test/run-pass/sepcomp-lib.rs new file mode 100644 index 0000000000000..28adb55399b44 --- /dev/null +++ b/src/test/run-pass/sepcomp-lib.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/run-pass/sepcomp-statics.rs b/src/test/run-pass/sepcomp-statics.rs new file mode 100644 index 0000000000000..26a652ae0ea4f --- /dev/null +++ b/src/test/run-pass/sepcomp-statics.rs @@ -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 or the MIT license +// , 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); +} + diff --git a/src/test/run-pass/sepcomp-unwind.rs b/src/test/run-pass/sepcomp-unwind.rs new file mode 100644 index 0000000000000..5d154e02af670 --- /dev/null +++ b/src/test/run-pass/sepcomp-unwind.rs @@ -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 or the MIT license +// , 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(); +}