diff options
| author | Irina Popa <irinagpopa@gmail.com> | 2018-06-27 17:57:25 +0300 |
|---|---|---|
| committer | Irina Popa <irinagpopa@gmail.com> | 2018-07-30 18:27:52 +0300 |
| commit | 249d5acaec0b10ee15b21b888977b5445baba42e (patch) | |
| tree | 966ea8d75c9e97d2b6e70d0bcd30ceef689d4b8a /src/librustc_codegen_llvm/back/lto.rs | |
| parent | af04e9426c71ac1050b9007c93b03864e45a81df (diff) | |
| download | rust-249d5acaec0b10ee15b21b888977b5445baba42e.tar.gz rust-249d5acaec0b10ee15b21b888977b5445baba42e.zip | |
rustc_codegen_llvm: use safe references for Context and Module.
Diffstat (limited to 'src/librustc_codegen_llvm/back/lto.rs')
| -rw-r--r-- | src/librustc_codegen_llvm/back/lto.rs | 326 |
1 files changed, 166 insertions, 160 deletions
diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 93cb9eb9767..fce34710b81 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -14,7 +14,7 @@ use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; use back::write; use errors::{FatalError, Handler}; use llvm::archive_ro::ArchiveRO; -use llvm::{ModuleRef, TargetMachineRef, True, False}; +use llvm::{TargetMachineRef, True, False}; use llvm; use rustc::hir::def_id::LOCAL_CRATE; use rustc::middle::exported_symbols::SymbolExportLevel; @@ -73,11 +73,13 @@ impl LtoModuleCodegen { match *self { LtoModuleCodegen::Fat { ref mut module, .. } => { let module = module.take().unwrap(); - let config = cgcx.config(module.kind); - let llmod = module.llvm().unwrap().llmod; - let tm = module.llvm().unwrap().tm; - run_pass_manager(cgcx, tm, llmod, config, false); - timeline.record("fat-done"); + { + let config = cgcx.config(module.kind); + let llmod = module.llvm().unwrap().llmod(); + let tm = &*module.llvm().unwrap().tm; + run_pass_manager(cgcx, tm, llmod, config, false); + timeline.record("fat-done"); + } Ok(module) } LtoModuleCodegen::Thin(ref mut thin) => thin.optimize(cgcx, timeline), @@ -223,66 +225,68 @@ fn fat_lto(cgcx: &CodegenContext, .filter(|&(_, module)| module.kind == ModuleKind::Regular) .map(|(i, module)| { let cost = unsafe { - llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod) + llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod()) }; (cost, i) }) .max() .expect("must be codegen'ing at least one module"); let module = modules.remove(costliest_module); - let llmod = module.llvm().expect("can't lto pre-codegened modules").llmod; - info!("using {:?} as a base module", module.llmod_id); - - // For all other modules we codegened we'll need to link them into our own - // bitcode. All modules were codegened in their own LLVM context, however, - // and we want to move everything to the same LLVM context. Currently the - // way we know of to do that is to serialize them to a string and them parse - // them later. Not great but hey, that's why it's "fat" LTO, right? - for module in modules { - let llvm = module.llvm().expect("can't lto pre-codegened modules"); - let buffer = ModuleBuffer::new(llvm.llmod); - let llmod_id = CString::new(&module.llmod_id[..]).unwrap(); - serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); - } - - // For all serialized bitcode files we parse them and link them in as we did - // above, this is all mostly handled in C++. Like above, though, we don't - // know much about the memory management here so we err on the side of being - // save and persist everything with the original module. let mut serialized_bitcode = Vec::new(); - let mut linker = Linker::new(llmod); - for (bc_decoded, name) in serialized_modules { - info!("linking {:?}", name); - time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || { - let data = bc_decoded.data(); - linker.add(&data).map_err(|()| { - let msg = format!("failed to load bc of {:?}", name); - write::llvm_err(&diag_handler, msg) - }) - })?; - timeline.record(&format!("link {:?}", name)); - serialized_bitcode.push(bc_decoded); - } - drop(linker); - cgcx.save_temp_bitcode(&module, "lto.input"); + { + let llmod = module.llvm().expect("can't lto pre-codegened modules").llmod(); + info!("using {:?} as a base module", module.llmod_id); + + // For all other modules we codegened we'll need to link them into our own + // bitcode. All modules were codegened in their own LLVM context, however, + // and we want to move everything to the same LLVM context. Currently the + // way we know of to do that is to serialize them to a string and them parse + // them later. Not great but hey, that's why it's "fat" LTO, right? + for module in modules { + let llvm = module.llvm().expect("can't lto pre-codegened modules"); + let buffer = ModuleBuffer::new(llvm.llmod()); + let llmod_id = CString::new(&module.llmod_id[..]).unwrap(); + serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); + } - // Internalize everything that *isn't* in our whitelist to help strip out - // more modules and such - unsafe { - let ptr = symbol_white_list.as_ptr(); - llvm::LLVMRustRunRestrictionPass(llmod, - ptr as *const *const libc::c_char, - symbol_white_list.len() as libc::size_t); - cgcx.save_temp_bitcode(&module, "lto.after-restriction"); - } + // For all serialized bitcode files we parse them and link them in as we did + // above, this is all mostly handled in C++. Like above, though, we don't + // know much about the memory management here so we err on the side of being + // save and persist everything with the original module. + let mut linker = Linker::new(llmod); + for (bc_decoded, name) in serialized_modules { + info!("linking {:?}", name); + time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || { + let data = bc_decoded.data(); + linker.add(&data).map_err(|()| { + let msg = format!("failed to load bc of {:?}", name); + write::llvm_err(&diag_handler, msg) + }) + })?; + timeline.record(&format!("link {:?}", name)); + serialized_bitcode.push(bc_decoded); + } + drop(linker); + cgcx.save_temp_bitcode(&module, "lto.input"); - if cgcx.no_landing_pads { + // Internalize everything that *isn't* in our whitelist to help strip out + // more modules and such unsafe { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + let ptr = symbol_white_list.as_ptr(); + llvm::LLVMRustRunRestrictionPass(llmod, + ptr as *const *const libc::c_char, + symbol_white_list.len() as libc::size_t); + cgcx.save_temp_bitcode(&module, "lto.after-restriction"); } - cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); + + if cgcx.no_landing_pads { + unsafe { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + } + cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); + } + timeline.record("passes"); } - timeline.record("passes"); Ok(vec![LtoModuleCodegen::Fat { module: Some(module), @@ -293,7 +297,7 @@ fn fat_lto(cgcx: &CodegenContext, struct Linker(llvm::LinkerRef); impl Linker { - fn new(llmod: ModuleRef) -> Linker { + fn new(llmod: &llvm::Module) -> Linker { unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } } @@ -371,7 +375,7 @@ fn thin_lto(diag_handler: &Handler, info!("local module: {} - {}", i, module.llmod_id); let llvm = module.llvm().expect("can't lto precodegened module"); let name = CString::new(module.llmod_id.clone()).unwrap(); - let buffer = ThinBuffer::new(llvm.llmod); + let buffer = ThinBuffer::new(llvm.llmod()); thin_modules.push(llvm::ThinLTOModule { identifier: name.as_ptr(), data: buffer.data().as_ptr(), @@ -449,7 +453,7 @@ fn thin_lto(diag_handler: &Handler, fn run_pass_manager(cgcx: &CodegenContext, tm: TargetMachineRef, - llmod: ModuleRef, + llmod: &llvm::Module, config: &ModuleConfig, thin: bool) { // Now we have one massive module inside of llmod. Time to run the @@ -531,7 +535,7 @@ unsafe impl Send for ModuleBuffer {} unsafe impl Sync for ModuleBuffer {} impl ModuleBuffer { - pub fn new(m: ModuleRef) -> ModuleBuffer { + pub fn new(m: &llvm::Module) -> ModuleBuffer { ModuleBuffer(unsafe { llvm::LLVMRustModuleBufferCreate(m) }) @@ -583,7 +587,7 @@ unsafe impl Send for ThinBuffer {} unsafe impl Sync for ThinBuffer {} impl ThinBuffer { - pub fn new(m: ModuleRef) -> ThinBuffer { + pub fn new(m: &llvm::Module) -> ThinBuffer { unsafe { let buffer = llvm::LLVMRustThinLTOBufferCreate(m); ThinBuffer(buffer) @@ -640,19 +644,18 @@ impl ThinModule { // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); - let llmod = llvm::LLVMRustParseBitcodeForThinLTO( + let llmod_raw = llvm::LLVMRustParseBitcodeForThinLTO( llcx, self.data().as_ptr(), self.data().len(), self.shared.module_names[self.idx].as_ptr(), - ); - if llmod.is_null() { + ).ok_or_else(|| { let msg = "failed to parse bitcode for thin LTO module".to_string(); - return Err(write::llvm_err(&diag_handler, msg)); - } + write::llvm_err(&diag_handler, msg) + })? as *const _; let module = ModuleCodegen { source: ModuleSource::Codegened(ModuleLlvm { - llmod, + llmod_raw, llcx, tm, }), @@ -660,104 +663,107 @@ impl ThinModule { name: self.name().to_string(), kind: ModuleKind::Regular, }; - cgcx.save_temp_bitcode(&module, "thin-lto-input"); - - // Before we do much else find the "main" `DICompileUnit` that we'll be - // using below. If we find more than one though then rustc has changed - // in a way we're not ready for, so generate an ICE by returning - // an error. - let mut cu1 = ptr::null_mut(); - let mut cu2 = ptr::null_mut(); - llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); - if !cu2.is_null() { - let msg = "multiple source DICompileUnits found".to_string(); - return Err(write::llvm_err(&diag_handler, msg)) - } + { + let llmod = module.llvm().unwrap().llmod(); + cgcx.save_temp_bitcode(&module, "thin-lto-input"); + + // Before we do much else find the "main" `DICompileUnit` that we'll be + // using below. If we find more than one though then rustc has changed + // in a way we're not ready for, so generate an ICE by returning + // an error. + let mut cu1 = ptr::null_mut(); + let mut cu2 = ptr::null_mut(); + llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); + if !cu2.is_null() { + let msg = "multiple source DICompileUnits found".to_string(); + return Err(write::llvm_err(&diag_handler, msg)) + } - // Like with "fat" LTO, get some better optimizations if landing pads - // are disabled by removing all landing pads. - if cgcx.no_landing_pads { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - cgcx.save_temp_bitcode(&module, "thin-lto-after-nounwind"); - timeline.record("nounwind"); - } + // Like with "fat" LTO, get some better optimizations if landing pads + // are disabled by removing all landing pads. + if cgcx.no_landing_pads { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + cgcx.save_temp_bitcode(&module, "thin-lto-after-nounwind"); + timeline.record("nounwind"); + } - // Up next comes the per-module local analyses that we do for Thin LTO. - // Each of these functions is basically copied from the LLVM - // implementation and then tailored to suit this implementation. Ideally - // each of these would be supported by upstream LLVM but that's perhaps - // a patch for another day! - // - // You can find some more comments about these functions in the LLVM - // bindings we've got (currently `PassWrapper.cpp`) - if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&module, "thin-lto-after-rename"); - timeline.record("rename"); - if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&module, "thin-lto-after-resolve"); - timeline.record("resolve"); - if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&module, "thin-lto-after-internalize"); - timeline.record("internalize"); - if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { - let msg = "failed to prepare thin LTO module".to_string(); - return Err(write::llvm_err(&diag_handler, msg)) + // Up next comes the per-module local analyses that we do for Thin LTO. + // Each of these functions is basically copied from the LLVM + // implementation and then tailored to suit this implementation. Ideally + // each of these would be supported by upstream LLVM but that's perhaps + // a patch for another day! + // + // You can find some more comments about these functions in the LLVM + // bindings we've got (currently `PassWrapper.cpp`) + if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module".to_string(); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-rename"); + timeline.record("rename"); + if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module".to_string(); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-resolve"); + timeline.record("resolve"); + if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module".to_string(); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-internalize"); + timeline.record("internalize"); + if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { + let msg = "failed to prepare thin LTO module".to_string(); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-import"); + timeline.record("import"); + + // Ok now this is a bit unfortunate. This is also something you won't + // find upstream in LLVM's ThinLTO passes! This is a hack for now to + // work around bugs in LLVM. + // + // First discovered in #45511 it was found that as part of ThinLTO + // importing passes LLVM will import `DICompileUnit` metadata + // information across modules. This means that we'll be working with one + // LLVM module that has multiple `DICompileUnit` instances in it (a + // bunch of `llvm.dbg.cu` members). Unfortunately there's a number of + // bugs in LLVM's backend which generates invalid DWARF in a situation + // like this: + // + // https://bugs.llvm.org/show_bug.cgi?id=35212 + // https://bugs.llvm.org/show_bug.cgi?id=35562 + // + // While the first bug there is fixed the second ended up causing #46346 + // which was basically a resurgence of #45511 after LLVM's bug 35212 was + // fixed. + // + // This function below is a huge hack around this problem. The function + // below is defined in `PassWrapper.cpp` and will basically "merge" + // all `DICompileUnit` instances in a module. Basically it'll take all + // the objects, rewrite all pointers of `DISubprogram` to point to the + // first `DICompileUnit`, and then delete all the other units. + // + // This is probably mangling to the debug info slightly (but hopefully + // not too much) but for now at least gets LLVM to emit valid DWARF (or + // so it appears). Hopefully we can remove this once upstream bugs are + // fixed in LLVM. + llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); + cgcx.save_temp_bitcode(&module, "thin-lto-after-patch"); + timeline.record("patch"); + + // Alright now that we've done everything related to the ThinLTO + // analysis it's time to run some optimizations! Here we use the same + // `run_pass_manager` as the "fat" LTO above except that we tell it to + // populate a thin-specific pass manager, which presumably LLVM treats a + // little differently. + info!("running thin lto passes over {}", module.name); + let config = cgcx.config(module.kind); + run_pass_manager(cgcx, module.llvm().unwrap().tm, llmod, config, true); + cgcx.save_temp_bitcode(&module, "thin-lto-after-pm"); + timeline.record("thin-done"); } - cgcx.save_temp_bitcode(&module, "thin-lto-after-import"); - timeline.record("import"); - - // Ok now this is a bit unfortunate. This is also something you won't - // find upstream in LLVM's ThinLTO passes! This is a hack for now to - // work around bugs in LLVM. - // - // First discovered in #45511 it was found that as part of ThinLTO - // importing passes LLVM will import `DICompileUnit` metadata - // information across modules. This means that we'll be working with one - // LLVM module that has multiple `DICompileUnit` instances in it (a - // bunch of `llvm.dbg.cu` members). Unfortunately there's a number of - // bugs in LLVM's backend which generates invalid DWARF in a situation - // like this: - // - // https://bugs.llvm.org/show_bug.cgi?id=35212 - // https://bugs.llvm.org/show_bug.cgi?id=35562 - // - // While the first bug there is fixed the second ended up causing #46346 - // which was basically a resurgence of #45511 after LLVM's bug 35212 was - // fixed. - // - // This function below is a huge hack around this problem. The function - // below is defined in `PassWrapper.cpp` and will basically "merge" - // all `DICompileUnit` instances in a module. Basically it'll take all - // the objects, rewrite all pointers of `DISubprogram` to point to the - // first `DICompileUnit`, and then delete all the other units. - // - // This is probably mangling to the debug info slightly (but hopefully - // not too much) but for now at least gets LLVM to emit valid DWARF (or - // so it appears). Hopefully we can remove this once upstream bugs are - // fixed in LLVM. - llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); - cgcx.save_temp_bitcode(&module, "thin-lto-after-patch"); - timeline.record("patch"); - - // Alright now that we've done everything related to the ThinLTO - // analysis it's time to run some optimizations! Here we use the same - // `run_pass_manager` as the "fat" LTO above except that we tell it to - // populate a thin-specific pass manager, which presumably LLVM treats a - // little differently. - info!("running thin lto passes over {}", module.name); - let config = cgcx.config(module.kind); - run_pass_manager(cgcx, tm, llmod, config, true); - cgcx.save_temp_bitcode(&module, "thin-lto-after-pm"); - timeline.record("thin-done"); Ok(module) } |
