aboutsummaryrefslogtreecommitdiff
path: root/tools/opt/Debugify.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-12-18 20:10:56 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-12-18 20:10:56 +0000
commit044eb2f6afba375a914ac9d8024f8f5142bb912e (patch)
tree1475247dc9f9fe5be155ebd4c9069c75aadf8c20 /tools/opt/Debugify.cpp
parenteb70dddbd77e120e5d490bd8fbe7ff3f8fa81c6b (diff)
downloadsrc-044eb2f6afba375a914ac9d8024f8f5142bb912e.tar.gz
src-044eb2f6afba375a914ac9d8024f8f5142bb912e.zip
Vendor import of llvm trunk r321017:vendor/llvm/llvm-trunk-r321017
Notes
Notes: svn path=/vendor/llvm/dist/; revision=326938 svn path=/vendor/llvm/llvm-trunk-r321017/; revision=326939; tag=vendor/llvm/llvm-trunk-r321017
Diffstat (limited to 'tools/opt/Debugify.cpp')
-rw-r--r--tools/opt/Debugify.cpp212
1 files changed, 212 insertions, 0 deletions
diff --git a/tools/opt/Debugify.cpp b/tools/opt/Debugify.cpp
new file mode 100644
index 000000000000..40ee545c098d
--- /dev/null
+++ b/tools/opt/Debugify.cpp
@@ -0,0 +1,212 @@
+//===- Debugify.cpp - Attach synthetic debug info to everything -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This pass attaches synthetic debug info to everything. It can be used
+/// to create targeted tests for debug info preservation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DIBuilder.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/IPO.h"
+
+using namespace llvm;
+
+namespace {
+
+bool applyDebugifyMetadata(Module &M) {
+ // Skip modules with debug info.
+ if (M.getNamedMetadata("llvm.dbg.cu")) {
+ errs() << "Debugify: Skipping module with debug info\n";
+ return false;
+ }
+
+ DIBuilder DIB(M);
+ LLVMContext &Ctx = M.getContext();
+
+ // Get a DIType which corresponds to Ty.
+ DenseMap<uint64_t, DIType *> TypeCache;
+ auto getCachedDIType = [&](Type *Ty) -> DIType * {
+ uint64_t Size = M.getDataLayout().getTypeAllocSizeInBits(Ty);
+ DIType *&DTy = TypeCache[Size];
+ if (!DTy) {
+ std::string Name = "ty" + utostr(Size);
+ DTy = DIB.createBasicType(Name, Size, dwarf::DW_ATE_unsigned);
+ }
+ return DTy;
+ };
+
+ unsigned NextLine = 1;
+ unsigned NextVar = 1;
+ auto File = DIB.createFile(M.getName(), "/");
+ auto CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile(M.getName(), "/"),
+ "debugify", /*isOptimized=*/true, "", 0);
+
+ // Visit each instruction.
+ for (Function &F : M) {
+ if (F.isDeclaration())
+ continue;
+
+ auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
+ bool IsLocalToUnit = F.hasPrivateLinkage() || F.hasInternalLinkage();
+ auto SP =
+ DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, SPType,
+ IsLocalToUnit, F.hasExactDefinition(), NextLine,
+ DINode::FlagZero, /*isOptimized=*/true);
+ F.setSubprogram(SP);
+ for (BasicBlock &BB : F) {
+ // Attach debug locations.
+ for (Instruction &I : BB)
+ I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP));
+
+ // Attach debug values.
+ for (Instruction &I : BB) {
+ // Skip void-valued instructions.
+ if (I.getType()->isVoidTy())
+ continue;
+
+ // Skip the terminator instruction and any just-inserted intrinsics.
+ if (isa<TerminatorInst>(&I) || isa<DbgValueInst>(&I))
+ break;
+
+ std::string Name = utostr(NextVar++);
+ const DILocation *Loc = I.getDebugLoc().get();
+ auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(),
+ getCachedDIType(I.getType()),
+ /*AlwaysPreserve=*/true);
+ DIB.insertDbgValueIntrinsic(&I, LocalVar, DIB.createExpression(), Loc,
+ BB.getTerminator());
+ }
+ }
+ DIB.finalizeSubprogram(SP);
+ }
+ DIB.finalize();
+
+ // Track the number of distinct lines and variables.
+ NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify");
+ auto *IntTy = Type::getInt32Ty(Ctx);
+ auto addDebugifyOperand = [&](unsigned N) {
+ NMD->addOperand(MDNode::get(
+ Ctx, ValueAsMetadata::getConstant(ConstantInt::get(IntTy, N))));
+ };
+ addDebugifyOperand(NextLine - 1); // Original number of lines.
+ addDebugifyOperand(NextVar - 1); // Original number of variables.
+ return true;
+}
+
+void checkDebugifyMetadata(Module &M) {
+ // Skip modules without debugify metadata.
+ NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify");
+ if (!NMD)
+ return;
+
+ auto getDebugifyOperand = [&](unsigned Idx) -> unsigned {
+ return mdconst::extract<ConstantInt>(NMD->getOperand(Idx)->getOperand(0))
+ ->getZExtValue();
+ };
+ unsigned OriginalNumLines = getDebugifyOperand(0);
+ unsigned OriginalNumVars = getDebugifyOperand(1);
+ bool HasErrors = false;
+
+ // Find missing lines.
+ BitVector MissingLines{OriginalNumLines, true};
+ for (Function &F : M) {
+ for (Instruction &I : instructions(F)) {
+ if (isa<DbgValueInst>(&I))
+ continue;
+
+ auto DL = I.getDebugLoc();
+ if (DL) {
+ MissingLines.reset(DL.getLine() - 1);
+ continue;
+ }
+
+ outs() << "ERROR: Instruction with empty DebugLoc -- ";
+ I.print(outs());
+ outs() << "\n";
+ HasErrors = true;
+ }
+ }
+ for (unsigned Idx : MissingLines.set_bits())
+ outs() << "WARNING: Missing line " << Idx + 1 << "\n";
+
+ // Find missing variables.
+ BitVector MissingVars{OriginalNumVars, true};
+ for (Function &F : M) {
+ for (Instruction &I : instructions(F)) {
+ auto *DVI = dyn_cast<DbgValueInst>(&I);
+ if (!DVI)
+ continue;
+
+ unsigned Var = ~0U;
+ (void)to_integer(DVI->getVariable()->getName(), Var, 10);
+ assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable");
+ MissingVars.reset(Var - 1);
+ }
+ }
+ for (unsigned Idx : MissingVars.set_bits())
+ outs() << "ERROR: Missing variable " << Idx + 1 << "\n";
+ HasErrors |= MissingVars.count() > 0;
+
+ outs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n";
+}
+
+/// Attach synthetic debug info to everything.
+struct DebugifyPass : public ModulePass {
+ bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); }
+
+ DebugifyPass() : ModulePass(ID) {}
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+
+ static char ID; // Pass identification.
+};
+
+/// Check debug info inserted by -debugify for completeness.
+struct CheckDebugifyPass : public ModulePass {
+ bool runOnModule(Module &M) override {
+ checkDebugifyMetadata(M);
+ return false;
+ }
+
+ CheckDebugifyPass() : ModulePass(ID) {}
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+
+ static char ID; // Pass identification.
+};
+
+} // end anonymous namespace
+
+char DebugifyPass::ID = 0;
+static RegisterPass<DebugifyPass> X("debugify",
+ "Attach debug info to everything");
+
+char CheckDebugifyPass::ID = 0;
+static RegisterPass<CheckDebugifyPass> Y("check-debugify",
+ "Check debug info from -debugify");