diff --git a/include/llvm/IR/Verifier.h b/include/llvm/IR/Verifier.h index 41a47226cafcc..70bec787a4c7d 100644 --- a/include/llvm/IR/Verifier.h +++ b/include/llvm/IR/Verifier.h @@ -41,10 +41,16 @@ bool verifyFunction(const Function &F, raw_ostream *OS = nullptr); /// \brief Check a module for errors. /// -/// If there are no errors, the function returns false. If an error is found, -/// a message describing the error is written to OS (if non-null) and true is -/// returned. -bool verifyModule(const Module &M, raw_ostream *OS = nullptr); +/// If there are no errors, the function returns false. If an error is +/// found, a message describing the error is written to OS (if +/// non-null) and true is returned. +/// +/// \return true if the module is broken. If BrokenDebugInfo is +/// supplied, DebugInfo verification failures won't be considered as +/// error and instead *BrokenDebugInfo will be set to true. Debug +/// info errors can be "recovered" from by stripping the debug info. +bool verifyModule(const Module &M, raw_ostream *OS = nullptr, + bool *BrokenDebugInfo = nullptr); /// \brief Create a verifier pass. /// diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 576aefdf29aae..e09f763f10bcf 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -4417,18 +4417,22 @@ bool llvm::verifyFunction(const Function &f, raw_ostream *OS) { return !V.verify(F); } -bool llvm::verifyModule(const Module &M, raw_ostream *OS) { +bool llvm::verifyModule(const Module &M, raw_ostream *OS, + bool *BrokenDebugInfo) { // Don't use a raw_null_ostream. Printing IR is expensive. - Verifier V(OS, /*ShouldTreatBrokenDebugInfoAsError=*/true); + Verifier V(OS, /*ShouldTreatBrokenDebugInfoAsError=*/!BrokenDebugInfo); bool Broken = false; for (const Function &F : M) if (!F.isDeclaration() && !F.isMaterializable()) Broken |= !V.verify(F); + Broken |= !V.verify(M); + if (BrokenDebugInfo) + *BrokenDebugInfo = V.hasBrokenDebugInfo(); // Note that this function's return value is inverted from what you would // expect of a function called "verify". - return !V.verify(M) || Broken; + return Broken; } namespace { diff --git a/lib/LTO/LTOCodeGenerator.cpp b/lib/LTO/LTOCodeGenerator.cpp index 226004a3353ae..0e1c46cff277b 100644 --- a/lib/LTO/LTOCodeGenerator.cpp +++ b/lib/LTO/LTOCodeGenerator.cpp @@ -26,6 +26,7 @@ #include "llvm/Config/config.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -78,6 +79,16 @@ cl::opt LTODiscardValueNames( cl::init(false), #endif cl::Hidden); + +cl::opt LTOStripInvalidDebugInfo( + "lto-strip-invalid-debug-info", + cl::desc("Strip invalid debug info metadata during LTO instead of aborting."), +#ifdef NDEBUG + cl::init(true), +#else + cl::init(false), +#endif + cl::Hidden); } LTOCodeGenerator::LTOCodeGenerator(LLVMContext &Context) @@ -488,6 +499,15 @@ void LTOCodeGenerator::verifyMergedModuleOnce() { return; HasVerifiedInput = true; + if (LTOStripInvalidDebugInfo) { + bool BrokenDebugInfo = false; + if (verifyModule(*MergedModule, &dbgs(), &BrokenDebugInfo)) + report_fatal_error("Broken module found, compilation aborted!"); + if (BrokenDebugInfo) { + emitWarning("Invalid debug info found, debug info will be stripped"); + StripDebugInfo(*MergedModule); + } + } if (verifyModule(*MergedModule, &dbgs())) report_fatal_error("Broken module found, compilation aborted!"); } diff --git a/test/LTO/X86/Inputs/strip-debug-info.bc b/test/LTO/X86/Inputs/strip-debug-info.bc new file mode 100644 index 0000000000000..c83195ff9caf5 Binary files /dev/null and b/test/LTO/X86/Inputs/strip-debug-info.bc differ diff --git a/test/LTO/X86/strip-debug-info.ll b/test/LTO/X86/strip-debug-info.ll new file mode 100644 index 0000000000000..ff45ca15243eb --- /dev/null +++ b/test/LTO/X86/strip-debug-info.ll @@ -0,0 +1,21 @@ +; RUN: not llvm-lto -lto-strip-invalid-debug-info=false \ +; RUN: -o %t.o %S/Inputs/strip-debug-info.bc 2>&1 | \ +; RUN: FileCheck %s -allow-empty -check-prefix=CHECK-ERR +; RUN: llvm-lto -lto-strip-invalid-debug-info=true \ +; RUN: -exported-symbol foo -exported-symbol _foo \ +; RUN: -o %t.o %S/Inputs/strip-debug-info.bc 2>&1 | \ +; RUN: FileCheck %s -allow-empty -check-prefix=CHECK-WARN +; RUN: llvm-nm %t.o | FileCheck %s + +; CHECK-ERR: Broken module found, compilation aborted +; CHECK-WARN: Invalid debug info found, debug info will be stripped +; CHECK: foo +define void @foo() { + ret void +} + +!llvm.module.flags = !{!0} +!llvm.dbg.cu = !{!1} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +!1 = !DIFile(filename: "broken", directory: "")