Skip to content

Commit e0ab5d5

Browse files
committed
rustc: Work around DICompileUnit bugs in LLVM
This commit implements a workaround for rust-lang#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 rust-lang#46346
1 parent a3a7203 commit e0ab5d5

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

src/librustc_llvm/ffi.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1728,4 +1728,8 @@ extern "C" {
17281728
Identifier: *const c_char,
17291729
) -> ModuleRef;
17301730
pub fn LLVMGetModuleIdentifier(M: ModuleRef, size: *mut usize) -> *const c_char;
1731+
pub fn LLVMRustThinLTOGetDICompileUnit(M: ModuleRef,
1732+
CU1: *mut *mut c_void,
1733+
CU2: *mut *mut c_void);
1734+
pub fn LLVMRustThinLTOPatchDICompileUnit(M: ModuleRef, CU: *mut c_void);
17311735
}

src/librustc_trans/back/lto.rs

+46
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource};
2626
use libc;
2727

2828
use std::ffi::CString;
29+
use std::ptr;
2930
use std::slice;
3031
use std::sync::Arc;
3132

@@ -629,6 +630,18 @@ impl ThinModule {
629630
};
630631
cgcx.save_temp_bitcode(&mtrans, "thin-lto-input");
631632

633+
// Before we do much else find the "main" `DICompileUnit` that we'll be
634+
// using below. If we find more than one though then rustc has changed
635+
// in a way we're not ready for, so generate an ICE by returning
636+
// an error.
637+
let mut cu1 = ptr::null_mut();
638+
let mut cu2 = ptr::null_mut();
639+
llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2);
640+
if !cu2.is_null() {
641+
let msg = format!("multiple source DICompileUnits found");
642+
return Err(write::llvm_err(&diag_handler, msg))
643+
}
644+
632645
// Like with "fat" LTO, get some better optimizations if landing pads
633646
// are disabled by removing all landing pads.
634647
if cgcx.no_landing_pads {
@@ -670,6 +683,39 @@ impl ThinModule {
670683
cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-import");
671684
timeline.record("import");
672685

686+
// Ok now this is a bit unfortunate. This is also something you won't
687+
// find upstream in LLVM's ThinLTO passes! This is a hack for now to
688+
// work around bugs in LLVM.
689+
//
690+
// First discovered in #45511 it was found that as part of ThinLTO
691+
// importing passes LLVM will import `DICompileUnit` metadata
692+
// information across modules. This means that we'll be working with one
693+
// LLVM module that has multiple `DICompileUnit` instances in it (a
694+
// bunch of `llvm.dbg.cu` members). Unfortunately there's a number of
695+
// bugs in LLVM's backend which generates invalid DWARF in a situation
696+
// like this:
697+
//
698+
// https://bugs.llvm.org/show_bug.cgi?id=35212
699+
// https://bugs.llvm.org/show_bug.cgi?id=35562
700+
//
701+
// While the first bug there is fixed the second ended up causing #46346
702+
// which was basically a resurgence of #45511 after LLVM's bug 35212 was
703+
// fixed.
704+
//
705+
// This function below is a huge hack around tihs problem. The function
706+
// below is defined in `PassWrapper.cpp` and will basically "merge"
707+
// all `DICompileUnit` instances in a module. Basically it'll take all
708+
// the objects, rewrite all pointers of `DISubprogram` to point to the
709+
// first `DICompileUnit`, and then delete all the other units.
710+
//
711+
// This is probably mangling to the debug info slightly (but hopefully
712+
// not too much) but for now at least gets LLVM to emit valid DWARF (or
713+
// so it appears). Hopefully we can remove this once upstream bugs are
714+
// fixed in LLVM.
715+
llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1);
716+
cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-patch");
717+
timeline.record("patch");
718+
673719
// Alright now that we've done everything related to the ThinLTO
674720
// analysis it's time to run some optimizations! Here we use the same
675721
// `run_pass_manager` as the "fat" LTO above except that we tell it to

src/rustllvm/PassWrapper.cpp

+80
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,74 @@ LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context,
11141114
return wrap(std::move(*SrcOrError).release());
11151115
}
11161116

1117+
// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
1118+
// the comment in `back/lto.rs` for why this exists.
1119+
extern "C" void
1120+
LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod,
1121+
DICompileUnit **A,
1122+
DICompileUnit **B) {
1123+
Module *M = unwrap(Mod);
1124+
DICompileUnit **Cur = A;
1125+
DICompileUnit **Next = B;
1126+
for (DICompileUnit *CU : M->debug_compile_units()) {
1127+
*Cur = CU;
1128+
Cur = Next;
1129+
Next = nullptr;
1130+
if (Cur == nullptr)
1131+
break;
1132+
}
1133+
}
1134+
1135+
// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
1136+
// the comment in `back/lto.rs` for why this exists.
1137+
extern "C" void
1138+
LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) {
1139+
Module *M = unwrap(Mod);
1140+
1141+
// If the original source module didn't have a `DICompileUnit` then try to
1142+
// merge all the existing compile units. If there aren't actually any though
1143+
// then there's not much for us to do so return.
1144+
if (Unit == nullptr) {
1145+
for (DICompileUnit *CU : M->debug_compile_units()) {
1146+
Unit = CU;
1147+
break;
1148+
}
1149+
if (Unit == nullptr)
1150+
return;
1151+
}
1152+
1153+
// Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and
1154+
// process it recursively. Note that we specifically iterate over instructions
1155+
// to ensure we feed everything into it.
1156+
DebugInfoFinder Finder;
1157+
Finder.processModule(*M);
1158+
for (Function &F : M->functions()) {
1159+
for (auto &FI : F) {
1160+
for (Instruction &BI : FI) {
1161+
if (auto Loc = BI.getDebugLoc())
1162+
Finder.processLocation(*M, Loc);
1163+
if (auto DVI = dyn_cast<DbgValueInst>(&BI))
1164+
Finder.processValue(*M, DVI);
1165+
if (auto DDI = dyn_cast<DbgDeclareInst>(&BI))
1166+
Finder.processDeclare(*M, DDI);
1167+
}
1168+
}
1169+
}
1170+
1171+
// After we've found all our debuginfo, rewrite all subprograms to point to
1172+
// the same `DICompileUnit`.
1173+
for (auto &F : Finder.subprograms()) {
1174+
F->replaceUnit(Unit);
1175+
}
1176+
1177+
// Erase any other references to other `DICompileUnit` instances, the verifier
1178+
// will later ensure that we don't actually have any other stale references to
1179+
// worry about.
1180+
auto *MD = M->getNamedMetadata("llvm.dbg.cu");
1181+
MD->clearOperands();
1182+
MD->addOperand(Unit);
1183+
}
1184+
11171185
#else
11181186

11191187
extern "C" bool
@@ -1192,4 +1260,16 @@ LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context,
11921260
const char *identifier) {
11931261
report_fatal_error("ThinLTO not available");
11941262
}
1263+
1264+
extern "C" void
1265+
LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod,
1266+
DICompileUnit **A,
1267+
DICompileUnit **B) {
1268+
report_fatal_error("ThinLTO not available");
1269+
}
1270+
1271+
extern "C" void
1272+
LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod) {
1273+
report_fatal_error("ThinLTO not available");
1274+
}
11951275
#endif // LLVM_VERSION_GE(4, 0)

0 commit comments

Comments
 (0)