about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/compiletest/runtest.rs13
-rw-r--r--src/librustc/back/link.rs572
-rw-r--r--src/librustc/back/lto.rs16
-rw-r--r--src/librustc/back/write.rs962
-rw-r--r--src/librustc/driver/config.rs25
-rw-r--r--src/librustc/driver/driver.rs46
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/metadata/encoder.rs9
-rw-r--r--src/librustc/middle/trans/_match.rs2
-rw-r--r--src/librustc/middle/trans/adt.rs6
-rw-r--r--src/librustc/middle/trans/base.rs502
-rw-r--r--src/librustc/middle/trans/build.rs10
-rw-r--r--src/librustc/middle/trans/builder.rs37
-rw-r--r--src/librustc/middle/trans/cabi_mips.rs2
-rw-r--r--src/librustc/middle/trans/cleanup.rs24
-rw-r--r--src/librustc/middle/trans/closure.rs16
-rw-r--r--src/librustc/middle/trans/common.rs52
-rw-r--r--src/librustc/middle/trans/consts.rs29
-rw-r--r--src/librustc/middle/trans/context.rs666
-rw-r--r--src/librustc/middle/trans/datum.rs2
-rw-r--r--src/librustc/middle/trans/debuginfo.rs58
-rw-r--r--src/librustc/middle/trans/expr.rs43
-rw-r--r--src/librustc/middle/trans/foreign.rs83
-rw-r--r--src/librustc/middle/trans/glue.rs75
-rw-r--r--src/librustc/middle/trans/inline.rs93
-rw-r--r--src/librustc/middle/trans/intrinsic.rs8
-rw-r--r--src/librustc/middle/trans/llrepr.rs4
-rw-r--r--src/librustc/middle/trans/machine.rs18
-rw-r--r--src/librustc/middle/trans/meth.rs45
-rw-r--r--src/librustc/middle/trans/monomorphize.rs84
-rw-r--r--src/librustc/middle/trans/reflect.rs2
-rw-r--r--src/librustc/middle/trans/tvec.rs6
-rw-r--r--src/librustc/middle/trans/type_.rs34
-rw-r--r--src/librustc/middle/trans/type_of.rs16
-rw-r--r--src/librustc_llvm/lib.rs2
-rw-r--r--src/librustdoc/test.rs4
-rw-r--r--src/libsyntax/attr.rs10
-rw-r--r--src/test/auxiliary/sepcomp-extern-lib.rs14
-rw-r--r--src/test/auxiliary/sepcomp_cci_lib.rs17
-rw-r--r--src/test/auxiliary/sepcomp_lib.rs31
-rw-r--r--src/test/compile-fail/sepcomp-lib-lto.rs28
-rw-r--r--src/test/run-make/output-type-permutations/Makefile39
-rw-r--r--src/test/run-make/sepcomp-cci-copies/Makefile10
-rw-r--r--src/test/run-make/sepcomp-cci-copies/cci_lib.rs19
-rw-r--r--src/test/run-make/sepcomp-cci-copies/foo.rs36
-rw-r--r--src/test/run-make/sepcomp-inlining/Makefile13
-rw-r--r--src/test/run-make/sepcomp-inlining/foo.rs35
-rw-r--r--src/test/run-make/sepcomp-separate/Makefile9
-rw-r--r--src/test/run-make/sepcomp-separate/foo.rs27
-rw-r--r--src/test/run-pass/sepcomp-cci.rs41
-rw-r--r--src/test/run-pass/sepcomp-extern.rs42
-rw-r--r--src/test/run-pass/sepcomp-fns-backwards.rs41
-rw-r--r--src/test/run-pass/sepcomp-fns.rs38
-rw-r--r--src/test/run-pass/sepcomp-lib.rs24
-rw-r--r--src/test/run-pass/sepcomp-statics.rs39
-rw-r--r--src/test/run-pass/sepcomp-unwind.rs38
56 files changed, 2954 insertions, 1164 deletions
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index ee787a5f0a9..32671722ba7 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 4e4a28cc538..5adf8b65381 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 6c6a07f3502..d7f183faa01 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 00000000000..627d455f06e
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use back::lto;
+use back::link::{get_cc_prog, remove};
+use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames};
+use driver::config::NoDebugInfo;
+use driver::session::Session;
+use driver::config;
+use llvm;
+use llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
+use util::common::time;
+use syntax::abi;
+use syntax::codemap;
+use syntax::diagnostic;
+use syntax::diagnostic::{Emitter, Handler, Level, mk_handler};
+
+use std::c_str::{ToCStr, CString};
+use std::io::Command;
+use std::io::fs;
+use std::iter::Unfold;
+use std::ptr;
+use std::str;
+use std::sync::{Arc, Mutex};
+use std::task::TaskBuilder;
+use libc::{c_uint, c_int};
+
+
+#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)]
+pub enum OutputType {
+    OutputTypeBitcode,
+    OutputTypeAssembly,
+    OutputTypeLlvmAssembly,
+    OutputTypeObject,
+    OutputTypeExe,
+}
+
+
+pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! {
+    unsafe {
+        let cstr = llvm::LLVMRustGetLastError();
+        if cstr == ptr::null() {
+            handler.fatal(msg.as_slice());
+        } else {
+            let err = CString::new(cstr, true);
+            let err = String::from_utf8_lossy(err.as_bytes());
+            handler.fatal(format!("{}: {}",
+                                  msg.as_slice(),
+                                  err.as_slice()).as_slice());
+        }
+    }
+}
+
+pub fn write_output_file(
+        handler: &diagnostic::Handler,
+        target: llvm::TargetMachineRef,
+        pm: llvm::PassManagerRef,
+        m: ModuleRef,
+        output: &Path,
+        file_type: llvm::FileType) {
+    unsafe {
+        output.with_c_str(|output| {
+            let result = llvm::LLVMRustWriteOutputFile(
+                    target, pm, m, output, file_type);
+            if !result {
+                llvm_err(handler, "could not write output".to_string());
+            }
+        })
+    }
+}
+
+
+struct Diagnostic {
+    msg: String,
+    code: Option<String>,
+    lvl: Level,
+}
+
+// We use an Arc instead of just returning a list of diagnostics from the
+// child task because we need to make sure that the messages are seen even
+// if the child task fails (for example, when `fatal` is called).
+#[deriving(Clone)]
+struct SharedEmitter {
+    buffer: Arc<Mutex<Vec<Diagnostic>>>,
+}
+
+impl SharedEmitter {
+    fn new() -> SharedEmitter {
+        SharedEmitter {
+            buffer: Arc::new(Mutex::new(Vec::new())),
+        }
+    }
+
+    fn dump(&mut self, handler: &Handler) {
+        let mut buffer = self.buffer.lock();
+        for diag in buffer.iter() {
+            match diag.code {
+                Some(ref code) => {
+                    handler.emit_with_code(None,
+                                           diag.msg.as_slice(),
+                                           code.as_slice(),
+                                           diag.lvl);
+                },
+                None => {
+                    handler.emit(None,
+                                 diag.msg.as_slice(),
+                                 diag.lvl);
+                },
+            }
+        }
+        buffer.clear();
+    }
+}
+
+impl Emitter for SharedEmitter {
+    fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
+            msg: &str, code: Option<&str>, lvl: Level) {
+        assert!(cmsp.is_none(), "SharedEmitter doesn't support spans");
+
+        self.buffer.lock().push(Diagnostic {
+            msg: msg.to_string(),
+            code: code.map(|s| s.to_string()),
+            lvl: lvl,
+        });
+    }
+
+    fn custom_emit(&mut self, _cm: &codemap::CodeMap,
+                   _sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) {
+        fail!("SharedEmitter doesn't support custom_emit");
+    }
+}
+
+
+// On android, we by default compile for armv7 processors. This enables
+// things like double word CAS instructions (rather than emulating them)
+// which are *far* more efficient. This is obviously undesirable in some
+// cases, so if any sort of target feature is specified we don't append v7
+// to the feature list.
+//
+// On iOS only armv7 and newer are supported. So it is useful to
+// get all hardware potential via VFP3 (hardware floating point)
+// and NEON (SIMD) instructions supported by LLVM.
+// Note that without those flags various linking errors might
+// arise as some of intrinsics are converted into function calls
+// and nobody provides implementations those functions
+fn target_feature<'a>(sess: &'a Session) -> &'a str {
+    match sess.targ_cfg.os {
+        abi::OsAndroid => {
+            if "" == sess.opts.cg.target_feature.as_slice() {
+                "+v7"
+            } else {
+                sess.opts.cg.target_feature.as_slice()
+            }
+        },
+        abi::OsiOS if sess.targ_cfg.arch == abi::Arm => {
+            "+v7,+thumb2,+vfp3,+neon"
+        },
+        _ => sess.opts.cg.target_feature.as_slice()
+    }
+}
+
+fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
+    match optimize {
+      config::No => llvm::CodeGenLevelNone,
+      config::Less => llvm::CodeGenLevelLess,
+      config::Default => llvm::CodeGenLevelDefault,
+      config::Aggressive => llvm::CodeGenLevelAggressive,
+    }
+}
+
+fn create_target_machine(sess: &Session) -> TargetMachineRef {
+    let reloc_model = match sess.opts.cg.relocation_model.as_slice() {
+        "pic" => llvm::RelocPIC,
+        "static" => llvm::RelocStatic,
+        "default" => llvm::RelocDefault,
+        "dynamic-no-pic" => llvm::RelocDynamicNoPic,
+        _ => {
+            sess.err(format!("{} is not a valid relocation mode",
+                             sess.opts
+                                 .cg
+                                 .relocation_model).as_slice());
+            sess.abort_if_errors();
+            unreachable!();
+        }
+    };
+
+    let opt_level = get_llvm_opt_level(sess.opts.optimize);
+    let use_softfp = sess.opts.cg.soft_float;
+
+    // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter.
+    // FIXME: #11954: mac64 unwinding may not work with fp elim
+    let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) ||
+                     (sess.targ_cfg.os == abi::OsMacos &&
+                      sess.targ_cfg.arch == abi::X86_64);
+
+    // OSX has -dead_strip, which doesn't rely on ffunction_sections
+    // FIXME(#13846) this should be enabled for windows
+    let ffunction_sections = sess.targ_cfg.os != abi::OsMacos &&
+                             sess.targ_cfg.os != abi::OsWindows;
+    let fdata_sections = ffunction_sections;
+
+    let code_model = match sess.opts.cg.code_model.as_slice() {
+        "default" => llvm::CodeModelDefault,
+        "small" => llvm::CodeModelSmall,
+        "kernel" => llvm::CodeModelKernel,
+        "medium" => llvm::CodeModelMedium,
+        "large" => llvm::CodeModelLarge,
+        _ => {
+            sess.err(format!("{} is not a valid code model",
+                             sess.opts
+                                 .cg
+                                 .code_model).as_slice());
+            sess.abort_if_errors();
+            unreachable!();
+        }
+    };
+
+    unsafe {
+        sess.targ_cfg
+             .target_strs
+             .target_triple
+             .as_slice()
+             .with_c_str(|t| {
+            sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| {
+                target_feature(sess).with_c_str(|features| {
+                    llvm::LLVMRustCreateTargetMachine(
+                        t, cpu, features,
+                        code_model,
+                        reloc_model,
+                        opt_level,
+                        true /* EnableSegstk */,
+                        use_softfp,
+                        no_fp_elim,
+                        ffunction_sections,
+                        fdata_sections,
+                    )
+                })
+            })
+        })
+    }
+}
+
+
+/// Module-specific configuration for `optimize_and_codegen`.
+#[deriving(Clone)]
+struct ModuleConfig {
+    /// LLVM TargetMachine to use for codegen.
+    tm: TargetMachineRef,
+    /// Names of additional optimization passes to run.
+    passes: Vec<String>,
+    /// Some(level) to optimize at a certain level, or None to run
+    /// absolutely no optimizations (used for the metadata module).
+    opt_level: Option<llvm::CodeGenOptLevel>,
+
+    // Flags indicating which outputs to produce.
+    emit_no_opt_bc: bool,
+    emit_bc: bool,
+    emit_lto_bc: bool,
+    emit_ir: bool,
+    emit_asm: bool,
+    emit_obj: bool,
+
+    // Miscellaneous flags.  These are mostly copied from command-line
+    // options.
+    no_verify: bool,
+    no_prepopulate_passes: bool,
+    no_builtins: bool,
+    time_passes: bool,
+}
+
+impl ModuleConfig {
+    fn new(tm: TargetMachineRef, passes: Vec<String>) -> ModuleConfig {
+        ModuleConfig {
+            tm: tm,
+            passes: passes,
+            opt_level: None,
+
+            emit_no_opt_bc: false,
+            emit_bc: false,
+            emit_lto_bc: false,
+            emit_ir: false,
+            emit_asm: false,
+            emit_obj: false,
+
+            no_verify: false,
+            no_prepopulate_passes: false,
+            no_builtins: false,
+            time_passes: false,
+        }
+    }
+
+    fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) {
+        self.no_verify = sess.no_verify();
+        self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
+        self.no_builtins = trans.no_builtins;
+        self.time_passes = sess.time_passes();
+    }
+}
+
+/// Additional resources used by optimize_and_codegen (not module specific)
+struct CodegenContext<'a> {
+    // Extra resources used for LTO: (sess, reachable).  This will be `None`
+    // when running in a worker thread.
+    lto_ctxt: Option<(&'a Session, &'a [String])>,
+    // Handler to use for diagnostics produced during codegen.
+    handler: &'a Handler,
+}
+
+impl<'a> CodegenContext<'a> {
+    fn new(handler: &'a Handler) -> CodegenContext<'a> {
+        CodegenContext {
+            lto_ctxt: None,
+            handler: handler,
+        }
+    }
+
+    fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
+        CodegenContext {
+            lto_ctxt: Some((sess, reachable)),
+            handler: sess.diagnostic().handler(),
+        }
+    }
+}
+
+// Unsafe due to LLVM calls.
+unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
+                               mtrans: ModuleTranslation,
+                               config: ModuleConfig,
+                               name_extra: String,
+                               output_names: OutputFilenames) {
+    let ModuleTranslation { llmod, llcx } = mtrans;
+    let tm = config.tm;
+
+    if config.emit_no_opt_bc {
+        let ext = format!("{}.no-opt.bc", name_extra);
+        output_names.with_extension(ext.as_slice()).with_c_str(|buf| {
+            llvm::LLVMWriteBitcodeToFile(llmod, buf);
+        })
+    }
+
+    match config.opt_level {
+        Some(opt_level) => {
+            // Create the two optimizing pass managers. These mirror what clang
+            // does, and are by populated by LLVM's default PassManagerBuilder.
+            // Each manager has a different set of passes, but they also share
+            // some common passes.
+            let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod);
+            let mpm = llvm::LLVMCreatePassManager();
+
+            // If we're verifying or linting, add them to the function pass
+            // manager.
+            let addpass = |pass: &str| {
+                pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s))
+            };
+            if !config.no_verify { assert!(addpass("verify")); }
+
+            if !config.no_prepopulate_passes {
+                llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
+                llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
+                populate_llvm_passes(fpm, mpm, llmod, opt_level,
+                                     config.no_builtins);
+            }
+
+            for pass in config.passes.iter() {
+                pass.as_slice().with_c_str(|s| {
+                    if !llvm::LLVMRustAddPass(mpm, s) {
+                        cgcx.handler.warn(format!("unknown pass {}, ignoring",
+                                                  *pass).as_slice());
+                    }
+                })
+            }
+
+            // Finally, run the actual optimization passes
+            time(config.time_passes, "llvm function passes", (), |()|
+                 llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
+            time(config.time_passes, "llvm module passes", (), |()|
+                 llvm::LLVMRunPassManager(mpm, llmod));
+
+            // Deallocate managers that we're now done with
+            llvm::LLVMDisposePassManager(fpm);
+            llvm::LLVMDisposePassManager(mpm);
+
+            match cgcx.lto_ctxt {
+                Some((sess, reachable)) if sess.lto() =>  {
+                    time(sess.time_passes(), "all lto passes", (), |()|
+                         lto::run(sess, llmod, tm, reachable));
+
+                    if config.emit_lto_bc {
+                        let name = format!("{}.lto.bc", name_extra);
+                        output_names.with_extension(name.as_slice()).with_c_str(|buf| {
+                            llvm::LLVMWriteBitcodeToFile(llmod, buf);
+                        })
+                    }
+                },
+                _ => {},
+            }
+        },
+        None => {},
+    }
+
+    // A codegen-specific pass manager is used to generate object
+    // files for an LLVM module.
+    //
+    // Apparently each of these pass managers is a one-shot kind of
+    // thing, so we create a new one for each type of output. The
+    // pass manager passed to the closure should be ensured to not
+    // escape the closure itself, and the manager should only be
+    // used once.
+    unsafe fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef,
+                    no_builtins: bool, f: |PassManagerRef|) {
+        let cpm = llvm::LLVMCreatePassManager();
+        llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
+        llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
+        f(cpm);
+        llvm::LLVMDisposePassManager(cpm);
+    }
+
+    if config.emit_bc {
+        let ext = format!("{}.bc", name_extra);
+        output_names.with_extension(ext.as_slice()).with_c_str(|buf| {
+            llvm::LLVMWriteBitcodeToFile(llmod, buf);
+        })
+    }
+
+    time(config.time_passes, "codegen passes", (), |()| {
+        if config.emit_ir {
+            let ext = format!("{}.ll", name_extra);
+            output_names.with_extension(ext.as_slice()).with_c_str(|output| {
+                with_codegen(tm, llmod, config.no_builtins, |cpm| {
+                    llvm::LLVMRustPrintModule(cpm, llmod, output);
+                })
+            })
+        }
+
+        if config.emit_asm {
+            let path = output_names.with_extension(format!("{}.s", name_extra).as_slice());
+            with_codegen(tm, llmod, config.no_builtins, |cpm| {
+                write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFile);
+            });
+        }
+
+        if config.emit_obj {
+            let path = output_names.with_extension(format!("{}.o", name_extra).as_slice());
+            with_codegen(tm, llmod, config.no_builtins, |cpm| {
+                write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFile);
+            });
+        }
+    });
+
+    llvm::LLVMDisposeModule(llmod);
+    llvm::LLVMContextDispose(llcx);
+    llvm::LLVMRustDisposeTargetMachine(tm);
+}
+
+pub fn run_passes(sess: &Session,
+                  trans: &CrateTranslation,
+                  output_types: &[OutputType],
+                  crate_output: &OutputFilenames) {
+    // It's possible that we have `codegen_units > 1` but only one item in
+    // `trans.modules`.  We could theoretically proceed and do LTO in that
+    // case, but it would be confusing to have the validity of
+    // `-Z lto -C codegen-units=2` depend on details of the crate being
+    // compiled, so we complain regardless.
+    if sess.lto() && sess.opts.cg.codegen_units > 1 {
+        // This case is impossible to handle because LTO expects to be able
+        // to combine the entire crate and all its dependencies into a
+        // single compilation unit, but each codegen unit is in a separate
+        // LLVM context, so they can't easily be combined.
+        sess.fatal("can't perform LTO when using multiple codegen units");
+    }
+
+    // Sanity check
+    assert!(trans.modules.len() == sess.opts.cg.codegen_units);
+
+    unsafe {
+        configure_llvm(sess);
+    }
+
+    let tm = create_target_machine(sess);
+
+    // Figure out what we actually need to build.
+
+    let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone());
+    let mut metadata_config = ModuleConfig::new(tm, vec!());
+
+    modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize));
+
+    // Save all versions of the bytecode if we're saving our temporaries.
+    if sess.opts.cg.save_temps {
+        modules_config.emit_no_opt_bc = true;
+        modules_config.emit_bc = true;
+        modules_config.emit_lto_bc = true;
+        metadata_config.emit_bc = true;
+    }
+
+    // Emit a bitcode file for the crate if we're emitting an rlib.
+    // Whenever an rlib is created, the bitcode is inserted into the
+    // archive in order to allow LTO against it.
+    let needs_crate_bitcode =
+            sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
+            sess.opts.output_types.contains(&OutputTypeExe) &&
+            sess.opts.cg.codegen_units == 1;
+    if needs_crate_bitcode {
+        modules_config.emit_bc = true;
+    }
+
+    for output_type in output_types.iter() {
+        match *output_type {
+            OutputTypeBitcode => { modules_config.emit_bc = true; },
+            OutputTypeLlvmAssembly => { modules_config.emit_ir = true; },
+            OutputTypeAssembly => {
+                modules_config.emit_asm = true;
+                // If we're not using the LLVM assembler, this function
+                // could be invoked specially with output_type_assembly, so
+                // in this case we still want the metadata object file.
+                if !sess.opts.output_types.contains(&OutputTypeAssembly) {
+                    metadata_config.emit_obj = true;
+                }
+            },
+            OutputTypeObject => { modules_config.emit_obj = true; },
+            OutputTypeExe => {
+                modules_config.emit_obj = true;
+                metadata_config.emit_obj = true;
+            },
+        }
+    }
+
+    modules_config.set_flags(sess, trans);
+    metadata_config.set_flags(sess, trans);
+
+
+    // Populate a buffer with a list of codegen tasks.  Items are processed in
+    // LIFO order, just because it's a tiny bit simpler that way.  (The order
+    // doesn't actually matter.)
+    let mut work_items = Vec::with_capacity(1 + trans.modules.len());
+
+    {
+        let work = build_work_item(sess,
+                                   trans.metadata_module,
+                                   metadata_config.clone(),
+                                   crate_output.clone(),
+                                   "metadata".to_string());
+        work_items.push(work);
+    }
+
+    for (index, mtrans) in trans.modules.iter().enumerate() {
+        let work = build_work_item(sess,
+                                   *mtrans,
+                                   modules_config.clone(),
+                                   crate_output.clone(),
+                                   format!("{}", index));
+        work_items.push(work);
+    }
+
+    // Process the work items, optionally using worker threads.
+    if sess.opts.cg.codegen_units == 1 {
+        run_work_singlethreaded(sess, trans.reachable.as_slice(), work_items);
+
+        if needs_crate_bitcode {
+            // The only bitcode file produced (aside from metadata) was
+            // "crate.0.bc".  Rename to "crate.bc" since that's what
+            // `link_rlib` expects to find.
+            fs::copy(&crate_output.with_extension("0.bc"),
+                     &crate_output.temp_path(OutputTypeBitcode)).unwrap();
+        }
+    } else {
+        run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units);
+
+        assert!(!needs_crate_bitcode,
+               "can't produce a crate bitcode file from multiple compilation units");
+    }
+
+    // All codegen is finished.
+    unsafe {
+        llvm::LLVMRustDisposeTargetMachine(tm);
+    }
+
+    // Produce final compile outputs.
+
+    let copy_if_one_unit = |ext: &str, output_type: OutputType| {
+        // Three cases:
+        if sess.opts.cg.codegen_units == 1 {
+            // 1) Only one codegen unit.  In this case it's no difficulty
+            //    to copy `foo.0.x` to `foo.x`.
+            fs::copy(&crate_output.with_extension(ext),
+                     &crate_output.path(output_type)).unwrap();
+            if !sess.opts.cg.save_temps {
+                // The user just wants `foo.x`, not `foo.0.x`.
+                remove(sess, &crate_output.with_extension(ext));
+            }
+        } else {
+            if crate_output.single_output_file.is_some() {
+                // 2) Multiple codegen units, with `-o some_name`.  We have
+                //    no good solution for this case, so warn the user.
+                sess.warn(format!("ignoring -o because multiple .{} files were produced",
+                                  ext).as_slice());
+            } else {
+                // 3) Multiple codegen units, but no `-o some_name`.  We
+                //    just leave the `foo.0.x` files in place.
+                // (We don't have to do any work in this case.)
+            }
+        }
+    };
+
+    let link_obj = |output_path: &Path| {
+        // Running `ld -r` on a single input is kind of pointless.
+        if sess.opts.cg.codegen_units == 1 {
+            fs::copy(&crate_output.with_extension("0.o"),
+                     output_path).unwrap();
+            // Leave the .0.o file around, to mimic the behavior of the normal
+            // code path.
+            return;
+        }
+
+        // Some builds of MinGW GCC will pass --force-exe-suffix to ld, which
+        // will automatically add a .exe extension if the extension is not
+        // already .exe or .dll.  To ensure consistent behavior on Windows, we
+        // add the .exe suffix explicitly and then rename the output file to
+        // the desired path.  This will give the correct behavior whether or
+        // not GCC adds --force-exe-suffix.
+        let windows_output_path =
+            if sess.targ_cfg.os == abi::OsWindows {
+                Some(output_path.with_extension("o.exe"))
+            } else {
+                None
+            };
+
+        let pname = get_cc_prog(sess);
+        let mut cmd = Command::new(pname.as_slice());
+
+        cmd.args(sess.targ_cfg.target_strs.cc_args.as_slice());
+        cmd.arg("-nostdlib");
+
+        for index in range(0, trans.modules.len()) {
+            cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice()));
+        }
+
+        cmd.arg("-r")
+           .arg("-o")
+           .arg(windows_output_path.as_ref().unwrap_or(output_path));
+
+        if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 {
+            println!("{}", &cmd);
+        }
+
+        cmd.stdin(::std::io::process::Ignored)
+           .stdout(::std::io::process::InheritFd(1))
+           .stderr(::std::io::process::InheritFd(2));
+        match cmd.status() {
+            Ok(_) => {},
+            Err(e) => {
+                sess.err(format!("could not exec the linker `{}`: {}",
+                                 pname,
+                                 e).as_slice());
+                sess.abort_if_errors();
+            },
+        }
+
+        match windows_output_path {
+            Some(ref windows_path) => {
+                fs::rename(windows_path, output_path).unwrap();
+            },
+            None => {
+                // The file is already named according to `output_path`.
+            }
+        }
+    };
+
+    // Flag to indicate whether the user explicitly requested bitcode.
+    // Otherwise, we produced it only as a temporary output, and will need
+    // to get rid of it.
+    // FIXME: Since we don't support LTO anyway, maybe we can avoid
+    // producing the temporary .0.bc's in the first place?
+    let mut save_bitcode = false;
+    for output_type in output_types.iter() {
+        match *output_type {
+            OutputTypeBitcode => {
+                save_bitcode = true;
+                copy_if_one_unit("0.bc", OutputTypeBitcode);
+            },
+            OutputTypeLlvmAssembly => { copy_if_one_unit("0.ll", OutputTypeLlvmAssembly); },
+            OutputTypeAssembly => { copy_if_one_unit("0.s", OutputTypeAssembly); },
+            OutputTypeObject => { link_obj(&crate_output.path(OutputTypeObject)); },
+            OutputTypeExe => {
+                // If OutputTypeObject is already in the list, then
+                // `crate.o` will be handled by the OutputTypeObject case.
+                // Otherwise, we need to create the temporary object so we
+                // can run the linker.
+                if !sess.opts.output_types.contains(&OutputTypeObject) {
+                    link_obj(&crate_output.temp_path(OutputTypeObject));
+                }
+            },
+        }
+    }
+    let save_bitcode = save_bitcode;
+
+    // Clean up unwanted temporary files.
+
+    // We create the following files by default:
+    //  - crate.0.bc
+    //  - crate.0.o
+    //  - crate.metadata.bc
+    //  - crate.metadata.o
+    //  - crate.o (linked from crate.##.o)
+    //  - crate.bc (copied from crate.0.bc)
+    // We may create additional files if requested by the user (through
+    // `-C save-temps` or `--emit=` flags).
+
+    if !sess.opts.cg.save_temps {
+        // Remove the temporary .0.o objects.  If the user didn't
+        // explicitly request bitcode (with --emit=bc), we must remove
+        // .0.bc as well.  (We don't touch the crate.bc that may have been
+        // produced earlier.)
+        for i in range(0, trans.modules.len()) {
+            if modules_config.emit_obj {
+                let ext = format!("{}.o", i);
+                remove(sess, &crate_output.with_extension(ext.as_slice()));
+            }
+
+            if modules_config.emit_bc && !save_bitcode {
+                let ext = format!("{}.bc", i);
+                remove(sess, &crate_output.with_extension(ext.as_slice()));
+            }
+        }
+
+        if metadata_config.emit_bc && !save_bitcode {
+            remove(sess, &crate_output.with_extension("metadata.bc"));
+        }
+    }
+
+    // We leave the following files around by default:
+    //  - crate.o
+    //  - crate.metadata.o
+    //  - crate.bc
+    // These are used in linking steps and will be cleaned up afterward.
+
+    // FIXME: time_llvm_passes support - does this use a global context or
+    // something?
+    //if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
+}
+
+type WorkItem = proc(&CodegenContext):Send;
+
+fn build_work_item(sess: &Session,
+                   mtrans: ModuleTranslation,
+                   config: ModuleConfig,
+                   output_names: OutputFilenames,
+                   name_extra: String) -> WorkItem {
+    let mut config = config;
+    config.tm = create_target_machine(sess);
+
+    proc(cgcx) unsafe {
+        optimize_and_codegen(cgcx, mtrans, config, name_extra, output_names);
+    }
+}
+
+fn run_work_singlethreaded(sess: &Session,
+                           reachable: &[String],
+                           work_items: Vec<WorkItem>) {
+    let cgcx = CodegenContext::new_with_session(sess, reachable);
+    let mut work_items = work_items;
+
+    // Since we're running single-threaded, we can pass the session to
+    // the proc, allowing `optimize_and_codegen` to perform LTO.
+    for work in Unfold::new((), |_| work_items.pop()) {
+        work(&cgcx);
+    }
+}
+
+fn run_work_multithreaded(sess: &Session,
+                          work_items: Vec<WorkItem>,
+                          num_workers: uint) {
+    // Run some workers to process the work items.
+    let work_items_arc = Arc::new(Mutex::new(work_items));
+    let mut diag_emitter = SharedEmitter::new();
+    let mut futures = Vec::with_capacity(num_workers);
+
+    for i in range(0, num_workers) {
+        let work_items_arc = work_items_arc.clone();
+        let diag_emitter = diag_emitter.clone();
+
+        let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() {
+            let diag_handler = mk_handler(box diag_emitter);
+
+            // Must construct cgcx inside the proc because it has non-Send
+            // fields.
+            let cgcx = CodegenContext::new(&diag_handler);
+
+            loop {
+                // Avoid holding the lock for the entire duration of the match.
+                let maybe_work = work_items_arc.lock().pop();
+                match maybe_work {
+                    Some(work) => {
+                        work(&cgcx);
+
+                        // Make sure to fail the worker so the main thread can
+                        // tell that there were errors.
+                        cgcx.handler.abort_if_errors();
+                    }
+                    None => break,
+                }
+            }
+        });
+        futures.push(future);
+    }
+
+    let mut failed = false;
+    for future in futures.move_iter() {
+        match future.unwrap() {
+            Ok(()) => {},
+            Err(_) => {
+                failed = true;
+            },
+        }
+        // Display any new diagnostics.
+        diag_emitter.dump(sess.diagnostic().handler());
+    }
+    if failed {
+        sess.fatal("aborting due to worker thread failure");
+    }
+}
+
+pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
+    let pname = get_cc_prog(sess);
+    let mut cmd = Command::new(pname.as_slice());
+
+    cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject))
+                           .arg(outputs.temp_path(OutputTypeAssembly));
+    debug!("{}", &cmd);
+
+    match cmd.output() {
+        Ok(prog) => {
+            if !prog.status.success() {
+                sess.err(format!("linking with `{}` failed: {}",
+                                 pname,
+                                 prog.status).as_slice());
+                sess.note(format!("{}", &cmd).as_slice());
+                let mut note = prog.error.clone();
+                note.push_all(prog.output.as_slice());
+                sess.note(str::from_utf8(note.as_slice()).unwrap());
+                sess.abort_if_errors();
+            }
+        },
+        Err(e) => {
+            sess.err(format!("could not exec the linker `{}`: {}",
+                             pname,
+                             e).as_slice());
+            sess.abort_if_errors();
+        }
+    }
+}
+
+unsafe fn configure_llvm(sess: &Session) {
+    use std::sync::{Once, ONCE_INIT};
+    static mut INIT: Once = ONCE_INIT;
+
+    // Copy what clang does by turning on loop vectorization at O2 and
+    // slp vectorization at O3
+    let vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
+                         (sess.opts.optimize == config::Default ||
+                          sess.opts.optimize == config::Aggressive);
+    let vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
+                        sess.opts.optimize == config::Aggressive;
+
+    let mut llvm_c_strs = Vec::new();
+    let mut llvm_args = Vec::new();
+    {
+        let add = |arg: &str| {
+            let s = arg.to_c_str();
+            llvm_args.push(s.as_ptr());
+            llvm_c_strs.push(s);
+        };
+        add("rustc"); // fake program name
+        if vectorize_loop { add("-vectorize-loops"); }
+        if vectorize_slp  { add("-vectorize-slp");   }
+        if sess.time_llvm_passes() { add("-time-passes"); }
+        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
+
+        for arg in sess.opts.cg.llvm_args.iter() {
+            add((*arg).as_slice());
+        }
+    }
+
+    INIT.doit(|| {
+        llvm::LLVMInitializePasses();
+
+        // Only initialize the platforms supported by Rust here, because
+        // using --llvm-root will have multiple platforms that rustllvm
+        // doesn't actually link to and it's pointless to put target info
+        // into the registry that Rust cannot generate machine code for.
+        llvm::LLVMInitializeX86TargetInfo();
+        llvm::LLVMInitializeX86Target();
+        llvm::LLVMInitializeX86TargetMC();
+        llvm::LLVMInitializeX86AsmPrinter();
+        llvm::LLVMInitializeX86AsmParser();
+
+        llvm::LLVMInitializeARMTargetInfo();
+        llvm::LLVMInitializeARMTarget();
+        llvm::LLVMInitializeARMTargetMC();
+        llvm::LLVMInitializeARMAsmPrinter();
+        llvm::LLVMInitializeARMAsmParser();
+
+        llvm::LLVMInitializeMipsTargetInfo();
+        llvm::LLVMInitializeMipsTarget();
+        llvm::LLVMInitializeMipsTargetMC();
+        llvm::LLVMInitializeMipsAsmPrinter();
+        llvm::LLVMInitializeMipsAsmParser();
+
+        llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
+                                     llvm_args.as_ptr());
+    });
+}
+
+unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef,
+                               mpm: llvm::PassManagerRef,
+                               llmod: ModuleRef,
+                               opt: llvm::CodeGenOptLevel,
+                               no_builtins: bool) {
+    // Create the PassManagerBuilder for LLVM. We configure it with
+    // reasonable defaults and prepare it to actually populate the pass
+    // manager.
+    let builder = llvm::LLVMPassManagerBuilderCreate();
+    match opt {
+        llvm::CodeGenLevelNone => {
+            // Don't add lifetime intrinsics at O0
+            llvm::LLVMRustAddAlwaysInlinePass(builder, false);
+        }
+        llvm::CodeGenLevelLess => {
+            llvm::LLVMRustAddAlwaysInlinePass(builder, true);
+        }
+        // numeric values copied from clang
+        llvm::CodeGenLevelDefault => {
+            llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
+                                                                225);
+        }
+        llvm::CodeGenLevelAggressive => {
+            llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
+                                                                275);
+        }
+    }
+    llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint);
+    llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins);
+
+    // Use the builder to populate the function/module pass managers.
+    llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm);
+    llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm);
+    llvm::LLVMPassManagerBuilderDispose(builder);
+
+    match opt {
+        llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => {
+            "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s));
+        }
+        _ => {}
+    };
+}
diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs
index c402c256a37..3e3a88ceffd 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<back::link::OutputType> ,
+    pub output_types: Vec<back::write::OutputType> ,
     // This was mutable for rustpkg, which updates search paths based on the
     // parsed code. It remains mutable in case its replacements wants to use
     // this.
