[llvm] r320202 - [Debugify] Add a pass to test debug info preservation

Vedant Kumar via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 8 13:57:28 PST 2017


Author: vedantk
Date: Fri Dec  8 13:57:28 2017
New Revision: 320202

URL: http://llvm.org/viewvc/llvm-project?rev=320202&view=rev
Log:
[Debugify] Add a pass to test debug info preservation

The Debugify pass synthesizes debug info for IR. It's paired with a
CheckDebugify pass which determines how much of the original debug info
is preserved. These passes make it easier to create targeted tests for
debug info preservation.

Here is the Debugify algorithm:

  NextLine = 1
  for (Instruction &I : M)
    attach DebugLoc(NextLine++) to I

  NextVar = 1
  for (Instruction &I : M)
    if (canAttachDebugValue(I))
      attach dbg.value(NextVar++) to I

The CheckDebugify pass expects contiguous ranges of DILocations and
DILocalVariables. If it fails to find all of the expected debug info, it
prints a specific error to stderr which can be FileChecked.

This was discussed on llvm-dev in the thread:
"Passes to add/validate synthetic debug info"

Differential Revision: https://reviews.llvm.org/D40512

Added:
    llvm/trunk/test/DebugInfo/debugify.ll
    llvm/trunk/tools/opt/Debugify.cpp
Modified:
    llvm/trunk/docs/ReleaseNotes.rst
    llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll
    llvm/trunk/tools/opt/CMakeLists.txt

Modified: llvm/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/ReleaseNotes.rst?rev=320202&r1=320201&r2=320202&view=diff
==============================================================================
--- llvm/trunk/docs/ReleaseNotes.rst (original)
+++ llvm/trunk/docs/ReleaseNotes.rst Fri Dec  8 13:57:28 2017
@@ -49,6 +49,11 @@ Non-comprehensive list of changes in thi
   the name used in the `def X : Target` definition to the call to
   `RegisterTarget`.
 
+* The ``Debugify`` pass was added to ``opt`` to facilitate testing of debug
+  info preservation. This pass attaches synthetic ``DILocations`` and
+  ``DIVariables`` to the instructions in a ``Module``. The ``CheckDebugify``
+  pass determines how much of the metadata is lost.
+
 * Note..
 
 .. NOTE

