<commit_before>// Copyright 2012-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::archive::{Archive, METADATA_FILENAME};
use back::rpath;
use back::svh::Svh;
use driver::driver::{CrateTranslation, OutputFilenames};
use driver::config::NoDebugInfo;
use driver::session::Session;
use driver::config;
use lib::llvm::llvm;
use lib::llvm::ModuleRef;
use lib;
use metadata::common::LinkMeta;
use metadata::{encoder, cstore, filesearch, csearch, loader};
use middle::trans::context::CrateContext;
use middle::trans::common::gensym_name;
use middle::ty;
use util::common::time;
use util::ppaux;
use util::sha2::{Digest, Sha256};

use std::c_str::{ToCStr, CString};
use std::char;
use std::io::{fs, TempDir, Command};
use std::io;
use std::ptr;
use std::str;
use std::string::String;
use flate;
use serialize::hex::ToHex;
use syntax::abi;
use syntax::ast;
use syntax::ast_map::{PathElem, PathElems, PathName};
use syntax::ast_map;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::crateid::CrateId;
use syntax::parse::token;

#[deriving(Clone, Eq, Ord, TotalOrd, TotalEq)]
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 = str::from_utf8_lossy(err.as_bytes());
            sess.fatal(format!("{}: {}",
                               msg.as_slice(),
                               err.as_slice()).as_slice());
        }
    }
}

pub fn WriteOutputFile(
        sess: &Session,
        target: lib::llvm::TargetMachineRef,
        pm: lib::llvm::PassManagerRef,
        m: ModuleRef,
        output: &Path,
        file_type: lib::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 back::lto;
    use back::link::{WriteOutputFile, OutputType};
    use back::link::{OutputTypeAssembly, OutputTypeBitcode};
    use back::link::{OutputTypeExe, OutputTypeLlvmAssembly};
    use back::link::{OutputTypeObject};
    use driver::driver::{CrateTranslation, OutputFilenames};
    use driver::config::NoDebugInfo;
    use driver::session::Session;
    use driver::config;
    use lib::llvm::llvm;
    use lib::llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
    use lib;
    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.
    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()
                }
            }
            _ => 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 => lib::llvm::CodeGenLevelNone,
              config::Less => lib::llvm::CodeGenLevelLess,
              config::Default => lib::llvm::CodeGenLevelDefault,
              config::Aggressive => lib::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::OsWin32;
            let fdata_sections = ffunction_sections;

            let reloc_model = match sess.opts.cg.relocation_model.as_slice() {
                "pic" => lib::llvm::RelocPIC,
                "static" => lib::llvm::RelocStatic,
                "default" => lib::llvm::RelocDefault,
                "dynamic-no-pic" => lib::llvm::RelocDynamicNoPic,
                _ => {
                    sess.err(format!("{} is not a valid relocation mode",
                                     sess.opts
                                         .cg
                                         .relocation_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,
                            lib::llvm::CodeModelDefault,
                            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| {
                            WriteOutputFile(sess, tm, cpm, llmod, &path,
                                            lib::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| {
                            WriteOutputFile(sess, tm, cpm, llmod, path,
                                            lib::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");
                        WriteOutputFile(sess, tm, cpm,
                                        trans.metadata_module, &out,
                                        lib::llvm::ObjectFile);
                    })
                }
            });

            llvm::LLVMRustDisposeTargetMachine(tm);
            llvm::LLVMDisposeModule(trans.metadata_module);
            llvm::LLVMDisposeModule(llmod);
            llvm::LLVMContextDispose(llcx);
            if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
        }
    }

    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()
                                                             .as_slice());
                    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 sync::one::{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.with_ref(|p| p));
                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: lib::llvm::PassManagerRef,
                                   mpm: lib::llvm::PassManagerRef,
                                   llmod: ModuleRef,
                                   opt: lib::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 {
            lib::llvm::CodeGenLevelNone => {
                // Don't add lifetime intrinsics at O0
                llvm::LLVMRustAddAlwaysInlinePass(builder, false);
            }
            lib::llvm::CodeGenLevelLess => {
                llvm::LLVMRustAddAlwaysInlinePass(builder, true);
            }
            // numeric values copied from clang
            lib::llvm::CodeGenLevelDefault => {
                llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
                                                                    225);
            }
            lib::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);
    }
}


/*
 * Name mangling and its relationship to metadata. This is complex. Read
 * carefully.
 *
 * The semantic model of Rust linkage is, broadly, that "there's no global
 * namespace" between crates. Our aim is to preserve the illusion of this
 * model despite the fact that it's not *quite* possible to implement on
 * modern linkers. We initially didn't use system linkers at all, but have
 * been convinced of their utility.
 *
 * There are a few issues to handle:
 *
 *  - Linkers operate on a flat namespace, so we have to flatten names.
 *    We do this using the C++ namespace-mangling technique. Foo::bar
 *    symbols and such.
 *
 *  - Symbols with the same name but different types need to get different
 *    linkage-names. We do this by hashing a string-encoding of the type into
 *    a fixed-size (currently 16-byte hex) cryptographic hash function (CHF:
 *    we use SHA256) to "prevent collisions". This is not airtight but 16 hex
 *    digits on uniform probability means you're going to need 2**32 same-name
 *    symbols in the same process before you're even hitting birthday-paradox
 *    collision probability.
 *
 *  - Symbols in different crates but with same names "within" the crate need
 *    to get different linkage-names.
 *
 *  - The hash shown in the filename needs to be predictable and stable for
 *    build tooling integration. It also needs to be using a hash function
 *    which is easy to use from Python, make, etc.
 *
 * So here is what we do:
 *
 *  - Consider the package id; every crate has one (specified with crate_id
 *    attribute).  If a package id isn't provided explicitly, we infer a
 *    versionless one from the output name. The version will end up being 0.0
 *    in this case. CNAME and CVERS are taken from this package id. For
 *    example, github.com/mozilla/CNAME#CVERS.
 *
 *  - Define CMH as SHA256(crateid).
 *
 *  - Define CMH8 as the first 8 characters of CMH.
 *
 *  - Compile our crate to lib CNAME-CMH8-CVERS.so
 *
 *  - Define STH(sym) as SHA256(CMH, type_str(sym))
 *
 *  - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
 *    name, non-name metadata, and type sense, and versioned in the way
 *    system linkers understand.
 */

// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
pub fn find_crate_id(attrs: &[ast::Attribute], out_filestem: &str) -> CrateId {
    match attr::find_crateid(attrs) {
        None => from_str(out_filestem).unwrap_or_else(|| {
            let mut s = out_filestem.chars().filter(|c| c.is_XID_continue());
            from_str(s.collect::<String>().as_slice())
                .or(from_str("rust-out")).unwrap()
        }),
        Some(s) => s,
    }
}

pub fn crate_id_hash(crate_id: &CrateId) -> String {
    // This calculates CMH as defined above. Note that we don't use the path of
    // the crate id in the hash because lookups are only done by (name/vers),
    // not by path.
    let mut s = Sha256::new();
    s.input_str(crate_id.short_name_with_version().as_slice());
    truncated_hash_result(&mut s).as_slice().slice_to(8).to_string()
}

// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
pub fn build_link_meta(krate: &ast::Crate, out_filestem: &str) -> LinkMeta {
    let r = LinkMeta {
        crateid: find_crate_id(krate.attrs.as_slice(), out_filestem),
        crate_hash: Svh::calculate(krate),
    };
    info!("{}", r);
    return r;
}

fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String {
    let output = symbol_hasher.result_bytes();
    // 64 bits should be enough to avoid collisions.
    output.slice_to(8).to_hex().to_string()
}


// This calculates STH for a symbol, as defined above
fn symbol_hash(tcx: &ty::ctxt,
               symbol_hasher: &mut Sha256,
               t: ty::t,
               link_meta: &LinkMeta)
               -> String {
    // NB: do *not* use abbrevs here as we want the symbol names
    // to be independent of one another in the crate.

    symbol_hasher.reset();
    symbol_hasher.input_str(link_meta.crateid.name.as_slice());
    symbol_hasher.input_str("-");
    symbol_hasher.input_str(link_meta.crate_hash.as_str());
    symbol_hasher.input_str("-");
    symbol_hasher.input_str(encoder::encoded_ty(tcx, t).as_slice());
    // Prefix with 'h' so that it never blends into adjacent digits
    let mut hash = String::from_str("h");
    hash.push_str(truncated_hash_result(symbol_hasher).as_slice());
    hash
}

fn get_symbol_hash(ccx: &CrateContext, t: ty::t) -> String {
    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());
    hash
}


// Name sanitation. LLVM will happily accept identifiers with weird names, but
// gas doesn't!
// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $
pub fn sanitize(s: &str) -> String {
    let mut result = String::new();
    for c in s.chars() {
        match c {
            // Escape these with $ sequences
            '@' => result.push_str("$SP$"),
            '~' => result.push_str("$UP$"),
            '*' => result.push_str("$RP$"),
            '&' => result.push_str("$BP$"),
            '<' => result.push_str("$LT$"),
            '>' => result.push_str("$GT$"),
            '(' => result.push_str("$LP$"),
            ')' => result.push_str("$RP$"),
            ',' => result.push_str("$C$"),

            // '.' doesn't occur in types and functions, so reuse it
            // for ':' and '-'
            '-' | ':' => result.push_char('.'),

            // These are legal symbols
            'a' .. 'z'
            | 'A' .. 'Z'
            | '0' .. '9'
            | '_' | '.' | '$' => result.push_char(c),

            _ => {
                let mut tstr = String::new();
                char::escape_unicode(c, |c| tstr.push_char(c));
                result.push_char('$');
                result.push_str(tstr.as_slice().slice_from(1));
            }
        }
    }

    // Underscore-qualify anything that didn't start as an ident.
    if result.len() > 0u &&
        result.as_slice()[0] != '_' as u8 &&
        ! char::is_XID_start(result.as_slice()[0] as char) {
        return format!("_{}", result.as_slice()).to_string();
    }

    return result;
}

pub fn mangle<PI: Iterator<PathElem>>(mut path: PI,
                                      hash: Option<&str>,
                                      vers: Option<&str>) -> String {
    // Follow C++ namespace-mangling style, see
    // http://en.wikipedia.org/wiki/Name_mangling for more info.
    //
    // It turns out that on OSX you can actually have arbitrary symbols in
    // function names (at least when given to LLVM), but this is not possible
    // when using unix's linker. Perhaps one day when we just use a linker from LLVM
    // we won't need to do this name mangling. The problem with name mangling is
    // that it seriously limits the available characters. For example we can't
    // have things like &T or ~[T] in symbol names when one would theoretically
    // want them for things like impls of traits on that type.
    //
    // To be able to work on all platforms and get *some* reasonable output, we
    // use C++ name-mangling.

    let mut n = String::from_str("_ZN"); // _Z == Begin name-sequence, N == nested

    fn push(n: &mut String, s: &str) {
        let sani = sanitize(s);
        n.push_str(format!("{}{}", sani.len(), sani).as_slice());
    }

    // First, connect each component with <len, name> pairs.
    for e in path {
        push(&mut n, token::get_name(e.name()).get().as_slice())
    }

    match hash {
        Some(s) => push(&mut n, s),
        None => {}
    }
    match vers {
        Some(s) => push(&mut n, s),
        None => {}
    }

    n.push_char('E'); // End name-sequence.
    n
}

pub fn exported_name(path: PathElems, hash: &str, vers: &str) -> String {
    // The version will get mangled to have a leading '_', but it makes more
    // sense to lead with a 'v' b/c this is a version...
    let vers = if vers.len() > 0 && !char::is_XID_start(vers.char_at(0)) {
        format!("v{}", vers)
    } else {
        vers.to_string()
    };

    mangle(path, Some(hash), Some(vers.as_slice()))
}

