Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc: Work around DICompileUnit bugs in LLVM #46772

Merged
merged 1 commit into from
Dec 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc_llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1728,4 +1728,8 @@ extern "C" {
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);
}
46 changes: 46 additions & 0 deletions src/librustc_trans/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource};
use libc;

use std::ffi::CString;
use std::ptr;
use std::slice;
use std::sync::Arc;

Expand Down Expand Up @@ -629,6 +630,18 @@ impl ThinModule {
};
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 {
Expand Down Expand Up @@ -670,6 +683,39 @@ impl ThinModule {
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
Expand Down
80 changes: 80 additions & 0 deletions src/rustllvm/PassWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)