diff options
Diffstat (limited to 'src/librustc_trans/back/write.rs')
| -rw-r--r-- | src/librustc_trans/back/write.rs | 821 |
1 files changed, 595 insertions, 226 deletions
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index ef6bf2504f3..cb883e0349f 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -8,43 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use back::lto; +use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; +use back::lto::{self, ModuleBuffer, ThinBuffer}; use back::link::{self, get_linker, remove}; use back::linker::LinkerInfo; use back::symbol_export::ExportedSymbols; +use base; +use consts; use rustc_incremental::{save_trans_partition, in_incr_comp_dir}; -use rustc::dep_graph::DepGraph; +use rustc::dep_graph::{DepGraph, WorkProductFileKind}; use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses, AllPasses, Sanitizer}; use rustc::session::Session; use rustc::util::nodemap::FxHashMap; -use time_graph::{self, TimeGraph}; +use rustc_back::LinkerFlavor; +use time_graph::{self, TimeGraph, Timeline}; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; -use llvm::SMDiagnosticRef; +use llvm::{SMDiagnosticRef, ContextRef}; use {CrateTranslation, ModuleSource, ModuleTranslation, CompiledModule, ModuleKind}; use CrateInfo; use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::ty::TyCtxt; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr, print_time_passes_entry}; use rustc::util::fs::{link_or_copy, rename_or_copy_remove}; -use errors::{self, Handler, Level, DiagnosticBuilder, FatalError}; +use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; use errors::emitter::{Emitter}; use syntax::attr; use syntax::ext::hygiene::Mark; use syntax_pos::MultiSpan; use syntax_pos::symbol::Symbol; +use type_::Type; use context::{is_pie_binary, get_reloc_model}; use jobserver::{Client, Acquired}; use rustc_demangle; use std::any::Any; -use std::ffi::CString; -use std::fmt; -use std::fs; +use std::ffi::{CString, CStr}; +use std::fs::{self, File}; use std::io; -use std::io::Write; +use std::io::{Read, Write}; +use std::mem; use std::path::{Path, PathBuf}; use std::str; use std::sync::Arc; @@ -72,6 +77,13 @@ pub const CODE_GEN_MODEL_ARGS : [(&'static str, llvm::CodeModel); 5] = [ ("large", llvm::CodeModel::Large), ]; +pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ + ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), + ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), + ("initial-exec", llvm::ThreadLocalMode::InitialExec), + ("local-exec", llvm::ThreadLocalMode::LocalExec), +]; + pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError { match llvm::last_error() { Some(err) => handler.fatal(&format!("{}: {}", msg, err)), @@ -143,6 +155,14 @@ fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize { } pub fn create_target_machine(sess: &Session) -> TargetMachineRef { + target_machine_factory(sess)().unwrap_or_else(|err| { + panic!(llvm_err(sess.diagnostic(), err)) + }) +} + +pub fn target_machine_factory(sess: &Session) + -> Arc<Fn() -> Result<TargetMachineRef, String> + Send + Sync> +{ let reloc_model = get_reloc_model(sess); let opt_level = get_llvm_opt_level(sess.opts.optimize); @@ -161,53 +181,58 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef { Some(x) => x.1, _ => { sess.err(&format!("{:?} is not a valid code model", - sess.opts - .cg - .code_model)); + code_model_arg)); sess.abort_if_errors(); bug!(); } }; + let singlethread = sess.target.target.options.singlethread; + let triple = &sess.target.target.llvm_target; - let tm = unsafe { - let triple = CString::new(triple.as_bytes()).unwrap(); - let cpu = match sess.opts.cg.target_cpu { - Some(ref s) => &**s, - None => &*sess.target.target.options.cpu - }; - let cpu = CString::new(cpu.as_bytes()).unwrap(); - let features = CString::new(target_feature(sess).as_bytes()).unwrap(); - llvm::LLVMRustCreateTargetMachine( - triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), - code_model, - reloc_model, - opt_level, - use_softfp, - is_pie_binary(sess), - ffunction_sections, - fdata_sections, - ) + let triple = CString::new(triple.as_bytes()).unwrap(); + let cpu = match sess.opts.cg.target_cpu { + Some(ref s) => &**s, + None => &*sess.target.target.options.cpu }; + let cpu = CString::new(cpu.as_bytes()).unwrap(); + let features = CString::new(target_feature(sess).as_bytes()).unwrap(); + let is_pie_binary = is_pie_binary(sess); + let trap_unreachable = sess.target.target.options.trap_unreachable; + + Arc::new(move || { + let tm = unsafe { + llvm::LLVMRustCreateTargetMachine( + triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), + code_model, + reloc_model, + opt_level, + use_softfp, + is_pie_binary, + ffunction_sections, + fdata_sections, + trap_unreachable, + singlethread, + ) + }; - if tm.is_null() { - let msg = format!("Could not create LLVM TargetMachine for triple: {}", - triple); - panic!(llvm_err(sess.diagnostic(), msg)); - } else { - return tm; - }; + if tm.is_null() { + Err(format!("Could not create LLVM TargetMachine for triple: {}", + triple.to_str().unwrap())) + } else { + Ok(tm) + } + }) } - /// Module-specific configuration for `optimize_and_codegen`. pub struct ModuleConfig { /// Names of additional optimization passes to run. passes: Vec<String>, /// Some(level) to optimize at a certain level, or None to run /// absolutely no optimizations (used for the metadata module). - opt_level: Option<llvm::CodeGenOptLevel>, + pub opt_level: Option<llvm::CodeGenOptLevel>, /// Some(level) to optimize binary size, or None to not affect program size. opt_size: Option<llvm::CodeGenOptSize>, @@ -215,6 +240,7 @@ pub struct ModuleConfig { // Flags indicating which outputs to produce. emit_no_opt_bc: bool, emit_bc: bool, + emit_bc_compressed: bool, emit_lto_bc: bool, emit_ir: bool, emit_asm: bool, @@ -244,6 +270,7 @@ impl ModuleConfig { emit_no_opt_bc: false, emit_bc: false, + emit_bc_compressed: false, emit_lto_bc: false, emit_ir: false, emit_asm: false, @@ -264,7 +291,7 @@ impl ModuleConfig { fn set_flags(&mut self, sess: &Session, no_builtins: bool) { self.no_verify = sess.no_verify(); self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; - self.no_builtins = no_builtins; + self.no_builtins = no_builtins || sess.target.target.options.no_builtins; self.time_passes = sess.time_passes(); self.inline_threshold = sess.opts.cg.inline_threshold; self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; @@ -293,7 +320,9 @@ pub struct CodegenContext { // Resouces needed when running LTO pub time_passes: bool, pub lto: bool, + pub thinlto: bool, pub no_landing_pads: bool, + pub save_temps: bool, pub exported_symbols: Arc<ExportedSymbols>, pub opts: Arc<config::Options>, pub crate_types: Vec<config::CrateType>, @@ -302,7 +331,15 @@ pub struct CodegenContext { regular_module_config: Arc<ModuleConfig>, metadata_module_config: Arc<ModuleConfig>, allocator_module_config: Arc<ModuleConfig>, - + pub tm_factory: Arc<Fn() -> Result<TargetMachineRef, String> + Send + Sync>, + pub msvc_imps_needed: bool, + pub target_pointer_width: String, + binaryen_linker: bool, + debuginfo: config::DebugInfoLevel, + wasm_import_memory: bool, + + // Number of cgus excluding the allocator/metadata modules + pub total_cgus: usize, // Handler to use for diagnostics produced during codegen. pub diag_emitter: SharedEmitter, // LLVM passes added by plugins. @@ -322,22 +359,62 @@ pub struct CodegenContext { } impl CodegenContext { - fn create_diag_handler(&self) -> Handler { + pub fn create_diag_handler(&self) -> Handler { Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) } - fn config(&self, kind: ModuleKind) -> &ModuleConfig { + pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { match kind { ModuleKind::Regular => &self.regular_module_config, ModuleKind::Metadata => &self.metadata_module_config, ModuleKind::Allocator => &self.allocator_module_config, } } + + pub fn save_temp_bitcode(&self, trans: &ModuleTranslation, name: &str) { + if !self.save_temps { + return + } + unsafe { + let ext = format!("{}.bc", name); + let cgu = Some(&trans.name[..]); + let path = self.output_filenames.temp_path_ext(&ext, cgu); + let cstr = path2cstr(&path); + let llmod = trans.llvm().unwrap().llmod; + llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); + } + } +} + +struct DiagnosticHandlers<'a> { + inner: Box<(&'a CodegenContext, &'a Handler)>, + llcx: ContextRef, } -struct HandlerFreeVars<'a> { - cgcx: &'a CodegenContext, - diag_handler: &'a Handler, +impl<'a> DiagnosticHandlers<'a> { + fn new(cgcx: &'a CodegenContext, + handler: &'a Handler, + llcx: ContextRef) -> DiagnosticHandlers<'a> { + let data = Box::new((cgcx, handler)); + unsafe { + let arg = &*data as &(_, _) as *const _ as *mut _; + llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, arg); + llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, arg); + } + DiagnosticHandlers { + inner: data, + llcx: llcx, + } + } +} + +impl<'a> Drop for DiagnosticHandlers<'a> { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, 0 as *mut _); + llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, 0 as *mut _); + } + } } unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, @@ -349,7 +426,10 @@ unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, user: *const c_void, cookie: c_uint) { - let HandlerFreeVars { cgcx, .. } = *(user as *const HandlerFreeVars); + if user.is_null() { + return + } + let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) .expect("non-UTF8 SMDiagnostic"); @@ -358,7 +438,10 @@ unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, } unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { - let HandlerFreeVars { cgcx, diag_handler, .. } = *(user as *const HandlerFreeVars); + if user.is_null() { + return + } + let (cgcx, diag_handler) = *(user as *const (&CodegenContext, &Handler)); match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { @@ -389,28 +472,21 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo } // Unsafe due to LLVM calls. -unsafe fn optimize_and_codegen(cgcx: &CodegenContext, - diag_handler: &Handler, - mtrans: ModuleTranslation, - tm: TargetMachineRef, - config: &ModuleConfig) - -> Result<CompiledModule, FatalError> +unsafe fn optimize(cgcx: &CodegenContext, + diag_handler: &Handler, + mtrans: &ModuleTranslation, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result<(), FatalError> { - let (llmod, llcx) = match mtrans.source { - ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx), + let (llmod, llcx, tm) = match mtrans.source { + ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), ModuleSource::Preexisting(_) => { bug!("optimize_and_codegen: called with ModuleSource::Preexisting") } }; - let fv = HandlerFreeVars { - cgcx, - diag_handler, - }; - let fv = &fv as *const HandlerFreeVars as *mut c_void; - - llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); - llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); + let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); let module_name = mtrans.name.clone(); let module_name = Some(&module_name[..]); @@ -453,7 +529,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if !config.no_prepopulate_passes { llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - with_llvm_pmb(llmod, &config, &mut |b| { + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); + with_llvm_pmb(llmod, &config, opt_level, &mut |b| { llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); }) @@ -479,30 +556,60 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, // Finally, run the actual optimization passes time(config.time_passes, &format!("llvm function passes [{}]", module_name.unwrap()), || llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); + timeline.record("fpm"); time(config.time_passes, &format!("llvm module passes [{}]", module_name.unwrap()), || llvm::LLVMRunPassManager(mpm, llmod)); // Deallocate managers that we're now done with llvm::LLVMDisposePassManager(fpm); llvm::LLVMDisposePassManager(mpm); + } + Ok(()) +} - if cgcx.lto { - time(cgcx.time_passes, "all lto passes", || { - let temp_no_opt_bc_filename = - cgcx.output_filenames.temp_path_ext("no-opt.lto.bc", module_name); - lto::run(cgcx, - diag_handler, - llmod, - tm, - &config, - &temp_no_opt_bc_filename) - })?; - if config.emit_lto_bc { - let out = cgcx.output_filenames.temp_path_ext("lto.bc", module_name); - let out = path2cstr(&out); - llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); - } +fn generate_lto_work(cgcx: &CodegenContext, + modules: Vec<ModuleTranslation>) + -> Vec<(WorkItem, u64)> +{ + let mut timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(TRANS_WORKER_TIMELINE, + TRANS_WORK_PACKAGE_KIND, + "generate lto") + }).unwrap_or(Timeline::noop()); + let mode = if cgcx.lto { + lto::LTOMode::WholeCrateGraph + } else { + lto::LTOMode::JustThisCrate + }; + let lto_modules = lto::run(cgcx, modules, mode, &mut timeline) + .unwrap_or_else(|e| panic!(e)); + + lto_modules.into_iter().map(|module| { + let cost = module.cost(); + (WorkItem::LTO(module), cost) + }).collect() +} + +unsafe fn codegen(cgcx: &CodegenContext, + diag_handler: &Handler, + mtrans: ModuleTranslation, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result<CompiledModule, FatalError> +{ + timeline.record("codegen"); + let (llmod, llcx, tm) = match mtrans.source { + ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), + ModuleSource::Preexisting(_) => { + bug!("codegen: called with ModuleSource::Preexisting") } + }; + let module_name = mtrans.name.clone(); + let module_name = Some(&module_name[..]); + let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); + + if cgcx.msvc_imps_needed { + create_msvc_imps(cgcx, llcx, llmod); } // A codegen-specific pass manager is used to generate object @@ -525,21 +632,53 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, f(cpm) } + // If we're going to generate wasm code from the assembly that llvm + // generates then we'll be transitively affecting a ton of options below. + // This only happens on the wasm target now. + let asm2wasm = cgcx.binaryen_linker && + !cgcx.crate_types.contains(&config::CrateTypeRlib) && + mtrans.kind == ModuleKind::Regular; + // Change what we write and cleanup based on whether obj files are // just llvm bitcode. In that case write bitcode, and possibly // delete the bitcode if it wasn't requested. Don't generate the // machine code, instead copy the .o file from the .bc - let write_bc = config.emit_bc || config.obj_is_bitcode; - let rm_bc = !config.emit_bc && config.obj_is_bitcode; - let write_obj = config.emit_obj && !config.obj_is_bitcode; - let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; + let write_bc = config.emit_bc || (config.obj_is_bitcode && !asm2wasm); + let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm; + let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm; + let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode && !asm2wasm; let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); - if write_bc { - let bc_out_c = path2cstr(&bc_out); - llvm::LLVMWriteBitcodeToFile(llmod, bc_out_c.as_ptr()); + + if write_bc || config.emit_bc_compressed { + let thin; + let old; + let data = if llvm::LLVMRustThinLTOAvailable() { + thin = ThinBuffer::new(llmod); + thin.data() + } else { + old = ModuleBuffer::new(llmod); + old.data() + }; + timeline.record("make-bc"); + + if write_bc { + if let Err(e) = File::create(&bc_out).and_then(|mut f| f.write_all(data)) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("write-bc"); + } + + if config.emit_bc_compressed { + let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); + let data = bytecode::encode(&mtrans.llmod_id, data); + if let Err(e) = File::create(&dst).and_then(|mut f| f.write_all(&data)) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("compress-bc"); + } } time(config.time_passes, &format!("codegen passes [{}]", module_name.unwrap()), @@ -582,10 +721,11 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, with_codegen(tm, llmod, config.no_builtins, |cpm| { llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback); llvm::LLVMDisposePassManager(cpm); - }) + }); + timeline.record("ir"); } - if config.emit_asm { + if config.emit_asm || (asm2wasm && config.emit_obj) { let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); // We can't use the same module for asm and binary output, because that triggers @@ -603,13 +743,23 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if config.emit_obj { llvm::LLVMDisposeModule(llmod); } + timeline.record("asm"); } - if write_obj { + if asm2wasm && config.emit_obj { + let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + binaryen_assemble(cgcx, diag_handler, &assembly, &obj_out); + timeline.record("binaryen"); + + if !config.emit_asm { + drop(fs::remove_file(&assembly)); + } + } else if write_obj { with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file(diag_handler, tm, cpm, llmod, &obj_out, llvm::FileType::ObjectFile) })?; + timeline.record("obj"); } Ok(()) @@ -629,7 +779,49 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } } - Ok(mtrans.into_compiled_module(config.emit_obj, config.emit_bc)) + drop(handlers); + Ok(mtrans.into_compiled_module(config.emit_obj, + config.emit_bc, + config.emit_bc_compressed, + &cgcx.output_filenames)) +} + +/// Translates the LLVM-generated `assembly` on the filesystem into a wasm +/// module using binaryen, placing the output at `object`. +/// +/// In this case the "object" is actually a full and complete wasm module. We +/// won't actually be doing anything else to the output for now. This is all +/// pretty janky and will get removed as soon as a linker for wasm exists. +fn binaryen_assemble(cgcx: &CodegenContext, + handler: &Handler, + assembly: &Path, + object: &Path) { + use rustc_binaryen::{Module, ModuleOptions}; + + let input = File::open(&assembly).and_then(|mut f| { + let mut contents = Vec::new(); + f.read_to_end(&mut contents)?; + Ok(CString::new(contents)?) + }); + let mut options = ModuleOptions::new(); + if cgcx.debuginfo != config::NoDebugInfo { + options.debuginfo(true); + } + if cgcx.crate_types.contains(&config::CrateTypeExecutable) { + options.start("main"); + } + options.stack(1024 * 1024); + options.import_memory(cgcx.wasm_import_memory); + let assembled = input.and_then(|input| { + Module::new(&input, &options) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + }); + let err = assembled.and_then(|binary| { + File::create(&object).and_then(|mut f| f.write_all(binary.data())) + }); + if let Err(e) = err { + handler.err(&format!("failed to run binaryen assembler: {}", e)); + } } pub struct CompiledModules { @@ -647,7 +839,8 @@ pub fn start_async_translation(tcx: TyCtxt, time_graph: Option<TimeGraph>, link: LinkMeta, metadata: EncodedMetadata, - coordinator_receive: Receiver<Box<Any + Send>>) + coordinator_receive: Receiver<Box<Any + Send>>, + total_cgus: usize) -> OngoingCrateTranslation { let sess = tcx.sess; let crate_output = tcx.output_filenames(LOCAL_CRATE); @@ -714,11 +907,12 @@ pub fn start_async_translation(tcx: TyCtxt, allocator_config.emit_bc = true; } - // Emit bitcode files for the crate if we're emitting an rlib. - // Whenever an rlib is created, the bitcode is inserted into the - // archive in order to allow LTO against it. + // Emit compressed bitcode files for the crate if we're emitting an rlib. + // Whenever an rlib is created, the bitcode is inserted into the archive in + // order to allow LTO against it. if need_crate_bitcode_for_rlib(sess) { - modules_config.emit_bc = true; + modules_config.emit_bc_compressed = true; + allocator_config.emit_bc_compressed = true; } for output_type in output_types_override.keys() { @@ -771,6 +965,7 @@ pub fn start_async_translation(tcx: TyCtxt, shared_emitter, trans_worker_send, coordinator_receive, + total_cgus, client, time_graph.clone(), Arc::new(modules_config), @@ -797,8 +992,7 @@ pub fn start_async_translation(tcx: TyCtxt, fn copy_module_artifacts_into_incr_comp_cache(sess: &Session, dep_graph: &DepGraph, - compiled_modules: &CompiledModules, - crate_output: &OutputFilenames) { + compiled_modules: &CompiledModules) { if sess.opts.incremental.is_none() { return; } @@ -806,21 +1000,17 @@ fn copy_module_artifacts_into_incr_comp_cache(sess: &Session, for module in compiled_modules.modules.iter() { let mut files = vec![]; - if module.emit_obj { - let path = crate_output.temp_path(OutputType::Object, Some(&module.name)); - files.push((OutputType::Object, path)); + if let Some(ref path) = module.object { + files.push((WorkProductFileKind::Object, path.clone())); } - - if module.emit_bc { - let path = crate_output.temp_path(OutputType::Bitcode, Some(&module.name)); - files.push((OutputType::Bitcode, path)); + if let Some(ref path) = module.bytecode { + files.push((WorkProductFileKind::Bytecode, path.clone())); + } + if let Some(ref path) = module.bytecode_compressed { + files.push((WorkProductFileKind::BytecodeCompressed, path.clone())); } - save_trans_partition(sess, - dep_graph, - &module.name, - module.symbol_name_hash, - &files); + save_trans_partition(sess, dep_graph, &module.name, &files); } } @@ -924,8 +1114,6 @@ fn produce_final_output_artifacts(sess: &Session, // well. // Specific rules for keeping .#module-name#.bc: - // - If we're building an rlib (`needs_crate_bitcode`), then keep - // it. // - If the user requested bitcode (`user_wants_bitcode`), and // codegen_units > 1, then keep it. // - If the user requested bitcode but codegen_units == 1, then we @@ -935,41 +1123,37 @@ fn produce_final_output_artifacts(sess: &Session, // If you change how this works, also update back::link::link_rlib, // where .#module-name#.bc files are (maybe) deleted after making an // rlib. - let needs_crate_bitcode = need_crate_bitcode_for_rlib(sess); let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); - let keep_numbered_bitcode = needs_crate_bitcode || - (user_wants_bitcode && sess.opts.cg.codegen_units > 1); + let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1; let keep_numbered_objects = needs_crate_object || - (user_wants_objects && sess.opts.cg.codegen_units > 1); + (user_wants_objects && sess.codegen_units() > 1); for module in compiled_modules.modules.iter() { - let module_name = Some(&module.name[..]); - - if module.emit_obj && !keep_numbered_objects { - let path = crate_output.temp_path(OutputType::Object, module_name); - remove(sess, &path); + if let Some(ref path) = module.object { + if !keep_numbered_objects { + remove(sess, path); + } } - if module.emit_bc && !keep_numbered_bitcode { - let path = crate_output.temp_path(OutputType::Bitcode, module_name); - remove(sess, &path); + if let Some(ref path) = module.bytecode { + if !keep_numbered_bitcode { + remove(sess, path); + } } } - if compiled_modules.metadata_module.emit_bc && !user_wants_bitcode { - let path = crate_output.temp_path(OutputType::Bitcode, - Some(&compiled_modules.metadata_module.name)); - remove(sess, &path); - } - - if let Some(ref allocator_module) = compiled_modules.allocator_module { - if allocator_module.emit_bc && !user_wants_bitcode { - let path = crate_output.temp_path(OutputType::Bitcode, - Some(&allocator_module.name)); + if !user_wants_bitcode { + if let Some(ref path) = compiled_modules.metadata_module.bytecode { remove(sess, &path); } + + if let Some(ref allocator_module) = compiled_modules.allocator_module { + if let Some(ref path) = allocator_module.bytecode { + remove(sess, path); + } + } } } @@ -981,46 +1165,57 @@ fn produce_final_output_artifacts(sess: &Session, } pub fn dump_incremental_data(trans: &CrateTranslation) { - let mut reuse = 0; - for mtrans in trans.modules.iter() { - if mtrans.pre_existing { - reuse += 1; - } - } - eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len()); + println!("[incremental] Re-using {} out of {} modules", + trans.modules.iter().filter(|m| m.pre_existing).count(), + trans.modules.len()); } -struct WorkItem { - mtrans: ModuleTranslation, - tm: TargetMachine, +enum WorkItem { + Optimize(ModuleTranslation), + LTO(lto::LtoModuleTranslation), } -impl fmt::Debug for WorkItem { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "WorkItem({})", self.mtrans.name) +impl WorkItem { + fn kind(&self) -> ModuleKind { + match *self { + WorkItem::Optimize(ref m) => m.kind, + WorkItem::LTO(_) => ModuleKind::Regular, + } } -} - -struct TargetMachine(TargetMachineRef); -unsafe impl Send for TargetMachine {} - -impl Drop for TargetMachine { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustDisposeTargetMachine(self.0); + fn name(&self) -> String { + match *self { + WorkItem::Optimize(ref m) => format!("optimize: {}", m.name), + WorkItem::LTO(ref m) => format!("lto: {}", m.name()), } } } -fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) - -> Result<CompiledModule, FatalError> +enum WorkItemResult { + Compiled(CompiledModule), + NeedsLTO(ModuleTranslation), +} + +fn execute_work_item(cgcx: &CodegenContext, + work_item: WorkItem, + timeline: &mut Timeline) + -> Result<WorkItemResult, FatalError> { let diag_handler = cgcx.create_diag_handler(); - let module_name = work_item.mtrans.name.clone(); - let config = cgcx.config(work_item.mtrans.kind); + let config = cgcx.config(work_item.kind()); + let mtrans = match work_item { + WorkItem::Optimize(mtrans) => mtrans, + WorkItem::LTO(mut lto) => { + unsafe { + let module = lto.optimize(cgcx, timeline)?; + let module = codegen(cgcx, &diag_handler, module, config, timeline)?; + return Ok(WorkItemResult::Compiled(module)) + } + } + }; + let module_name = mtrans.name.clone(); - let pre_existing = match work_item.mtrans.source { + let pre_existing = match mtrans.source { ModuleSource::Translated(_) => None, ModuleSource::Preexisting(ref wp) => Some(wp.clone()), }; @@ -1029,13 +1224,33 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) let incr_comp_session_dir = cgcx.incr_comp_session_dir .as_ref() .unwrap(); - let name = &work_item.mtrans.name; + let name = &mtrans.name; + let mut object = None; + let mut bytecode = None; + let mut bytecode_compressed = None; for (kind, saved_file) in wp.saved_files { - let obj_out = cgcx.output_filenames.temp_path(kind, Some(name)); + let obj_out = match kind { + WorkProductFileKind::Object => { + let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(name)); + object = Some(path.clone()); + path + } + WorkProductFileKind::Bytecode => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)); + bytecode = Some(path.clone()); + path + } + WorkProductFileKind::BytecodeCompressed => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)) + .with_extension(RLIB_BYTECODE_EXTENSION); + bytecode_compressed = Some(path.clone()); + path + } + }; let source_file = in_incr_comp_dir(&incr_comp_session_dir, &saved_file); debug!("copying pre-existing module `{}` from {:?} to {}", - work_item.mtrans.name, + mtrans.name, source_file, obj_out.display()); match link_or_copy(&source_file, &obj_out) { @@ -1048,31 +1263,58 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) } } } + assert_eq!(object.is_some(), config.emit_obj); + assert_eq!(bytecode.is_some(), config.emit_bc); + assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed); - Ok(CompiledModule { + Ok(WorkItemResult::Compiled(CompiledModule { + llmod_id: mtrans.llmod_id.clone(), name: module_name, kind: ModuleKind::Regular, pre_existing: true, - symbol_name_hash: work_item.mtrans.symbol_name_hash, - emit_bc: config.emit_bc, - emit_obj: config.emit_obj, - }) + object, + bytecode, + bytecode_compressed, + })) } else { debug!("llvm-optimizing {:?}", module_name); unsafe { - optimize_and_codegen(cgcx, - &diag_handler, - work_item.mtrans, - work_item.tm.0, - config) + optimize(cgcx, &diag_handler, &mtrans, config, timeline)?; + + let lto = cgcx.lto; + + let auto_thin_lto = + cgcx.thinlto && + cgcx.total_cgus > 1 && + mtrans.kind != ModuleKind::Allocator; + + // If we're a metadata module we never participate in LTO. + // + // If LTO was explicitly requested on the command line, we always + // LTO everything else. + // + // If LTO *wasn't* explicitly requested and we're not a metdata + // module, then we may automatically do ThinLTO if we've got + // multiple codegen units. Note, however, that the allocator module + // doesn't participate here automatically because of linker + // shenanigans later on. + if mtrans.kind == ModuleKind::Metadata || (!lto && !auto_thin_lto) { + let module = codegen(cgcx, &diag_handler, mtrans, config, timeline)?; + Ok(WorkItemResult::Compiled(module)) + } else { + Ok(WorkItemResult::NeedsLTO(mtrans)) + } } } } -#[derive(Debug)] enum Message { Token(io::Result<Acquired>), + NeedsLTO { + result: ModuleTranslation, + worker_id: usize, + }, Done { result: Result<CompiledModule, ()>, worker_id: usize, @@ -1087,7 +1329,7 @@ enum Message { struct Diagnostic { msg: String, - code: Option<String>, + code: Option<DiagnosticId>, lvl: Level, } @@ -1103,12 +1345,13 @@ fn start_executing_work(tcx: TyCtxt, shared_emitter: SharedEmitter, trans_worker_send: Sender<Message>, coordinator_receive: Receiver<Box<Any + Send>>, + total_cgus: usize, jobserver: Client, time_graph: Option<TimeGraph>, modules_config: Arc<ModuleConfig>, metadata_config: Arc<ModuleConfig>, allocator_config: Arc<ModuleConfig>) - -> thread::JoinHandle<CompiledModules> { + -> thread::JoinHandle<Result<CompiledModules, ()>> { let coordinator_send = tcx.tx_to_llvm_workers.clone(); let mut exported_symbols = FxHashMap(); exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE)); @@ -1135,17 +1378,36 @@ fn start_executing_work(tcx: TyCtxt, let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { - if link::ignored_for_lto(crate_info, cnum) { + if link::ignored_for_lto(sess, crate_info, cnum) { return } each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); + let crate_types = sess.crate_types.borrow(); + let only_rlib = crate_types.len() == 1 && + crate_types[0] == config::CrateTypeRlib; + + let wasm_import_memory = + attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory"); + let cgcx = CodegenContext { crate_types: sess.crate_types.borrow().clone(), each_linked_rlib_for_lto, - lto: sess.lto(), + // If we're only building an rlibc then allow the LTO flag to be passed + // but don't actually do anything, the full LTO will happen later + lto: sess.lto() && !only_rlib, + + // Enable ThinLTO if requested, but only if the target we're compiling + // for doesn't require full LTO. Some targets require one LLVM module + // (they effectively don't have a linker) so it's up to us to use LTO to + // link everything together. + thinlto: sess.thinlto() && + !sess.target.target.options.requires_lto && + unsafe { llvm::LLVMRustThinLTOAvailable() }, + no_landing_pads: sess.no_landing_pads(), + save_temps: sess.opts.cg.save_temps, opts: Arc::new(sess.opts.clone()), time_passes: sess.time_passes(), exported_symbols, @@ -1160,6 +1422,13 @@ fn start_executing_work(tcx: TyCtxt, regular_module_config: modules_config, metadata_module_config: metadata_config, allocator_module_config: allocator_config, + tm_factory: target_machine_factory(tcx.sess), + total_cgus, + msvc_imps_needed: msvc_imps_needed(tcx), + target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), + binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen, + debuginfo: tcx.sess.opts.debuginfo, + wasm_import_memory: wasm_import_memory, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1282,6 +1551,21 @@ fn start_executing_work(tcx: TyCtxt, // and whenever we're done with that work we release the semaphore. In this // manner we can ensure that the maximum number of parallel workers is // capped at any one point in time. + // + // LTO and the coordinator thread + // ------------------------------ + // + // The final job the coordinator thread is responsible for is managing LTO + // and how that works. When LTO is requested what we'll to is collect all + // optimized LLVM modules into a local vector on the coordinator. Once all + // modules have been translated and optimized we hand this to the `lto` + // module for further optimization. The `lto` module will return back a list + // of more modules to work on, which the coordinator will continue to spawn + // work for. + // + // Each LLVM module is automatically sent back to the coordinator for LTO if + // necessary. There's already optimizations in place to avoid sending work + // back to the coordinator if LTO isn't requested. return thread::spawn(move || { // We pretend to be within the top-level LLVM time-passes task here: set_time_depth(1); @@ -1304,6 +1588,8 @@ fn start_executing_work(tcx: TyCtxt, let mut compiled_modules = vec![]; let mut compiled_metadata_module = None; let mut compiled_allocator_module = None; + let mut needs_lto = Vec::new(); + let mut started_lto = false; // This flag tracks whether all items have gone through translations let mut translation_done = false; @@ -1325,6 +1611,7 @@ fn start_executing_work(tcx: TyCtxt, while !translation_done || work_items.len() > 0 || running > 0 || + needs_lto.len() > 0 || main_thread_worker_state != MainThreadWorkerState::Idle { // While there are still CGUs to be translated, the coordinator has @@ -1348,13 +1635,34 @@ fn start_executing_work(tcx: TyCtxt, worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; - maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + maybe_start_llvm_timer(cgcx.config(item.kind()), &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } } } else { + // If we've finished everything related to normal translation + // then it must be the case that we've got some LTO work to do. + // Perform the serial work here of figuring out what we're + // going to LTO and then push a bunch of work items onto our + // queue to do LTO + if work_items.len() == 0 && + running == 0 && + main_thread_worker_state == MainThreadWorkerState::Idle { + assert!(!started_lto); + assert!(needs_lto.len() > 0); + started_lto = true; + let modules = mem::replace(&mut needs_lto, Vec::new()); + for (work, cost) in generate_lto_work(&cgcx, modules) { + let insertion_index = work_items + .binary_search_by_key(&cost, |&(_, cost)| cost) + .unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + helper.request_token(); + } + } + // In this branch, we know that everything has been translated, // so it's just a matter of determining whether the implicit // Token is free to use for LLVM work. @@ -1365,7 +1673,7 @@ fn start_executing_work(tcx: TyCtxt, worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; - maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + maybe_start_llvm_timer(cgcx.config(item.kind()), &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); @@ -1396,7 +1704,7 @@ fn start_executing_work(tcx: TyCtxt, while work_items.len() > 0 && running < tokens.len() { let (item, _) = work_items.pop().unwrap(); - maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + maybe_start_llvm_timer(cgcx.config(item.kind()), &mut llvm_start_time); let cgcx = CodegenContext { @@ -1499,10 +1807,21 @@ fn start_executing_work(tcx: TyCtxt, } } } + Message::NeedsLTO { result, worker_id } => { + assert!(!started_lto); + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; + } else { + running -= 1; + } + + free_worker_ids.push(worker_id); + needs_lto.push(result); + } Message::Done { result: Err(()), worker_id: _ } => { - shared_emitter.fatal("aborting due to worker thread panic"); + shared_emitter.fatal("aborting due to worker thread failure"); // Exit the coordinator thread - panic!("aborting due to worker thread panic") + return Err(()) } Message::TranslateItem => { bug!("the coordinator should not receive translation requests") @@ -1520,14 +1839,19 @@ fn start_executing_work(tcx: TyCtxt, total_llvm_time); } + // Regardless of what order these modules completed in, report them to + // the backend in the same order every time to ensure that we're handing + // out deterministic results. + compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); + let compiled_metadata_module = compiled_metadata_module .expect("Metadata module not compiled?"); - CompiledModules { + Ok(CompiledModules { modules: compiled_modules, metadata_module: compiled_metadata_module, allocator_module: compiled_allocator_module, - } + }) }); // A heuristic that determines if we have enough LLVM WorkItems in the @@ -1570,20 +1894,22 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // we exit. struct Bomb { coordinator_send: Sender<Box<Any + Send>>, - result: Option<CompiledModule>, + result: Option<WorkItemResult>, worker_id: usize, } impl Drop for Bomb { fn drop(&mut self) { - let result = match self.result.take() { - Some(compiled_module) => Ok(compiled_module), - None => Err(()) + let worker_id = self.worker_id; + let msg = match self.result.take() { + Some(WorkItemResult::Compiled(m)) => { + Message::Done { result: Ok(m), worker_id } + } + Some(WorkItemResult::NeedsLTO(m)) => { + Message::NeedsLTO { result: m, worker_id } + } + None => Message::Done { result: Err(()), worker_id } }; - - drop(self.coordinator_send.send(Box::new(Message::Done { - result, - worker_id: self.worker_id, - }))); + drop(self.coordinator_send.send(Box::new(msg))); } } @@ -1596,22 +1922,17 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // Execute the work itself, and if it finishes successfully then flag // ourselves as a success as well. // - // Note that we ignore the result coming out of `execute_work_item` - // which will tell us if the worker failed with a `FatalError`. If that - // has happened, however, then a diagnostic was sent off to the main - // thread, along with an `AbortIfErrors` message. In that case the main - // thread is already exiting anyway most likely. - // - // In any case, there's no need for us to take further action here, so - // we just ignore the result and then send off our message saying that - // we're done, which if `execute_work_item` failed is unlikely to be - // seen by the main thread, but hey we might as well try anyway. + // Note that we ignore any `FatalError` coming out of `execute_work_item`, + // as a diagnostic was already sent off to the main thread - just + // surface that there was an error in this worker. bomb.result = { - let _timing_guard = cgcx.time_graph - .as_ref() - .map(|tg| tg.start(time_graph::TimelineId(cgcx.worker), - LLVM_WORK_PACKAGE_KIND)); - Some(execute_work_item(&cgcx, work).unwrap()) + let timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(time_graph::TimelineId(cgcx.worker), + LLVM_WORK_PACKAGE_KIND, + &work.name()) + }); + let mut timeline = timeline.unwrap_or(Timeline::noop()); + execute_work_item(&cgcx, work, &mut timeline).ok() }; }); } @@ -1651,16 +1972,17 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { pub unsafe fn with_llvm_pmb(llmod: ModuleRef, config: &ModuleConfig, + opt_level: llvm::CodeGenOptLevel, f: &mut FnMut(llvm::PassManagerBuilderRef)) { // Create the PassManagerBuilder for LLVM. We configure it with // reasonable defaults and prepare it to actually populate the pass // manager. let builder = llvm::LLVMPassManagerBuilderCreate(); - let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone); let inline_threshold = config.inline_threshold; - llvm::LLVMRustConfigurePassManagerBuilder(builder, opt_level, + llvm::LLVMRustConfigurePassManagerBuilder(builder, + opt_level, config.merge_functions, config.vectorize_slp, config.vectorize_loop); @@ -1780,7 +2102,7 @@ impl SharedEmitterMain { Some(ref code) => { handler.emit_with_code(&MultiSpan::new(), &diag.msg, - &code, + code.clone(), diag.lvl); } None => { @@ -1823,7 +2145,7 @@ pub struct OngoingCrateTranslation { coordinator_send: Sender<Box<Any + Send>>, trans_worker_receive: Receiver<Message>, shared_emitter_main: SharedEmitterMain, - future: thread::JoinHandle<CompiledModules>, + future: thread::JoinHandle<Result<CompiledModules, ()>>, output_filenames: Arc<OutputFilenames>, } @@ -1831,7 +2153,11 @@ impl OngoingCrateTranslation { pub fn join(self, sess: &Session, dep_graph: &DepGraph) -> CrateTranslation { self.shared_emitter_main.check(sess, true); let compiled_modules = match self.future.join() { - Ok(compiled_modules) => compiled_modules, + Ok(Ok(compiled_modules)) => compiled_modules, + Ok(Err(())) => { + sess.abort_if_errors(); + panic!("expected abort due to worker thread errors") + }, Err(_) => { sess.fatal("Error during translation/LLVM phase."); } @@ -1845,15 +2171,14 @@ impl OngoingCrateTranslation { copy_module_artifacts_into_incr_comp_cache(sess, dep_graph, - &compiled_modules, - &self.output_filenames); + &compiled_modules); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); // FIXME: time_llvm_passes support - does this use a global context or // something? - if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { + if sess.codegen_units() == 1 && sess.time_llvm_passes() { unsafe { llvm::LLVMRustPrintPassTimings(); } } @@ -1867,6 +2192,7 @@ impl OngoingCrateTranslation { modules: compiled_modules.modules, allocator_module: compiled_modules.allocator_module, + metadata_module: compiled_modules.metadata_module, }; if self.no_integrated_as { @@ -1918,9 +2244,7 @@ impl OngoingCrateTranslation { Ok(Message::TranslateItem) => { // Nothing to do } - Ok(message) => { - panic!("unexpected message: {:?}", message) - } + Ok(_) => panic!("unexpected message"), Err(_) => { // One of the LLVM threads must have panicked, fall through so // error handling can be reached. @@ -1932,12 +2256,57 @@ impl OngoingCrateTranslation { pub fn submit_translated_module_to_llvm(tcx: TyCtxt, mtrans: ModuleTranslation, cost: u64) { - let llvm_work_item = WorkItem { - mtrans, - tm: TargetMachine(create_target_machine(tcx.sess)), - }; + let llvm_work_item = WorkItem::Optimize(mtrans); drop(tcx.tx_to_llvm_workers.send(Box::new(Message::TranslationDone { llvm_work_item, cost, }))); } + +fn msvc_imps_needed(tcx: TyCtxt) -> bool { + tcx.sess.target.target.options.is_like_msvc && + tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) +} + +// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`. +// This is required to satisfy `dllimport` references to static data in .rlibs +// when using MSVC linker. We do this only for data, as linker can fix up +// code references on its own. +// See #26591, #27438 +fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) { + if !cgcx.msvc_imps_needed { + return + } + // The x86 ABI seems to require that leading underscores are added to symbol + // names, so we need an extra underscore on 32-bit. There's also a leading + // '\x01' here which disables LLVM's symbol mangling (e.g. no extra + // underscores added in front). + let prefix = if cgcx.target_pointer_width == "32" { + "\x01__imp__" + } else { + "\x01__imp_" + }; + unsafe { + let i8p_ty = Type::i8p_llcx(llcx); + let globals = base::iter_globals(llmod) + .filter(|&val| { + llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage && + llvm::LLVMIsDeclaration(val) == 0 + }) + .map(move |val| { + let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); + let mut imp_name = prefix.as_bytes().to_vec(); + imp_name.extend(name.to_bytes()); + let imp_name = CString::new(imp_name).unwrap(); + (imp_name, val) + }) + .collect::<Vec<_>>(); + for (imp_name, val) in globals { + let imp = llvm::LLVMAddGlobal(llmod, + i8p_ty.to_ref(), + imp_name.as_ptr() as *const _); + llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); + llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); + } + } +} |
