Skip to content

Commit

Permalink
Add -strip-nonlinetable-debuginfo capability
Browse files Browse the repository at this point in the history
This adds a new function to DebugInfo.cpp that takes an llvm::Module
as input and removes all debug info metadata that is not directly
needed for line tables, thus effectively stripping all type and
variable information from the module.

The primary motivation for this feature was the bitcode work flow
(cf. http://lists.llvm.org/pipermail/llvm-dev/2016-June/100643.html
for more background). This is not wired up yet, but will be in
subsequent patches.  For testing, the new functionality is exposed to
opt with a -strip-nonlinetable-debuginfo option.

The secondary use-case (and one that works right now!) is as a
reduction pass in bugpoint. I added two new bugpoint options
(-disable-strip-debuginfo and -disable-strip-debug-types) to control
the new features. By default it will first attempt to remove all debug
information, then only the type info, and then proceed to hack at any
remaining MDNodes.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283473 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
milseman committed Oct 6, 2016
1 parent de50b32 commit bda4e02
Show file tree
Hide file tree
Showing 14 changed files with 584 additions and 5 deletions.
12 changes: 12 additions & 0 deletions include/llvm/IR/DebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions include/llvm/InitializePasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,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&);
Expand Down
4 changes: 4 additions & 0 deletions include/llvm/Transforms/IPO.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
309 changes: 309 additions & 0 deletions lib/IR/DebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,315 @@ bool llvm::StripDebugInfo(Module &M) {
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;
if (Replacements.count(M))
return Replacements.lookup(M);

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();
auto Result = Opened.insert(N);
if (!Result.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.
if (Function *Declare = M.getFunction("llvm.dbg.declare")) {
while (!Declare->use_empty()) {
auto *DDI = cast<DbgDeclareInst>(Declare->user_back());
DDI->eraseFromParent();
}
Declare->eraseFromParent();
Changed = true;
}
if (Function *DbgVal = M.getFunction("llvm.dbg.value")) {
while (!DbgVal->use_empty()) {
auto *DVI = cast<DbgValueInst>(DbgVal->user_back());
DVI->eraseFromParent();
}
DbgVal->eraseFromParent();
Changed = true;
}

// 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.getContext().pImpl->GlobalObjectMetadata[&GV].erase(LLVMContext::MD_dbg);

DebugTypeInfoRemoval Mapper(M.getContext());

// Rewrite the DebugLocs to be equivalent to what
// -gline-tables-only would have created.
for (auto &F : M) {
auto *SP = F.getSubprogram();
if (SP) {
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();
auto *InlinedAt = DL.getInlinedAt();

// Remap scope.
if (Scope) {
Mapper.traverseAndRemap(Scope);
auto *NewScope = Mapper.mapNode(Scope);
Changed |= Scope != NewScope;
Scope = NewScope;
}

// Remap inlinedAt.
if (InlinedAt) {
Mapper.traverseAndRemap(InlinedAt);
auto *NewIA = Mapper.mapNode(InlinedAt);
Changed |= InlinedAt != NewIA;
InlinedAt = cast_or_null<DILocation>(NewIA);
}

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()) {
Mapper.traverseAndRemap(Op);
auto *NewOp = Mapper.mapNode(Op);
Changed |= NewOp != Op;
Ops.push_back(NewOp);
}

if (Changed) {
NMD.dropAllReferences();
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")))
Expand Down
1 change: 1 addition & 0 deletions lib/Transforms/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ add_llvm_library(LLVMTransformUtils
SimplifyInstructions.cpp
SimplifyLibCalls.cpp
SplitModule.cpp
StripNonLineTableDebugInfo.cpp
SymbolRewriter.cpp
UnifyFunctionExitNodes.cpp
Utils.cpp
Expand Down
Loading

0 comments on commit bda4e02

Please sign in to comment.