@@ -303,6 +303,13 @@ macro_rules! cgoptions(
             }
         }
 
+        fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool {
+            use std::from_str::FromStr;
+            match v.and_then(FromStr::from_str) {
+                Some(i) => { *slot = i; true },
+                None => false
+            }
+        }
     }
 ) )
 
@@ -347,6 +354,8 @@ cgoptions!(
          "metadata to mangle symbol names with"),
     extra_filename: String = ("".to_string(), parse_string,
          "extra data to put in each output filename"),
+    codegen_units: uint = (1, parse_uint,
+        "divide crate into N units to optimize in parallel"),
 )
 
 pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
@@ -646,11 +655,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         for unparsed_output_type in unparsed_output_types.iter() {
             for part in unparsed_output_type.as_slice().split(',') {
                 let output_type = match part.as_slice() {
-                    "asm"  => link::OutputTypeAssembly,
-                    "ir"   => link::OutputTypeLlvmAssembly,
-                    "bc"   => link::OutputTypeBitcode,
-                    "obj"  => link::OutputTypeObject,
-                    "link" => link::OutputTypeExe,
+                    "asm"  => write::OutputTypeAssembly,
+                    "ir"   => write::OutputTypeLlvmAssembly,
+                    "bc"   => write::OutputTypeBitcode,
+                    "obj"  => write::OutputTypeObject,
+                    "link" => write::OutputTypeExe,
                     _ => {
                         early_error(format!("unknown emission type: `{}`",
                                             part).as_slice())
@@ -663,7 +672,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     output_types.as_mut_slice().sort();
     output_types.dedup();
     if output_types.len() == 0 {
-        output_types.push(link::OutputTypeExe);
+        output_types.push(write::OutputTypeExe);
     }
 
     let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 3d0678aa0e7..09bf69bff4c 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<ModuleTranslation>,
+    pub metadata_module: ModuleTranslation,
     pub link: LinkMeta,
     pub metadata: Vec<u8>,
     pub reachable: Vec<String>,
@@ -472,23 +477,23 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
                                trans: &CrateTranslation,
                                outputs: &OutputFilenames) {
     if sess.opts.cg.no_integrated_as {
-        let output_type = link::OutputTypeAssembly;
+        let output_type = write::OutputTypeAssembly;
 
         time(sess.time_passes(), "LLVM passes", (), |_|
-            link::write::run_passes(sess, trans, [output_type], outputs));
+            write::run_passes(sess, trans, [output_type], outputs));
 
-        link::write::run_assembler(sess, outputs);
+        write::run_assembler(sess, outputs);
 
         // Remove assembly source, unless --save-temps was specified
         if !sess.opts.cg.save_temps {
-            fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
+            fs::unlink(&outputs.temp_path(write::OutputTypeAssembly)).unwrap();
         }
     } else {
         time(sess.time_passes(), "LLVM passes", (), |_|
-            link::write::run_passes(sess,
-                                    trans,
-                                    sess.opts.output_types.as_slice(),
-                                    outputs));
+            write::run_passes(sess,
+                              trans,
+                              sess.opts.output_types.as_slice(),
+                              outputs));
     }
 }
 
@@ -532,7 +537,7 @@ pub fn stop_after_phase_2(sess: &Session) -> bool {
 }
 
 pub fn stop_after_phase_5(sess: &Session) -> bool {
-    if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
+    if !sess.opts.output_types.iter().any(|&i| i == write::OutputTypeExe) {
         debug!("not building executable, returning early from compile_input");
         return true;
     }
@@ -548,7 +553,7 @@ fn write_out_deps(sess: &Session,
     for output_type in sess.opts.output_types.iter() {
         let file = outputs.path(*output_type);
         match *output_type {
-            link::OutputTypeExe => {
+            write::OutputTypeExe => {
                 for output in sess.crate_types.borrow().iter() {
                     let p = link::filename_for_input(sess, *output,
                                                      id, &file);
@@ -679,6 +684,7 @@ pub fn collect_crate_metadata(session: &Session,
     session.opts.cg.metadata.clone()
 }
 
+#[deriving(Clone)]
 pub struct OutputFilenames {
     pub out_directory: Path,
     pub out_filestem: String,
@@ -687,7 +693,7 @@ pub struct OutputFilenames {
 }
 
 impl OutputFilenames {
-    pub fn path(&self, flavor: link::OutputType) -> Path {
+    pub fn path(&self, flavor: write::OutputType) -> Path {
         match self.single_output_file {
             Some(ref path) => return path.clone(),
             None => {}
@@ -695,14 +701,14 @@ impl OutputFilenames {
         self.temp_path(flavor)
     }
 
-    pub fn temp_path(&self, flavor: link::OutputType) -> Path {
+    pub fn temp_path(&self, flavor: write::OutputType) -> Path {
         let base = self.out_directory.join(self.filestem());
         match flavor {
-            link::OutputTypeBitcode => base.with_extension("bc"),
-            link::OutputTypeAssembly => base.with_extension("s"),
-            link::OutputTypeLlvmAssembly => base.with_extension("ll"),
-            link::OutputTypeObject => base.with_extension("o"),
-            link::OutputTypeExe => base,
+            write::OutputTypeBitcode => base.with_extension("bc"),
+            write::OutputTypeAssembly => base.with_extension("s"),
+            write::OutputTypeLlvmAssembly => base.with_extension("ll"),
+            write::OutputTypeObject => base.with_extension("o"),
+            write::OutputTypeExe => base,
         }
     }
 
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 03dfcec18db..e4eac80c4af 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 1386e23b77d..da73f25b6d1 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 040577e7048..1b93e197441 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<Opt<'a>> {
             }
             ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => {
                 // This is either an enum variant or a variable binding.
-                let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id);
+                let opt_def = ccx.tcx().def_map.borrow().find_copy(&cur.id);
                 match opt_def {
                     Some(def::DefVariant(enum_id, var_id, _)) => {
                         let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index 8a5581e592a..21673a67ea8 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<Repr> {
 /// Decides how to represent a given type.
 pub fn represent_type(cx: &CrateContext, t: ty::t) -> Rc<Repr> {
     debug!("Representing: {}", ty_to_string(cx.tcx(), t));
-    match cx.adt_reprs.borrow().find(&t) {
+    match cx.adt_reprs().borrow().find(&t) {
         Some(repr) => return repr.clone(),
         None => {}
     }
 
     let repr = Rc::new(represent_type_uncached(cx, t));
     debug!("Represented as: {:?}", repr)
-    cx.adt_reprs.borrow_mut().insert(t, repr.clone());
+    cx.adt_reprs().borrow_mut().insert(t, repr.clone());
     repr
 }
 
@@ -423,7 +423,7 @@ fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntTyp
             attempts = choose_shortest;
         },
         attr::ReprPacked => {
-            cx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
+            cx.tcx().sess.bug("range_to_inttype: found ReprPacked on an enum");
         }
     }
     for &ity in attempts.iter() {
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 76e2266fcb9..49e058333e5 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<String>,
     start: u64,
     istart: uint,
@@ -149,7 +151,7 @@ impl<'a> StatRecorder<'a> {
         } else {
             0
         };
-        let istart = ccx.stats.n_llvm_insns.get();
+        let istart = ccx.stats().n_llvm_insns.get();
         StatRecorder {
             ccx: ccx,
             name: Some(name),
@@ -165,13 +167,13 @@ impl<'a> Drop for StatRecorder<'a> {
         if self.ccx.sess().trans_stats() {
             let end = time::precise_time_ns();
             let elapsed = ((end - self.start) / 1_000_000) as uint;
-            let iend = self.ccx.stats.n_llvm_insns.get();
-            self.ccx.stats.fn_stats.borrow_mut().push((self.name.take().unwrap(),
+            let iend = self.ccx.stats().n_llvm_insns.get();
+            self.ccx.stats().fn_stats.borrow_mut().push((self.name.take().unwrap(),
                                                        elapsed,
                                                        iend - self.istart));
-            self.ccx.stats.n_fns.set(self.ccx.stats.n_fns.get() + 1);
+            self.ccx.stats().n_fns.set(self.ccx.stats().n_fns.get() + 1);
             // Reset LLVM insn count to avoid compound costs.
-            self.ccx.stats.n_llvm_insns.set(self.istart);
+            self.ccx.stats().n_llvm_insns.set(self.istart);
         }
     }
 }
@@ -182,7 +184,7 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
 
     let llfn: ValueRef = name.with_c_str(|buf| {
         unsafe {
-            llvm::LLVMGetOrInsertFunction(ccx.llmod, buf, ty.to_ref())
+            llvm::LLVMGetOrInsertFunction(ccx.llmod(), buf, ty.to_ref())
         }
     });
 
@@ -198,7 +200,7 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
         _ => {}
     }
 
-    if ccx.tcx.sess.opts.cg.no_redzone {
+    if ccx.tcx().sess.opts.cg.no_redzone {
         unsafe {
             llvm::LLVMAddFunctionAttribute(llfn,
                                            llvm::FunctionIndex as c_uint,
@@ -243,7 +245,7 @@ pub fn get_extern_fn(ccx: &CrateContext,
 }
 
 fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::DefId) -> ValueRef {
-    match ccx.externs.borrow().find_equiv(&name) {
+    match ccx.externs().borrow().find_equiv(&name) {
         Some(n) => return *n,
         None => ()
     }
@@ -254,7 +256,7 @@ fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::De
         set_llvm_fn_attrs(attrs.as_slice(), f)
     });
 
-    ccx.externs.borrow_mut().insert(name.to_string(), f);
+    ccx.externs().borrow_mut().insert(name.to_string(), f);
     f
 }
 
@@ -264,14 +266,14 @@ pub fn self_type_for_unboxed_closure(ccx: &CrateContext,
     let unboxed_closure_type = ty::mk_unboxed_closure(ccx.tcx(),
                                                       closure_id,
                                                       ty::ReStatic);
-    let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+    let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
     let unboxed_closure = unboxed_closures.get(&closure_id);
     match unboxed_closure.kind {
         ty::FnUnboxedClosureKind => {
-            ty::mk_imm_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type)
+            ty::mk_imm_rptr(ccx.tcx(), ty::ReStatic, unboxed_closure_type)
         }
         ty::FnMutUnboxedClosureKind => {
-            ty::mk_mut_rptr(&ccx.tcx, ty::ReStatic, unboxed_closure_type)
+            ty::mk_mut_rptr(ccx.tcx(), ty::ReStatic, unboxed_closure_type)
         }
         ty::FnOnceUnboxedClosureKind => unboxed_closure_type,
     }
@@ -279,7 +281,7 @@ pub fn self_type_for_unboxed_closure(ccx: &CrateContext,
 
 pub fn kind_for_unboxed_closure(ccx: &CrateContext, closure_id: ast::DefId)
                                 -> ty::UnboxedClosureKind {
-    let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+    let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
     unboxed_closures.get(&closure_id).kind
 }
 
@@ -292,7 +294,7 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
             (f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx)))
         }
         ty::ty_unboxed_closure(closure_did, _) => {
-            let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+            let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
             let unboxed_closure = unboxed_closures.get(&closure_did);
             let function_type = unboxed_closure.closure_type.clone();
             let self_type = self_type_for_unboxed_closure(ccx, closure_did);
@@ -308,7 +310,7 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
     let llfty = type_of_rust_fn(ccx, env, inputs.as_slice(), output, abi);
     debug!("decl_rust_fn(input count={},type={})",
            inputs.len(),
-           ccx.tn.type_to_string(llfty));
+           ccx.tn().type_to_string(llfty));
 
     let llfn = decl_fn(ccx, name, llvm::CCallConv, llfty, output);
     let attrs = get_fn_llvm_attributes(ccx, fn_ty);
@@ -413,15 +415,15 @@ pub fn malloc_raw_dyn_managed<'a>(
 // Type descriptor and type glue stuff
 
 pub fn get_tydesc(ccx: &CrateContext, t: ty::t) -> Rc<tydesc_info> {
-    match ccx.tydescs.borrow().find(&t) {
+    match ccx.tydescs().borrow().find(&t) {
         Some(inf) => return inf.clone(),
         _ => { }
     }
 
-    ccx.stats.n_static_tydescs.set(ccx.stats.n_static_tydescs.get() + 1u);
+    ccx.stats().n_static_tydescs.set(ccx.stats().n_static_tydescs.get() + 1u);
     let inf = Rc::new(glue::declare_tydesc(ccx, t));
 
-    ccx.tydescs.borrow_mut().insert(t, inf.clone());
+    ccx.tydescs().borrow_mut().insert(t, inf.clone());
     inf
 }
 
@@ -492,10 +494,10 @@ pub fn unset_split_stack(f: ValueRef) {
 // Double-check that we never ask LLVM to declare the same symbol twice. It
 // silently mangles such symbols, breaking our linkage model.
 pub fn note_unique_llvm_symbol(ccx: &CrateContext, sym: String) {
-    if ccx.all_llvm_symbols.borrow().contains(&sym) {
+    if ccx.all_llvm_symbols().borrow().contains(&sym) {
         ccx.sess().bug(format!("duplicate LLVM symbol: {}", sym).as_slice());
     }
-    ccx.all_llvm_symbols.borrow_mut().insert(sym);
+    ccx.all_llvm_symbols().borrow_mut().insert(sym);
 }
 
 
@@ -532,7 +534,7 @@ pub fn get_res_dtor(ccx: &CrateContext,
         let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID,
                                      [glue::get_drop_glue_type(ccx, t)], ty::mk_nil());
         get_extern_fn(ccx,
-                      &mut *ccx.externs.borrow_mut(),
+                      &mut *ccx.externs().borrow_mut(),
                       name.as_slice(),
                       llvm::CCallConv,
                       llty,
@@ -961,8 +963,8 @@ pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> Val
         }
         _ => {
             let llty = type_of(ccx, t);
-            get_extern_const(&mut *ccx.externs.borrow_mut(),
-                             ccx.llmod,
+            get_extern_const(&mut *ccx.externs().borrow_mut(),
+                             ccx.llmod(),
                              name.as_slice(),
                              llty)
         }
@@ -1165,7 +1167,7 @@ pub fn call_memcpy(cx: &Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef,
     let memcpy = ccx.get_intrinsic(&key);
     let src_ptr = PointerCast(cx, src, Type::i8p(ccx));
     let dst_ptr = PointerCast(cx, dst, Type::i8p(ccx));
-    let size = IntCast(cx, n_bytes, ccx.int_type);
+    let size = IntCast(cx, n_bytes, ccx.int_type());
     let align = C_i32(ccx, align as i32);
     let volatile = C_bool(ccx, false);
     Call(cx, memcpy, [dst_ptr, src_ptr, size, align, volatile], None);
@@ -1426,7 +1428,7 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
            if id == -1 {
                "".to_string()
            } else {
-               ccx.tcx.map.path_to_string(id).to_string()
+               ccx.tcx().map.path_to_string(id).to_string()
            },
            id, param_substs.repr(ccx.tcx()));
 
@@ -1786,7 +1788,7 @@ pub fn trans_closure(ccx: &CrateContext,
                      is_unboxed_closure: IsUnboxedClosureFlag,
                      maybe_load_env: <'a>|&'a Block<'a>, ScopeId|
                                          -> &'a Block<'a>) {
-    ccx.stats.n_closures.set(ccx.stats.n_closures.get() + 1);
+    ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);
 
     let _icx = push_ctxt("trans_closure");
     set_uwtable(llfndecl);
@@ -1820,7 +1822,7 @@ pub fn trans_closure(ccx: &CrateContext,
                ty_to_string(ccx.tcx(), *monomorphized_arg_type));
     }
     debug!("trans_closure: function lltype: {}",
-           bcx.fcx.ccx.tn.val_to_string(bcx.fcx.llfn));
+           bcx.fcx.ccx.tn().val_to_string(bcx.fcx.llfn));
 
     let arg_datums = if abi != RustCall {
         create_datums_for_fn_args(&fcx,
@@ -1912,7 +1914,7 @@ pub fn trans_fn(ccx: &CrateContext,
                 param_substs: &param_substs,
                 id: ast::NodeId,
                 attrs: &[ast::Attribute]) {
-    let _s = StatRecorder::new(ccx, ccx.tcx.map.path_to_string(id).to_string());
+    let _s = StatRecorder::new(ccx, ccx.tcx().map.path_to_string(id).to_string());
     debug!("trans_fn(param_substs={})", param_substs.repr(ccx.tcx()));
     let _icx = push_ctxt("trans_fn");
     let fn_ty = ty::node_id_to_type(ccx.tcx(), id);
@@ -1958,7 +1960,7 @@ pub fn trans_named_tuple_constructor<'a>(mut bcx: &'a Block<'a>,
                                          dest: expr::Dest) -> Result<'a> {
 
     let ccx = bcx.fcx.ccx;
-    let tcx = &ccx.tcx;
+    let tcx = ccx.tcx();
 
     let result_ty = match ty::get(ctor_ty).sty {
         ty::ty_bare_fn(ref bft) => bft.sig.output,
@@ -2064,7 +2066,7 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
 fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) {
     let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully
 
-    let levels = ccx.tcx.node_lint_levels.borrow();
+    let levels = ccx.tcx().node_lint_levels.borrow();
     let lint_id = lint::LintId::of(lint::builtin::VARIANT_SIZE_DIFFERENCE);
     let lvlsrc = match levels.find(&(id, lint_id)) {
         None | Some(&(lint::Allow, _)) => return,
@@ -2114,7 +2116,7 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span,
 }
 
 pub struct TransItemVisitor<'a> {
-    pub ccx: &'a CrateContext,
+    pub ccx: &'a CrateContext<'a>,
 }
 
 impl<'a> Visitor<()> for TransItemVisitor<'a> {
@@ -2123,29 +2125,95 @@ impl<'a> Visitor<()> for TransItemVisitor<'a> {
     }
 }
 
+/// Enum describing the origin of an LLVM `Value`, for linkage purposes.
+pub enum ValueOrigin {
+    /// The LLVM `Value` is in this context because the corresponding item was
+    /// assigned to the current compilation unit.
+    OriginalTranslation,
+    /// The `Value`'s corresponding item was assigned to some other compilation
+    /// unit, but the `Value` was translated in this context anyway because the
+    /// item is marked `#[inline]`.
+    InlinedCopy,
+}
+
+/// Set the appropriate linkage for an LLVM `ValueRef` (function or global).
+/// If the `llval` is the direct translation of a specific Rust item, `id`
+/// should be set to the `NodeId` of that item.  (This mapping should be
+/// 1-to-1, so monomorphizations and drop/visit glue should have `id` set to
+/// `None`.)  `llval_origin` indicates whether `llval` is the translation of an
+/// item assigned to `ccx`'s compilation unit or an inlined copy of an item
+/// assigned to a different compilation unit.
+pub fn update_linkage(ccx: &CrateContext,
+                      llval: ValueRef,
+                      id: Option<ast::NodeId>,
+                      llval_origin: ValueOrigin) {
+    match llval_origin {
+        InlinedCopy => {
+            // `llval` is a translation of an item defined in a separate
+            // compilation unit.  This only makes sense if there are at least
+            // two compilation units.
+            assert!(ccx.sess().opts.cg.codegen_units > 1);
+            // `llval` is a copy of something defined elsewhere, so use
+            // `AvailableExternallyLinkage` to avoid duplicating code in the
+            // output.
+            llvm::SetLinkage(llval, llvm::AvailableExternallyLinkage);
+            return;
+        },
+        OriginalTranslation => {},
+    }
+
+    match id {
+        Some(id) if ccx.reachable().contains(&id) => {
+            llvm::SetLinkage(llval, llvm::ExternalLinkage);
+        },
+        _ => {
+            // `id` does not refer to an item in `ccx.reachable`.
+            if ccx.sess().opts.cg.codegen_units > 1 {
+                llvm::SetLinkage(llval, llvm::ExternalLinkage);
+            } else {
+                llvm::SetLinkage(llval, llvm::InternalLinkage);
+            }
+        },
+    }
+}
+
 pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
     let _icx = push_ctxt("trans_item");
+
+    let from_external = ccx.external_srcs().borrow().contains_key(&item.id);
+
     match item.node {
       ast::ItemFn(ref decl, _fn_style, abi, ref generics, ref body) => {
         if !generics.is_type_parameterized() {
-            let llfn = get_item_val(ccx, item.id);
-            if abi != Rust {
-                foreign::trans_rust_fn_with_foreign_abi(ccx,
-                                                        &**decl,
-                                                        &**body,
-                                                        item.attrs.as_slice(),
-                                                        llfn,
-                                                        &param_substs::empty(),
-                                                        item.id,
-                                                        None);
-            } else {
-                trans_fn(ccx,
-                         &**decl,
-                         &**body,
-                         llfn,
-                         &param_substs::empty(),
-                         item.id,
-                         item.attrs.as_slice());
+            let trans_everywhere = attr::requests_inline(item.attrs.as_slice());
+            // Ignore `trans_everywhere` for cross-crate inlined items
+            // (`from_external`).  `trans_item` will be called once for each
+            // compilation unit that references the item, so it will still get
+            // translated everywhere it's needed.
+            for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
+                let llfn = get_item_val(ccx, item.id);
+                if abi != Rust {
+                    foreign::trans_rust_fn_with_foreign_abi(ccx,
+                                                            &**decl,
+                                                            &**body,
+                                                            item.attrs.as_slice(),
+                                                            llfn,
+                                                            &param_substs::empty(),
+                                                            item.id,
+                                                            None);
+                } else {
+                    trans_fn(ccx,
+                             &**decl,
+                             &**body,
+                             llfn,
+                             &param_substs::empty(),
+                             item.id,
+                             item.attrs.as_slice());
+                }
+                update_linkage(ccx,
+                               llfn,
+                               Some(item.id),
+                               if is_origin { OriginalTranslation } else { InlinedCopy });
             }
         }
 
@@ -2162,7 +2230,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
                          item.id);
       }
       ast::ItemMod(ref m) => {
-        trans_mod(ccx, m);
+        trans_mod(&ccx.rotate(), m);
       }
       ast::ItemEnum(ref enum_definition, _) => {
         enum_variant_size_lint(ccx, enum_definition, item.span, item.id);
@@ -2171,7 +2239,18 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
           // Recurse on the expression to catch items in blocks
           let mut v = TransItemVisitor{ ccx: ccx };
           v.visit_expr(&**expr, ());
-          consts::trans_const(ccx, m, item.id);
+
+          let trans_everywhere = attr::requests_inline(item.attrs.as_slice());
+          for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
+              consts::trans_const(ccx, m, item.id);
+
+              let g = get_item_val(ccx, item.id);
+              update_linkage(ccx,
+                             g,
+                             Some(item.id),
+                             if is_origin { OriginalTranslation } else { InlinedCopy });
+          }
+
           // Do static_assert checking. It can't really be done much earlier
           // because we need to get the value of the bool out of LLVM
           if attr::contains_name(item.attrs.as_slice(), "static_assert") {
@@ -2181,7 +2260,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
                                          static");
               }
 
-              let v = ccx.const_values.borrow().get_copy(&item.id);
+              let v = ccx.const_values().borrow().get_copy(&item.id);
               unsafe {
                   if !(llvm::LLVMConstIntGetZExtValue(v) != 0) {
                       ccx.sess().span_fatal(expr.span, "static assertion failed");
@@ -2218,21 +2297,17 @@ pub fn trans_mod(ccx: &CrateContext, m: &ast::Mod) {
 
 fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::NodeId,
                       llfn: ValueRef) {
-    ccx.item_symbols.borrow_mut().insert(node_id, sym);
-
-    if !ccx.reachable.contains(&node_id) {
-        llvm::SetLinkage(llfn, llvm::InternalLinkage);
-    }
+    ccx.item_symbols().borrow_mut().insert(node_id, sym);
 
     // The stack exhaustion lang item shouldn't have a split stack because
     // otherwise it would continue to be exhausted (bad), and both it and the
     // eh_personality functions need to be externally linkable.
     let def = ast_util::local_def(node_id);
-    if ccx.tcx.lang_items.stack_exhausted() == Some(def) {
+    if ccx.tcx().lang_items.stack_exhausted() == Some(def) {
         unset_split_stack(llfn);
         llvm::SetLinkage(llfn, llvm::ExternalLinkage);
     }
-    if ccx.tcx.lang_items.eh_personality() == Some(def) {
+    if ccx.tcx().lang_items.eh_personality() == Some(def) {
         llvm::SetLinkage(llfn, llvm::ExternalLinkage);
     }
 
@@ -2268,7 +2343,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
         ty::ty_closure(ref f) => (f.sig.clone(), f.abi, true),
         ty::ty_bare_fn(ref f) => (f.sig.clone(), f.abi, false),
         ty::ty_unboxed_closure(closure_did, _) => {
-            let unboxed_closures = ccx.tcx.unboxed_closures.borrow();
+            let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
             let ref function_type = unboxed_closures.get(&closure_did)
                                                     .closure_type;
 
@@ -2485,8 +2560,8 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
     fn create_entry_fn(ccx: &CrateContext,
                        rust_main: ValueRef,
                        use_start_lang_item: bool) {
-        let llfty = Type::func([ccx.int_type, Type::i8p(ccx).ptr_to()],
-                               &ccx.int_type);
+        let llfty = Type::func([ccx.int_type(), Type::i8p(ccx).ptr_to()],
+                               &ccx.int_type());
 
         let llfn = decl_cdecl_fn(ccx, "main", llfty, ty::mk_nil());
 
@@ -2498,15 +2573,15 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
 
         let llbb = "top".with_c_str(|buf| {
             unsafe {
-                llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llfn, buf)
+                llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, buf)
             }
         });
-        let bld = ccx.builder.b;
+        let bld = ccx.raw_builder();
         unsafe {
             llvm::LLVMPositionBuilderAtEnd(bld, llbb);
 
             let (start_fn, args) = if use_start_lang_item {
-                let start_def_id = match ccx.tcx.lang_items.require(StartFnLangItem) {
+                let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) {
                     Ok(id) => id,
                     Err(s) => { ccx.sess().fatal(s.as_slice()); }
                 };
@@ -2553,11 +2628,20 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
 
 fn exported_name(ccx: &CrateContext, id: ast::NodeId,
                  ty: ty::t, attrs: &[ast::Attribute]) -> String {
+    match ccx.external_srcs().borrow().find(&id) {
+        Some(&did) => {
+            let sym = csearch::get_symbol(&ccx.sess().cstore, did);
+            debug!("found item {} in other crate...", sym);
+            return sym;
+        }
+        None => {}
+    }
+
     match attr::first_attr_value_str_by_name(attrs, "export_name") {
         // Use provided name
         Some(name) => name.get().to_string(),
 
-        _ => ccx.tcx.map.with_path(id, |mut path| {
+        _ => ccx.tcx().map.with_path(id, |mut path| {
             if attr::contains_name(attrs, "no_mangle") {
                 // Don't mangle
                 path.last().unwrap().to_string()
@@ -2577,13 +2661,12 @@ fn exported_name(ccx: &CrateContext, id: ast::NodeId,
 pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
     debug!("get_item_val(id=`{:?}`)", id);
 
-    match ccx.item_vals.borrow().find_copy(&id) {
+    match ccx.item_vals().borrow().find_copy(&id) {
         Some(v) => return v,
         None => {}
     }
 
-    let mut foreign = false;
-    let item = ccx.tcx.map.get(id);
+    let item = ccx.tcx().map.get(id);
     let val = match item {
         ast_map::NodeItem(i) => {
             let ty = ty::node_id_to_type(ccx.tcx(), i.id);
@@ -2596,33 +2679,20 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
                     // using the current crate's name/version
                     // information in the hash of the symbol
                     debug!("making {}", sym);
-                    let (sym, is_local) = {
-                        match ccx.external_srcs.borrow().find(&i.id) {
-                            Some(&did) => {
-                                debug!("but found in other crate...");
-                                (csearch::get_symbol(&ccx.sess().cstore,
-                                                     did), false)
-                            }
-                            None => (sym, true)
-                        }
-                    };
+                    let is_local = !ccx.external_srcs().borrow().contains_key(&id);
 
                     // We need the translated value here, because for enums the
                     // LLVM type is not fully determined by the Rust type.
                     let (v, inlineable, _) = consts::const_expr(ccx, &**expr, is_local);
-                    ccx.const_values.borrow_mut().insert(id, v);
+                    ccx.const_values().borrow_mut().insert(id, v);
                     let mut inlineable = inlineable;
 
                     unsafe {
                         let llty = llvm::LLVMTypeOf(v);
                         let g = sym.as_slice().with_c_str(|buf| {
-                            llvm::LLVMAddGlobal(ccx.llmod, llty, buf)
+                            llvm::LLVMAddGlobal(ccx.llmod(), llty, buf)
                         });
 
-                        if !ccx.reachable.contains(&id) {
-                            llvm::SetLinkage(g, llvm::InternalLinkage);
-                        }
-
                         // Apply the `unnamed_addr` attribute if
                         // requested
                         if !ast_util::static_has_significant_address(
@@ -2655,11 +2725,11 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
 
                         if !inlineable {
                             debug!("{} not inlined", sym);
-                            ccx.non_inlineable_statics.borrow_mut()
+                            ccx.non_inlineable_statics().borrow_mut()
                                                       .insert(id);
                         }
 
-                        ccx.item_symbols.borrow_mut().insert(i.id, sym);
+                        ccx.item_symbols().borrow_mut().insert(i.id, sym);
                         g
                     }
                 }
@@ -2713,11 +2783,9 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
         }
 
         ast_map::NodeForeignItem(ni) => {
-            foreign = true;
-
             match ni.node {
                 ast::ForeignItemFn(..) => {
-                    let abi = ccx.tcx.map.get_foreign_abi(id);
+                    let abi = ccx.tcx().map.get_foreign_abi(id);
                     let ty = ty::node_id_to_type(ccx.tcx(), ni.id);
                     let name = foreign::link_name(&*ni);
                     foreign::register_foreign_item_fn(ccx, abi, ty,
@@ -2740,8 +2808,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
             };
             assert!(args.len() != 0u);
             let ty = ty::node_id_to_type(ccx.tcx(), id);
-            let parent = ccx.tcx.map.get_parent(id);
-            let enm = ccx.tcx.map.expect_item(parent);
+            let parent = ccx.tcx().map.get_parent(id);
+            let enm = ccx.tcx().map.expect_item(parent);
             let sym = exported_name(ccx,
                                     id,
                                     ty,
@@ -2766,8 +2834,8 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
                 }
                 Some(ctor_id) => ctor_id,
             };
-            let parent = ccx.tcx.map.get_parent(id);
-            let struct_item = ccx.tcx.map.expect_item(parent);
+            let parent = ccx.tcx().map.get_parent(id);
+            let struct_item = ccx.tcx().map.expect_item(parent);
             let ty = ty::node_id_to_type(ccx.tcx(), ctor_id);
             let sym = exported_name(ccx,
                                     id,
@@ -2786,14 +2854,16 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
         }
     };
 
-    // foreign items (extern fns and extern statics) don't have internal
-    // linkage b/c that doesn't quite make sense. Otherwise items can
-    // have internal linkage if they're not reachable.
-    if !foreign && !ccx.reachable.contains(&id) {
-        llvm::SetLinkage(val, llvm::InternalLinkage);
-    }
+    // All LLVM globals and functions are initially created as external-linkage
+    // declarations.  If `trans_item`/`trans_fn` later turns the declaration
+    // into a definition, it adjusts the linkage then (using `update_linkage`).
+    //
+    // The exception is foreign items, which have their linkage set inside the
+    // call to `foreign::register_*` above.  We don't touch the linkage after
+    // that (`foreign::trans_foreign_mod` doesn't adjust the linkage like the
+    // other item translation functions do).
 
-    ccx.item_vals.borrow_mut().insert(id, val);
+    ccx.item_vals().borrow_mut().insert(id, val);
     val
 }
 
@@ -2810,26 +2880,27 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId,
 
 pub fn p2i(ccx: &CrateContext, v: ValueRef) -> ValueRef {
     unsafe {
-        return llvm::LLVMConstPtrToInt(v, ccx.int_type.to_ref());
+        return llvm::LLVMConstPtrToInt(v, ccx.int_type().to_ref());
     }
 }
 
-pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeInlinedItem<'r>)
+pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r SharedCrateContext,
+                                      ie: encoder::EncodeInlinedItem<'r>)
     -> encoder::EncodeParams<'r> {
         encoder::EncodeParams {
             diag: cx.sess().diagnostic(),
             tcx: cx.tcx(),
-            reexports2: &cx.exp_map2,
-            item_symbols: &cx.item_symbols,
-            non_inlineable_statics: &cx.non_inlineable_statics,
-            link_meta: &cx.link_meta,
+            reexports2: cx.exp_map2(),
+            item_symbols: cx.item_symbols(),
+            non_inlineable_statics: cx.non_inlineable_statics(),
+            link_meta: cx.link_meta(),
             cstore: &cx.sess().cstore,
             encode_inlined_item: ie,
-            reachable: &cx.reachable,
+            reachable: cx.reachable(),
         }
 }
 
-pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
+pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
     use flate;
 
     let any_library = cx.sess().crate_types.borrow().iter().any(|ty| {
@@ -2851,14 +2922,14 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
                              cx.sess().fatal("failed to compress metadata")
                          }
                      }.as_slice());
-    let llmeta = C_bytes(cx, compressed.as_slice());
-    let llconst = C_struct(cx, [llmeta], false);
+    let llmeta = C_bytes_in_context(cx.metadata_llcx(), compressed.as_slice());
+    let llconst = C_struct_in_context(cx.metadata_llcx(), [llmeta], false);
     let name = format!("rust_metadata_{}_{}",
-                       cx.link_meta.crate_name,
-                       cx.link_meta.crate_hash);
+                       cx.link_meta().crate_name,
+                       cx.link_meta().crate_hash);
     let llglobal = name.with_c_str(|buf| {
         unsafe {
-            llvm::LLVMAddGlobal(cx.metadata_llmod, val_ty(llconst).to_ref(), buf)
+            llvm::LLVMAddGlobal(cx.metadata_llmod(), val_ty(llconst).to_ref(), buf)
         }
     });
     unsafe {
@@ -2871,6 +2942,84 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
     return metadata;
 }
 
+/// Find any symbols that are defined in one compilation unit, but not declared
+/// in any other compilation unit.  Give these symbols internal linkage.
+fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<String>) {
+    use std::c_str::CString;
+
+    unsafe {
+        let mut declared = HashSet::new();
+
+        let iter_globals = |llmod| {
+            ValueIter {
+                cur: llvm::LLVMGetFirstGlobal(llmod),
+                step: llvm::LLVMGetNextGlobal,
+            }
+        };
+
+        let iter_functions = |llmod| {
+            ValueIter {
+                cur: llvm::LLVMGetFirstFunction(llmod),
+                step: llvm::LLVMGetNextFunction,
+            }
+        };
+
+        // Collect all external declarations in all compilation units.
+        for ccx in cx.iter() {
+            for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
+                let linkage = llvm::LLVMGetLinkage(val);
+                // We only care about external declarations (not definitions)
+                // and available_externally definitions.
+                if !(linkage == llvm::ExternalLinkage as c_uint &&
+                     llvm::LLVMIsDeclaration(val) != 0) &&
+                   !(linkage == llvm::AvailableExternallyLinkage as c_uint) {
+                    continue
+                }
+
+                let name = CString::new(llvm::LLVMGetValueName(val), false);
+                declared.insert(name);
+            }
+        }
+
+        // Examine each external definition.  If the definition is not used in
+        // any other compilation unit, and is not reachable from other crates,
+        // then give it internal linkage.
+        for ccx in cx.iter() {
+            for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
+                // We only care about external definitions.
+                if !(llvm::LLVMGetLinkage(val) == llvm::ExternalLinkage as c_uint &&
+                     llvm::LLVMIsDeclaration(val) == 0) {
+                    continue
+                }
+
+                let name = CString::new(llvm::LLVMGetValueName(val), false);
+                if !declared.contains(&name) &&
+                   !reachable.contains_equiv(&name.as_str().unwrap()) {
+                    llvm::SetLinkage(val, llvm::InternalLinkage);
+                }
+            }
+        }
+    }
+
+
+    struct ValueIter {
+        cur: ValueRef,
+        step: unsafe extern "C" fn(ValueRef) -> ValueRef,
+    }
+
+    impl Iterator<ValueRef> for ValueIter {
+        fn next(&mut self) -> Option<ValueRef> {
+            let old = self.cur;
+            if !old.is_null() {
+                self.cur = unsafe { (self.step)(old) };
+                Some(old)
+            } else {
+                None
+            }
+        }
+    }
+}
+
 pub fn trans_crate(krate: ast::Crate,
                    analysis: CrateAnalysis) -> (ty::ctxt, CrateTranslation) {
     let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis;
@@ -2895,52 +3044,55 @@ pub fn trans_crate(krate: ast::Crate,
 
     let link_meta = link::build_link_meta(&tcx.sess, &krate, name);
 
-    // Append ".rs" to crate name as LLVM module identifier.
-    //
-    // LLVM code generator emits a ".file filename" directive
-    // for ELF backends. Value of the "filename" is set as the
-    // LLVM module identifier.  Due to a LLVM MC bug[1], LLVM
-    // crashes if the module identifier is same as other symbols
-    // such as a function name in the module.
-    // 1. http://llvm.org/bugs/show_bug.cgi?id=11479
-    let mut llmod_id = link_meta.crate_name.clone();
-    llmod_id.push_str(".rs");
-
-    let 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<String> = ccx.reachable.iter().filter_map(|id| {
-        ccx.item_symbols.borrow().find(id).map(|s| s.to_string())
+    let mut reachable: Vec<String> = shared_ccx.reachable().iter().filter_map(|id| {
+        shared_ccx.item_symbols().borrow().find(id).map(|s| s.to_string())
     }).collect();
 
     // For the purposes of LTO, we add to the reachable set all of the upstream
     // reachable extern fns. These functions are all part of the public ABI of
     // the final product, so LTO needs to preserve them.
-    ccx.sess().cstore.iter_crate_data(|cnum, _| {
-        let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum);
+    shared_ccx.sess().cstore.iter_crate_data(|cnum, _| {
+        let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum);
         reachable.extend(syms.move_iter().map(|did| {
-            csearch::get_symbol(&ccx.sess().cstore, did)
+            csearch::get_symbol(&shared_ccx.sess().cstore, did)
         }));
     });
 
@@ -2986,18 +3138,26 @@ pub fn trans_crate(krate: ast::Crate,
     // referenced from rt/rust_try.ll
     reachable.push("rust_eh_personality_catch".to_string());
 
-    let metadata_module = ccx.metadata_llmod;
-    let formats = ccx.tcx.dependency_formats.borrow().clone();
+    if codegen_units > 1 {
+        internalize_symbols(&shared_ccx, &reachable.iter().map(|x| x.clone()).collect());
+    }
+
+    let metadata_module = ModuleTranslation {
+        llcx: shared_ccx.metadata_llcx(),
+        llmod: shared_ccx.metadata_llmod(),
+    };
+    let formats = shared_ccx.tcx().dependency_formats.borrow().clone();
     let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins");
 
-    (ccx.tcx, CrateTranslation {
-        context: llcx,
-        module: llmod,
-        link: link_meta,
+    let translation = CrateTranslation {
+        modules: modules,
         metadata_module: metadata_module,
+        link: link_meta,
         metadata: metadata,
         reachable: reachable,
         crate_formats: formats,
         no_builtins: no_builtins,
-    })
+    };
+
+    (shared_ccx.take_tcx(), translation)
 }
diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs
index 76059e8df4f..fd988eb7fc1 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 b3192a405be..322a6a3cc90 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::<Vec<String>>()
                    .connect(", "));
 
@@ -488,7 +489,7 @@ impl<'a> Builder<'a> {
             let v = [min, max];
 
             llvm::LLVMSetMetadata(value, llvm::MD_range as c_uint,
-                                  llvm::LLVMMDNodeInContext(self.ccx.llcx,
+                                  llvm::LLVMMDNodeInContext(self.ccx.llcx(),
                                                             v.as_ptr(), v.len() as c_uint));
         }
 
@@ -497,8 +498,8 @@ impl<'a> Builder<'a> {
 
     pub fn store(&self, val: ValueRef, ptr: ValueRef) {
         debug!("Store {} -> {}",
-               self.ccx.tn.val_to_string(val),
-               self.ccx.tn.val_to_string(ptr));
+               self.ccx.tn().val_to_string(val),
+               self.ccx.tn().val_to_string(ptr));
         assert!(self.llbuilder.is_not_null());
         self.count_insn("store");
         unsafe {
@@ -508,8 +509,8 @@ impl<'a> Builder<'a> {
 
     pub fn volatile_store(&self, val: ValueRef, ptr: ValueRef) {
         debug!("Store {} -> {}",
-               self.ccx.tn.val_to_string(val),
-               self.ccx.tn.val_to_string(ptr));
+               self.ccx.tn().val_to_string(val),
+               self.ccx.tn().val_to_string(ptr));
         assert!(self.llbuilder.is_not_null());
         self.count_insn("store.volatile");
         unsafe {
@@ -520,8 +521,8 @@ impl<'a> Builder<'a> {
 
     pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, order: AtomicOrdering) {
         debug!("Store {} -> {}",
-               self.ccx.tn.val_to_string(val),
-               self.ccx.tn.val_to_string(ptr));
+               self.ccx.tn().val_to_string(val),
+               self.ccx.tn().val_to_string(ptr));
         self.count_insn("store.atomic");
         unsafe {
             let ty = Type::from_ref(llvm::LLVMTypeOf(ptr));
@@ -794,11 +795,11 @@ impl<'a> Builder<'a> {
                          else          { llvm::False };
 
         let argtys = inputs.iter().map(|v| {
-            debug!("Asm Input Type: {:?}", self.ccx.tn.val_to_string(*v));
+            debug!("Asm Input Type: {:?}", self.ccx.tn().val_to_string(*v));
             val_ty(*v)
         }).collect::<Vec<_>>();
 
-        debug!("Asm Output Type: {:?}", self.ccx.tn.type_to_string(output));
+        debug!("Asm Output Type: {:?}", self.ccx.tn().type_to_string(output));
         let fty = Type::func(argtys.as_slice(), &output);
         unsafe {
             let v = llvm::LLVMInlineAsm(
@@ -812,9 +813,9 @@ impl<'a> Builder<'a> {
         self.count_insn("call");
 
         debug!("Call {} with args ({})",
-               self.ccx.tn.val_to_string(llfn),
+               self.ccx.tn().val_to_string(llfn),
                args.iter()
-                   .map(|&v| self.ccx.tn.val_to_string(v))
+                   .map(|&v| self.ccx.tn().val_to_string(v))
                    .collect::<Vec<String>>()
                    .connect(", "));
 
diff --git a/src/librustc/middle/trans/cabi_mips.rs b/src/librustc/middle/trans/cabi_mips.rs
index 77815285428..90bd1521705 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<Type> {
     let r = size % 32;
     if r > 0 {
         unsafe {
-            args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx, r as c_uint)));
+            args.push(Type::from_ref(llvm::LLVMIntTypeInContext(ccx.llcx(), r as c_uint)));
         }
     }
 
diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs
index 515413e03f0..15d296ac723 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 17f1b6ca526..4e9431f2629 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<ValueRef> {
-    if !ccx.tcx.unboxed_closures.borrow().contains_key(&closure_id) {
+    if !ccx.tcx().unboxed_closures.borrow().contains_key(&closure_id) {
         // Not an unboxed closure.
         return None
     }
 
-    match ccx.unboxed_closure_vals.borrow().find(&closure_id) {
+    match ccx.unboxed_closure_vals().borrow().find(&closure_id) {
         Some(llfn) => {
             debug!("get_or_create_declaration_if_unboxed_closure(): found \
                     closure");
@@ -441,10 +441,10 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext,
         None => {}
     }
 
-    let function_type = ty::mk_unboxed_closure(&ccx.tcx,
+    let function_type = ty::mk_unboxed_closure(ccx.tcx(),
                                                closure_id,
                                                ty::ReStatic);
-    let symbol = ccx.tcx.map.with_path(closure_id.node, |path| {
+    let symbol = ccx.tcx().map.with_path(closure_id.node, |path| {
         mangle_internal_name_by_path_and_seq(path, "unboxed_closure")
     });
 
@@ -456,8 +456,8 @@ pub fn get_or_create_declaration_if_unboxed_closure(ccx: &CrateContext,
     debug!("get_or_create_declaration_if_unboxed_closure(): inserting new \
             closure {} (type {})",
            closure_id,
-           ccx.tn.type_to_string(val_ty(llfn)));
-    ccx.unboxed_closure_vals.borrow_mut().insert(closure_id, llfn);
+           ccx.tn().type_to_string(val_ty(llfn)));
+    ccx.unboxed_closure_vals().borrow_mut().insert(closure_id, llfn);
 
     Some(llfn)
 }
@@ -554,7 +554,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
         }
     };
 
-    match ccx.closure_bare_wrapper_cache.borrow().find(&fn_ptr) {
+    match ccx.closure_bare_wrapper_cache().borrow().find(&fn_ptr) {
         Some(&llval) => return llval,
         None => {}
     }
@@ -581,7 +581,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
         decl_rust_fn(ccx, closure_ty, name.as_slice())
     };
 
-    ccx.closure_bare_wrapper_cache.borrow_mut().insert(fn_ptr, llfn);
+    ccx.closure_bare_wrapper_cache().borrow_mut().insert(fn_ptr, llfn);
 
     // This is only used by statics inlined from a different crate.
     if !is_local {
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index d92364b2570..4ea60a4e128 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<Block<'a>>,
 
     // This function's enclosing crate context.
-    pub ccx: &'a CrateContext,
+    pub ccx: &'a CrateContext<'a>,
 
     // Used and maintained by the debuginfo module.
     pub debug_context: debuginfo::FunctionDebugContext,
@@ -342,7 +342,7 @@ impl<'a> FunctionContext<'a> {
 
             self.llreturn.set(Some(unsafe {
                 "return".with_c_str(|buf| {
-                    llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx, self.llfn, buf)
+                    llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), self.llfn, buf)
                 })
             }))
         }
@@ -365,7 +365,7 @@ impl<'a> FunctionContext<'a> {
                      -> &'a Block<'a> {
         unsafe {
             let llbb = name.with_c_str(|buf| {
-                    llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx,
+                    llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(),
                                                         self.llfn,
                                                         buf)
                 });
@@ -449,9 +449,9 @@ impl<'a> Block<'a> {
         })
     }
 
-    pub fn ccx(&self) -> &'a CrateContext { self.fcx.ccx }
+    pub fn ccx(&self) -> &'a CrateContext<'a> { self.fcx.ccx }
     pub fn tcx(&self) -> &'a ty::ctxt {
-        &self.fcx.ccx.tcx
+        self.fcx.ccx.tcx()
     }
     pub fn sess(&self) -> &'a Session { self.fcx.ccx.sess() }
 
@@ -478,11 +478,11 @@ impl<'a> Block<'a> {
     }
 
     pub fn val_to_string(&self, val: ValueRef) -> String {
-        self.ccx().tn.val_to_string(val)
+        self.ccx().tn().val_to_string(val)
     }
 
     pub fn llty_str(&self, ty: Type) -> String {
-        self.ccx().tn.type_to_string(ty)
+        self.ccx().tn().type_to_string(ty)
     }
 
     pub fn ty_to_string(&self, t: ty::t) -> String {
@@ -601,11 +601,11 @@ pub fn C_u64(ccx: &CrateContext, i: u64) -> ValueRef {
 }
 
 pub fn C_int(ccx: &CrateContext, i: int) -> ValueRef {
-    C_integral(ccx.int_type, i as u64, true)
+    C_integral(ccx.int_type(), i as u64, true)
 }
 
 pub fn C_uint(ccx: &CrateContext, i: uint) -> ValueRef {
-    C_integral(ccx.int_type, i as u64, false)
+    C_integral(ccx.int_type(), i as u64, false)
 }
 
 pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef {
@@ -617,25 +617,25 @@ pub fn C_u8(ccx: &CrateContext, i: uint) -> ValueRef {
 // our boxed-and-length-annotated strings.
 pub fn C_cstr(cx: &CrateContext, s: InternedString, null_terminated: bool) -> ValueRef {
     unsafe {
-        match cx.const_cstr_cache.borrow().find(&s) {
+        match cx.const_cstr_cache().borrow().find(&s) {
             Some(&llval) => return llval,
             None => ()
         }
 
-        let sc = llvm::LLVMConstStringInContext(cx.llcx,
+        let sc = llvm::LLVMConstStringInContext(cx.llcx(),
                                                 s.get().as_ptr() as *const c_char,
                                                 s.get().len() as c_uint,
                                                 !null_terminated as Bool);
 
         let gsym = token::gensym("str");
         let g = format!("str{}", gsym.uint()).with_c_str(|buf| {
-            llvm::LLVMAddGlobal(cx.llmod, val_ty(sc).to_ref(), buf)
+            llvm::LLVMAddGlobal(cx.llmod(), val_ty(sc).to_ref(), buf)
         });
         llvm::LLVMSetInitializer(g, sc);
         llvm::LLVMSetGlobalConstant(g, True);
         llvm::SetLinkage(g, llvm::InternalLinkage);
 
-        cx.const_cstr_cache.borrow_mut().insert(s, g);
+        cx.const_cstr_cache().borrow_mut().insert(s, g);
         g
     }
 }
@@ -647,7 +647,7 @@ pub fn C_str_slice(cx: &CrateContext, s: InternedString) -> ValueRef {
         let len = s.get().len();
         let cs = llvm::LLVMConstPointerCast(C_cstr(cx, s, false),
                                             Type::i8p(cx).to_ref());
-        C_named_struct(cx.tn.find_type("str_slice").unwrap(), [cs, C_uint(cx, len)])
+        C_named_struct(cx.tn().find_type("str_slice").unwrap(), [cs, C_uint(cx, len)])
     }
 }
 
@@ -658,7 +658,7 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef {
 
         let gsym = token::gensym("binary");
         let g = format!("binary{}", gsym.uint()).with_c_str(|buf| {
-            llvm::LLVMAddGlobal(cx.llmod, val_ty(lldata).to_ref(), buf)
+            llvm::LLVMAddGlobal(cx.llmod(), val_ty(lldata).to_ref(), buf)
         });
         llvm::LLVMSetInitializer(g, lldata);
         llvm::LLVMSetGlobalConstant(g, True);
@@ -669,9 +669,13 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef {
     }
 }
 
-pub fn C_struct(ccx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef {
+pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef {
+    C_struct_in_context(cx.llcx(), elts, packed)
+}
+
+pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef {
     unsafe {
-        llvm::LLVMConstStructInContext(ccx.llcx,
+        llvm::LLVMConstStructInContext(llcx,
                                        elts.as_ptr(), elts.len() as c_uint,
                                        packed as Bool)
     }
@@ -689,10 +693,14 @@ pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef {
     }
 }
 
-pub fn C_bytes(ccx: &CrateContext, bytes: &[u8]) -> ValueRef {
+pub fn C_bytes(cx: &CrateContext, bytes: &[u8]) -> ValueRef {
+    C_bytes_in_context(cx.llcx(), bytes)
+}
+
+pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef {
     unsafe {
         let ptr = bytes.as_ptr() as *const c_char;
-        return llvm::LLVMConstStringInContext(ccx.llcx, ptr, bytes.len() as c_uint, True);
+        return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True);
     }
 }
 
@@ -702,7 +710,7 @@ pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint])
         let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint);
 
         debug!("const_get_elt(v={}, us={:?}, r={})",
-               cx.tn.val_to_string(v), us, cx.tn.val_to_string(r));
+               cx.tn().val_to_string(v), us, cx.tn().val_to_string(r));
 
         return r;
     }
diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs
index 10b6adad1e3..bd5132ea427 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 bb528790314..5bdd5f6739d 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<Vec<(String, uint, uint)> >,
 }
 
-pub struct CrateContext {
-    pub llmod: ModuleRef,
-    pub llcx: ContextRef,
-    pub metadata_llmod: ModuleRef,
-    pub td: TargetData,
-    pub tn: TypeNames,
-    pub externs: RefCell<ExternMap>,
-    pub item_vals: RefCell<NodeMap<ValueRef>>,
-    pub exp_map2: resolve::ExportMap2,
-    pub reachable: NodeSet,
-    pub item_symbols: RefCell<NodeMap<String>>,
-    pub link_meta: LinkMeta,
-    pub drop_glues: RefCell<HashMap<ty::t, ValueRef>>,
-    pub tydescs: RefCell<HashMap<ty::t, Rc<tydesc_info>>>,
+/// The shared portion of a `CrateContext`.  There is one `SharedCrateContext`
+/// per crate.  The data here is shared between all compilation units of the
+/// crate, so it must not contain references to any LLVM data structures
+/// (aside from metadata-related ones).
+pub struct SharedCrateContext {
+    local_ccxs: Vec<LocalCrateContext>,
+
+    metadata_llmod: ModuleRef,
+    metadata_llcx: ContextRef,
+
+    exp_map2: resolve::ExportMap2,
+    reachable: NodeSet,
+    item_symbols: RefCell<NodeMap<String>>,
+    link_meta: LinkMeta,
+    /// A set of static items which cannot be inlined into other crates. This
+    /// will prevent in IIItem() structures from being encoded into the metadata
+    /// that is generated
+    non_inlineable_statics: RefCell<NodeSet>,
+    symbol_hasher: RefCell<Sha256>,
+    tcx: ty::ctxt,
+    stats: Stats,
+
+    available_monomorphizations: RefCell<HashSet<String>>,
+    available_drop_glues: RefCell<HashMap<ty::t, String>>,
+    available_visit_glues: RefCell<HashMap<ty::t, String>>,
+}
+
+/// The local portion of a `CrateContext`.  There is one `LocalCrateContext`
+/// per compilation unit.  Each one has its own LLVM `ContextRef` so that
+/// several compilation units may be optimized in parallel.  All other LLVM
+/// data structures in the `LocalCrateContext` are tied to that `ContextRef`.
+pub struct LocalCrateContext {
+    llmod: ModuleRef,
+    llcx: ContextRef,
+    td: TargetData,
+    tn: TypeNames,
+    externs: RefCell<ExternMap>,
+    item_vals: RefCell<NodeMap<ValueRef>>,
+    drop_glues: RefCell<HashMap<ty::t, ValueRef>>,
+    tydescs: RefCell<HashMap<ty::t, Rc<tydesc_info>>>,
     /// Set when running emit_tydescs to enforce that no more tydescs are
     /// created.
-    pub finished_tydescs: Cell<bool>,
+    finished_tydescs: Cell<bool>,
     /// Track mapping of external ids to local items imported for inlining
-    pub external: RefCell<DefIdMap<Option<ast::NodeId>>>,
+    external: RefCell<DefIdMap<Option<ast::NodeId>>>,
     /// Backwards version of the `external` map (inlined items to where they
     /// came from)
-    pub external_srcs: RefCell<NodeMap<ast::DefId>>,
-    /// A set of static items which cannot be inlined into other crates. This
-    /// will prevent in IIItem() structures from being encoded into the metadata
-    /// that is generated
-    pub non_inlineable_statics: RefCell<NodeSet>,
+    external_srcs: RefCell<NodeMap<ast::DefId>>,
     /// Cache instances of monomorphized functions
-    pub monomorphized: RefCell<HashMap<MonoId, ValueRef>>,
-    pub monomorphizing: RefCell<DefIdMap<uint>>,
+    monomorphized: RefCell<HashMap<MonoId, ValueRef>>,
+    monomorphizing: RefCell<DefIdMap<uint>>,
     /// Cache generated vtables
-    pub vtables: RefCell<HashMap<(ty::t, MonoId), ValueRef>>,
+    vtables: RefCell<HashMap<(ty::t, MonoId), ValueRef>>,
     /// Cache of constant strings,
-    pub const_cstr_cache: RefCell<HashMap<InternedString, ValueRef>>,
+    const_cstr_cache: RefCell<HashMap<InternedString, ValueRef>>,
 
     /// Reverse-direction for const ptrs cast from globals.
     /// Key is an int, cast from a ValueRef holding a *T,
@@ -93,106 +115,295 @@ pub struct CrateContext {
     /// when we ptrcast, and we have to ptrcast during translation
     /// of a [T] const because we form a slice, a [*T,int] pair, not
     /// a pointer to an LLVM array type.
-    pub const_globals: RefCell<HashMap<int, ValueRef>>,
+    const_globals: RefCell<HashMap<int, ValueRef>>,
 
     /// Cache of emitted const values
-    pub const_values: RefCell<NodeMap<ValueRef>>,
+    const_values: RefCell<NodeMap<ValueRef>>,
 
     /// Cache of external const values
-    pub extern_const_values: RefCell<DefIdMap<ValueRef>>,
+    extern_const_values: RefCell<DefIdMap<ValueRef>>,
 
-    pub impl_method_cache: RefCell<HashMap<(ast::DefId, ast::Name), ast::DefId>>,
+    impl_method_cache: RefCell<HashMap<(ast::DefId, ast::Name), ast::DefId>>,
 
     /// Cache of closure wrappers for bare fn's.
-    pub closure_bare_wrapper_cache: RefCell<HashMap<ValueRef, ValueRef>>,
-
-    pub lltypes: RefCell<HashMap<ty::t, Type>>,
-    pub llsizingtypes: RefCell<HashMap<ty::t, Type>>,
-    pub adt_reprs: RefCell<HashMap<ty::t, Rc<adt::Repr>>>,
-    pub symbol_hasher: RefCell<Sha256>,
-    pub type_hashcodes: RefCell<HashMap<ty::t, String>>,
-    pub all_llvm_symbols: RefCell<HashSet<String>>,
-    pub tcx: ty::ctxt,
-    pub stats: Stats,
-    pub int_type: Type,
-    pub opaque_vec_type: Type,
-    pub builder: BuilderRef_res,
+    closure_bare_wrapper_cache: RefCell<HashMap<ValueRef, ValueRef>>,
+
+    lltypes: RefCell<HashMap<ty::t, Type>>,
+    llsizingtypes: RefCell<HashMap<ty::t, Type>>,
+    adt_reprs: RefCell<HashMap<ty::t, Rc<adt::Repr>>>,
+    type_hashcodes: RefCell<HashMap<ty::t, String>>,
+    all_llvm_symbols: RefCell<HashSet<String>>,
+    int_type: Type,
+    opaque_vec_type: Type,
+    builder: BuilderRef_res,
 
     /// Holds the LLVM values for closure IDs.
-    pub unboxed_closure_vals: RefCell<DefIdMap<ValueRef>>,
+    unboxed_closure_vals: RefCell<DefIdMap<ValueRef>>,
 
-    pub dbg_cx: Option<debuginfo::CrateDebugContext>,
+    dbg_cx: Option<debuginfo::CrateDebugContext>,
 
-    pub eh_personality: RefCell<Option<ValueRef>>,
+    eh_personality: RefCell<Option<ValueRef>>,
 
     intrinsics: RefCell<HashMap<&'static str, ValueRef>>,
+
+    /// Number of LLVM instructions translated into this `LocalCrateContext`.
+    /// This is used to perform some basic load-balancing to keep all LLVM
+    /// contexts around the same size.
+    n_llvm_insns: Cell<uint>,
+}
+
+pub struct CrateContext<'a> {
+    shared: &'a SharedCrateContext,
+    local: &'a LocalCrateContext,
+    /// The index of `local` in `shared.local_ccxs`.  This is used in
+    /// `maybe_iter(true)` to identify the original `LocalCrateContext`.
+    index: uint,
+}
+
+pub struct CrateContextIterator<'a> {
+    shared: &'a SharedCrateContext,
+    index: uint,
+}
+
+impl<'a> Iterator<CrateContext<'a>> for CrateContextIterator<'a> {
+    fn next(&mut self) -> Option<CrateContext<'a>> {
+        if self.index >= self.shared.local_ccxs.len() {
+            return None;
+        }
+
+        let index = self.index;
+        self.index += 1;
+
+        Some(CrateContext {
+            shared: self.shared,
+            local: &self.shared.local_ccxs[index],
+            index: index,
+        })
+    }
+}
+
+/// The iterator produced by `CrateContext::maybe_iter`.
+pub struct CrateContextMaybeIterator<'a> {
+    shared: &'a SharedCrateContext,
+    index: uint,
+    single: bool,
+    origin: uint,
 }
 
-impl 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<NodeMap<String>> {
+        &self.item_symbols
+    }
+
+    pub fn link_meta<'a>(&'a self) -> &'a LinkMeta {
+        &self.link_meta
+    }
+
+    pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell<NodeSet> {
+        &self.non_inlineable_statics
+    }
+
+    pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell<Sha256> {
+        &self.symbol_hasher
+    }
+
+    pub fn tcx<'a>(&'a self) -> &'a ty::ctxt {
+        &self.tcx
+    }
+
+    pub fn take_tcx(self) -> ty::ctxt {
+        self.tcx
+    }
+
+    pub fn sess<'a>(&'a self) -> &'a Session {
+        &self.tcx.sess
+    }
+
+    pub fn stats<'a>(&'a self) -> &'a Stats {
+        &self.stats
+    }
+}
+
+impl LocalCrateContext {
+    fn new(shared: &SharedCrateContext,
+           name: &str)
+           -> LocalCrateContext {
         unsafe {
-            let llcx = llvm::LLVMContextCreate();
-            let llmod = name.with_c_str(|buf| {
-                llvm::LLVMModuleCreateWithNameInContext(buf, llcx)
-            });
-            let metadata_llmod = format!("{}_metadata", name).with_c_str(|buf| {
-                llvm::LLVMModuleCreateWithNameInContext(buf, llcx)
-            });
-            tcx.sess
-               .targ_cfg
-               .target_strs
-               .data_layout
-               .as_slice()
-               .with_c_str(|buf| {
-                llvm::LLVMSetDataLayout(llmod, buf);
-                llvm::LLVMSetDataLayout(metadata_llmod, buf);
-            });
-            tcx.sess
-               .targ_cfg
-               .target_strs
-               .target_triple
-               .as_slice()
-               .with_c_str(|buf| {
-                llvm::LLVMRustSetNormalizedTarget(llmod, buf);
-                llvm::LLVMRustSetNormalizedTarget(metadata_llmod, buf);
-            });
-
-            let 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<ExternMap> {
+        &self.local.externs
+    }
+
+    pub fn item_vals<'a>(&'a self) -> &'a RefCell<NodeMap<ValueRef>> {
+        &self.local.item_vals
+    }
+
+    pub fn exp_map2<'a>(&'a self) -> &'a resolve::ExportMap2 {
+        &self.shared.exp_map2
+    }
+
+    pub fn reachable<'a>(&'a self) -> &'a NodeSet {
+        &self.shared.reachable
+    }
+
+    pub fn item_symbols<'a>(&'a self) -> &'a RefCell<NodeMap<String>> {
+        &self.shared.item_symbols
+    }
+
+    pub fn link_meta<'a>(&'a self) -> &'a LinkMeta {
+        &self.shared.link_meta
+    }
+
+    pub fn drop_glues<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, ValueRef>> {
+        &self.local.drop_glues
+    }
+
+    pub fn tydescs<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Rc<tydesc_info>>> {
+        &self.local.tydescs
+    }
+
+    pub fn finished_tydescs<'a>(&'a self) -> &'a Cell<bool> {
+        &self.local.finished_tydescs
+    }
+
+    pub fn external<'a>(&'a self) -> &'a RefCell<DefIdMap<Option<ast::NodeId>>> {
+        &self.local.external
+    }
+
+    pub fn external_srcs<'a>(&'a self) -> &'a RefCell<NodeMap<ast::DefId>> {
+        &self.local.external_srcs
+    }
+
+    pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell<NodeSet> {
+        &self.shared.non_inlineable_statics
+    }
+
+    pub fn monomorphized<'a>(&'a self) -> &'a RefCell<HashMap<MonoId, ValueRef>> {
+        &self.local.monomorphized
+    }
+
+    pub fn monomorphizing<'a>(&'a self) -> &'a RefCell<DefIdMap<uint>> {
+        &self.local.monomorphizing
+    }
+
+    pub fn vtables<'a>(&'a self) -> &'a RefCell<HashMap<(ty::t, MonoId), ValueRef>> {
+        &self.local.vtables
+    }
+
+    pub fn const_cstr_cache<'a>(&'a self) -> &'a RefCell<HashMap<InternedString, ValueRef>> {
+        &self.local.const_cstr_cache
+    }
+
+    pub fn const_globals<'a>(&'a self) -> &'a RefCell<HashMap<int, ValueRef>> {
+        &self.local.const_globals
+    }
+
+    pub fn const_values<'a>(&'a self) -> &'a RefCell<NodeMap<ValueRef>> {
+        &self.local.const_values
+    }
+
+    pub fn extern_const_values<'a>(&'a self) -> &'a RefCell<DefIdMap<ValueRef>> {
+        &self.local.extern_const_values
+    }
+
+    pub fn impl_method_cache<'a>(&'a self)
+            -> &'a RefCell<HashMap<(ast::DefId, ast::Name), ast::DefId>> {
+        &self.local.impl_method_cache
+    }
+
+    pub fn closure_bare_wrapper_cache<'a>(&'a self) -> &'a RefCell<HashMap<ValueRef, ValueRef>> {
+        &self.local.closure_bare_wrapper_cache
+    }
+
+    pub fn lltypes<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Type>> {
+        &self.local.lltypes
+    }
+
+    pub fn llsizingtypes<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Type>> {
+        &self.local.llsizingtypes
+    }
+
+    pub fn adt_reprs<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, Rc<adt::Repr>>> {
+        &self.local.adt_reprs
+    }
+
+    pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell<Sha256> {
+        &self.shared.symbol_hasher
+    }
+
+    pub fn type_hashcodes<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, String>> {
+        &self.local.type_hashcodes
+    }
+
+    pub fn all_llvm_symbols<'a>(&'a self) -> &'a RefCell<HashSet<String>> {
+        &self.local.all_llvm_symbols
+    }
+
+    pub fn stats<'a>(&'a self) -> &'a Stats {
+        &self.shared.stats
+    }
+
+    pub fn available_monomorphizations<'a>(&'a self) -> &'a RefCell<HashSet<String>> {
+        &self.shared.available_monomorphizations
+    }
+
+    pub fn available_drop_glues<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, String>> {
+        &self.shared.available_drop_glues
+    }
+
+    pub fn available_visit_glues<'a>(&'a self) -> &'a RefCell<HashMap<ty::t, String>> {
+        &self.shared.available_visit_glues
+    }
+
+    pub fn int_type(&self) -> Type {
+        self.local.int_type
+    }
+
+    pub fn opaque_vec_type(&self) -> Type {
+        self.local.opaque_vec_type
+    }
+
+    pub fn unboxed_closure_vals<'a>(&'a self) -> &'a RefCell<DefIdMap<ValueRef>> {
+        &self.local.unboxed_closure_vals
+    }
+
+    pub fn dbg_cx<'a>(&'a self) -> &'a Option<debuginfo::CrateDebugContext> {
+        &self.local.dbg_cx
+    }
+
+    pub fn eh_personality<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
+        &self.local.eh_personality
+    }
+
+    fn intrinsics<'a>(&'a self) -> &'a RefCell<HashMap<&'static str, ValueRef>> {
+        &self.local.intrinsics
+    }
+
+    pub fn count_llvm_insn(&self) {
+        self.local.n_llvm_insns.set(self.local.n_llvm_insns.get() + 1);
+    }
 }
 
 fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef> {
@@ -293,7 +719,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
         ($name:expr fn() -> $ret:expr) => (
             if *key == $name {
                 let f = base::decl_cdecl_fn(ccx, $name, Type::func([], &$ret), ty::mk_nil());
-                ccx.intrinsics.borrow_mut().insert($name, f.clone());
+                ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
             }
         );
@@ -301,7 +727,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
             if *key == $name {
                 let f = base::decl_cdecl_fn(ccx, $name,
                                   Type::func([$($arg),*], &$ret), ty::mk_nil());
-                ccx.intrinsics.borrow_mut().insert($name, f.clone());
+                ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
             }
         )