pub fn mangle_exported_name(ccx: &CrateContext, path: PathElems,
                            t: ty::t, id: ast::NodeId) -> String {
    let mut hash = get_symbol_hash(ccx, t);

    // Paths can be completely identical for different nodes,
    // e.g. `fn foo() { { fn a() {} } { fn a() {} } }`, so we
    // generate unique characters from the node id. For now
    // hopefully 3 characters is enough to avoid collisions.
    static EXTRA_CHARS: &'static str =
        "abcdefghijklmnopqrstuvwxyz\
         ABCDEFGHIJKLMNOPQRSTUVWXYZ\
         0123456789";
    let id = id as uint;
    let extra1 = id % EXTRA_CHARS.len();
    let id = id / EXTRA_CHARS.len();
    let extra2 = id % EXTRA_CHARS.len();
    let id = id / EXTRA_CHARS.len();
    let extra3 = id % EXTRA_CHARS.len();
    hash.push_char(EXTRA_CHARS[extra1] as char);
    hash.push_char(EXTRA_CHARS[extra2] as char);
    hash.push_char(EXTRA_CHARS[extra3] as char);

    exported_name(path,
                  hash.as_slice(),
                  ccx.link_meta.crateid.version_or_default())
}

pub fn mangle_internal_name_by_type_and_seq(ccx: &CrateContext,
                                            t: ty::t,
                                            name: &str) -> String {
    let s = ppaux::ty_to_str(ccx.tcx(), t);
    let path = [PathName(token::intern(s.as_slice())),
                gensym_name(name)];
    let hash = get_symbol_hash(ccx, t);
    mangle(ast_map::Values(path.iter()), Some(hash.as_slice()), None)
}

pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> String {
    mangle(path.chain(Some(gensym_name(flav)).move_iter()), None, None)
}

pub fn output_lib_filename(id: &CrateId) -> String {
    format_strbuf!("{}-{}-{}",
                   id.name,
                   crate_id_hash(id),
                   id.version_or_default())
}

pub fn get_cc_prog(sess: &Session) -> String {
    match sess.opts.cg.linker {
        Some(ref linker) => return linker.to_string(),
        None => {}
    }

    // In the future, FreeBSD will use clang as default compiler.
    // It would be flexible to use cc (system's default C compiler)
    // instead of hard-coded gcc.
    // For win32, there is no cc command, so we add a condition to make it use gcc.
    match sess.targ_cfg.os {
        abi::OsWin32 => "gcc",
        _ => "cc",
    }.to_string()
}

pub fn get_ar_prog(sess: &Session) -> String {
    match sess.opts.cg.ar {
        Some(ref ar) => (*ar).clone(),
        None => "ar".to_string()
    }
}

fn remove(sess: &Session, path: &Path) {
    match fs::unlink(path) {
        Ok(..) => {}
        Err(e) => {
            sess.err(format!("failed to remove {}: {}",
                             path.display(),
                             e).as_slice());
        }
    }
}

/// Perform the linkage portion of the compilation phase. This will generate all
/// of the requested outputs for this compilation session.
pub fn link_binary(sess: &Session,
                   trans: &CrateTranslation,
                   outputs: &OutputFilenames,
                   id: &CrateId) -> Vec<Path> {
    let mut out_filenames = Vec::new();
    for &crate_type in sess.crate_types.borrow().iter() {
        let out_file = link_binary_output(sess, trans, crate_type, outputs, id);
        out_filenames.push(out_file);
    }

    // Remove the temporary object file and metadata if we aren't saving temps
    if !sess.opts.cg.save_temps {
        let obj_filename = outputs.temp_path(OutputTypeObject);
        if !sess.opts.output_types.contains(&OutputTypeObject) {
            remove(sess, &obj_filename);
        }
        remove(sess, &obj_filename.with_extension("metadata.o"));
    }

    out_filenames
}

fn is_writeable(p: &Path) -> bool {
    match p.stat() {
        Err(..) => true,
        Ok(m) => m.perm & io::UserWrite == io::UserWrite
    }
}

pub fn filename_for_input(sess: &Session, crate_type: config::CrateType,
                          id: &CrateId, out_filename: &Path) -> Path {
    let libname = output_lib_filename(id);
    match crate_type {
        config::CrateTypeRlib => {
            out_filename.with_filename(format_strbuf!("lib{}.rlib", libname))
        }
        config::CrateTypeDylib => {
            let (prefix, suffix) = match sess.targ_cfg.os {
                abi::OsWin32 => (loader::WIN32_DLL_PREFIX, loader::WIN32_DLL_SUFFIX),
                abi::OsMacos => (loader::MACOS_DLL_PREFIX, loader::MACOS_DLL_SUFFIX),
                abi::OsLinux => (loader::LINUX_DLL_PREFIX, loader::LINUX_DLL_SUFFIX),
                abi::OsAndroid => (loader::ANDROID_DLL_PREFIX, loader::ANDROID_DLL_SUFFIX),
                abi::OsFreebsd => (loader::FREEBSD_DLL_PREFIX, loader::FREEBSD_DLL_SUFFIX),
            };
            out_filename.with_filename(format_strbuf!("{}{}{}",
                                                      prefix,
                                                      libname,
                                                      suffix))
        }
        config::CrateTypeStaticlib => {
            out_filename.with_filename(format_strbuf!("lib{}.a", libname))
        }
        config::CrateTypeExecutable => out_filename.clone(),
    }
}

fn link_binary_output(sess: &Session,
                      trans: &CrateTranslation,
                      crate_type: config::CrateType,
                      outputs: &OutputFilenames,
                      id: &CrateId) -> Path {
    let obj_filename = outputs.temp_path(OutputTypeObject);
    let out_filename = match outputs.single_output_file {
        Some(ref file) => file.clone(),
        None => {
            let out_filename = outputs.path(OutputTypeExe);
            filename_for_input(sess, crate_type, id, &out_filename)
        }
    };

    // Make sure the output and obj_filename are both writeable.
    // Mac, FreeBSD, and Windows system linkers check this already --
    // however, the Linux linker will happily overwrite a read-only file.
    // We should be consistent.
    let obj_is_writeable = is_writeable(&obj_filename);
    let out_is_writeable = is_writeable(&out_filename);
    if !out_is_writeable {
        sess.fatal(format!("output file {} is not writeable -- check its \
                            permissions.",
                           out_filename.display()).as_slice());
    }
    else if !obj_is_writeable {
        sess.fatal(format!("object file {} is not writeable -- check its \
                            permissions.",
                           obj_filename.display()).as_slice());
    }

    match crate_type {
        config::CrateTypeRlib => {
            link_rlib(sess, Some(trans), &obj_filename, &out_filename);
        }
        config::CrateTypeStaticlib => {
            link_staticlib(sess, &obj_filename, &out_filename);
        }
        config::CrateTypeExecutable => {
            link_natively(sess, trans, false, &obj_filename, &out_filename);
        }
        config::CrateTypeDylib => {
            link_natively(sess, trans, true, &obj_filename, &out_filename);
        }
    }

    out_filename
}

