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);
/// \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();
void initializeStripDeadPrototypesLegacyPassPass(PassRegistry&);
void initializeStripDebugDeclarePass(PassRegistry&);
void initializeStripNonDebugSymbolsPass(PassRegistry&);
+void initializeStripNonLineTableDebugInfoPass(PassRegistry&);
void initializeStripSymbolsPass(PassRegistry&);
void initializeStructurizeCFGPass(PassRegistry&);
void initializeTailCallElimPass(PassRegistry&);
//
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.
return Changed;
}
+namespace {
+
+/// Helper class to downgrade -g metadata to -gline-tables-only metadata.
+class DebugTypeInfoRemoval {
+ DenseMap<Metadata *, Metadata *> 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<DISubprogram *, StringRef> NewToLinkageName;
+
+ // TODO: Remember the distinct subprogram we created for a given linkage name,
+ // so that we can continue to unique whenever possible. Map <newly created
+ // node, old linkage name> 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<MDNode>(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<DIFile>(map(MDS->getFile()));
+ StringRef LinkageName = MDS->getName().empty() ? MDS->getLinkageName() : "";
+ DISubprogram *Declaration = nullptr;
+ auto *Type = cast_or_null<DISubroutineType>(map(MDS->getType()));
+ DITypeRef ContainingType(map(MDS->getContainingType()));
+ auto *Unit = cast_or_null<DICompileUnit>(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<DIFile>(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<Metadata *, 8> 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<DISubprogram>(N)) {
+ remap(MDSub->getUnit());
+ return getReplacementSubprogram(MDSub);
+ }
+ if (isa<DISubroutineType>(N))
+ return EmptySubroutineType;
+ if (auto *CU = dyn_cast<DICompileUnit>(N))
+ return getReplacementCU(CU);
+ if (isa<DIFile>(N))
+ return N;
+ if (auto *MDLB = dyn_cast<DILexicalBlockBase>(N))
+ // Remap to our referenced scope (recursively).
+ return mapNode(MDLB->getScope());
+ if (auto *MLD = dyn_cast<DILocation>(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<DINode>(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<DISubprogram>(Parent))
+ return Child == MDS->getVariables().get();
+ return false;
+ };
+
+ SmallVector<MDNode *, 16> ToVisit;
+ DenseSet<MDNode *> 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<MDNode>(I))
+ if (!Opened.count(MDN) && !Replacements.count(MDN) && !prune(N, MDN) &&
+ !isa<DICompileUnit>(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<Instruction>(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<DISubprogram>(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<MDNode *, 8> 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<ConstantInt>(
M.getModuleFlag("Debug Info Version")))
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); }
SimplifyInstructions.cpp
SimplifyLibCalls.cpp
SplitModule.cpp
+ StripNonLineTableDebugInfo.cpp
SymbolRewriter.cpp
UnifyFunctionExitNodes.cpp
Utils.cpp
--- /dev/null
+//===- 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();
+}
initializeLowerSwitchPass(Registry);
initializeNameAnonGlobalLegacyPassPass(Registry);
initializePromoteLegacyPassPass(Registry);
+ initializeStripNonLineTableDebugInfoPass(Registry);
initializeUnifyFunctionExitNodesPass(Registry);
initializeInstSimplifierPass(Registry);
initializeMetaRenamerPass(Registry);
-; 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")
!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)
!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)
-; 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
--- /dev/null
+; 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)
--- /dev/null
+; 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}
--- /dev/null
+; 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)
--- /dev/null
+; 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)
#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"
cl::opt<bool> NoNamedMDRM("disable-namedmd-remove",
cl::desc("Do not remove global named metadata"),
cl::init(false));
+cl::opt<bool> NoStripDebugInfo("disable-strip-debuginfo",
+ cl::desc("Do not strip debug info metadata"),
+ cl::init(false));
+cl::opt<bool> NoStripDebugTypeInfo("disable-strip-debug-types",
+ cl::desc("Do not strip debug type info metadata"),
+ cl::init(false));
cl::opt<bool> VerboseErrors("verbose-errors",
cl::desc("Print the output of crashing program"),
cl::init(false));
if (Error E = ReduceInsts(BD, TestFn))
return E;
+ // Attempt to strip debug info metadata.
+ auto stripMetadata = [&](std::function<bool(Module &)> strip) {
+ std::unique_ptr<Module> 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),