@@ -437,7 +863,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option<ValueRef
                 let f = base::decl_cdecl_fn(ccx, stringify!($cname),
                                       Type::func([$($arg),*], &$ret),
                                       ty::mk_nil());
-                ccx.intrinsics.borrow_mut().insert($name, f.clone());
+                ccx.intrinsics().borrow_mut().insert($name, f.clone());
                 return Some(f);
             }
         )
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index 74093d26fea..008af804a60 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -627,7 +627,7 @@ impl<K:KindOps> Datum<K> {
     #[allow(dead_code)] // useful for debugging
     pub fn to_string(&self, ccx: &CrateContext) -> String {
         format!("Datum({}, {}, {:?})",
-                ccx.tn.val_to_string(self.val),
+                ccx.tn().val_to_string(self.val),
                 ty_to_string(ccx.tcx(), self.ty),
                 self.kind)
     }
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 8403e84f7b6..01633bea956 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<ast::NodeId, DIScope>) {
-    let def_map = &cx.tcx.def_map;
+    let def_map = &cx.tcx().def_map;
 
     struct ScopeStackEntry {
         scope_metadata: DIScope,
@@ -3290,7 +3290,7 @@ fn populate_scope_map(cx: &CrateContext,
                     scope_stack: &mut Vec<ScopeStackEntry> ,
                     scope_map: &mut HashMap<ast::NodeId, DIScope>) {
 
-        let def_map = &cx.tcx.def_map;
+        let def_map = &cx.tcx().def_map;
 
         // Unfortunately, we cannot just use pat_util::pat_bindings() or
         // ast_util::walk_pat() here because we have to visit *all* nodes in
@@ -3942,7 +3942,7 @@ impl NamespaceTreeNode {
 }
 
 fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str {
-    cx.link_meta.crate_name.as_slice()
+    cx.link_meta().crate_name.as_slice()
 }
 
 fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc<NamespaceTreeNode> {
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 4fe687da4b1..61c27292a37 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 fe9b593c11c..8ed45f89c29 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(), &param_substs.substs);
 
-        let ps = ccx.tcx.map.with_path(id, |path| {
+        let ps = ccx.tcx().map.with_path(id, |path| {
             let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name));
             link::mangle(path.chain(abi.move_iter()), hash)
         });
@@ -619,13 +631,13 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
             _ => {
                 ccx.sess().bug(format!("build_rust_fn: extern fn {} has ty {}, \
                                         expected a bare fn ty",
-                                       ccx.tcx.map.path_to_string(id),
+                                       ccx.tcx().map.path_to_string(id),
                                        t.repr(tcx)).as_slice());
             }
         };
 
         debug!("build_rust_fn: path={} id={} t={}",
-               ccx.tcx.map.path_to_string(id),
+               ccx.tcx().map.path_to_string(id),
                id, t.repr(tcx));
 
         let llfn = base::decl_internal_rust_fn(ccx, t, ps.as_slice());
@@ -644,8 +656,8 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
         let tcx = ccx.tcx();
 
         debug!("build_wrap_fn(llrustfn={}, llwrapfn={}, t={})",
-               ccx.tn.val_to_string(llrustfn),
-               ccx.tn.val_to_string(llwrapfn),
+               ccx.tn().val_to_string(llrustfn),
+               ccx.tn().val_to_string(llwrapfn),
                t.repr(ccx.tcx()));
 
         // Avoid all the Rust generation stuff and just generate raw
@@ -661,7 +673,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
 
         let the_block =
             "the block".with_c_str(
-                |s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llwrapfn, s));
+                |s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn, s));
 
         let builder = ccx.builder();
         builder.position_at_end(the_block);