// Create an 'rlib'
//
// An rlib in its current incarnation is essentially a renamed .a file. The
// rlib primarily contains the object file of the crate, but it also contains
// all of the object files from native libraries. This is done by unzipping
// native libraries and inserting all of the contents into this archive.
fn link_rlib<'a>(sess: &'a Session,
                 trans: Option<&CrateTranslation>, // None == no metadata/bytecode
                 obj_filename: &Path,
                 out_filename: &Path) -> Archive<'a> {
    let mut a = Archive::create(sess, out_filename, obj_filename);

    for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() {
        match kind {
            cstore::NativeStatic => {
                a.add_native_library(l.as_slice()).unwrap();
            }
            cstore::NativeFramework | cstore::NativeUnknown => {}
        }
    }

    // Note that it is important that we add all of our non-object "magical
    // files" *after* all of the object files in the archive. The reason for
    // this is as follows:
    //
    // * When performing LTO, this archive will be modified to remove
    //   obj_filename from above. The reason for this is described below.
    //
    // * When the system linker looks at an archive, it will attempt to
    //   determine the architecture of the archive in order to see whether its
    //   linkable.
    //
    //   The algorithm for this detection is: iterate over the files in the
    //   archive. Skip magical SYMDEF names. Interpret the first file as an
    //   object file. Read architecture from the object file.
    //
    // * As one can probably see, if "metadata" and "foo.bc" were placed
    //   before all of the objects, then the architecture of this archive would
    //   not be correctly inferred once 'foo.o' is removed.
    //
    // Basically, all this means is that this code should not move above the
    // code above.
    match trans {
        Some(trans) => {
            // Instead of putting the metadata in an object file section, rlibs
            // contain the metadata in a separate file. We use a temp directory
            // here so concurrent builds in the same directory don't try to use
            // the same filename for metadata (stomping over one another)
            let tmpdir = TempDir::new("rustc").expect("needs a temp dir");
            let metadata = tmpdir.path().join(METADATA_FILENAME);
            match fs::File::create(&metadata).write(trans.metadata
                                                         .as_slice()) {
                Ok(..) => {}
                Err(e) => {
                    sess.err(format!("failed to write {}: {}",
                                     metadata.display(),
                                     e).as_slice());
                    sess.abort_if_errors();
                }
            }
            a.add_file(&metadata, false);
            remove(sess, &metadata);

            // For LTO purposes, the bytecode of this library is also inserted
            // into the archive.
            let bc = obj_filename.with_extension("bc");
            let bc_deflated = obj_filename.with_extension("bc.deflate");
            match fs::File::open(&bc).read_to_end().and_then(|data| {
                fs::File::create(&bc_deflated)
                    .write(match flate::deflate_bytes(data.as_slice()) {
                        Some(compressed) => compressed,
                        None => sess.fatal("failed to compress bytecode")
                     }.as_slice())
            }) {
                Ok(()) => {}
                Err(e) => {
                    sess.err(format!("failed to write compressed bytecode: \
                                      {}",
                                     e).as_slice());
                    sess.abort_if_errors()
                }
            }
            a.add_file(&bc_deflated, false);
            remove(sess, &bc_deflated);
            if !sess.opts.cg.save_temps &&
               !sess.opts.output_types.contains(&OutputTypeBitcode) {
                remove(sess, &bc);
            }

            // After adding all files to the archive, we need to update the
            // symbol table of the archive. This currently dies on OSX (see
            // #11162), and isn't necessary there anyway
            match sess.targ_cfg.os {
                abi::OsMacos => {}
                _ => { a.update_symbols(); }
            }
        }

        None => {}
    }
    return a;
}

// Create a static archive
//
// This is essentially the same thing as an rlib, but it also involves adding
// all of the upstream crates' objects into the archive. This will slurp in
// all of the native libraries of upstream dependencies as well.
//
// Additionally, there's no way for us to link dynamic libraries, so we warn
// about all dynamic library dependencies that they're not linked in.
//
// There's no need to include metadata in a static archive, so ensure to not
// link in the metadata object file (and also don't prepare the archive with a
// metadata file).
fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
    let mut a = link_rlib(sess, None, obj_filename, out_filename);
    a.add_native_library("morestack").unwrap();
    a.add_native_library("compiler-rt").unwrap();

    let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
    for &(cnum, ref path) in crates.iter() {
        let name = sess.cstore.get_crate_data(cnum).name.clone();
        let p = match *path {
            Some(ref p) => p.clone(), None => {
                sess.err(format!("could not find rlib for: `{}`",
                                 name).as_slice());
                continue
            }
        };
        a.add_rlib(&p, name.as_slice(), sess.lto()).unwrap();
        let native_libs = csearch::get_native_libraries(&sess.cstore, cnum);
        for &(kind, ref lib) in native_libs.iter() {
            let name = match kind {
                cstore::NativeStatic => "static library",
                cstore::NativeUnknown => "library",
                cstore::NativeFramework => "framework",
            };
            sess.warn(format!("unlinked native {}: {}",
                              name,
                              *lib).as_slice());
        }
    }
}

