Auto merge of #46772 - alexcrichton:thinlto-passes, r=michaelwoerister
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
This commit is contained in:
commit
de38f49528
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<DbgValueInst>(&BI))
|
||||
Finder.processValue(*M, DVI);
|
||||
if (auto DDI = dyn_cast<DbgDeclareInst>(&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)
|
||||
|
Loading…
Reference in New Issue
Block a user