@@ -702,11 +714,11 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
             match foreign_outptr {
                 Some(llforeign_outptr) => {
                     debug!("out pointer, foreign={}",
-                           ccx.tn.val_to_string(llforeign_outptr));
+                           ccx.tn().val_to_string(llforeign_outptr));
                     let llrust_retptr =
                         builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to());
                     debug!("out pointer, foreign={} (casted)",
-                           ccx.tn.val_to_string(llrust_retptr));
+                           ccx.tn().val_to_string(llrust_retptr));
                     llrust_args.push(llrust_retptr);
                     return_alloca = None;
                 }
@@ -717,8 +729,8 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
                             allocad={}, \
                             llrust_ret_ty={}, \
                             return_ty={}",
-                           ccx.tn.val_to_string(slot),
-                           ccx.tn.type_to_string(llrust_ret_ty),
+                           ccx.tn().val_to_string(slot),
+                           ccx.tn().type_to_string(llrust_ret_ty),
                            tys.fn_sig.output.repr(tcx));
                     llrust_args.push(slot);
                     return_alloca = Some(slot);
@@ -752,7 +764,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
             let mut llforeign_arg = get_param(llwrapfn, foreign_index);
 
             debug!("llforeign_arg {}{}: {}", "#",
-                   i, ccx.tn.val_to_string(llforeign_arg));
+                   i, ccx.tn().val_to_string(llforeign_arg));
             debug!("rust_indirect = {}, foreign_indirect = {}",
                    rust_indirect, foreign_indirect);
 