// Create a dynamic library or executable
//
// This will invoke the system linker/cc to create the resulting file. This
// links to all upstream files as well.
fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
                 obj_filename: &Path, out_filename: &Path) {
    let tmpdir = TempDir::new("rustc").expect("needs a temp dir");

    // The invocations of cc share some flags across platforms
    let pname = get_cc_prog(sess);
    let mut cmd = Command::new(pname.as_slice());
<commit_msg>auto merge of #14472 : huonw/rust/native-lib-warnings, r=alexcrichton<commit_after>// Copyright 2012-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::archive::{Archive, METADATA_FILENAME};
use back::rpath;
use back::svh::Svh;
use driver::driver::{CrateTranslation, OutputFilenames};
use driver::config::NoDebugInfo;
use driver::session::Session;
use driver::config;
use lib::llvm::llvm;
use lib::llvm::ModuleRef;
use lib;
use metadata::common::LinkMeta;
use metadata::{encoder, cstore, filesearch, csearch, loader};
use middle::trans::context::CrateContext;
use middle::trans::common::gensym_name;
use middle::ty;
use util::common::time;
use util::ppaux;
use util::sha2::{Digest, Sha256};

use std::c_str::{ToCStr, CString};
use std::char;
use std::io::{fs, TempDir, Command};
use std::io;
use std::ptr;
use std::str;
use std::string::String;
use flate;
use serialize::hex::ToHex;
use syntax::abi;
use syntax::ast;
use syntax::ast_map::{PathElem, PathElems, PathName};
use syntax::ast_map;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::crateid::CrateId;
use syntax::parse::token;

#[deriving(Clone, Eq, Ord, TotalOrd, TotalEq)]
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 = str::from_utf8_lossy(err.as_bytes());
            sess.fatal(format!("{}: {}",
                               msg.as_slice(),
                               err.as_slice()).as_slice());
        }
    }
}

pub fn WriteOutputFile(
        sess: &Session,
        target: lib::llvm::TargetMachineRef,
        pm: lib::llvm::PassManagerRef,
        m: ModuleRef,
        output: &Path,
        file_type: lib::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 back::lto;
    use back::link::{WriteOutputFile, OutputType};
    use back::link::{OutputTypeAssembly, OutputTypeBitcode};
    use back::link::{OutputTypeExe, OutputTypeLlvmAssembly};
    use back::link::{OutputTypeObject};
    use driver::driver::{CrateTranslation, OutputFilenames};
    use driver::config::NoDebugInfo;
    use driver::session::Session;
    use driver::config;
    use lib::llvm::llvm;
    use lib::llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
    use lib;
    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.
    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()
                }
            }
            _ => 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 => lib::llvm::CodeGenLevelNone,
              config::Less => lib::llvm::CodeGenLevelLess,
              config::Default => lib::llvm::CodeGenLevelDefault,
              config::Aggressive => lib::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::OsWin32;
            let fdata_sections = ffunction_sections;

            let reloc_model = match sess.opts.cg.relocation_model.as_slice() {
                "pic" => lib::llvm::RelocPIC,
                "static" => lib::llvm::RelocStatic,
                "default" => lib::llvm::RelocDefault,
                "dynamic-no-pic" => lib::llvm::RelocDynamicNoPic,
                _ => {
                    sess.err(format!("{} is not a valid relocation mode",
                                     sess.opts
                                         .cg
                                         .relocation_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,
                            lib::llvm::CodeModelDefault,
                            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| {
                            WriteOutputFile(sess, tm, cpm, llmod, &path,
                                            lib::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| {
                            WriteOutputFile(sess, tm, cpm, llmod, path,
                                            lib::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");
                        WriteOutputFile(sess, tm, cpm,
                                        trans.metadata_module, &out,
                                        lib::llvm::ObjectFile);
                    })
                }
            });

            llvm::LLVMRustDisposeTargetMachine(tm);
            llvm::LLVMDisposeModule(trans.metadata_module);
            llvm::LLVMDisposeModule(llmod);
            llvm::LLVMContextDispose(llcx);
            if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
        }
    }

    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()
                                                             .as_slice());
                    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 sync::one::{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.with_ref(|p| p));
                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: lib::llvm::PassManagerRef,
                                   mpm: lib::llvm::PassManagerRef,
                                   llmod: ModuleRef,
                                   opt: lib::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 {
            lib::llvm::CodeGenLevelNone => {
                // Don't add lifetime intrinsics at O0
                llvm::LLVMRustAddAlwaysInlinePass(builder, false);
            }
            lib::llvm::CodeGenLevelLess => {
                llvm::LLVMRustAddAlwaysInlinePass(builder, true);
            }
            // numeric values copied from clang
            lib::llvm::CodeGenLevelDefault => {
                llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
                                                                    225);
            }
            lib::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);
    }
}


/*
 * Name mangling and its relationship to metadata. This is complex. Read
 * carefully.
 *
 * The semantic model of Rust linkage is, broadly, that "there's no global
 * namespace" between crates. Our aim is to preserve the illusion of this
 * model despite the fact that it's not *quite* possible to implement on
 * modern linkers. We initially didn't use system linkers at all, but have
 * been convinced of their utility.
 *
 * There are a few issues to handle:
 *
 *  - Linkers operate on a flat namespace, so we have to flatten names.
 *    We do this using the C++ namespace-mangling technique. Foo::bar
 *    symbols and such.
 *
 *  - Symbols with the same name but different types need to get different
 *    linkage-names. We do this by hashing a string-encoding of the type into
 *    a fixed-size (currently 16-byte hex) cryptographic hash function (CHF:
 *    we use SHA256) to "prevent collisions". This is not airtight but 16 hex
 *    digits on uniform probability means you're going to need 2**32 same-name
 *    symbols in the same process before you're even hitting birthday-paradox
 *    collision probability.
 *
 *  - Symbols in different crates but with same names "within" the crate need
 *    to get different linkage-names.
 *
 *  - The hash shown in the filename needs to be predictable and stable for
 *    build tooling integration. It also needs to be using a hash function
 *    which is easy to use from Python, make, etc.
 *
 * So here is what we do:
 *
 *  - Consider the package id; every crate has one (specified with crate_id
 *    attribute).  If a package id isn't provided explicitly, we infer a
 *    versionless one from the output name. The version will end up being 0.0
 *    in this case. CNAME and CVERS are taken from this package id. For
 *    example, github.com/mozilla/CNAME#CVERS.
 *
 *  - Define CMH as SHA256(crateid).
 *
 *  - Define CMH8 as the first 8 characters of CMH.
 *
 *  - Compile our crate to lib CNAME-CMH8-CVERS.so
 *
 *  - Define STH(sym) as SHA256(CMH, type_str(sym))
 *
 *  - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
 *    name, non-name metadata, and type sense, and versioned in the way
 *    system linkers understand.
 */

// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
pub fn find_crate_id(attrs: &[ast::Attribute], out_filestem: &str) -> CrateId {
    match attr::find_crateid(attrs) {
        None => from_str(out_filestem).unwrap_or_else(|| {
            let mut s = out_filestem.chars().filter(|c| c.is_XID_continue());
            from_str(s.collect::<String>().as_slice())
                .or(from_str("rust-out")).unwrap()
        }),
        Some(s) => s,
    }
}

pub fn crate_id_hash(crate_id: &CrateId) -> String {
    // This calculates CMH as defined above. Note that we don't use the path of
    // the crate id in the hash because lookups are only done by (name/vers),
    // not by path.
    let mut s = Sha256::new();
    s.input_str(crate_id.short_name_with_version().as_slice());
    truncated_hash_result(&mut s).as_slice().slice_to(8).to_string()
}

// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
pub fn build_link_meta(krate: &ast::Crate, out_filestem: &str) -> LinkMeta {
    let r = LinkMeta {
        crateid: find_crate_id(krate.attrs.as_slice(), out_filestem),
        crate_hash: Svh::calculate(krate),
    };
    info!("{}", r);
    return r;
}

fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String {
    let output = symbol_hasher.result_bytes();
    // 64 bits should be enough to avoid collisions.
    output.slice_to(8).to_hex().to_string()
}


// This calculates STH for a symbol, as defined above
fn symbol_hash(tcx: &ty::ctxt,
               symbol_hasher: &mut Sha256,
               t: ty::t,
               link_meta: &LinkMeta)
               -> String {
    // NB: do *not* use abbrevs here as we want the symbol names
    // to be independent of one another in the crate.

    symbol_hasher.reset();
    symbol_hasher.input_str(link_meta.crateid.name.as_slice());
    symbol_hasher.input_str("-");
    symbol_hasher.input_str(link_meta.crate_hash.as_str());
    symbol_hasher.input_str("-");
    symbol_hasher.input_str(encoder::encoded_ty(tcx, t).as_slice());
    // Prefix with 'h' so that it never blends into adjacent digits
    let mut hash = String::from_str("h");
    hash.push_str(truncated_hash_result(symbol_hasher).as_slice());
    hash
}

fn get_symbol_hash(ccx: &CrateContext, t: ty::t) -> String {
    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());
    hash
}


// Name sanitation. LLVM will happily accept identifiers with weird names, but
// gas doesn't!
// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $
pub fn sanitize(s: &str) -> String {
    let mut result = String::new();
    for c in s.chars() {
        match c {
            // Escape these with $ sequences
            '@' => result.push_str("$SP$"),
            '~' => result.push_str("$UP$"),
            '*' => result.push_str("$RP$"),
            '&' => result.push_str("$BP$"),
            '<' => result.push_str("$LT$"),
            '>' => result.push_str("$GT$"),
            '(' => result.push_str("$LP$"),
            ')' => result.push_str("$RP$"),
            ',' => result.push_str("$C$"),

            // '.' doesn't occur in types and functions, so reuse it
            // for ':' and '-'
            '-' | ':' => result.push_char('.'),

            // These are legal symbols
            'a' .. 'z'
            | 'A' .. 'Z'
            | '0' .. '9'
            | '_' | '.' | '$' => result.push_char(c),

            _ => {
                let mut tstr = String::new();
                char::escape_unicode(c, |c| tstr.push_char(c));
                result.push_char('$');
                result.push_str(tstr.as_slice().slice_from(1));
            }
        }
    }

    // Underscore-qualify anything that didn't start as an ident.
    if result.len() > 0u &&
        result.as_slice()[0] != '_' as u8 &&
        ! char::is_XID_start(result.as_slice()[0] as char) {
        return format!("_{}", result.as_slice()).to_string();
    }

    return result;
}

pub fn mangle<PI: Iterator<PathElem>>(mut path: PI,
                                      hash: Option<&str>,
                                      vers: Option<&str>) -> String {
    // Follow C++ namespace-mangling style, see
    // http://en.wikipedia.org/wiki/Name_mangling for more info.
    //
    // It turns out that on OSX you can actually have arbitrary symbols in
    // function names (at least when given to LLVM), but this is not possible
    // when using unix's linker. Perhaps one day when we just use a linker from LLVM
    // we won't need to do this name mangling. The problem with name mangling is
    // that it seriously limits the available characters. For example we can't
    // have things like &T or ~[T] in symbol names when one would theoretically
    // want them for things like impls of traits on that type.
    //
    // To be able to work on all platforms and get *some* reasonable output, we
    // use C++ name-mangling.

    let mut n = String::from_str("_ZN"); // _Z == Begin name-sequence, N == nested

    fn push(n: &mut String, s: &str) {
        let sani = sanitize(s);
        n.push_str(format!("{}{}", sani.len(), sani).as_slice());
    }

    // First, connect each component with <len, name> pairs.
    for e in path {
        push(&mut n, token::get_name(e.name()).get().as_slice())
    }

    match hash {
        Some(s) => push(&mut n, s),
        None => {}
    }
    match vers {
        Some(s) => push(&mut n, s),
        None => {}
    }

    n.push_char('E'); // End name-sequence.
    n
}

pub fn exported_name(path: PathElems, hash: &str, vers: &str) -> String {
    // The version will get mangled to have a leading '_', but it makes more
    // sense to lead with a 'v' b/c this is a version...
    let vers = if vers.len() > 0 && !char::is_XID_start(vers.char_at(0)) {
        format!("v{}", vers)
    } else {
        vers.to_string()
    };

    mangle(path, Some(hash), Some(vers.as_slice()))
}

pub fn mangle_exported_name(ccx: &CrateContext, path: PathElems,
                            t: ty::t, id: ast::NodeId) -> String {
    let mut hash = get_symbol_hash(ccx, t);

    // Paths can be completely identical for different nodes,
    // e.g. `fn foo() { { fn a() {} } { fn a() {} } }`, so we
    // generate unique characters from the node id. For now
    // hopefully 3 characters is enough to avoid collisions.
    static EXTRA_CHARS: &'static str =
        "abcdefghijklmnopqrstuvwxyz\
         ABCDEFGHIJKLMNOPQRSTUVWXYZ\
         0123456789";
    let id = id as uint;
    let extra1 = id % EXTRA_CHARS.len();
    let id = id / EXTRA_CHARS.len();
    let extra2 = id % EXTRA_CHARS.len();
    let id = id / EXTRA_CHARS.len();
    let extra3 = id % EXTRA_CHARS.len();
    hash.push_char(EXTRA_CHARS[extra1] as char);
    hash.push_char(EXTRA_CHARS[extra2] as char);
    hash.push_char(EXTRA_CHARS[extra3] as char);

    exported_name(path,
                  hash.as_slice(),
                  ccx.link_meta.crateid.version_or_default())
}