Added: llvm/trunk/test/DebugInfo/debugify.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/DebugInfo/debugify.ll?rev=320202&view=auto
==============================================================================
--- llvm/trunk/test/DebugInfo/debugify.ll (added)
+++ llvm/trunk/test/DebugInfo/debugify.ll Fri Dec  8 13:57:28 2017
@@ -0,0 +1,65 @@
+; RUN: opt -debugify -S -o - < %s | FileCheck %s
+
+; RUN: opt -debugify -debugify -S -o - < %s 2>&1 | \
+; RUN:   FileCheck %s -check-prefix=CHECK-REPEAT
+
+; RUN: opt -debugify -check-debugify -S -o - < %s | \
+; RUN:   FileCheck %s -implicit-check-not="CheckDebugify: FAIL"
+
+; RUN: opt -debugify -strip -check-debugify -S -o - < %s | \
+; RUN:   FileCheck %s -check-prefix=CHECK-FAIL
+
+; CHECK-LABEL: define void @foo
+define void @foo() {
+; CHECK: ret void, !dbg ![[RET1:.*]]
+  ret void
+}
+
+; CHECK-LABEL: define i32 @bar
+define i32 @bar() {
+; CHECK: call void @foo(), !dbg ![[CALL1:.*]]
+  call void @foo()
+
+; CHECK: add i32 0, 1, !dbg ![[ADD1:.*]]
+  %sum = add i32 0, 1
+
+; CHECK: ret i32 0, !dbg ![[RET2:.*]]
+  ret i32 0
+}
+
+; CHECK-DAG: !llvm.dbg.cu = !{![[CU:.*]]}
+; CHECK-DAG: !llvm.debugify = !{![[NUM_INSTS:.*]], ![[NUM_VARS:.*]]}
+
+; CHECK-DAG: ![[CU]] = distinct !DICompileUnit(language: DW_LANG_C, file: {{.*}}, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: {{.*}})
+; CHECK-DAG: !DIFile(filename: "<stdin>", directory: "/")
+; CHECK-DAG: distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: {{.*}}, line: 1, type: {{.*}}, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: {{.*}}, variables: {{.*}})
+; CHECK-DAG: distinct !DISubprogram(name: "bar", linkageName: "bar", scope: null, file: {{.*}}, line: 2, type: {{.*}}, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: true, unit: {{.*}}, variables: {{.*}})
+
+; --- DILocations
+; CHECK-DAG: ![[RET1]] = !DILocation(line: 1, column: 1
+; CHECK-DAG: ![[CALL1]] = !DILocation(line: 2, column: 1
+; CHECK-DAG: ![[ADD1]] = !DILocation(line: 3, column: 1
+; CHECK-DAG: ![[RET2]] = !DILocation(line: 4, column: 1
+
+; --- DILocalVariables
+; CHECK-DAG: ![[TY32:.*]] = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
+; CHECK-DAG: !DILocalVariable(name: "1", scope: {{.*}}, file: {{.*}}, line: 3, type: ![[TY32]])
+
+; --- Metadata counts
+; CHECK-DAG: ![[NUM_INSTS]] = !{i32 4}
+; CHECK-DAG: ![[NUM_VARS]] = !{i32 1}
+
+; --- Repeat case
+; CHECK-REPEAT: Debugify: Skipping module with debug info
+
+; --- Failure case
+; CHECK-FAIL: ERROR: Instruction with empty DebugLoc --   ret void
+; CHECK-FAIL: ERROR: Instruction with empty DebugLoc --   call void @foo()
+; CHECK-FAIL: ERROR: Instruction with empty DebugLoc --   {{.*}} add i32 0, 1
+; CHECK-FAIL: ERROR: Instruction with empty DebugLoc --   ret i32 0
+; CHECK-FAIL: WARNING: Missing line 1
+; CHECK-FAIL: WARNING: Missing line 2
+; CHECK-FAIL: WARNING: Missing line 3
+; CHECK-FAIL: WARNING: Missing line 4
+; CHECK-FAIL: ERROR: Missing variable 1
+; CHECK-FAIL: CheckDebugify: FAIL

Modified: llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll?rev=320202&r1=320201&r2=320202&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll (original)
+++ llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll Fri Dec  8 13:57:28 2017
@@ -1,5 +1,8 @@
 ; Simple sanity check testcase.  Both alloca's should be eliminated.
-; RUN: opt < %s -mem2reg -S | not grep alloca
+; RUN: opt < %s -debugify -mem2reg -check-debugify -S | FileCheck %s
+
+; CHECK-NOT: alloca
+; CHECK: CheckDebugify: PASS
 
 define double @testfunc(i32 %i, double %j) {
 	%I = alloca i32		; <i32*> [#uses=4]

Modified: llvm/trunk/tools/opt/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/CMakeLists.txt?rev=320202&r1=320201&r2=320202&view=diff
==============================================================================
--- llvm/trunk/tools/opt/CMakeLists.txt (original)
+++ llvm/trunk/tools/opt/CMakeLists.txt Fri Dec  8 13:57:28 2017
@@ -25,6 +25,7 @@ set(LLVM_NO_DEAD_STRIP 1)
 add_llvm_tool(opt
   AnalysisWrappers.cpp
   BreakpointPrinter.cpp
+  Debugify.cpp
   GraphPrinters.cpp
   NewPMDriver.cpp
   PassPrinters.cpp

Added: llvm/trunk/tools/opt/Debugify.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/Debugify.cpp?rev=320202&view=auto
==============================================================================
--- llvm/trunk/tools/opt/Debugify.cpp (added)
+++ llvm/trunk/tools/opt/Debugify.cpp Fri Dec  8 13:57:28 2017
@@ -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");




More information about the llvm-commits mailing list