[llvm] r332312 - [Debugify] Add -debugify-each for testing each pass in a pipeline

Vedant Kumar via llvm-commits llvm-commits at lists.llvm.org
Mon May 14 17:29:27 PDT 2018


Author: vedantk
Date: Mon May 14 17:29:27 2018
New Revision: 332312

URL: http://llvm.org/viewvc/llvm-project?rev=332312&view=rev
Log:
[Debugify] Add -debugify-each for testing each pass in a pipeline

This adds a -debugify-each mode to opt which, when enabled, wraps each
{Module,Function}Pass in a pipeline with logic to add, check, and strip
synthetic debug info for testing purposes.

This mode can be used to test complex pipelines for debug info bugs, or
to collect statistics about the number of debug values & locations lost
throughout various stages of a pipeline.

Patch by Son Tuan Vu!

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

Added:
    llvm/trunk/test/DebugInfo/debugify-each.ll
Modified:
    llvm/trunk/include/llvm/IR/IRPrintingPasses.h
    llvm/trunk/lib/IR/IRPrintingPasses.cpp
    llvm/trunk/test/DebugInfo/debugify.ll
    llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll
    llvm/trunk/tools/opt/Debugify.cpp
    llvm/trunk/tools/opt/PassPrinters.h
    llvm/trunk/tools/opt/opt.cpp

Modified: llvm/trunk/include/llvm/IR/IRPrintingPasses.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/IRPrintingPasses.h?rev=332312&r1=332311&r2=332312&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/IRPrintingPasses.h (original)
+++ llvm/trunk/include/llvm/IR/IRPrintingPasses.h Mon May 14 17:29:27 2018
@@ -23,6 +23,7 @@
 #include <string>
 
 namespace llvm {
+class Pass;
 class BasicBlockPass;
 class Function;
 class FunctionPass;
@@ -54,6 +55,9 @@ BasicBlockPass *createPrintBasicBlockPas
 /// non-printable characters in it.
 void printLLVMNameWithoutPrefix(raw_ostream &OS, StringRef Name);
 
+/// Return true if a pass is for IR printing.
+bool isIRPrintingPass(Pass *P);
+
 /// Pass for printing a Module as LLVM's text IR assembly.
 ///
 /// Note: This pass is for use with the new pass manager. Use the create...Pass

Modified: llvm/trunk/lib/IR/IRPrintingPasses.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/IRPrintingPasses.cpp?rev=332312&r1=332311&r2=332312&view=diff
==============================================================================
--- llvm/trunk/lib/IR/IRPrintingPasses.cpp (original)
+++ llvm/trunk/lib/IR/IRPrintingPasses.cpp Mon May 14 17:29:27 2018
@@ -150,3 +150,11 @@ BasicBlockPass *llvm::createPrintBasicBl
                                                 const std::string &Banner) {
   return new PrintBasicBlockPass(OS, Banner);
 }
+
+bool llvm::isIRPrintingPass(Pass *P) {
+  const char *PID = (const char*)P->getPassID();
+
+  return (PID == &PrintModulePassWrapper::ID)
+      || (PID == &PrintFunctionPassWrapper::ID)
+      || (PID == &PrintBasicBlockPass::ID);
+}

Added: llvm/trunk/test/DebugInfo/debugify-each.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/DebugInfo/debugify-each.ll?rev=332312&view=auto
==============================================================================
--- llvm/trunk/test/DebugInfo/debugify-each.ll (added)
+++ llvm/trunk/test/DebugInfo/debugify-each.ll Mon May 14 17:29:27 2018
@@ -0,0 +1,24 @@
+; RUN: opt -debugify-each -O3 -S -o - < %s | FileCheck %s
+; RUN: opt -debugify-each -instrprof -sroa -sccp -S -o - < %s | FileCheck %s
+
+define void @foo() {
+  ret void
+}
+
+define void @bar() {
+  ret void
+}
+
+; Verify that the module & function (check-)debugify passes run at least twice.
+
+; CHECK-DAG: CheckModuleDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS
+
+; CHECK-DAG: CheckModuleDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS
+; CHECK-DAG: CheckFunctionDebugify: PASS