@@ -791,12 +803,13 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
             };
 
             debug!("llrust_arg {}{}: {}", "#",
-                   i, ccx.tn.val_to_string(llrust_arg));
+                   i, ccx.tn().val_to_string(llrust_arg));
             llrust_args.push(llrust_arg);
         }
 
         // Perform the call itself
-        debug!("calling llrustfn = {}, t = {}", ccx.tn.val_to_string(llrustfn), t.repr(ccx.tcx()));
+        debug!("calling llrustfn = {}, t = {}",
+               ccx.tn().val_to_string(llrustfn), t.repr(ccx.tcx()));
         let attributes = base::get_fn_llvm_attributes(ccx, t);
         let llrust_ret_val = builder.call(llrustfn, llrust_args.as_slice(), Some(attributes));
 
@@ -915,10 +928,10 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext,
            fn_ty={} -> {}, \
            ret_def={}",
            ty.repr(ccx.tcx()),
-           ccx.tn.types_to_str(llsig.llarg_tys.as_slice()),
-           ccx.tn.type_to_string(llsig.llret_ty),
-           ccx.tn.types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>().as_slice()),
-           ccx.tn.type_to_string(fn_ty.ret_ty.ty),
+           ccx.tn().types_to_str(llsig.llarg_tys.as_slice()),
+           ccx.tn().type_to_string(llsig.llret_ty),
+           ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>().as_slice()),
+           ccx.tn().type_to_string(fn_ty.ret_ty.ty),
            ret_def);
 
     ForeignTypes {
diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs
index 51cd9a115cd..c8a47532a92 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 4b1f37fcdc2..0713b2b535c 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)
                                  &param_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 3c61708fb7b..f10df00ca91 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 2740e5695be..5aec1cfbf26 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 15bbc5ae845..938cbc21764 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 83bdcc9dead..c002f3e72c8 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,