pub fn mangle_internal_name_by_type_and_seq(ccx: &CrateContext,
                                            t: ty::t,
                                            name: &str) -> String {
    let s = ppaux::ty_to_str(ccx.tcx(), t);
    let path = [PathName(token::intern(s.as_slice())),
                gensym_name(name)];
    let hash = get_symbol_hash(ccx, t);
    mangle(ast_map::Values(path.iter()), Some(hash.as_slice()), None)
}

pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> String {
    mangle(path.chain(Some(gensym_name(flav)).move_iter()), None, None)
}

pub fn output_lib_filename(id: &CrateId) -> String {
    format_strbuf!("{}-{}-{}",
                   id.name,
                   crate_id_hash(id),
                   id.version_or_default())
}

pub fn get_cc_prog(sess: &Session) -> String {
    match sess.opts.cg.linker {
        Some(ref linker) => return linker.to_string(),
        None => {}
    }

    // In the future, FreeBSD will use clang as default compiler.
    // It would be flexible to use cc (system's default C compiler)
    // instead of hard-coded gcc.
    // For win32, there is no cc command, so we add a condition to make it use gcc.
    match sess.targ_cfg.os {
        abi::OsWin32 => "gcc",
        _ => "cc",
    }.to_string()
}

pub fn get_ar_prog(sess: &Session) -> String {
    match sess.opts.cg.ar {
        Some(ref ar) => (*ar).clone(),
        None => "ar".to_string()
    }
}

fn remove(sess: &Session, path: &Path) {
    match fs::unlink(path) {
        Ok(..) => {}
        Err(e) => {
            sess.err(format!("failed to remove {}: {}",
                             path.display(),
                             e).as_slice());
        }
    }
}

/// Perform the linkage portion of the compilation phase. This will generate all
/// of the requested outputs for this compilation session.
pub fn link_binary(sess: &Session,
                   trans: &CrateTranslation,
                   outputs: &OutputFilenames,
                   id: &CrateId) -> Vec<Path> {
    let mut out_filenames = Vec::new();
    for &crate_type in sess.crate_types.borrow().iter() {
        let out_file = link_binary_output(sess, trans, crate_type, outputs, id);
        out_filenames.push(out_file);
    }

    // Remove the temporary object file and metadata if we aren't saving temps
    if !sess.opts.cg.save_temps {
        let obj_filename = outputs.temp_path(OutputTypeObject);
        if !sess.opts.output_types.contains(&OutputTypeObject) {
            remove(sess, &obj_filename);
        }
        remove(sess, &obj_filename.with_extension("metadata.o"));
    }

    out_filenames
}

fn is_writeable(p: &Path) -> bool {
    match p.stat() {
        Err(..) => true,
        Ok(m) => m.perm & io::UserWrite == io::UserWrite
    }
}

pub fn filename_for_input(sess: &Session, crate_type: config::CrateType,
                          id: &CrateId, out_filename: &Path) -> Path {
    let libname = output_lib_filename(id);
    match crate_type {
        config::CrateTypeRlib => {
            out_filename.with_filename(format_strbuf!("lib{}.rlib", libname))
        }
        config::CrateTypeDylib => {
            let (prefix, suffix) = match sess.targ_cfg.os {
                abi::OsWin32 => (loader::WIN32_DLL_PREFIX, loader::WIN32_DLL_SUFFIX),
                abi::OsMacos => (loader::MACOS_DLL_PREFIX, loader::MACOS_DLL_SUFFIX),
                abi::OsLinux => (loader::LINUX_DLL_PREFIX, loader::LINUX_DLL_SUFFIX),
                abi::OsAndroid => (loader::ANDROID_DLL_PREFIX, loader::ANDROID_DLL_SUFFIX),
                abi::OsFreebsd => (loader::FREEBSD_DLL_PREFIX, loader::FREEBSD_DLL_SUFFIX),
            };
            out_filename.with_filename(format_strbuf!("{}{}{}",
                                                      prefix,
                                                      libname,
                                                      suffix))
        }
        config::CrateTypeStaticlib => {
            out_filename.with_filename(format_strbuf!("lib{}.a", libname))
        }
        config::CrateTypeExecutable => out_filename.clone(),
    }
}

fn link_binary_output(sess: &Session,
                      trans: &CrateTranslation,
                      crate_type: config::CrateType,
                      outputs: &OutputFilenames,
                      id: &CrateId) -> Path {
    let obj_filename = outputs.temp_path(OutputTypeObject);
    let out_filename = match outputs.single_output_file {
        Some(ref file) => file.clone(),
        None => {
            let out_filename = outputs.path(OutputTypeExe);
            filename_for_input(sess, crate_type, id, &out_filename)
        }
    };

    // Make sure the output and obj_filename are both writeable.
    // Mac, FreeBSD, and Windows system linkers check this already --
    // however, the Linux linker will happily overwrite a read-only file.
    // We should be consistent.
    let obj_is_writeable = is_writeable(&obj_filename);
    let out_is_writeable = is_writeable(&out_filename);
    if !out_is_writeable {
        sess.fatal(format!("output file {} is not writeable -- check its \
                            permissions.",
                           out_filename.display()).as_slice());
    }
    else if !obj_is_writeable {
        sess.fatal(format!("object file {} is not writeable -- check its \
                            permissions.",
                           obj_filename.display()).as_slice());
    }

    match crate_type {
        config::CrateTypeRlib => {
            link_rlib(sess, Some(trans), &obj_filename, &out_filename);
        }
        config::CrateTypeStaticlib => {
            link_staticlib(sess, &obj_filename, &out_filename);
        }
        config::CrateTypeExecutable => {
            link_natively(sess, trans, false, &obj_filename, &out_filename);
        }
        config::CrateTypeDylib => {
            link_natively(sess, trans, true, &obj_filename, &out_filename);
        }
    }

    out_filename
}