Modified: llvm/trunk/test/DebugInfo/debugify.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/DebugInfo/debugify.ll?rev=332312&r1=332311&r2=332312&view=diff
==============================================================================
--- llvm/trunk/test/DebugInfo/debugify.ll (original)
+++ llvm/trunk/test/DebugInfo/debugify.ll Mon May 14 17:29:27 2018
@@ -7,11 +7,11 @@
 ; RUN:   FileCheck %s -check-prefix=CHECK-REPEAT
 
 ; RUN: opt -debugify -check-debugify -S -o - < %s | \
-; RUN:   FileCheck %s -implicit-check-not="CheckDebugify: FAIL"
+; RUN:   FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL"
 ; RUN: opt -passes=debugify,check-debugify -S -o - < %s | \
-; RUN:   FileCheck %s -implicit-check-not="CheckDebugify: FAIL"
+; RUN:   FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL"
 ; RUN: opt -enable-debugify -passes=verify -S -o - < %s | \
-; RUN:   FileCheck %s -implicit-check-not="CheckDebugify: FAIL"
+; RUN:   FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL"
 
 ; RUN: opt -debugify -strip -check-debugify -S -o - < %s | \
 ; RUN:   FileCheck %s -check-prefix=CHECK-FAIL
@@ -68,18 +68,18 @@ define weak_odr zeroext i1 @baz() {
 ; CHECK-DAG: ![[NUM_VARS]] = !{i32 1}
 
 ; --- Repeat case
-; CHECK-REPEAT: Debugify: Skipping module with debug info
+; CHECK-REPEAT: ModuleDebugify: 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: ERROR: Instruction with empty DebugLoc in function foo --   ret void
+; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar --   call void @foo()
+; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar --   {{.*}} add i32 0, 1
+; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar --   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
+; CHECK-FAIL: CheckModuleDebugify: FAIL
 
-; PASS: CheckDebugify: PASS
+; PASS: CheckModuleDebugify: PASS

Modified: llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll?rev=332312&r1=332311&r2=332312&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll (original)
+++ llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll Mon May 14 17:29:27 2018
@@ -2,7 +2,7 @@
 ; RUN: opt < %s -debugify -mem2reg -check-debugify -S | FileCheck %s
 
 ; CHECK-NOT: alloca
-; CHECK: CheckDebugify: PASS
+; CHECK: CheckModuleDebugify: PASS
 
 define double @testfunc(i32 %i, double %j) {
 	%I = alloca i32		; <i32*> [#uses=4]

Modified: llvm/trunk/tools/opt/Debugify.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/Debugify.cpp?rev=332312&r1=332311&r2=332312&view=diff
==============================================================================
--- llvm/trunk/tools/opt/Debugify.cpp (original)
+++ llvm/trunk/tools/opt/Debugify.cpp Mon May 14 17:29:27 2018
@@ -39,10 +39,12 @@ bool isFunctionSkipped(Function &F) {
   return F.isDeclaration() || !F.hasExactDefinition();
 }
 
-bool applyDebugifyMetadata(Module &M) {
+bool applyDebugifyMetadata(Module &M,
+                           iterator_range<Module::iterator> Functions,
+                           StringRef Banner) {
   // Skip modules with debug info.
   if (M.getNamedMetadata("llvm.dbg.cu")) {
-    errs() << "Debugify: Skipping module with debug info\n";
+    errs() << Banner << "Skipping module with debug info\n";
     return false;
   }
 
@@ -65,12 +67,11 @@ bool applyDebugifyMetadata(Module &M) {
   unsigned NextLine = 1;
   unsigned NextVar = 1;
   auto File = DIB.createFile(M.getName(), "/");
-  auto CU =
-      DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile(M.getName(), "/"),
+  auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File,
                             "debugify", /*isOptimized=*/true, "", 0);
 
   // Visit each instruction.
-  for (Function &F : M) {
+  for (Function &F : Functions) {
     if (isFunctionSkipped(F))
       continue;
 
@@ -118,54 +119,57 @@ bool applyDebugifyMetadata(Module &M) {
   };
   addDebugifyOperand(NextLine - 1); // Original number of lines.
   addDebugifyOperand(NextVar - 1);  // Original number of variables.
+  assert(NMD->getNumOperands() == 2 &&
+         "llvm.debugify should have exactly 2 operands!");
   return true;
 }
 
-void checkDebugifyMetadata(Module &M) {
+bool checkDebugifyMetadata(Module &M,
+                           iterator_range<Module::iterator> Functions,
+                           StringRef Banner,
+                           bool Strip) {
   // Skip modules without debugify metadata.
   NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify");
-  if (!NMD)
-    return;
+  if (!NMD) {
+    errs() << Banner << "Skipping module without debugify metadata\n";
+    return false;
+  }
 
   auto getDebugifyOperand = [&](unsigned Idx) -> unsigned {
     return mdconst::extract<ConstantInt>(NMD->getOperand(Idx)->getOperand(0))
         ->getZExtValue();
   };
+  assert(NMD->getNumOperands() == 2 &&
+         "llvm.debugify should have exactly 2 operands!");
   unsigned OriginalNumLines = getDebugifyOperand(0);
   unsigned OriginalNumVars = getDebugifyOperand(1);
   bool HasErrors = false;
 
-  // Find missing lines.
   BitVector MissingLines{OriginalNumLines, true};
-  for (Function &F : M) {
+  BitVector MissingVars{OriginalNumVars, true};
+  for (Function &F : Functions) {
     if (isFunctionSkipped(F))
       continue;
 
+    // Find missing lines.
     for (Instruction &I : instructions(F)) {
       if (isa<DbgValueInst>(&I))
         continue;
 
       auto DL = I.getDebugLoc();
-      if (DL) {
+      if (DL && DL.getLine() != 0) {
         MissingLines.reset(DL.getLine() - 1);
         continue;
       }
 
-      outs() << "ERROR: Instruction with empty DebugLoc -- ";
+      outs() << "ERROR: Instruction with empty DebugLoc in function ";
+      outs() << F.getName() << " --";
       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) {
-    if (isFunctionSkipped(F))
-      continue;
 
+    // Find missing variables.
     for (Instruction &I : instructions(F)) {
       auto *DVI = dyn_cast<DbgValueInst>(&I);
       if (!DVI)
@@ -177,18 +181,58 @@ void checkDebugifyMetadata(Module &M) {
       MissingVars.reset(Var - 1);
     }
   }
+
+  // Print the results.
+  for (unsigned Idx : MissingLines.set_bits())
+    outs() << "WARNING: Missing line " << Idx + 1 << "\n";
+
   for (unsigned Idx : MissingVars.set_bits())
     outs() << "ERROR: Missing variable " << Idx + 1 << "\n";
   HasErrors |= MissingVars.count() > 0;
 
-  outs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n";
+  outs() << Banner << (HasErrors ? "FAIL" : "PASS") << '\n';
+  if (HasErrors) {
+    outs() << "Module IR Dump\n";
+    M.print(outs(), nullptr, false);
+  }
+
+  // Strip the Debugify Metadata if required.
+  if (Strip) {
+    StripDebugInfo(M);
+    M.eraseNamedMetadata(NMD);
+    return true;
+  }
+
+  return false;
 }
 
-/// Attach synthetic debug info to everything.
-struct DebugifyPass : public ModulePass {
-  bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); }
+/// ModulePass for attaching synthetic debug info to everything, used with the
+/// legacy module pass manager.
+struct DebugifyModulePass : public ModulePass {
+  bool runOnModule(Module &M) override {
+    return applyDebugifyMetadata(M, M.functions(), "ModuleDebugify: ");
+  }
+
+  DebugifyModulePass() : ModulePass(ID) {}
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesAll();
+  }
+
+  static char ID; // Pass identification.
+};
+
+/// FunctionPass for attaching synthetic debug info to instructions within a
+/// single function, used with the legacy module pass manager.
+struct DebugifyFunctionPass : public FunctionPass {
+  bool runOnFunction(Function &F) override {
+    Module &M = *F.getParent();
+    auto FuncIt = F.getIterator();
+    return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
+                     "FunctionDebugify: ");
+  }
 
-  DebugifyPass() : ModulePass(ID) {}
+  DebugifyFunctionPass() : FunctionPass(ID) {}
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesAll();
@@ -197,43 +241,85 @@ struct DebugifyPass : public ModulePass
   static char ID; // Pass identification.
 };
 
-/// Check debug info inserted by -debugify for completeness.
-struct CheckDebugifyPass : public ModulePass {
+/// ModulePass for checking debug info inserted by -debugify, used with the
+/// legacy module pass manager.
+struct CheckDebugifyModulePass : public ModulePass {
   bool runOnModule(Module &M) override {
-    checkDebugifyMetadata(M);
-    return false;
+    return checkDebugifyMetadata(M, M.functions(), "CheckModuleDebugify: ",
+                                 Strip);
   }
 
-  CheckDebugifyPass() : ModulePass(ID) {}
+  CheckDebugifyModulePass(bool Strip = false) : ModulePass(ID), Strip(Strip) {}
+
+  static char ID; // Pass identification.
+
+private:
+  bool Strip;
+};
+
+/// FunctionPass for checking debug info inserted by -debugify-function, used
+/// with the legacy module pass manager.
+struct CheckDebugifyFunctionPass : public FunctionPass {
+  bool runOnFunction(Function &F) override {
+    Module &M = *F.getParent();
+    auto FuncIt = F.getIterator();
+    return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
+                     "CheckFunctionDebugify: ", Strip);
+  }
+
+  CheckDebugifyFunctionPass(bool Strip = false) : FunctionPass(ID), Strip(Strip) {}
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesAll();
   }
 
   static char ID; // Pass identification.
+
+private:
+  bool Strip;
 };
 
 } // end anonymous namespace
 
-ModulePass *createDebugifyPass() { return new DebugifyPass(); }
+ModulePass *createDebugifyModulePass() {
+  return new DebugifyModulePass();
+}
+
+FunctionPass *createDebugifyFunctionPass() {
+  return new DebugifyFunctionPass();
+}
 
 PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) {
-  applyDebugifyMetadata(M);
+  applyDebugifyMetadata(M, M.functions(), "ModuleDebugify: ");
   return PreservedAnalyses::all();
 }
 
-ModulePass *createCheckDebugifyPass() { return new CheckDebugifyPass(); }
+ModulePass *createCheckDebugifyModulePass(bool Strip) {
+  return new CheckDebugifyModulePass(Strip);
+}
+
+FunctionPass *createCheckDebugifyFunctionPass(bool Strip) {
+  return new CheckDebugifyFunctionPass(Strip);
+}
 
 PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M,
                                               ModuleAnalysisManager &) {
-  checkDebugifyMetadata(M);
+  checkDebugifyMetadata(M, M.functions(), "CheckModuleDebugify: ", false);
   return PreservedAnalyses::all();
 }
 
-char DebugifyPass::ID = 0;
-static RegisterPass<DebugifyPass> X("debugify",
+char DebugifyModulePass::ID = 0;
+static RegisterPass<DebugifyModulePass> DM("debugify",
                                     "Attach debug info to everything");
 
-char CheckDebugifyPass::ID = 0;
-static RegisterPass<CheckDebugifyPass> Y("check-debugify",
+char CheckDebugifyModulePass::ID = 0;
+static RegisterPass<CheckDebugifyModulePass> CDM("check-debugify",
                                          "Check debug info from -debugify");
+
+char DebugifyFunctionPass::ID = 0;
+static RegisterPass<DebugifyFunctionPass> DF("debugify-function",
+                                    "Attach debug info to a function");
+
+char CheckDebugifyFunctionPass::ID = 0;
+static RegisterPass<CheckDebugifyFunctionPass> CDF("check-debugify-function",
+                                         "Check debug info from -debugify-function");

Modified: llvm/trunk/tools/opt/PassPrinters.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/PassPrinters.h?rev=332312&r1=332311&r2=332312&view=diff
==============================================================================
--- llvm/trunk/tools/opt/PassPrinters.h (original)
+++ llvm/trunk/tools/opt/PassPrinters.h Mon May 14 17:29:27 2018
@@ -49,13 +49,15 @@ BasicBlockPass *createBasicBlockPassPrin
 
 } // end namespace llvm
 
-llvm::ModulePass *createDebugifyPass();
+llvm::ModulePass *createDebugifyModulePass();
+llvm::FunctionPass *createDebugifyFunctionPass();
 
 struct NewPMDebugifyPass : public llvm::PassInfoMixin<NewPMDebugifyPass> {
   llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
 };
 
-llvm::ModulePass *createCheckDebugifyPass();
+llvm::ModulePass *createCheckDebugifyModulePass(bool Strip = false);
+llvm::FunctionPass *createCheckDebugifyFunctionPass(bool Strip = false);
 
 struct NewPMCheckDebugifyPass
     : public llvm::PassInfoMixin<NewPMCheckDebugifyPass> {

Modified: llvm/trunk/tools/opt/opt.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/opt.cpp?rev=332312&r1=332311&r2=332312&view=diff
==============================================================================
--- llvm/trunk/tools/opt/opt.cpp (original)
+++ llvm/trunk/tools/opt/opt.cpp Mon May 14 17:29:27 2018
@@ -207,6 +207,11 @@ static cl::opt<bool> EnableDebugify(
     cl::desc(
         "Start the pipeline with debugify and end it with check-debugify"));
 
+static cl::opt<bool> DebugifyEach(
+    "debugify-each",
+    cl::desc(
+        "Start each pass with debugify and end it with check-debugify"));
+
 static cl::opt<bool>
 PrintBreakpoints("print-breakpoints-for-testing",
                  cl::desc("Print select breakpoints location for testing"));
@@ -256,6 +261,37 @@ static cl::opt<std::string>
                     cl::desc("YAML output filename for pass remarks"),
                     cl::value_desc("filename"));
 
+class OptCustomPassManager : public legacy::PassManager {
+public:
+  using super = legacy::PassManager;
+
+  void add(Pass *P) override {
+    bool WrapWithDebugify =
+        DebugifyEach && !P->getAsImmutablePass() && !isIRPrintingPass(P);
+    if (!WrapWithDebugify) {
+      super::add(P);
+      return;
+    }
+    PassKind Kind = P->getPassKind();
+    // TODO: Implement Debugify for BasicBlockPass, LoopPass.
+    switch (Kind) {
+      case PT_Function:
+        super::add(createDebugifyFunctionPass());
+        super::add(P);
+        super::add(createCheckDebugifyFunctionPass(true));
+        break;
+      case PT_Module:
+        super::add(createDebugifyModulePass());
+        super::add(P);
+        super::add(createCheckDebugifyModulePass(true));
+        break;
+      default:
+        super::add(P);
+        break;
+    }
+  }
+};
+
 static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
   // Add the pass to the pass manager...
   PM.add(P);
@@ -556,8 +592,8 @@ int main(int argc, char **argv) {
 
   // Create a PassManager to hold and optimize the collection of passes we are
   // about to build.
-  //
-  legacy::PassManager Passes;
+  OptCustomPassManager Passes;
+  bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach;
 
   // Add an appropriate TargetLibraryInfo pass for the module's triple.
   TargetLibraryInfoImpl TLII(ModuleTriple);
@@ -571,8 +607,8 @@ int main(int argc, char **argv) {
   Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis()
                                                      : TargetIRAnalysis()));
 
-  if (EnableDebugify)
-    Passes.add(createDebugifyPass());
+  if (AddOneTimeDebugifyPasses)
+    Passes.add(createDebugifyModulePass());
 
   std::unique_ptr<legacy::FunctionPassManager> FPasses;
   if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz ||
@@ -719,8 +755,8 @@ int main(int argc, char **argv) {
   if (!NoVerify && !VerifyEach)
     Passes.add(createVerifierPass());
 
-  if (EnableDebugify)
-    Passes.add(createCheckDebugifyPass());
+  if (AddOneTimeDebugifyPasses)
+    Passes.add(createCheckDebugifyModulePass(false));
 
   // In run twice mode, we want to make sure the output is bit-by-bit
   // equivalent if we run the pass manager again, so setup two buffers and




More information about the llvm-commits mailing list