-                             &param_substs::empty(),
-                             method.id,
-                             []);
+                    let trans_everywhere = attr::requests_inline(method.attrs.as_slice());
+                    for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
+                        let llfn = get_item_val(ccx, method.id);
+                        trans_fn(ccx,
+                                 &*method.pe_fn_decl(),
+                                 &*method.pe_body(),
+                                 llfn,
+                                 &param_substs::empty(),
+                                 method.id,
+                                 []);
+                        update_linkage(ccx,
+                                       llfn,
+                                       Some(method.id),
+                                       if is_origin { OriginalTranslation } else { InlinedCopy });
+                    }
                 }
                 let mut v = TransItemVisitor {
                     ccx: ccx,
@@ -196,7 +203,7 @@ pub fn trans_static_method_callee(bcx: &Block,
     let vtable_key = MethodCall::expr(expr_id);
     let vtbls = resolve_vtables_in_fn_ctxt(
         bcx.fcx,
-        ccx.tcx.vtable_map.borrow().get(&vtable_key));
+        ccx.tcx().vtable_map.borrow().get(&vtable_key));
 
     match *vtbls.get_self().unwrap().get(0) {
         typeck::vtable_static(impl_did, ref rcvr_substs, ref rcvr_origins) => {
@@ -228,12 +235,12 @@ pub fn trans_static_method_callee(bcx: &Block,
 
 fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name)
                     -> ast::DefId {
-    match ccx.impl_method_cache.borrow().find_copy(&(impl_id, name)) {
+    match ccx.impl_method_cache().borrow().find_copy(&(impl_id, name)) {
         Some(m) => return m,
         None => {}
     }
 
-    let impl_items = ccx.tcx.impl_items.borrow();
+    let impl_items = ccx.tcx().impl_items.borrow();
     let impl_items =
         impl_items.find(&impl_id)
                   .expect("could not find impl while translating");
@@ -241,7 +248,7 @@ fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name)
                              .find(|&did| {
                                 match *did {
                                     ty::MethodTraitItemId(did) => {
-                                        ty::impl_or_trait_item(&ccx.tcx,
+                                        ty::impl_or_trait_item(ccx.tcx(),
                                                                did).ident()
                                                                    .name ==
                                             name
@@ -250,7 +257,7 @@ fn method_with_name(ccx: &CrateContext, impl_id: ast::DefId, name: ast::Name)
                              }).expect("could not find method while \
                                         translating");
 
-    ccx.impl_method_cache.borrow_mut().insert((impl_id, name),
+    ccx.impl_method_cache().borrow_mut().insert((impl_id, name),
                                               meth_did.def_id());
     meth_did.def_id()
 }
@@ -502,7 +509,7 @@ fn get_vtable(bcx: &Block,
 
     // Check the cache.
     let hash_id = (self_ty, monomorphize::make_vtable_id(ccx, origins.get(0)));
-    match ccx.vtables.borrow().find(&hash_id) {
+    match ccx.vtables().borrow().find(&hash_id) {
         Some(&val) => { return val }
         None => { }
     }
@@ -594,7 +601,7 @@ fn get_vtable(bcx: &Block,
     let drop_glue = glue::get_drop_glue(ccx, self_ty);
     let vtable = make_vtable(ccx, drop_glue, ll_size, ll_align, methods);
 
-    ccx.vtables.borrow_mut().insert(hash_id, vtable);
+    ccx.vtables().borrow_mut().insert(hash_id, vtable);
     vtable
 }
 
@@ -614,7 +621,7 @@ pub fn make_vtable<I: Iterator<ValueRef>>(ccx: &CrateContext,
         let tbl = C_struct(ccx, components.as_slice(), false);
         let sym = token::gensym("vtable");
         let vt_gvar = format!("vtable{}", sym.uint()).with_c_str(|buf| {
-            llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl).to_ref(), buf)
+            llvm::LLVMAddGlobal(ccx.llmod(), val_ty(tbl).to_ref(), buf)
         });
         llvm::LLVMSetInitializer(vt_gvar, tbl);
         llvm::LLVMSetGlobalConstant(vt_gvar, llvm::True);
@@ -684,9 +691,9 @@ pub fn vtable_ptr<'a>(bcx: &'a Block<'a>,
                       self_ty: ty::t) -> ValueRef {
     let ccx = bcx.ccx();
     let origins = {
-        let vtable_map = ccx.tcx.vtable_map.borrow();
+        let vtable_map = ccx.tcx().vtable_map.borrow();
         // This trait cast might be because of implicit coercion
-        let adjs = ccx.tcx.adjustments.borrow();
+        let adjs = ccx.tcx().adjustments.borrow();
         let adjust = adjs.find(&id);
         let method_call = if adjust.is_some() && ty::adjust_is_object(adjust.unwrap()) {
             MethodCall::autoobject(id)
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index f2a7f1dc4f8..1cf3e55967d 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 a49dc1b9150..1fcf4c18912 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 a6ba758d5f8..3701d83d6a1 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 7b98d65a310..3df1ce32fc7 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 ab7e71c41d3..54f24516867 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 12d10ec6630..ceb29ddcf8f 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 9df748e74e8..d2345614b25 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<Path>, externs: core::Exte
         maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
         addl_lib_search_paths: RefCell::new(libs),
         crate_types: vec!(config::CrateTypeExecutable),
-        output_types: vec!(link::OutputTypeExe),
+        output_types: vec!(write::OutputTypeExe),
         no_trans: no_run,
         externs: externs,
         cg: config::CodegenOptions {
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index c234bea0a33..dd422d02149 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 00000000000..8f5d3b5768a
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[no_mangle]
+pub extern "C" fn foo() -> uint {
+    1234
+}
diff --git a/src/test/auxiliary/sepcomp_cci_lib.rs b/src/test/auxiliary/sepcomp_cci_lib.rs
new file mode 100644
index 00000000000..1cb7ead2cff
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[inline]
+pub fn cci_fn() -> uint {
+    1200
+}
+
+#[inline]
+pub static CCI_STATIC: uint = 34;
diff --git a/src/test/auxiliary/sepcomp_lib.rs b/src/test/auxiliary/sepcomp_lib.rs
new file mode 100644
index 00000000000..d1d9e3b8ff3
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C codegen-units=3 --crate-type=rlib,dylib
+
+pub mod a {
+    pub fn one() -> uint {
+        1
+    }
+}
+
+pub mod b {
+    pub fn two() -> uint {
+        2
+    }
+}
+
+pub mod c {
+    use a::one;
+    use b::two;
+    pub fn three() -> uint {
+        one() + two()
+    }
+}
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 00000000000..59706e20bed
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Make sure we give a sane error message when the user requests LTO with a
+// library built with -C codegen-units > 1.
+
+// aux-build:sepcomp_lib.rs
+// compile-flags: -Z lto
+// error-pattern:missing compressed bytecode
+// no-prefer-dynamic
+
+extern crate sepcomp_lib;
+use sepcomp_lib::a::one;
+use sepcomp_lib::b::two;
+use sepcomp_lib::c::three;
+
+fn main() {
+    assert_eq!(one(), 1);
+    assert_eq!(two(), 2);
+    assert_eq!(three(), 3);
+}
diff --git a/src/test/run-make/output-type-permutations/Makefile b/src/test/run-make/output-type-permutations/Makefile
index c163a5bec08..fed071d1a43 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 00000000000..fdb39f85197
--- /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 00000000000..099101d6f26
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_type = "rlib"]
+
+#[inline]
+pub fn cci_fn() -> uint {
+    1234
+}
+
+#[inline]
+pub static CCI_STATIC: uint = 2345;
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 00000000000..c702e578c09
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate cci_lib;
+use cci_lib::{cci_fn, CCI_STATIC};
+
+fn call1() -> uint {
+    cci_fn() + CCI_STATIC
+}
+
+mod a {
+    use cci_lib::cci_fn;
+    pub fn call2() -> uint {
+        cci_fn()
+    }
+}
+
+mod b {
+    use cci_lib::CCI_STATIC;
+    pub fn call3() -> uint {
+        CCI_STATIC
+    }
+}
+
+fn main() {
+    call1();
+    a::call2();
+    b::call3();
+}
diff --git a/src/test/run-make/sepcomp-inlining/Makefile b/src/test/run-make/sepcomp-inlining/Makefile
new file mode 100644
index 00000000000..6cb9f9a3f31
--- /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 00000000000..20fd18b8295
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[inline]
+fn inlined() -> u32 {
+    1234
+}
+
+fn normal() -> u32 {
+    2345
+}
+
+mod a {
+    pub fn f() -> u32 {
+        ::inlined() + ::normal()
+    }
+}
+
+mod b {
+    pub fn f() -> u32 {
+        ::inlined() + ::normal()
+    }
+}
+
+fn main() {
+    a::f();
+    b::f();
+}
diff --git a/src/test/run-make/sepcomp-separate/Makefile b/src/test/run-make/sepcomp-separate/Makefile
new file mode 100644
index 00000000000..265bd68bd2e
--- /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 00000000000..fe6a7b5a18f
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn magic_fn() -> uint {
+    1234
+}
+
+mod a {
+    pub fn magic_fn() -> uint {
+        2345
+    }
+}
+
+mod b {
+    pub fn magic_fn() -> uint {
+        3456
+    }
+}
+
+fn main() { }
diff --git a/src/test/run-pass/sepcomp-cci.rs b/src/test/run-pass/sepcomp-cci.rs
new file mode 100644
index 00000000000..0178b5e786d
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C codegen-units=3
+// aux-build:sepcomp_cci_lib.rs
+
+// Test accessing cross-crate inlined items from multiple compilation units.
+
+extern crate sepcomp_cci_lib;
+use sepcomp_cci_lib::{cci_fn, CCI_STATIC};
+
+fn call1() -> uint {
+    cci_fn() + CCI_STATIC
+}
+
+mod a {
+    use sepcomp_cci_lib::{cci_fn, CCI_STATIC};
+    pub fn call2() -> uint {
+        cci_fn() + CCI_STATIC
+    }
+}
+
+mod b {
+    use sepcomp_cci_lib::{cci_fn, CCI_STATIC};
+    pub fn call3() -> uint {
+        cci_fn() + CCI_STATIC
+    }
+}
+
+fn main() {
+    assert_eq!(call1(), 1234);
+    assert_eq!(a::call2(), 1234);
+    assert_eq!(b::call3(), 1234);
+}
diff --git a/src/test/run-pass/sepcomp-extern.rs b/src/test/run-pass/sepcomp-extern.rs
new file mode 100644
index 00000000000..a5506e3fc76
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C codegen-units=3
+// aux-build:sepcomp-extern-lib.rs
+
+// Test accessing external items from multiple compilation units.
+
+#[link(name = "sepcomp-extern-lib")]
+extern {
+    #[allow(ctypes)]
+    fn foo() -> uint;
+}
+
+fn call1() -> uint {
+    unsafe { foo() }
+}
+
+mod a {
+    pub fn call2() -> uint {
+        unsafe { ::foo() }
+    }
+}
+
+mod b {
+    pub fn call3() -> uint {
+        unsafe { ::foo() }
+    }
+}
+
+fn main() {
+    assert_eq!(call1(), 1234);
+    assert_eq!(a::call2(), 1234);
+    assert_eq!(b::call3(), 1234);
+}
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 00000000000..61f008ad854
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C codegen-units=3
+
+// Test references to items that haven't been translated yet.
+
+// Generate some code in the first compilation unit before declaring any
+// modules.  This ensures that the first module doesn't go into the same
+// compilation unit as the top-level module.
+fn pad() -> uint { 0 }
+
+mod b {
+    pub fn three() -> uint {
+        ::one() + ::a::two()
+    }
+}
+
+mod a {
+    pub fn two() -> uint {
+        ::one() + ::one()
+    }
+}
+
+fn one() -> uint {
+    1
+}
+
+fn main() {
+    assert_eq!(one(), 1);
+    assert_eq!(a::two(), 2);
+    assert_eq!(b::three(), 3);
+}
+
diff --git a/src/test/run-pass/sepcomp-fns.rs b/src/test/run-pass/sepcomp-fns.rs
new file mode 100644
index 00000000000..09f2a4281be
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C codegen-units=3
+
+// Test basic separate compilation functionality.  The functions should be able
+// to call each other even though they will be placed in different compilation
+// units.
+
+// Generate some code in the first compilation unit before declaring any
+// modules.  This ensures that the first module doesn't go into the same
+// compilation unit as the top-level module.
+fn one() -> uint { 1 }
+
+mod a {
+    pub fn two() -> uint {
+        ::one() + ::one()
+    }
+}
+
+mod b {
+    pub fn three() -> uint {
+        ::one() + ::a::two()
+    }
+}
+
+fn main() {
+    assert_eq!(one(), 1);
+    assert_eq!(a::two(), 2);
+    assert_eq!(b::three(), 3);
+}
diff --git a/src/test/run-pass/sepcomp-lib.rs b/src/test/run-pass/sepcomp-lib.rs
new file mode 100644
index 00000000000..28adb55399b
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:sepcomp_lib.rs
+
+// Test linking against a library built with -C codegen-units > 1
+
+extern crate sepcomp_lib;
+use sepcomp_lib::a::one;
+use sepcomp_lib::b::two;
+use sepcomp_lib::c::three;
+
+fn main() {
+    assert_eq!(one(), 1);
+    assert_eq!(two(), 2);
+    assert_eq!(three(), 3);
+}
diff --git a/src/test/run-pass/sepcomp-statics.rs b/src/test/run-pass/sepcomp-statics.rs
new file mode 100644
index 00000000000..26a652ae0ea
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C codegen-units=3
+
+// Test references to static items across compilation units.
+
+fn pad() -> uint { 0 }
+
+static ONE: uint = 1;
+
+mod b {
+    // Separate compilation always switches to the LLVM module with the fewest
+    // instructions.  Make sure we have some instructions in this module so
+    // that `a` and `b` don't go into the same compilation unit.
+    fn pad() -> uint { 0 }
+
+    pub static THREE: uint = ::ONE + ::a::TWO;
+}
+
+mod a {
+    fn pad() -> uint { 0 }
+
+    pub static TWO: uint = ::ONE + ::ONE;
+}
+
+fn main() {
+    assert_eq!(ONE, 1);
+    assert_eq!(a::TWO, 2);
+    assert_eq!(b::THREE, 3);
+}
+
diff --git a/src/test/run-pass/sepcomp-unwind.rs b/src/test/run-pass/sepcomp-unwind.rs
new file mode 100644
index 00000000000..5d154e02af6
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C codegen-units=3
+
+// Test unwinding through multiple compilation units.
+
+// According to acrichto, in the distant past `ld -r` (which is used during
+// linking when codegen-units > 1) was known to produce object files with
+// damaged unwinding tables.  This may be related to GNU binutils bug #6893
+// ("Partial linking results in corrupt .eh_frame_hdr"), but I'm not certain.
+// In any case, this test should let us know if enabling parallel codegen ever
+// breaks unwinding.
+
+fn pad() -> uint { 0 }
+
+mod a {
+    pub fn f() {
+        fail!();
+    }
+}
+
+mod b {
+    pub fn g() {
+        ::a::f();
+    }
+}
+
+fn main() {
+    std::task::try(proc() { ::b::g() }).unwrap_err();
+}