From e0ab5d5feb4eb2d8af11b8dd9446c2b45fada8af Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 16 Dec 2017 08:20:54 -0800 Subject: [PATCH] rustc: Work around `DICompileUnit` bugs in LLVM This commit implements a workaround for #46346 which basically just avoids triggering the situation that LLVM's bug https://bugs.llvm.org/show_bug.cgi?id=35562 arises. More details can be found in the code itself but this commit is also intended to ... Closes #46346 --- src/librustc_llvm/ffi.rs | 4 ++ src/librustc_trans/back/lto.rs | 46 +++++++++++++++++++ src/rustllvm/PassWrapper.cpp | 80 ++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index 1c2fa1bbb48..cb385923067 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -1728,4 +1728,8 @@ pub fn LLVMRustParseBitcodeForThinLTO( Identifier: *const c_char, ) -> ModuleRef; pub fn LLVMGetModuleIdentifier(M: ModuleRef, size: *mut usize) -> *const c_char; + pub fn LLVMRustThinLTOGetDICompileUnit(M: ModuleRef, + CU1: *mut *mut c_void, + CU2: *mut *mut c_void); + pub fn LLVMRustThinLTOPatchDICompileUnit(M: ModuleRef, CU: *mut c_void); } diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index aa7754a7ab0..ba8c26bc819 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -26,6 +26,7 @@ use libc; use std::ffi::CString; +use std::ptr; use std::slice; use std::sync::Arc; @@ -629,6 +630,18 @@ unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) }; cgcx.save_temp_bitcode(&mtrans, "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 = format!("multiple source DICompileUnits found"); + 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 { @@ -670,6 +683,39 @@ unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) cgcx.save_temp_bitcode(&mtrans, "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 tihs 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(&mtrans, "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 diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index e0a14f9b14f..776e4a3e65a 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -1114,6 +1114,74 @@ LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context, return wrap(std::move(*SrcOrError).release()); } +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, + DICompileUnit **A, + DICompileUnit **B) { + Module *M = unwrap(Mod); + DICompileUnit **Cur = A; + DICompileUnit **Next = B; + for (DICompileUnit *CU : M->debug_compile_units()) { + *Cur = CU; + Cur = Next; + Next = nullptr; + if (Cur == nullptr) + break; + } +} + +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { + Module *M = unwrap(Mod); + + // If the original source module didn't have a `DICompileUnit` then try to + // merge all the existing compile units. If there aren't actually any though + // then there's not much for us to do so return. + if (Unit == nullptr) { + for (DICompileUnit *CU : M->debug_compile_units()) { + Unit = CU; + break; + } + if (Unit == nullptr) + return; + } + + // Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and + // process it recursively. Note that we specifically iterate over instructions + // to ensure we feed everything into it. + DebugInfoFinder Finder; + Finder.processModule(*M); + for (Function &F : M->functions()) { + for (auto &FI : F) { + for (Instruction &BI : FI) { + if (auto Loc = BI.getDebugLoc()) + Finder.processLocation(*M, Loc); + if (auto DVI = dyn_cast(&BI)) + Finder.processValue(*M, DVI); + if (auto DDI = dyn_cast(&BI)) + Finder.processDeclare(*M, DDI); + } + } + } + + // After we've found all our debuginfo, rewrite all subprograms to point to + // the same `DICompileUnit`. + for (auto &F : Finder.subprograms()) { + F->replaceUnit(Unit); + } + + // Erase any other references to other `DICompileUnit` instances, the verifier + // will later ensure that we don't actually have any other stale references to + // worry about. + auto *MD = M->getNamedMetadata("llvm.dbg.cu"); + MD->clearOperands(); + MD->addOperand(Unit); +} + #else extern "C" bool @@ -1192,4 +1260,16 @@ LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context, const char *identifier) { report_fatal_error("ThinLTO not available"); } + +extern "C" void +LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, + DICompileUnit **A, + DICompileUnit **B) { + report_fatal_error("ThinLTO not available"); +} + +extern "C" void +LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod) { + report_fatal_error("ThinLTO not available"); +} #endif // LLVM_VERSION_GE(4, 0)