// Create an 'rlib'
//
// An rlib in its current incarnation is essentially a renamed .a file. The
// rlib primarily contains the object file of the crate, but it also contains
// all of the object files from native libraries. This is done by unzipping
// native libraries and inserting all of the contents into this archive.
fn link_rlib<'a>(sess: &'a Session,
                 trans: Option<&CrateTranslation>, // None == no metadata/bytecode
                 obj_filename: &Path,
                 out_filename: &Path) -> Archive<'a> {
    let mut a = Archive::create(sess, out_filename, obj_filename);

    for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() {
        match kind {
            cstore::NativeStatic => {
                a.add_native_library(l.as_slice()).unwrap();
            }
            cstore::NativeFramework | cstore::NativeUnknown => {}
        }
    }

    // Note that it is important that we add all of our non-object "magical
    // files" *after* all of the object files in the archive. The reason for
    // this is as follows:
    //
    // * When performing LTO, this archive will be modified to remove
    //   obj_filename from above. The reason for this is described below.
    //
    // * When the system linker looks at an archive, it will attempt to
    //   determine the architecture of the archive in order to see whether its
    //   linkable.
    //
    //   The algorithm for this detection is: iterate over the files in the
    //   archive. Skip magical SYMDEF names. Interpret the first file as an
    //   object file. Read architecture from the object file.
    //
    // * As one can probably see, if "metadata" and "foo.bc" were placed
    //   before all of the objects, then the architecture of this archive would
    //   not be correctly inferred once 'foo.o' is removed.
    //
    // Basically, all this means is that this code should not move above the
    // code above.
    match trans {
        Some(trans) => {
            // Instead of putting the metadata in an object file section, rlibs
            // contain the metadata in a separate file. We use a temp directory
            // here so concurrent builds in the same directory don't try to use
            // the same filename for metadata (stomping over one another)
            let tmpdir = TempDir::new("rustc").expect("needs a temp dir");
            let metadata = tmpdir.path().join(METADATA_FILENAME);
            match fs::File::create(&metadata).write(trans.metadata
                                                         .as_slice()) {
                Ok(..) => {}
                Err(e) => {
                    sess.err(format!("failed to write {}: {}",
                                     metadata.display(),
                                     e).as_slice());
                    sess.abort_if_errors();
                }
            }
            a.add_file(&metadata, false);
            remove(sess, &metadata);

            // For LTO purposes, the bytecode of this library is also inserted
            // into the archive.
            let bc = obj_filename.with_extension("bc");
            let bc_deflated = obj_filename.with_extension("bc.deflate");
            match fs::File::open(&bc).read_to_end().and_then(|data| {
                fs::File::create(&bc_deflated)
                    .write(match flate::deflate_bytes(data.as_slice()) {
                        Some(compressed) => compressed,
                        None => sess.fatal("failed to compress bytecode")
                     }.as_slice())
            }) {
                Ok(()) => {}
                Err(e) => {
                    sess.err(format!("failed to write compressed bytecode: \
                                      {}",
                                     e).as_slice());
                    sess.abort_if_errors()
                }
            }
            a.add_file(&bc_deflated, false);
            remove(sess, &bc_deflated);
            if !sess.opts.cg.save_temps &&
               !sess.opts.output_types.contains(&OutputTypeBitcode) {
                remove(sess, &bc);
            }

            // After adding all files to the archive, we need to update the
            // symbol table of the archive. This currently dies on OSX (see
            // #11162), and isn't necessary there anyway
            match sess.targ_cfg.os {
                abi::OsMacos => {}
                _ => { a.update_symbols(); }
            }
        }

        None => {}
    }
    return a;
}

// Create a static archive
//
// This is essentially the same thing as an rlib, but it also involves adding
// all of the upstream crates' objects into the archive. This will slurp in
// all of the native libraries of upstream dependencies as well.
//
// Additionally, there's no way for us to link dynamic libraries, so we warn
// about all dynamic library dependencies that they're not linked in.
//
// There's no need to include metadata in a static archive, so ensure to not
// link in the metadata object file (and also don't prepare the archive with a
// metadata file).
fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
    let mut a = link_rlib(sess, None, obj_filename, out_filename);
    a.add_native_library("morestack").unwrap();
    a.add_native_library("compiler-rt").unwrap();

    let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
    let mut all_native_libs = vec![];

    for &(cnum, ref path) in crates.iter() {
        let name = sess.cstore.get_crate_data(cnum).name.clone();
        let p = match *path {
            Some(ref p) => p.clone(), None => {
                sess.err(format!("could not find rlib for: `{}`",
                                 name).as_slice());
                continue
            }
        };
        a.add_rlib(&p, name.as_slice(), sess.lto()).unwrap();

        let native_libs = csearch::get_native_libraries(&sess.cstore, cnum);
        all_native_libs.extend(native_libs.move_iter());
    }

    if !all_native_libs.is_empty() {
        sess.warn("link against the following native artifacts when linking against \
                  this static library");
        sess.note("the order and any duplication can be significant on some platforms, \
                  and so may need to be preserved");
    }

    for &(kind, ref lib) in all_native_libs.iter() {
        let name = match kind {
            cstore::NativeStatic => "static library",
            cstore::NativeUnknown => "library",
            cstore::NativeFramework => "framework",
        };
        sess.note(format!("{}: {}", name, *lib).as_slice());
    }
}

// Create a dynamic library or executable
//
// This will invoke the system linker/cc to create the resulting file. This
// links to all upstream files as well.
fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
                 obj_filename: &Path, out_filename: &Path) {
    let tmpdir = TempDir::new("rustc").expect("needs a temp dir");

    // The invocations of cc share some flags across platforms
    let pname = get_cc_prog(sess);
    let mut cmd = Command::new(pname.as_slice());

{'fraction_non_alphanumeric': 0.11394375331643182, 'fraction_numerical': 0.0035964860562466834, 'mean_word_length': 1.7805245901639344, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 251, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 104, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7026682', 'n_tokens_mistral': 24571, 'n_tokens_neox': 23724, 'n_words': 6139}