diff --git a/include/llvm/IR/DebugInfo.h b/include/llvm/IR/DebugInfo.h index 972042432b7..c110659257d 100644 --- a/include/llvm/IR/DebugInfo.h +++ b/include/llvm/IR/DebugInfo.h @@ -44,6 +44,18 @@ DISubprogram *getDISubprogram(const MDNode *Scope); bool StripDebugInfo(Module &M); bool stripDebugInfo(Function &F); +/// Downgrade the debug info in a module to contain only line table information. +/// +/// In order to convert debug info to what -gline-tables-only would have +/// created, this does the following: +/// 1) Delete all debug intrinsics. +/// 2) Delete all non-CU named metadata debug info nodes. +/// 3) Create new DebugLocs for each instruction. +/// 4) Create a new CU debug info, and similarly for every metadata node +/// that's reachable from the CU debug info. +/// All debug type metadata nodes are unreachable and garbage collected. +bool stripNonLineTableDebugInfo(Module &M); + /// \brief Return Debug Info Metadata Version by checking module flags. unsigned getDebugMetadataVersionFromModule(const Module &M); diff --git a/include/llvm/IR/Metadata.h b/include/llvm/IR/Metadata.h index 0ce88829214..25d4316087e 100644 --- a/include/llvm/IR/Metadata.h +++ b/include/llvm/IR/Metadata.h @@ -1299,8 +1299,10 @@ public: /// \brief Drop all references and remove the node from parent module. void eraseFromParent(); - /// \brief Remove all uses and clear node vector. - void dropAllReferences(); + /// Remove all uses and clear node vector. + void dropAllReferences() { clearOperands(); } + /// Drop all references to this node's operands. + void clearOperands(); ~NamedMDNode(); diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index 4020bd5e802..970343790fe 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -330,6 +330,7 @@ void initializeStripDeadDebugInfoPass(PassRegistry&); void initializeStripDeadPrototypesLegacyPassPass(PassRegistry&); void initializeStripDebugDeclarePass(PassRegistry&); void initializeStripNonDebugSymbolsPass(PassRegistry&); +void initializeStripNonLineTableDebugInfoPass(PassRegistry&); void initializeStripSymbolsPass(PassRegistry&); void initializeStructurizeCFGPass(PassRegistry&); void initializeTailCallElimPass(PassRegistry&); diff --git a/include/llvm/Transforms/IPO.h b/include/llvm/Transforms/IPO.h index 9ef38817117..0d9c1baf1b1 100644 --- a/include/llvm/Transforms/IPO.h +++ b/include/llvm/Transforms/IPO.h @@ -43,6 +43,10 @@ ModulePass *createStripSymbolsPass(bool OnlyDebugInfo = false); // ModulePass *createStripNonDebugSymbolsPass(); +/// This function returns a new pass that downgrades the debug info in the +/// module to line tables only. +ModulePass *createStripNonLineTableDebugInfoPass(); + //===----------------------------------------------------------------------===// // // These pass removes llvm.dbg.declare intrinsics. diff --git a/lib/IR/DebugInfo.cpp b/lib/IR/DebugInfo.cpp index be00387682f..7f91b49d828 100644 --- a/lib/IR/DebugInfo.cpp +++ b/lib/IR/DebugInfo.cpp @@ -300,6 +300,299 @@ bool llvm::StripDebugInfo(Module &M) { return Changed; } +namespace { + +/// Helper class to downgrade -g metadata to -gline-tables-only metadata. +class DebugTypeInfoRemoval { + DenseMap Replacements; + +public: + /// The (void)() type. + MDNode *EmptySubroutineType; + +private: + /// Remember what linkage name we originally had before stripping. If we end + /// up making two subprograms identical who originally had different linkage + /// names, then we need to make one of them distinct, to avoid them getting + /// uniqued. Maps the new node to the old linkage name. + DenseMap NewToLinkageName; + + // TODO: Remember the distinct subprogram we created for a given linkage name, + // so that we can continue to unique whenever possible. Map to the first (possibly distinct) mdsubprogram + // created for that combination. This is not strictly needed for correctness, + // but can cut down on the number of MDNodes and let us diff cleanly with the + // output of -gline-tables-only. + +public: + DebugTypeInfoRemoval(LLVMContext &C) + : EmptySubroutineType(DISubroutineType::get(C, DINode::FlagZero, 0, + MDNode::get(C, {}))) {} + + Metadata *map(Metadata *M) { + if (!M) + return nullptr; + auto Replacement = Replacements.find(M); + if (Replacement != Replacements.end()) + return Replacement->second; + + return M; + } + MDNode *mapNode(Metadata *N) { return dyn_cast_or_null(map(N)); } + + /// Recursively remap N and all its referenced children. Does a DF post-order + /// traversal, so as to remap bottoms up. + void traverseAndRemap(MDNode *N) { traverse(N); } + +private: + // Create a new DISubprogram, to replace the one given. + DISubprogram *getReplacementSubprogram(DISubprogram *MDS) { + auto *FileAndScope = cast_or_null(map(MDS->getFile())); + StringRef LinkageName = MDS->getName().empty() ? MDS->getLinkageName() : ""; + DISubprogram *Declaration = nullptr; + auto *Type = cast_or_null(map(MDS->getType())); + DITypeRef ContainingType(map(MDS->getContainingType())); + auto *Unit = cast_or_null(map(MDS->getUnit())); + auto Variables = nullptr; + auto TemplateParams = nullptr; + + // Make a distinct DISubprogram, for situations that warrent it. + auto distinctMDSubprogram = [&]() { + return DISubprogram::getDistinct( + MDS->getContext(), FileAndScope, MDS->getName(), LinkageName, + FileAndScope, MDS->getLine(), Type, MDS->isLocalToUnit(), + MDS->isDefinition(), MDS->getScopeLine(), ContainingType, + MDS->getVirtuality(), MDS->getVirtualIndex(), + MDS->getThisAdjustment(), MDS->getFlags(), MDS->isOptimized(), Unit, + TemplateParams, Declaration, Variables); + }; + + if (MDS->isDistinct()) + return distinctMDSubprogram(); + + auto *NewMDS = DISubprogram::get( + MDS->getContext(), FileAndScope, MDS->getName(), LinkageName, + FileAndScope, MDS->getLine(), Type, MDS->isLocalToUnit(), + MDS->isDefinition(), MDS->getScopeLine(), ContainingType, + MDS->getVirtuality(), MDS->getVirtualIndex(), MDS->getThisAdjustment(), + MDS->getFlags(), MDS->isOptimized(), Unit, TemplateParams, Declaration, + Variables); + + StringRef OldLinkageName = MDS->getLinkageName(); + + // See if we need to make a distinct one. + auto OrigLinkage = NewToLinkageName.find(NewMDS); + if (OrigLinkage != NewToLinkageName.end()) { + if (OrigLinkage->second == OldLinkageName) + // We're good. + return NewMDS; + + // Otherwise, need to make a distinct one. + // TODO: Query the map to see if we already have one. + return distinctMDSubprogram(); + } + + NewToLinkageName.insert({NewMDS, MDS->getLinkageName()}); + return NewMDS; + } + + /// Create a new compile unit, to replace the one given + DICompileUnit *getReplacementCU(DICompileUnit *CU) { + // Drop skeleton CUs. + if (CU->getDWOId()) + return nullptr; + + auto *File = cast_or_null(map(CU->getFile())); + MDTuple *EnumTypes = nullptr; + MDTuple *RetainedTypes = nullptr; + MDTuple *GlobalVariables = nullptr; + MDTuple *ImportedEntities = nullptr; + return DICompileUnit::getDistinct( + CU->getContext(), CU->getSourceLanguage(), File, CU->getProducer(), + CU->isOptimized(), CU->getFlags(), CU->getRuntimeVersion(), + CU->getSplitDebugFilename(), DICompileUnit::LineTablesOnly, EnumTypes, + RetainedTypes, GlobalVariables, ImportedEntities, CU->getMacros(), + CU->getDWOId(), CU->getSplitDebugInlining()); + } + + DILocation *getReplacementMDLocation(DILocation *MLD) { + auto *Scope = map(MLD->getScope()); + auto *InlinedAt = map(MLD->getInlinedAt()); + if (MLD->isDistinct()) + return DILocation::getDistinct(MLD->getContext(), MLD->getLine(), + MLD->getColumn(), Scope, InlinedAt); + return DILocation::get(MLD->getContext(), MLD->getLine(), MLD->getColumn(), + Scope, InlinedAt); + } + + /// Create a new generic MDNode, to replace the one given + MDNode *getReplacementMDNode(MDNode *N) { + SmallVector Ops; + Ops.reserve(N->getNumOperands()); + for (auto &I : N->operands()) + if (I) + Ops.push_back(map(I)); + auto *Ret = MDNode::get(N->getContext(), Ops); + return Ret; + } + + /// Attempt to re-map N to a newly created node. + void remap(MDNode *N) { + if (Replacements.count(N)) + return; + + auto doRemap = [&](MDNode *N) -> MDNode * { + if (!N) + return nullptr; + if (auto *MDSub = dyn_cast(N)) { + remap(MDSub->getUnit()); + return getReplacementSubprogram(MDSub); + } + if (isa(N)) + return EmptySubroutineType; + if (auto *CU = dyn_cast(N)) + return getReplacementCU(CU); + if (isa(N)) + return N; + if (auto *MDLB = dyn_cast(N)) + // Remap to our referenced scope (recursively). + return mapNode(MDLB->getScope()); + if (auto *MLD = dyn_cast(N)) + return getReplacementMDLocation(MLD); + + // Otherwise, if we see these, just drop them now. Not strictly necessary, + // but this speeds things up a little. + if (isa(N)) + return nullptr; + + return getReplacementMDNode(N); + }; + Replacements[N] = doRemap(N); + } + + /// Do the remapping traversal. + void traverse(MDNode *); +}; + +} // Anonymous namespace. + +void DebugTypeInfoRemoval::traverse(MDNode *N) { + if (!N || Replacements.count(N)) + return; + + // To avoid cycles, as well as for efficiency sake, we will sometimes prune + // parts of the graph. + auto prune = [](MDNode *Parent, MDNode *Child) { + if (auto *MDS = dyn_cast(Parent)) + return Child == MDS->getVariables().get(); + return false; + }; + + SmallVector ToVisit; + DenseSet Opened; + + // Visit each node starting at N in post order, and map them. + ToVisit.push_back(N); + while (!ToVisit.empty()) { + auto *N = ToVisit.back(); + if (!Opened.insert(N).second) { + // Close it. + remap(N); + ToVisit.pop_back(); + continue; + } + for (auto &I : N->operands()) + if (auto *MDN = dyn_cast_or_null(I)) + if (!Opened.count(MDN) && !Replacements.count(MDN) && !prune(N, MDN) && + !isa(MDN)) + ToVisit.push_back(MDN); + } +} + +bool llvm::stripNonLineTableDebugInfo(Module &M) { + bool Changed = false; + + // First off, delete the debug intrinsics. + auto RemoveUses = [&](StringRef Name) { + if (auto *DbgVal = M.getFunction(Name)) { + while (!DbgVal->use_empty()) + cast(DbgVal->user_back())->eraseFromParent(); + DbgVal->eraseFromParent(); + Changed = true; + } + }; + RemoveUses("llvm.dbg.declare"); + RemoveUses("llvm.dbg.value"); + + // Delete non-CU debug info named metadata nodes. + for (auto NMI = M.named_metadata_begin(), NME = M.named_metadata_end(); + NMI != NME;) { + NamedMDNode *NMD = &*NMI; + ++NMI; + // Specifically keep dbg.cu around. + if (NMD->getName() == "llvm.dbg.cu") + continue; + } + + // Drop all dbg attachments from global variables. + for (auto &GV : M.globals()) + GV.eraseMetadata(LLVMContext::MD_dbg); + + DebugTypeInfoRemoval Mapper(M.getContext()); + auto remap = [&](llvm::MDNode *Node) -> llvm::MDNode * { + if (!Node) + return nullptr; + Mapper.traverseAndRemap(Node); + auto *NewNode = Mapper.mapNode(Node); + Changed |= Node != NewNode; + Node = NewNode; + return NewNode; + }; + + // Rewrite the DebugLocs to be equivalent to what + // -gline-tables-only would have created. + for (auto &F : M) { + if (auto *SP = F.getSubprogram()) { + Mapper.traverseAndRemap(SP); + auto *NewSP = cast(Mapper.mapNode(SP)); + Changed |= SP != NewSP; + F.setSubprogram(NewSP); + } + for (auto &BB : F) { + for (auto &I : BB) { + if (I.getDebugLoc() == DebugLoc()) + continue; + + // Make a replacement. + auto &DL = I.getDebugLoc(); + auto *Scope = DL.getScope(); + MDNode *InlinedAt = DL.getInlinedAt(); + Scope = remap(Scope); + InlinedAt = remap(InlinedAt); + I.setDebugLoc( + DebugLoc::get(DL.getLine(), DL.getCol(), Scope, InlinedAt)); + } + } + } + + // Create a new llvm.dbg.cu, which is equivalent to the one + // -gline-tables-only would have created. + for (auto &NMD : M.getNamedMDList()) { + SmallVector Ops; + for (MDNode *Op : NMD.operands()) + Ops.push_back(remap(Op)); + + if (!Changed) + continue; + + NMD.clearOperands(); + for (auto *Op : Ops) + if (Op) + NMD.addOperand(Op); + } + return Changed; +} + unsigned llvm::getDebugMetadataVersionFromModule(const Module &M) { if (auto *Val = mdconst::dyn_extract_or_null( M.getModuleFlag("Debug Info Version"))) diff --git a/lib/IR/Metadata.cpp b/lib/IR/Metadata.cpp index 17aa1216b3e..adc91573a7f 100644 --- a/lib/IR/Metadata.cpp +++ b/lib/IR/Metadata.cpp @@ -1054,7 +1054,7 @@ void NamedMDNode::setOperand(unsigned I, MDNode *New) { void NamedMDNode::eraseFromParent() { getParent()->eraseNamedMetadata(this); } -void NamedMDNode::dropAllReferences() { getNMDOps(Operands).clear(); } +void NamedMDNode::clearOperands() { getNMDOps(Operands).clear(); } StringRef NamedMDNode::getName() const { return StringRef(Name); } diff --git a/lib/Transforms/Utils/CMakeLists.txt b/lib/Transforms/Utils/CMakeLists.txt index b5c03ec8006..86e5949531b 100644 --- a/lib/Transforms/Utils/CMakeLists.txt +++ b/lib/Transforms/Utils/CMakeLists.txt @@ -43,6 +43,7 @@ add_llvm_library(LLVMTransformUtils SimplifyInstructions.cpp SimplifyLibCalls.cpp SplitModule.cpp + StripNonLineTableDebugInfo.cpp SymbolRewriter.cpp UnifyFunctionExitNodes.cpp Utils.cpp diff --git a/lib/Transforms/Utils/StripNonLineTableDebugInfo.cpp b/lib/Transforms/Utils/StripNonLineTableDebugInfo.cpp new file mode 100644 index 00000000000..66dbf335cb9 --- /dev/null +++ b/lib/Transforms/Utils/StripNonLineTableDebugInfo.cpp @@ -0,0 +1,42 @@ +//===- StripNonLineTableDebugInfo.cpp -- Strip parts of Debug Info --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/IPO.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/Pass.h" +using namespace llvm; + +namespace { + +/// This pass strips all debug info that is not related line tables. +/// The result will be the same as if the program where compiled with +/// -gline-tables-only. +struct StripNonLineTableDebugInfo : public ModulePass { + static char ID; // Pass identification, replacement for typeid + StripNonLineTableDebugInfo() : ModulePass(ID) { + initializeStripNonLineTableDebugInfoPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + bool runOnModule(Module &M) override { + return llvm::stripNonLineTableDebugInfo(M); + } +}; +} + +char StripNonLineTableDebugInfo::ID = 0; +INITIALIZE_PASS(StripNonLineTableDebugInfo, "strip-nonlinetable-debuginfo", + "Strip all debug info except linetables", false, false) + +ModulePass *llvm::createStripNonLineTableDebugInfoPass() { + return new StripNonLineTableDebugInfo(); +} diff --git a/lib/Transforms/Utils/Utils.cpp b/lib/Transforms/Utils/Utils.cpp index cfd85516108..7b9de2eadc6 100644 --- a/lib/Transforms/Utils/Utils.cpp +++ b/lib/Transforms/Utils/Utils.cpp @@ -31,6 +31,7 @@ void llvm::initializeTransformUtils(PassRegistry &Registry) { initializeLowerSwitchPass(Registry); initializeNameAnonGlobalLegacyPassPass(Registry); initializePromoteLegacyPassPass(Registry); + initializeStripNonLineTableDebugInfoPass(Registry); initializeUnifyFunctionExitNodesPass(Registry); initializeInstSimplifierPass(Registry); initializeMetaRenamerPass(Registry); diff --git a/test/BugPoint/metadata.ll b/test/BugPoint/metadata.ll index 1f2f6a90183..ac77b9e5a7d 100644 --- a/test/BugPoint/metadata.ll +++ b/test/BugPoint/metadata.ll @@ -1,10 +1,22 @@ -; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -bugpoint-crashcalls -silence-passes -disable-namedmd-remove > /dev/null -; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s ; REQUIRES: loadable_module - +; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -bugpoint-crashcalls -silence-passes -disable-namedmd-remove -disable-strip-debuginfo -disable-strip-debug-types > /dev/null +; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s +; +; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t-nodebug -bugpoint-crashcalls -silence-passes -disable-namedmd-remove > /dev/null +; RUN: llvm-dis %t-nodebug-reduced-simplified.bc -o - | FileCheck %s --check-prefix=NODEBUG +; +; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t-notype -bugpoint-crashcalls -silence-passes -disable-namedmd-remove -disable-strip-debuginfo > /dev/null +; RUN: llvm-dis %t-notype-reduced-simplified.bc -o - | FileCheck %s --check-prefix=NOTYPE +; ; Bugpoint should keep the call's metadata attached to the call. ; CHECK: call void @foo(), !dbg ![[LOC:[0-9]+]], !attach ![[CALL:[0-9]+]] +; NODEBUG: call void @foo(), !attach ![[CALL:[0-9]+]] +; NOTYPE: call void @foo(), !dbg ![[LOC:[0-9]+]], !attach ![[CALL:[0-9]+]] +; NODEBUG-NOT: call void @foo(), !attach ![[CALL:[0-9]+]] +; NOTYPE-NOT: !DIBasicType +; NOTYPE: !DICompileUnit +; NOTYPE-NOT: !DIBasicType ; CHECK-DAG: ![[LOC]] = !DILocation(line: 104, column: 105, scope: ![[SCOPE:[0-9]+]]) ; CHECK-DAG: ![[SCOPE]] = distinct !DISubprogram(name: "test",{{.*}}file: ![[FILE:[0-9]+]] ; CHECK-DAG: ![[FILE]] = !DIFile(filename: "source.c", directory: "/dir") @@ -32,7 +44,7 @@ declare void @foo() !4 = !{!"filler"} !8 = distinct !DICompileUnit(language: DW_LANG_C99, file: !15) -!9 = distinct !DISubprogram(name: "test", file: !15, unit: !8) +!9 = distinct !DISubprogram(name: "test", file: !15, type: !18, unit: !8) !10 = !DILocation(line: 100, column: 101, scope: !9) !11 = !DILocation(line: 102, column: 103, scope: !9) !12 = !DILocation(line: 104, column: 105, scope: !9) @@ -41,3 +53,6 @@ declare void @foo() !15 = !DIFile(filename: "source.c", directory: "/dir") !16 = !{} !17 = !{i32 1, !"Debug Info Version", i32 3} +!18 = !DISubroutineType(types: !19) +!19 = !{!20, !20} +!20 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) diff --git a/test/BugPoint/named-md.ll b/test/BugPoint/named-md.ll index 1fffa2cb978..1ed34435fbe 100644 --- a/test/BugPoint/named-md.ll +++ b/test/BugPoint/named-md.ll @@ -1,4 +1,4 @@ -; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -bugpoint-crash-too-many-cus -silence-passes > /dev/null +; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -bugpoint-crash-too-many-cus -silence-passes -disable-strip-debuginfo > /dev/null ; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s ; RUN-DISABLE: bugpoint -disable-namedmd-remove -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -bugpoint-crash-too-many-cus -silence-passes > /dev/null ; RUN-DISABLE: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s diff --git a/test/Transforms/Util/strip-nonlinetable-debuginfo-containingtypes.ll b/test/Transforms/Util/strip-nonlinetable-debuginfo-containingtypes.ll new file mode 100644 index 00000000000..6c6b4996bfe --- /dev/null +++ b/test/Transforms/Util/strip-nonlinetable-debuginfo-containingtypes.ll @@ -0,0 +1,92 @@ +; RUN: opt -S -strip-nonlinetable-debuginfo %s -o %t.ll +; RUN: cat %t.ll | FileCheck %s +; RUN: cat %t.ll | FileCheck %s --check-prefix=CHECK-NEG +; +; This test provides coverage for setting the containing type of a DISubprogram. +; +; Generated an reduced from: +; struct A { +; virtual ~A(); +; }; +; struct B : A {}; +; B b; + +source_filename = "t.cpp" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.12.0" + +%struct.B = type { %struct.A } +%struct.A = type { i32 (...)** } + +; CHECK: @b = global +; CHECK-NOT: !dbg +@b = global %struct.B zeroinitializer, align 8, !dbg !0 + +declare void @_ZN1BC2Ev(%struct.B*) unnamed_addr + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #0 + +; Function Attrs: inlinehint nounwind ssp uwtable +; CHECK: define +define linkonce_odr void @_ZN1BC1Ev(%struct.B* %this) unnamed_addr #1 align 2 !dbg !24 { +entry: + %this.addr = alloca %struct.B*, align 8 + store %struct.B* %this, %struct.B** %this.addr, align 8 + ; CHECK-NOT: @llvm.dbg.declare + call void @llvm.dbg.declare(metadata %struct.B** %this.addr, metadata !29, metadata !31), !dbg !32 + %this1 = load %struct.B*, %struct.B** %this.addr, align 8 + ; CHECK: call void @_ZN1BC2Ev(%struct.B* %this1){{.*}} !dbg ! + call void @_ZN1BC2Ev(%struct.B* %this1) #2, !dbg !33 + ret void, !dbg !33 +} + +attributes #0 = { nounwind readnone } +attributes #1 = { inlinehint nounwind ssp uwtable } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!1} +!llvm.module.flags = !{!20, !21, !22} +!llvm.ident = !{!23} + +; CHECK-NEG-NOT: !DI{{Basic|Composite|Derived}}Type + +!0 = distinct !DIGlobalVariable(name: "b", scope: !1, file: !2, line: 5, type: !5, isLocal: false, isDefinition: true) +!1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, producer: "clang version 4.0.0 (trunk 282583) (llvm/trunk 282611)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3, globals: !4) +!2 = !DIFile(filename: "t.cpp", directory: "/") +!3 = !{} +!4 = !{!0} +!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", file: !2, line: 4, size: 64, align: 64, elements: !6, vtableHolder: !8, identifier: "_ZTS1B") +!6 = !{!7} +!7 = !DIDerivedType(tag: DW_TAG_inheritance, scope: !5, baseType: !8) +!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !2, line: 1, size: 64, align: 64, elements: !9, vtableHolder: !8, identifier: "_ZTS1A") +!9 = !{!10, !16} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "_vptr$A", scope: !2, file: !2, baseType: !11, size: 64, flags: DIFlagArtificial) +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "__vtbl_ptr_type", baseType: !13, size: 64) +!13 = !DISubroutineType(types: !14) +!14 = !{!15} +!15 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +; Only referenced by the type system. +; CHECK-NEG-NOT: !DISubprogram(name: "~A" +!16 = !DISubprogram(name: "~A", scope: !8, file: !2, line: 2, type: !17, isLocal: false, isDefinition: false, scopeLine: 2, containingType: !8, virtuality: DW_VIRTUALITY_virtual, virtualIndex: 0, flags: DIFlagPrototyped, isOptimized: false) +!17 = !DISubroutineType(types: !18) +!18 = !{null, !19} +!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!20 = !{i32 2, !"Dwarf Version", i32 4} +!21 = !{i32 2, !"Debug Info Version", i32 3} +!22 = !{i32 1, !"PIC Level", i32 2} +!23 = !{!"clang version 4.0.0 (trunk 282583) (llvm/trunk 282611)"} +; CHECK: !DISubprogram(name: "B", scope: ![[FILE:.*]], file: ![[FILE]], +; CHECK-NOT: containingType: +!24 = distinct !DISubprogram(name: "B", linkageName: "_ZN1BC1Ev", scope: !5, file: !2, line: 4, type: !25, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagArtificial | DIFlagPrototyped, isOptimized: false, unit: !1, declaration: !28, variables: !3) +!25 = !DISubroutineType(types: !26) +!26 = !{null, !27} +!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +; CHECK-NEG-NOT: !DISubprogram(name: "B", {{.*}}, isDefinition: false +!28 = !DISubprogram(name: "B", scope: !5, type: !25, isLocal: false, isDefinition: false, flags: DIFlagArtificial | DIFlagPrototyped, isOptimized: false) +!29 = !DILocalVariable(name: "this", arg: 1, scope: !24, type: !30, flags: DIFlagArtificial | DIFlagObjectPointer) +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64, align: 64) +!31 = !DIExpression() +!32 = !DILocation(line: 0, scope: !24) +!33 = !DILocation(line: 4, column: 8, scope: !24) diff --git a/test/Transforms/Util/strip-nonlinetable-debuginfo-cus.ll b/test/Transforms/Util/strip-nonlinetable-debuginfo-cus.ll new file mode 100644 index 00000000000..f7ffdf9cf9a --- /dev/null +++ b/test/Transforms/Util/strip-nonlinetable-debuginfo-cus.ll @@ -0,0 +1,24 @@ +; RUN: opt -S -strip-nonlinetable-debuginfo %s -o - | FileCheck %s +!llvm.dbg.cu = !{!2, !6} +!llvm.gcov = !{!3} +!llvm.module.flags = !{!7} + +!1 = !DIFile(filename: "path/to/file", directory: "/path/to/dir") +; The first CU is used for the line table, the second one is a module skeleton +; and should be stripped. +; CHECK: !llvm.dbg.cu = !{![[CU:[0-9]+]]} +; CHECK: ![[CU]] = distinct !DICompileUnit({{.*}}"abc.debug"{{.*}}LineTablesOnly +; CHECK-NOT: retainedTypes: +; CHECK-SAME: ) +; CHECK-NOT: DICompositeType +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", + isOptimized: true, flags: "-O2", runtimeVersion: 2, + splitDebugFilename: "abc.debug", emissionKind: FullDebug, + retainedTypes: !4) +!3 = !{!"path/to/file.o", !2} +!4 = !{!5} +!5 = !DICompositeType(tag: DW_TAG_structure_type, file: !1, identifier: "ThisWillBeStripped") +!6 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", + splitDebugFilename: "abc.dwo", emissionKind: FullDebug, + dwoId: 1234) +!7 = !{i32 1, !"Debug Info Version", i32 3} diff --git a/test/Transforms/Util/strip-nonlinetable-debuginfo-localvars.ll b/test/Transforms/Util/strip-nonlinetable-debuginfo-localvars.ll new file mode 100644 index 00000000000..7ef6b1c8771 --- /dev/null +++ b/test/Transforms/Util/strip-nonlinetable-debuginfo-localvars.ll @@ -0,0 +1,36 @@ +; RUN: opt -S -strip-nonlinetable-debuginfo %s -o - | FileCheck %s +; CHECK: define void @f() !dbg ![[F:[0-9]+]] +define void @f() !dbg !4 { +entry: + %i = alloca i32, align 4 + ; CHECK-NOT: llvm.dbg.declare + call void @llvm.dbg.declare(metadata i32* %i, metadata !11, metadata !13), !dbg !14 + store i32 42, i32* %i, align 4, !dbg !14 + ret void, !dbg !15 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2) +!1 = !DIFile(filename: "f.c", directory: "/") +!2 = !{} +; CHECK: ![[F]] = distinct !DISubprogram(name: "f" +; CHECK-NOT: variables: +; CHECK-NOT: distinct !DISubprogram(name: "f" +!4 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, variables: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!7 = !{i32 2, !"Dwarf Version", i32 2} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"PIC Level", i32 2} +!10 = !{!"LLVM"} +!11 = !DILocalVariable(name: "i", scope: !4, file: !1, line: 1, type: !12) +!12 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!13 = !DIExpression() +!14 = !DILocation(line: 1, column: 16, scope: !4) +!15 = !DILocation(line: 1, column: 24, scope: !4) diff --git a/test/Transforms/Util/strip-nonlinetable-debuginfo-subroutinetypes.ll b/test/Transforms/Util/strip-nonlinetable-debuginfo-subroutinetypes.ll new file mode 100644 index 00000000000..4245a7b411a --- /dev/null +++ b/test/Transforms/Util/strip-nonlinetable-debuginfo-subroutinetypes.ll @@ -0,0 +1,22 @@ +; RUN: opt -S -strip-nonlinetable-debuginfo %s -o - | FileCheck %s +; Test that subroutine types are downgraded to (void)(). +define internal i32 @"__hidden#2878_"() #0 !dbg !12 { + ret i32 0, !dbg !634 +} +!llvm.dbg.cu = !{!18} +!llvm.module.flags = !{!482} +!5 = !{} +!2 = !{!12} +; CHECK-NOT: DICompositeType +; CHECK: distinct !DISubprogram(name: "f", {{.*}}, type: ![[FNTY:[0-9]+]] +; CHECK: ![[FNTY]] = !DISubroutineType(types: ![[VOID:[0-9]+]]) +; CHECK: ![[VOID]] = !{} +; CHECK-NOT: DICompositeType +!12 = distinct !DISubprogram(name: "f", scope: !16, file: !16, line: 133, type: !13, isLocal: true, isDefinition: true, scopeLine: 133, flags: DIFlagPrototyped, isOptimized: true, unit: !18, variables: !5) +!13 = !DISubroutineType(types: !14) +!14 = !{!17} +!16 = !DIFile(filename: "f.m", directory: "/") +!17 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "e", scope: !18, file: !16, line: 13, size: 32, align: 32, flags: DIFlagFwdDecl) +!18 = distinct !DICompileUnit(language: DW_LANG_ObjC, file: !16, producer: "clang", isOptimized: true, runtimeVersion: 2, emissionKind: 1, enums: !14, retainedTypes: !14, globals: !5, imports: !5) +!482 = !{i32 2, !"Debug Info Version", i32 3} +!634 = !DILocation(line: 143, column: 5, scope: !12) diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp index a472fff0975..0cae0669477 100644 --- a/tools/bugpoint/CrashDebugger.cpp +++ b/tools/bugpoint/CrashDebugger.cpp @@ -19,6 +19,7 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LegacyPassManager.h" @@ -54,6 +55,12 @@ cl::opt DontReducePassList("disable-pass-list-reduction", cl::opt NoNamedMDRM("disable-namedmd-remove", cl::desc("Do not remove global named metadata"), cl::init(false)); +cl::opt NoStripDebugInfo("disable-strip-debuginfo", + cl::desc("Do not strip debug info metadata"), + cl::init(false)); +cl::opt NoStripDebugTypeInfo("disable-strip-debug-types", + cl::desc("Do not strip debug type info metadata"), + cl::init(false)); cl::opt VerboseErrors("verbose-errors", cl::desc("Print the output of crashing program"), cl::init(false)); @@ -1123,6 +1130,22 @@ static Error DebugACrash(BugDriver &BD, if (Error E = ReduceInsts(BD, TestFn)) return E; + // Attempt to strip debug info metadata. + auto stripMetadata = [&](std::function strip) { + std::unique_ptr M = CloneModule(BD.getProgram()); + strip(*M); + if (TestFn(BD, M.get())) + BD.setNewProgram(M.release()); + }; + if (!NoStripDebugInfo && !BugpointIsInterrupted) { + outs() << "\n*** Attempting to strip the debug info: "; + stripMetadata(StripDebugInfo); + } + if (!NoStripDebugTypeInfo && !BugpointIsInterrupted) { + outs() << "\n*** Attempting to strip the debug type info: "; + stripMetadata(stripNonLineTableDebugInfo); + } + if (!NoNamedMDRM) { if (!BugpointIsInterrupted) { // Try to reduce the amount of global metadata (particularly debug info),