about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2017-12-16 08:20:54 -0800
committerAlex Crichton <alex@alexcrichton.com>2017-12-18 11:44:00 -0800
commite0ab5d5feb4eb2d8af11b8dd9446c2b45fada8af (patch)
treea502ff2d52a8329aa843e13780e95db3ad24625a /src
parenta3a7203e2c9ed30a501da86f3fa1f9efe707ac94 (diff)
downloadrust-e0ab5d5feb4eb2d8af11b8dd9446c2b45fada8af.tar.gz
rust-e0ab5d5feb4eb2d8af11b8dd9446c2b45fada8af.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/librustc_llvm/ffi.rs4
-rw-r--r--src/librustc_trans/back/lto.rs46
-rw-r--r--src/rustllvm/PassWrapper.cpp80
3 files changed, 130 insertions, 0 deletions
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 @@ 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);
 }
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 {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource};
 use libc;
 
 use std::ffi::CString;
+use std::ptr;
 use std::slice;
 use std::sync::Arc;
 
@@ -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 {
@@ -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
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<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)