diff options
| author | Brian Anderson <banderson@mozilla.com> | 2012-11-06 19:44:58 -0800 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2012-11-07 13:53:39 -0800 |
| commit | 69a8b4d8e20df44766fad153b6300b0a6e7e3d5e (patch) | |
| tree | 5e4ea1b0d1bce8f3c4d96dba09ec50f1ba47de40 /src/librustc/back | |
| parent | f72ef31ede98d0605195b2a6baba38f71d0177a0 (diff) | |
| download | rust-69a8b4d8e20df44766fad153b6300b0a6e7e3d5e.tar.gz rust-69a8b4d8e20df44766fad153b6300b0a6e7e3d5e.zip | |
Rename src/rustc to src/librustc. Use the driver crate
Diffstat (limited to 'src/librustc/back')
| -rw-r--r-- | src/librustc/back/abi.rs | 78 | ||||
| -rw-r--r-- | src/librustc/back/link.rs | 798 | ||||
| -rw-r--r-- | src/librustc/back/rpath.rs | 333 | ||||
| -rw-r--r-- | src/librustc/back/target_strs.rs | 7 | ||||
| -rw-r--r-- | src/librustc/back/upcall.rs | 59 | ||||
| -rw-r--r-- | src/librustc/back/x86.rs | 51 | ||||
| -rw-r--r-- | src/librustc/back/x86_64.rs | 57 |
7 files changed, 1383 insertions, 0 deletions
diff --git a/src/librustc/back/abi.rs b/src/librustc/back/abi.rs new file mode 100644 index 00000000000..7c35e3d892d --- /dev/null +++ b/src/librustc/back/abi.rs @@ -0,0 +1,78 @@ + + + +const rc_base_field_refcnt: uint = 0u; + +const task_field_refcnt: uint = 0u; + +const task_field_stk: uint = 2u; + +const task_field_runtime_sp: uint = 3u; + +const task_field_rust_sp: uint = 4u; + +const task_field_gc_alloc_chain: uint = 5u; + +const task_field_dom: uint = 6u; + +const n_visible_task_fields: uint = 7u; + +const dom_field_interrupt_flag: uint = 1u; + +const frame_glue_fns_field_mark: uint = 0u; + +const frame_glue_fns_field_drop: uint = 1u; + +const frame_glue_fns_field_reloc: uint = 2u; + +const box_field_refcnt: uint = 0u; +const box_field_tydesc: uint = 1u; +const box_field_prev: uint = 2u; +const box_field_next: uint = 3u; +const box_field_body: uint = 4u; + +const general_code_alignment: uint = 16u; + +const tydesc_field_size: uint = 0u; +const tydesc_field_align: uint = 1u; +const tydesc_field_take_glue: uint = 2u; +const tydesc_field_drop_glue: uint = 3u; +const tydesc_field_free_glue: uint = 4u; +const tydesc_field_visit_glue: uint = 5u; +const tydesc_field_shape: uint = 6u; +const tydesc_field_shape_tables: uint = 7u; +const n_tydesc_fields: uint = 8u; + +// The two halves of a closure: code and environment. +const fn_field_code: uint = 0u; +const fn_field_box: uint = 1u; + +const vec_elt_fill: uint = 0u; + +const vec_elt_alloc: uint = 1u; + +const vec_elt_elems: uint = 2u; + +const slice_elt_base: uint = 0u; +const slice_elt_len: uint = 1u; + +const worst_case_glue_call_args: uint = 7u; + +const abi_version: uint = 1u; + +fn memcpy_glue_name() -> ~str { return ~"rust_memcpy_glue"; } + +fn bzero_glue_name() -> ~str { return ~"rust_bzero_glue"; } + +fn yield_glue_name() -> ~str { return ~"rust_yield_glue"; } + +fn no_op_type_glue_name() -> ~str { return ~"rust_no_op_type_glue"; } +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs new file mode 100644 index 00000000000..61fd68c193a --- /dev/null +++ b/src/librustc/back/link.rs @@ -0,0 +1,798 @@ +use libc::{c_int, c_uint, c_char}; +use driver::session; +use session::Session; +use lib::llvm::llvm; +use syntax::attr; +use middle::ty; +use metadata::{encoder, cstore}; +use middle::trans::common::crate_ctxt; +use metadata::common::link_meta; +use std::map::HashMap; +use std::sha1::sha1; +use syntax::ast; +use syntax::print::pprust; +use lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data, True, False, + PassManagerRef, FileType}; +use metadata::filesearch; +use syntax::ast_map::{path, path_mod, path_name}; +use io::{Writer, WriterUtil}; + +enum output_type { + output_type_none, + output_type_bitcode, + output_type_assembly, + output_type_llvm_assembly, + output_type_object, + output_type_exe, +} + +impl output_type : cmp::Eq { + pure fn eq(other: &output_type) -> bool { + (self as uint) == ((*other) as uint) + } + pure fn ne(other: &output_type) -> bool { !self.eq(other) } +} + +fn llvm_err(sess: Session, msg: ~str) -> ! unsafe { + let cstr = llvm::LLVMRustGetLastError(); + if cstr == ptr::null() { + sess.fatal(msg); + } else { sess.fatal(msg + ~": " + str::raw::from_c_str(cstr)); } +} + +fn WriteOutputFile(sess: Session, + PM: lib::llvm::PassManagerRef, M: ModuleRef, + Triple: *c_char, + // FIXME: When #2334 is fixed, change + // c_uint to FileType + Output: *c_char, FileType: c_uint, + OptLevel: c_int, + EnableSegmentedStacks: bool) { + let result = llvm::LLVMRustWriteOutputFile( + PM, M, Triple, Output, FileType, OptLevel, EnableSegmentedStacks); + if (!result) { + llvm_err(sess, ~"Could not write output"); + } +} + +mod jit { + #[legacy_exports]; + #[nolink] + #[abi = "rust-intrinsic"] + extern mod rusti { + #[legacy_exports]; + fn morestack_addr() -> *(); + } + + struct Closure { + code: *(), + env: *(), + } + + fn exec(sess: Session, + pm: PassManagerRef, + m: ModuleRef, + opt: c_int, + stacks: bool) unsafe { + let manager = llvm::LLVMRustPrepareJIT(rusti::morestack_addr()); + + // We need to tell JIT where to resolve all linked + // symbols from. The equivalent of -lstd, -lcore, etc. + // By default the JIT will resolve symbols from the std and + // core linked into rustc. We don't want that, + // incase the user wants to use an older std library. + + let cstore = sess.cstore; + for cstore::get_used_crate_files(cstore).each |cratepath| { + let path = cratepath.to_str(); + + debug!("linking: %s", path); + + let _: () = str::as_c_str( + path, + |buf_t| { + if !llvm::LLVMRustLoadCrate(manager, buf_t) { + llvm_err(sess, ~"Could not link"); + } + debug!("linked: %s", path); + }); + } + + // The execute function will return a void pointer + // to the _rust_main function. We can do closure + // magic here to turn it straight into a callable rust + // closure. It will also cleanup the memory manager + // for us. + + let entry = llvm::LLVMRustExecuteJIT(manager, + pm, m, opt, stacks); + + if ptr::is_null(entry) { + llvm_err(sess, ~"Could not JIT"); + } else { + let closure = Closure { + code: entry, + env: ptr::null() + }; + let func: fn(++argv: ~[~str]) = cast::transmute(move closure); + + func(~[sess.opts.binary]); + } + } +} + +mod write { + #[legacy_exports]; + fn is_object_or_assembly_or_exe(ot: output_type) -> bool { + if ot == output_type_assembly || ot == output_type_object || + ot == output_type_exe { + return true; + } + return false; + } + + fn run_passes(sess: Session, llmod: ModuleRef, output: &Path) { + let opts = sess.opts; + if sess.time_llvm_passes() { llvm::LLVMRustEnableTimePasses(); } + let mut pm = mk_pass_manager(); + let td = mk_target_data( + sess.targ_cfg.target_strs.data_layout); + llvm::LLVMAddTargetData(td.lltd, pm.llpm); + // FIXME (#2812): run the linter here also, once there are llvm-c + // bindings for it. + + // Generate a pre-optimization intermediate file if -save-temps was + // specified. + + + if opts.save_temps { + match opts.output_type { + output_type_bitcode => { + if opts.optimize != session::No { + let filename = output.with_filetype("no-opt.bc"); + str::as_c_str(filename.to_str(), |buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + } + } + _ => { + let filename = output.with_filetype("bc"); + str::as_c_str(filename.to_str(), |buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + } + } + } + if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } + // FIXME (#2396): This is mostly a copy of the bits of opt's -O2 that + // are available in the C api. + // Also: We might want to add optimization levels like -O1, -O2, + // -Os, etc + // Also: Should we expose and use the pass lists used by the opt + // tool? + + if opts.optimize != session::No { + let fpm = mk_pass_manager(); + llvm::LLVMAddTargetData(td.lltd, fpm.llpm); + + let FPMB = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderSetOptLevel(FPMB, 2u as c_uint); + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(FPMB, + fpm.llpm); + llvm::LLVMPassManagerBuilderDispose(FPMB); + + llvm::LLVMRunPassManager(fpm.llpm, llmod); + let mut threshold = 225; + if opts.optimize == session::Aggressive { threshold = 275; } + + let MPMB = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderSetOptLevel(MPMB, + opts.optimize as c_uint); + llvm::LLVMPassManagerBuilderSetSizeLevel(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableUnitAtATime(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableSimplifyLibCalls(MPMB, + False); + + if threshold != 0u { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold + (MPMB, threshold as c_uint); + } + llvm::LLVMPassManagerBuilderPopulateModulePassManager(MPMB, + pm.llpm); + + llvm::LLVMPassManagerBuilderDispose(MPMB); + } + if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } + if is_object_or_assembly_or_exe(opts.output_type) || opts.jit { + let LLVMOptNone = 0 as c_int; // -O0 + let LLVMOptLess = 1 as c_int; // -O1 + let LLVMOptDefault = 2 as c_int; // -O2, -Os + let LLVMOptAggressive = 3 as c_int; // -O3 + + let mut CodeGenOptLevel = match opts.optimize { + session::No => LLVMOptNone, + session::Less => LLVMOptLess, + session::Default => LLVMOptDefault, + session::Aggressive => LLVMOptAggressive + }; + + if opts.jit { + // If we are using JIT, go ahead and create and + // execute the engine now. + // JIT execution takes ownership of the module, + // so don't dispose and return. + + jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true); + + if sess.time_llvm_passes() { + llvm::LLVMRustPrintPassTimings(); + } + return; + } + + let mut FileType; + if opts.output_type == output_type_object || + opts.output_type == output_type_exe { + FileType = lib::llvm::ObjectFile; + } else { FileType = lib::llvm::AssemblyFile; } + // Write optimized bitcode if --save-temps was on. + + if opts.save_temps { + // Always output the bitcode file with --save-temps + + let filename = output.with_filetype("opt.bc"); + llvm::LLVMRunPassManager(pm.llpm, llmod); + str::as_c_str(filename.to_str(), |buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + pm = mk_pass_manager(); + // Save the assembly file if -S is used + + if opts.output_type == output_type_assembly { + let _: () = str::as_c_str( + sess.targ_cfg.target_strs.target_triple, + |buf_t| { + str::as_c_str(output.to_str(), |buf_o| { + WriteOutputFile( + sess, + pm.llpm, + llmod, + buf_t, + buf_o, + lib::llvm::AssemblyFile as c_uint, + CodeGenOptLevel, + true) + }) + }); + } + + + // Save the object file for -c or --save-temps alone + // This .o is needed when an exe is built + if opts.output_type == output_type_object || + opts.output_type == output_type_exe { + let _: () = str::as_c_str( + sess.targ_cfg.target_strs.target_triple, + |buf_t| { + str::as_c_str(output.to_str(), |buf_o| { + WriteOutputFile( + sess, + pm.llpm, + llmod, + buf_t, + buf_o, + lib::llvm::ObjectFile as c_uint, + CodeGenOptLevel, + true) + }) + }); + } + } else { + // If we aren't saving temps then just output the file + // type corresponding to the '-c' or '-S' flag used + + let _: () = str::as_c_str( + sess.targ_cfg.target_strs.target_triple, + |buf_t| { + str::as_c_str(output.to_str(), |buf_o| { + WriteOutputFile( + sess, + pm.llpm, + llmod, + buf_t, + buf_o, + FileType as c_uint, + CodeGenOptLevel, + true) + }) + }); + } + // Clean up and return + + llvm::LLVMDisposeModule(llmod); + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + return; + } + + if opts.output_type == output_type_llvm_assembly { + // Given options "-S --emit-llvm": output LLVM assembly + str::as_c_str(output.to_str(), |buf_o| { + llvm::LLVMRustAddPrintModulePass(pm.llpm, llmod, buf_o)}); + } else { + // If only a bitcode file is asked for by using the '--emit-llvm' + // flag, then output it here + llvm::LLVMRunPassManager(pm.llpm, llmod); + str::as_c_str(output.to_str(), + |buf| llvm::LLVMWriteBitcodeToFile(llmod, buf) ); + } + + llvm::LLVMDisposeModule(llmod); + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + } +} + + +/* + * 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 SHA1) 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. + * + * So here is what we do: + * + * - Separate the meta tags into two sets: exported and local. Only work with + * the exported ones when considering linkage. + * + * - Consider two exported tags as special (and mandatory): name and vers. + * Every crate gets them; if it doesn't name them explicitly we infer them + * as basename(crate) and "0.1", respectively. Call these CNAME, CVERS. + * + * - Define CMETA as all the non-name, non-vers exported meta tags in the + * crate (in sorted order). + * + * - Define CMH as hash(CMETA + hashes of dependent crates). + * + * - Compile our crate to lib CNAME-CMH-CVERS.so + * + * - Define STH(sym) as hash(CNAME, 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. + * + */ + +fn build_link_meta(sess: Session, c: ast::crate, output: &Path, + symbol_hasher: &hash::State) -> link_meta { + + type provided_metas = + {name: Option<~str>, + vers: Option<~str>, + cmh_items: ~[@ast::meta_item]}; + + fn provided_link_metas(sess: Session, c: ast::crate) -> + provided_metas { + let mut name: Option<~str> = None; + let mut vers: Option<~str> = None; + let mut cmh_items: ~[@ast::meta_item] = ~[]; + let linkage_metas = attr::find_linkage_metas(c.node.attrs); + attr::require_unique_names(sess.diagnostic(), linkage_metas); + for linkage_metas.each |meta| { + if attr::get_meta_item_name(*meta) == ~"name" { + match attr::get_meta_item_value_str(*meta) { + Some(v) => { name = Some(v); } + None => cmh_items.push(*meta) + } + } else if attr::get_meta_item_name(*meta) == ~"vers" { + match attr::get_meta_item_value_str(*meta) { + Some(v) => { vers = Some(v); } + None => cmh_items.push(*meta) + } + } else { cmh_items.push(*meta); } + } + return {name: name, vers: vers, cmh_items: cmh_items}; + } + + // This calculates CMH as defined above + fn crate_meta_extras_hash(symbol_hasher: &hash::State, + _crate: ast::crate, + metas: provided_metas, + dep_hashes: ~[~str]) -> ~str { + fn len_and_str(s: ~str) -> ~str { + return fmt!("%u_%s", str::len(s), s); + } + + fn len_and_str_lit(l: ast::lit) -> ~str { + return len_and_str(pprust::lit_to_str(@l)); + } + + let cmh_items = attr::sort_meta_items(metas.cmh_items); + + symbol_hasher.reset(); + for cmh_items.each |m| { + match m.node { + ast::meta_name_value(key, value) => { + symbol_hasher.write_str(len_and_str(key)); + symbol_hasher.write_str(len_and_str_lit(value)); + } + ast::meta_word(name) => { + symbol_hasher.write_str(len_and_str(name)); + } + ast::meta_list(_, _) => { + // FIXME (#607): Implement this + fail ~"unimplemented meta_item variant"; + } + } + } + + for dep_hashes.each |dh| { + symbol_hasher.write_str(len_and_str(*dh)); + } + + return truncated_hash_result(symbol_hasher); + } + + fn warn_missing(sess: Session, name: ~str, default: ~str) { + if !sess.building_library { return; } + sess.warn(fmt!("missing crate link meta `%s`, using `%s` as default", + name, default)); + } + + fn crate_meta_name(sess: Session, _crate: ast::crate, + output: &Path, metas: provided_metas) -> ~str { + return match metas.name { + Some(v) => v, + None => { + let name = match output.filestem() { + None => sess.fatal(fmt!("output file name `%s` doesn't\ + appear to have a stem", + output.to_str())), + Some(s) => s + }; + warn_missing(sess, ~"name", name); + name + } + }; + } + + fn crate_meta_vers(sess: Session, _crate: ast::crate, + metas: provided_metas) -> ~str { + return match metas.vers { + Some(v) => v, + None => { + let vers = ~"0.0"; + warn_missing(sess, ~"vers", vers); + vers + } + }; + } + + let provided_metas = provided_link_metas(sess, c); + let name = crate_meta_name(sess, c, output, provided_metas); + let vers = crate_meta_vers(sess, c, provided_metas); + let dep_hashes = cstore::get_dep_hashes(sess.cstore); + let extras_hash = + crate_meta_extras_hash(symbol_hasher, c, provided_metas, dep_hashes); + + return {name: name, vers: vers, extras_hash: extras_hash}; +} + +fn truncated_hash_result(symbol_hasher: &hash::State) -> ~str unsafe { + symbol_hasher.result_str() +} + + +// This calculates STH for a symbol, as defined above +fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &hash::State, t: ty::t, + link_meta: link_meta) -> ~str { + // 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.write_str(link_meta.name); + symbol_hasher.write_str(~"-"); + symbol_hasher.write_str(link_meta.extras_hash); + symbol_hasher.write_str(~"-"); + symbol_hasher.write_str(encoder::encoded_ty(tcx, t)); + let hash = truncated_hash_result(symbol_hasher); + // Prefix with _ so that it never blends into adjacent digits + + return ~"_" + hash; +} + +fn get_symbol_hash(ccx: @crate_ctxt, t: ty::t) -> ~str { + match ccx.type_hashcodes.find(t) { + Some(h) => return h, + None => { + let hash = symbol_hash(ccx.tcx, ccx.symbol_hasher, t, ccx.link_meta); + ccx.type_hashcodes.insert(t, hash); + return hash; + } + } +} + + +// Name sanitation. LLVM will happily accept identifiers with weird names, but +// gas doesn't! +fn sanitize(s: ~str) -> ~str { + let mut result = ~""; + for str::chars_each(s) |c| { + match c { + '@' => result += ~"_sbox_", + '~' => result += ~"_ubox_", + '*' => result += ~"_ptr_", + '&' => result += ~"_ref_", + ',' => result += ~"_", + + '{' | '(' => result += ~"_of_", + 'a' .. 'z' + | 'A' .. 'Z' + | '0' .. '9' + | '_' => str::push_char(&mut result, c), + _ => { + if c > 'z' && char::is_XID_continue(c) { + str::push_char(&mut result, c); + } + } + } + } + + // Underscore-qualify anything that didn't start as an ident. + if result.len() > 0u && + result[0] != '_' as u8 && + ! char::is_XID_start(result[0] as char) { + return ~"_" + result; + } + + return result; +} + +fn mangle(sess: Session, ss: path) -> ~str { + // Follow C++ namespace-mangling style + + let mut n = ~"_ZN"; // Begin name-sequence. + + for ss.each |s| { + match *s { path_name(s) | path_mod(s) => { + let sani = sanitize(sess.str_of(s)); + n += fmt!("%u%s", str::len(sani), sani); + } } + } + n += ~"E"; // End name-sequence. + n +} + +fn exported_name(sess: Session, path: path, hash: ~str, vers: ~str) -> ~str { + return mangle(sess, + vec::append_one( + vec::append_one(path, path_name(sess.ident_of(hash))), + path_name(sess.ident_of(vers)))); +} + +fn mangle_exported_name(ccx: @crate_ctxt, path: path, t: ty::t) -> ~str { + let hash = get_symbol_hash(ccx, t); + return exported_name(ccx.sess, path, hash, ccx.link_meta.vers); +} + +fn mangle_internal_name_by_type_only(ccx: @crate_ctxt, + t: ty::t, name: ~str) -> + ~str { + let s = util::ppaux::ty_to_short_str(ccx.tcx, t); + let hash = get_symbol_hash(ccx, t); + return mangle(ccx.sess, + ~[path_name(ccx.sess.ident_of(name)), + path_name(ccx.sess.ident_of(s)), + path_name(ccx.sess.ident_of(hash))]); +} + +fn mangle_internal_name_by_path_and_seq(ccx: @crate_ctxt, path: path, + flav: ~str) -> ~str { + return mangle(ccx.sess, + vec::append_one(path, path_name(ccx.names(flav)))); +} + +fn mangle_internal_name_by_path(ccx: @crate_ctxt, path: path) -> ~str { + return mangle(ccx.sess, path); +} + +fn mangle_internal_name_by_seq(ccx: @crate_ctxt, flav: ~str) -> ~str { + return fmt!("%s_%u", flav, ccx.names(flav).repr); +} + +// If the user wants an exe generated we need to invoke +// cc to link the object file with some libs +fn link_binary(sess: Session, + obj_filename: &Path, + out_filename: &Path, + lm: link_meta) { + // Converts a library file-stem into a cc -l argument + fn unlib(config: @session::config, stem: ~str) -> ~str { + if stem.starts_with("lib") && + config.os != session::os_win32 { + stem.slice(3, stem.len()) + } else { + stem + } + } + + let output = if sess.building_library { + let long_libname = + os::dll_filename(fmt!("%s-%s-%s", + lm.name, lm.extras_hash, lm.vers)); + debug!("link_meta.name: %s", lm.name); + debug!("long_libname: %s", long_libname); + debug!("out_filename: %s", out_filename.to_str()); + debug!("dirname(out_filename): %s", out_filename.dir_path().to_str()); + + out_filename.dir_path().push(long_libname) + } else { + *out_filename + }; + + log(debug, ~"output: " + output.to_str()); + + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let stage: ~str = ~"-L" + sess.filesearch.get_target_lib_path().to_str(); + + // 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. + let cc_prog: ~str = + if sess.targ_cfg.os == session::os_win32 { ~"gcc" } else { ~"cc" }; + // The invocations of cc share some flags across platforms + + let mut cc_args = + vec::append(~[stage], sess.targ_cfg.target_strs.cc_args); + cc_args.push(~"-o"); + cc_args.push(output.to_str()); + cc_args.push(obj_filename.to_str()); + + let mut lib_cmd; + let os = sess.targ_cfg.os; + if os == session::os_macos { + lib_cmd = ~"-dynamiclib"; + } else { + lib_cmd = ~"-shared"; + } + + // # Crate linking + + let cstore = sess.cstore; + for cstore::get_used_crate_files(cstore).each |cratepath| { + if cratepath.filetype() == Some(~".rlib") { + cc_args.push(cratepath.to_str()); + loop; + } + let dir = cratepath.dirname(); + if dir != ~"" { cc_args.push(~"-L" + dir); } + let libarg = unlib(sess.targ_cfg, cratepath.filestem().get()); + cc_args.push(~"-l" + libarg); + } + + let ula = cstore::get_used_link_args(cstore); + for ula.each |arg| { cc_args.push(*arg); } + + // # Extern library linking + + // User-supplied library search paths (-L on the cammand line) These are + // the same paths used to find Rust crates, so some of them may have been + // added already by the previous crate linking code. This only allows them + // to be found at compile time so it is still entirely up to outside + // forces to make sure that library can be found at runtime. + + let addl_paths = sess.opts.addl_lib_search_paths; + for addl_paths.each |path| { cc_args.push(~"-L" + path.to_str()); } + + // The names of the extern libraries + let used_libs = cstore::get_used_libraries(cstore); + for used_libs.each |l| { cc_args.push(~"-l" + *l); } + + if sess.building_library { + cc_args.push(lib_cmd); + + // On mac we need to tell the linker to let this library + // be rpathed + if sess.targ_cfg.os == session::os_macos { + cc_args.push(~"-Wl,-install_name,@rpath/" + + output.filename().get()); + } + } + + // Always want the runtime linked in + cc_args.push(~"-lrustrt"); + + // On linux librt and libdl are an indirect dependencies via rustrt, + // and binutils 2.22+ won't add them automatically + if sess.targ_cfg.os == session::os_linux { + cc_args.push_all(~[~"-lrt", ~"-ldl"]); + + // LLVM implements the `frem` instruction as a call to `fmod`, + // which lives in libm. Similar to above, on some linuxes we + // have to be explicit about linking to it. See #2510 + cc_args.push(~"-lm"); + } + + if sess.targ_cfg.os == session::os_freebsd { + cc_args.push_all(~[~"-pthread", ~"-lrt", + ~"-L/usr/local/lib", ~"-lexecinfo", + ~"-L/usr/local/lib/gcc46", + ~"-L/usr/local/lib/gcc44", ~"-lstdc++", + ~"-Wl,-z,origin", + ~"-Wl,-rpath,/usr/local/lib/gcc46", + ~"-Wl,-rpath,/usr/local/lib/gcc44"]); + } + + // OS X 10.6 introduced 'compact unwind info', which is produced by the + // linker from the dwarf unwind info. Unfortunately, it does not seem to + // understand how to unwind our __morestack frame, so we have to turn it + // off. This has impacted some other projects like GHC. + if sess.targ_cfg.os == session::os_macos { + cc_args.push(~"-Wl,-no_compact_unwind"); + } + + // Stack growth requires statically linking a __morestack function + cc_args.push(~"-lmorestack"); + + // FIXME (#2397): At some point we want to rpath our guesses as to where + // extern libraries might live, based on the addl_lib_search_paths + cc_args.push_all(rpath::get_rpath_flags(sess, &output)); + + debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" ")); + // We run 'cc' here + let prog = run::program_output(cc_prog, cc_args); + if 0 != prog.status { + sess.err(fmt!("linking with `%s` failed with code %d", + cc_prog, prog.status)); + sess.note(fmt!("%s arguments: %s", + cc_prog, str::connect(cc_args, ~" "))); + sess.note(prog.err + prog.out); + sess.abort_if_errors(); + } + + // Clean up on Darwin + if sess.targ_cfg.os == session::os_macos { + run::run_program(~"dsymutil", ~[output.to_str()]); + } + + // Remove the temporary object file if we aren't saving temps + if !sess.opts.save_temps { + if ! os::remove_file(obj_filename) { + sess.warn(fmt!("failed to delete object file `%s`", + obj_filename.to_str())); + } + } +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs new file mode 100644 index 00000000000..8038d7bb6dd --- /dev/null +++ b/src/librustc/back/rpath.rs @@ -0,0 +1,333 @@ +use std::map; +use std::map::HashMap; +use metadata::cstore; +use driver::session; +use metadata::filesearch; + +export get_rpath_flags; + +pure fn not_win32(os: session::os) -> bool { + match os { + session::os_win32 => false, + _ => true + } +} + +fn get_rpath_flags(sess: session::Session, out_filename: &Path) -> ~[~str] { + let os = sess.targ_cfg.os; + + // No rpath on windows + if os == session::os_win32 { + return ~[]; + } + + debug!("preparing the RPATH!"); + + let sysroot = sess.filesearch.sysroot(); + let output = out_filename; + let libs = cstore::get_used_crate_files(sess.cstore); + // We don't currently rpath extern libraries, but we know + // where rustrt is and we know every rust program needs it + let libs = vec::append_one(libs, get_sysroot_absolute_rt_lib(sess)); + + let target_triple = sess.opts.target_triple; + let rpaths = get_rpaths(os, &sysroot, output, libs, target_triple); + rpaths_to_flags(rpaths) +} + +fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path { + let r = filesearch::relative_target_lib_path(sess.opts.target_triple); + sess.filesearch.sysroot().push_rel(&r).push(os::dll_filename("rustrt")) +} + +fn rpaths_to_flags(rpaths: &[Path]) -> ~[~str] { + vec::map(rpaths, |rpath| fmt!("-Wl,-rpath,%s",rpath.to_str())) +} + +fn get_rpaths(os: session::os, + sysroot: &Path, + output: &Path, + libs: &[Path], + target_triple: &str) -> ~[Path] { + debug!("sysroot: %s", sysroot.to_str()); + debug!("output: %s", output.to_str()); + debug!("libs:"); + for libs.each |libpath| { + debug!(" %s", libpath.to_str()); + } + debug!("target_triple: %s", target_triple); + + // Use relative paths to the libraries. Binaries can be moved + // as long as they maintain the relative relationship to the + // crates they depend on. + let rel_rpaths = get_rpaths_relative_to_output(os, output, libs); + + // Make backup absolute paths to the libraries. Binaries can + // be moved as long as the crates they link against don't move. + let abs_rpaths = get_absolute_rpaths(libs); + + // And a final backup rpath to the global library location. + let fallback_rpaths = ~[get_install_prefix_rpath(target_triple)]; + + fn log_rpaths(desc: &str, rpaths: &[Path]) { + debug!("%s rpaths:", desc); + for rpaths.each |rpath| { + debug!(" %s", rpath.to_str()); + } + } + + log_rpaths(~"relative", rel_rpaths); + log_rpaths(~"absolute", abs_rpaths); + log_rpaths(~"fallback", fallback_rpaths); + + let mut rpaths = rel_rpaths; + rpaths.push_all(abs_rpaths); + rpaths.push_all(fallback_rpaths); + + // Remove duplicates + let rpaths = minimize_rpaths(rpaths); + return rpaths; +} + +fn get_rpaths_relative_to_output(os: session::os, + output: &Path, + libs: &[Path]) -> ~[Path] { + vec::map(libs, |a| { + get_rpath_relative_to_output(os, output, a) + }) +} + +fn get_rpath_relative_to_output(os: session::os, + output: &Path, + lib: &Path) -> Path { + assert not_win32(os); + + // Mac doesn't appear to support $ORIGIN + let prefix = match os { + session::os_linux | session::os_freebsd => "$ORIGIN", + session::os_macos => "@executable_path", + session::os_win32 => core::util::unreachable() + }; + + Path(prefix).push_rel(&get_relative_to(&os::make_absolute(output), + &os::make_absolute(lib))) +} + +// Find the relative path from one file to another +fn get_relative_to(abs1: &Path, abs2: &Path) -> Path { + assert abs1.is_absolute; + assert abs2.is_absolute; + let abs1 = abs1.normalize(); + let abs2 = abs2.normalize(); + debug!("finding relative path from %s to %s", + abs1.to_str(), abs2.to_str()); + let split1 = abs1.components; + let split2 = abs2.components; + let len1 = vec::len(split1); + let len2 = vec::len(split2); + assert len1 > 0; + assert len2 > 0; + + let max_common_path = uint::min(len1, len2) - 1; + let mut start_idx = 0; + while start_idx < max_common_path + && split1[start_idx] == split2[start_idx] { + start_idx += 1; + } + + let mut path = ~[]; + for uint::range(start_idx, len1 - 1) |_i| { path.push(~".."); }; + + path.push_all(vec::view(split2, start_idx, len2 - 1)); + + if vec::is_not_empty(path) { + return Path("").push_many(path); + } else { + return Path("."); + } +} + +fn get_absolute_rpaths(libs: &[Path]) -> ~[Path] { + vec::map(libs, |a| get_absolute_rpath(a) ) +} + +fn get_absolute_rpath(lib: &Path) -> Path { + os::make_absolute(lib).dir_path() +} + +fn get_install_prefix_rpath(target_triple: &str) -> Path { + let install_prefix = env!("CFG_PREFIX"); + + if install_prefix == ~"" { + fail ~"rustc compiled without CFG_PREFIX environment variable"; + } + + let tlib = filesearch::relative_target_lib_path(target_triple); + os::make_absolute(&Path(install_prefix).push_rel(&tlib)) +} + +fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] { + let set = map::HashMap(); + let mut minimized = ~[]; + for rpaths.each |rpath| { + let s = rpath.to_str(); + if !set.contains_key(s) { + minimized.push(*rpath); + set.insert(s, ()); + } + } + return minimized; +} + +#[cfg(unix)] +mod test { + #[legacy_exports]; + #[test] + fn test_rpaths_to_flags() { + let flags = rpaths_to_flags(~[Path("path1"), + Path("path2")]); + assert flags == ~[~"-Wl,-rpath,path1", ~"-Wl,-rpath,path2"]; + } + + #[test] + fn test_prefix_rpath() { + let res = get_install_prefix_rpath("triple"); + let d = Path(env!("CFG_PREFIX")) + .push_rel(&Path("lib/rustc/triple/lib")); + debug!("test_prefix_path: %s vs. %s", + res.to_str(), + d.to_str()); + assert str::ends_with(res.to_str(), d.to_str()); + } + + #[test] + fn test_prefix_rpath_abs() { + let res = get_install_prefix_rpath("triple"); + assert res.is_absolute; + } + + #[test] + fn test_minimize1() { + let res = minimize_rpaths([Path("rpath1"), + Path("rpath2"), + Path("rpath1")]); + assert res == ~[Path("rpath1"), Path("rpath2")]; + } + + #[test] + fn test_minimize2() { + let res = minimize_rpaths(~[Path("1a"), Path("2"), Path("2"), + Path("1a"), Path("4a"),Path("1a"), + Path("2"), Path("3"), Path("4a"), + Path("3")]); + assert res == ~[Path("1a"), Path("2"), Path("4a"), Path("3")]; + } + + #[test] + fn test_relative_to1() { + let p1 = Path("/usr/bin/rustc"); + let p2 = Path("/usr/lib/mylib"); + let res = get_relative_to(&p1, &p2); + assert res == Path("../lib"); + } + + #[test] + fn test_relative_to2() { + let p1 = Path("/usr/bin/rustc"); + let p2 = Path("/usr/bin/../lib/mylib"); + let res = get_relative_to(&p1, &p2); + assert res == Path("../lib"); + } + + #[test] + fn test_relative_to3() { + let p1 = Path("/usr/bin/whatever/rustc"); + let p2 = Path("/usr/lib/whatever/mylib"); + let res = get_relative_to(&p1, &p2); + assert res == Path("../../lib/whatever"); + } + + #[test] + fn test_relative_to4() { + let p1 = Path("/usr/bin/whatever/../rustc"); + let p2 = Path("/usr/lib/whatever/mylib"); + let res = get_relative_to(&p1, &p2); + assert res == Path("../lib/whatever"); + } + + #[test] + fn test_relative_to5() { + let p1 = Path("/usr/bin/whatever/../rustc"); + let p2 = Path("/usr/lib/whatever/../mylib"); + let res = get_relative_to(&p1, &p2); + assert res == Path("../lib"); + } + + #[test] + fn test_relative_to6() { + let p1 = Path("/1"); + let p2 = Path("/2/3"); + let res = get_relative_to(&p1, &p2); + assert res == Path("2"); + } + + #[test] + fn test_relative_to7() { + let p1 = Path("/1/2"); + let p2 = Path("/3"); + let res = get_relative_to(&p1, &p2); + assert res == Path(".."); + } + + #[test] + fn test_relative_to8() { + let p1 = Path("/home/brian/Dev/rust/build/").push_rel( + &Path("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so")); + let p2 = Path("/home/brian/Dev/rust/build/stage2/bin/..").push_rel( + &Path("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so")); + let res = get_relative_to(&p1, &p2); + debug!("test_relative_tu8: %s vs. %s", + res.to_str(), + Path(".").to_str()); + assert res == Path("."); + } + + #[test] + #[cfg(target_os = "linux")] + fn test_rpath_relative() { + let o = session::os_linux; + let res = get_rpath_relative_to_output(o, + &Path("bin/rustc"), &Path("lib/libstd.so")); + assert res.to_str() == ~"$ORIGIN/../lib"; + } + + #[test] + #[cfg(target_os = "freebsd")] + fn test_rpath_relative() { + let o = session::os_freebsd; + let res = get_rpath_relative_to_output(o, + &Path("bin/rustc"), &Path("lib/libstd.so")); + assert res.to_str() == ~"$ORIGIN/../lib"; + } + + #[test] + #[cfg(target_os = "macos")] + fn test_rpath_relative() { + // this is why refinements would be nice + let o = session::os_macos; + let res = get_rpath_relative_to_output(o, + &Path("bin/rustc"), + &Path("lib/libstd.so")); + assert res.to_str() == ~"@executable_path/../lib"; + } + + #[test] + fn test_get_absolute_rpath() { + let res = get_absolute_rpath(&Path("lib/libstd.so")); + debug!("test_get_absolute_rpath: %s vs. %s", + res.to_str(), + os::make_absolute(&Path("lib")).to_str()); + + assert res == os::make_absolute(&Path("lib")); + } +} diff --git a/src/librustc/back/target_strs.rs b/src/librustc/back/target_strs.rs new file mode 100644 index 00000000000..798c4868a7c --- /dev/null +++ b/src/librustc/back/target_strs.rs @@ -0,0 +1,7 @@ +type t = { + module_asm: ~str, + meta_sect_name: ~str, + data_layout: ~str, + target_triple: ~str, + cc_args: ~[~str] +}; diff --git a/src/librustc/back/upcall.rs b/src/librustc/back/upcall.rs new file mode 100644 index 00000000000..4b823680f14 --- /dev/null +++ b/src/librustc/back/upcall.rs @@ -0,0 +1,59 @@ + +use driver::session; +use middle::trans::base; +use middle::trans::common::{T_fn, T_i1, T_i8, T_i32, + T_int, T_nil, + T_opaque_vec, T_ptr, T_unique_ptr, + T_size_t, T_void, T_vec2}; +use lib::llvm::{type_names, ModuleRef, ValueRef, TypeRef}; + +type upcalls = + {trace: ValueRef, + call_shim_on_c_stack: ValueRef, + call_shim_on_rust_stack: ValueRef, + rust_personality: ValueRef, + reset_stack_limit: ValueRef}; + +fn declare_upcalls(targ_cfg: @session::config, + llmod: ModuleRef) -> @upcalls { + fn decl(llmod: ModuleRef, prefix: ~str, name: ~str, + tys: ~[TypeRef], rv: TypeRef) -> + ValueRef { + let arg_tys = tys.map(|t| *t); + let fn_ty = T_fn(arg_tys, rv); + return base::decl_cdecl_fn(llmod, prefix + name, fn_ty); + } + fn nothrow(f: ValueRef) -> ValueRef { + base::set_no_unwind(f); f + } + let d = |a,b,c| decl(llmod, ~"upcall_", a, b, c); + let dv = |a,b| decl(llmod, ~"upcall_", a, b, T_void()); + + let int_t = T_int(targ_cfg); + + return @{trace: dv(~"trace", ~[T_ptr(T_i8()), + T_ptr(T_i8()), + int_t]), + call_shim_on_c_stack: + d(~"call_shim_on_c_stack", + // arguments: void *args, void *fn_ptr + ~[T_ptr(T_i8()), T_ptr(T_i8())], + int_t), + call_shim_on_rust_stack: + d(~"call_shim_on_rust_stack", + ~[T_ptr(T_i8()), T_ptr(T_i8())], int_t), + rust_personality: + nothrow(d(~"rust_personality", ~[], T_i32())), + reset_stack_limit: + nothrow(dv(~"reset_stack_limit", ~[])) + }; +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/librustc/back/x86.rs b/src/librustc/back/x86.rs new file mode 100644 index 00000000000..8d4b50f9ac4 --- /dev/null +++ b/src/librustc/back/x86.rs @@ -0,0 +1,51 @@ +use driver::session; +use session::sess_os_to_meta_os; +use metadata::loader::meta_section_name; + +fn get_target_strs(target_os: session::os) -> target_strs::t { + return { + module_asm: ~"", + + meta_sect_name: meta_section_name(sess_os_to_meta_os(target_os)), + + data_layout: match target_os { + session::os_macos => { + ~"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16" + + ~"-i32:32:32-i64:32:64" + + ~"-f32:32:32-f64:32:64-v64:64:64" + + ~"-v128:128:128-a0:0:64-f80:128:128" + ~"-n8:16:32" + } + + session::os_win32 => { + ~"e-p:32:32-f64:64:64-i64:64:64-f80:32:32-n8:16:32" + } + + session::os_linux => { + ~"e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32" + } + + session::os_freebsd => { + ~"e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32" + } + }, + + target_triple: match target_os { + session::os_macos => ~"i686-apple-darwin", + session::os_win32 => ~"i686-pc-mingw32", + session::os_linux => ~"i686-unknown-linux-gnu", + session::os_freebsd => ~"i686-unknown-freebsd" + }, + + cc_args: ~[~"-m32"] + }; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/librustc/back/x86_64.rs b/src/librustc/back/x86_64.rs new file mode 100644 index 00000000000..ec2f5b18490 --- /dev/null +++ b/src/librustc/back/x86_64.rs @@ -0,0 +1,57 @@ +use driver::session; +use session::sess_os_to_meta_os; +use metadata::loader::meta_section_name; + +fn get_target_strs(target_os: session::os) -> target_strs::t { + return { + module_asm: ~"", + + meta_sect_name: meta_section_name(sess_os_to_meta_os(target_os)), + + data_layout: match target_os { + session::os_macos => { + ~"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + ~"f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + ~"s0:64:64-f80:128:128-n8:16:32:64" + } + + session::os_win32 => { + // FIXME: Test this. Copied from linux (#2398) + ~"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + ~"f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + ~"s0:64:64-f80:128:128-n8:16:32:64-S128" + } + + session::os_linux => { + ~"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + ~"f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + ~"s0:64:64-f80:128:128-n8:16:32:64-S128" + } + + session::os_freebsd => { + ~"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + ~"f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + ~"s0:64:64-f80:128:128-n8:16:32:64-S128" + } + }, + + target_triple: match target_os { + session::os_macos => ~"x86_64-apple-darwin", + session::os_win32 => ~"x86_64-pc-mingw32", + session::os_linux => ~"x86_64-unknown-linux-gnu", + session::os_freebsd => ~"x86_64-unknown-freebsd", + }, + + cc_args: ~[~"-m64"] + }; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// |
