Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -101,6 +101,7 @@ void initializeCostModelAnalysisPass(PassRegistry&); void initializeEntryExitInstrumenterPass(PassRegistry&); void initializePostInlineEntryExitInstrumenterPass(PassRegistry&); +void initializeCheckDebugifyLegacyPassPass(PassRegistry&); void initializeCrossDSOCFIPass(PassRegistry&); void initializeDAEPass(PassRegistry&); void initializeDAHPass(PassRegistry&); @@ -109,6 +110,7 @@ void initializeDataFlowSanitizerPass(PassRegistry&); void initializeDeadInstEliminationPass(PassRegistry&); void initializeDeadMachineInstructionElimPass(PassRegistry&); +void initializeDebugifyLegacyPassPass(PassRegistry&); void initializeDelinearizationPass(PassRegistry&); void initializeDemandedBitsWrapperPassPass(PassRegistry&); void initializeDependenceAnalysisPass(PassRegistry&); Index: include/llvm/LinkAllPasses.h =================================================================== --- include/llvm/LinkAllPasses.h +++ include/llvm/LinkAllPasses.h @@ -88,6 +88,8 @@ (void) llvm::createDeadCodeEliminationPass(); (void) llvm::createDeadInstEliminationPass(); (void) llvm::createDeadStoreEliminationPass(); + (void) llvm::createDebugifyPass(); + (void) llvm::createCheckDebugifyPass(); (void) llvm::createDependenceAnalysisWrapperPass(); (void) llvm::createDivergenceAnalysisPass(); (void) llvm::createDomOnlyPrinterPass(); Index: include/llvm/Transforms/IPO.h =================================================================== --- include/llvm/Transforms/IPO.h +++ include/llvm/Transforms/IPO.h @@ -211,6 +211,16 @@ // ModulePass *createMetaRenamerPass(); +//===----------------------------------------------------------------------===// +// createDebugifyPass - Attach synthetic debug info to everything. +// +ModulePass *createDebugifyPass(); + +//===----------------------------------------------------------------------===// +// createCheckDebugifyPass - Check debug info from -debugify. +// +ModulePass *createCheckDebugifyPass(); + //===----------------------------------------------------------------------===// /// createBarrierNoopPass - This pass is purely a module pass barrier in a pass /// manager. Index: include/llvm/Transforms/Utils/Debugify.h =================================================================== --- /dev/null +++ include/llvm/Transforms/Utils/Debugify.h @@ -0,0 +1,38 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// Attach synthetic debug info to everything to test debug info preservation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_UTILS_DEBUGIFY_H +#define LLVM_TRANSFORMS_UTILS_DEBUGIFY_H + +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class DebugifyPass : public PassInfoMixin { +public: + DebugifyPass() = default; + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +class CheckDebugifyPass : public PassInfoMixin { +public: + CheckDebugifyPass() = default; + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_UTILS_DEBUGIFY_H Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -136,6 +136,7 @@ #include "llvm/Transforms/Scalar/TailRecursionElimination.h" #include "llvm/Transforms/Utils/AddDiscriminators.h" #include "llvm/Transforms/Utils/BreakCriticalEdges.h" +#include "llvm/Transforms/Utils/Debugify.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" #include "llvm/Transforms/Utils/LCSSA.h" #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h" Index: lib/Passes/PassRegistry.def =================================================================== --- lib/Passes/PassRegistry.def +++ lib/Passes/PassRegistry.def @@ -40,9 +40,11 @@ #endif MODULE_PASS("always-inline", AlwaysInlinerPass()) MODULE_PASS("called-value-propagation", CalledValuePropagationPass()) +MODULE_PASS("check-debugify", CheckDebugifyPass()) MODULE_PASS("constmerge", ConstantMergePass()) MODULE_PASS("cross-dso-cfi", CrossDSOCFIPass()) MODULE_PASS("deadargelim", DeadArgumentEliminationPass()) +MODULE_PASS("debugify", DebugifyPass()) MODULE_PASS("elim-avail-extern", EliminateAvailableExternallyPass()) MODULE_PASS("forceattrs", ForceFunctionAttrsPass()) MODULE_PASS("function-import", FunctionImportPass()) Index: lib/Transforms/Utils/CMakeLists.txt =================================================================== --- lib/Transforms/Utils/CMakeLists.txt +++ lib/Transforms/Utils/CMakeLists.txt @@ -9,6 +9,7 @@ CloneModule.cpp CodeExtractor.cpp CtorUtils.cpp + Debugify.cpp DemoteRegToStack.cpp EntryExitInstrumenter.cpp EscapeEnumerator.cpp Index: lib/Transforms/Utils/Debugify.cpp =================================================================== --- /dev/null +++ lib/Transforms/Utils/Debugify.cpp @@ -0,0 +1,234 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This pass attaches synthetic debug info to everything. It can be used to +// create targeted tests for debug info preservation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/Debugify.h" +#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")) + return false; + + DIBuilder DIB(M); + auto &Ctx = M.getContext(); + + // Get a DIType which corresponds to Ty. + DenseMap TypeCache; + auto getCachedDIType = [&](Type *Ty) { + 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(&I) || isa(&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(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(&I)) + continue; + + auto DL = I.getDebugLoc(); + if (DL) { + MissingLines.reset(DL.getLine() - 1); + continue; + } + + errs() << "ERROR: Instruction with empty DebugLoc -- "; + I.print(errs()); + errs() << "\n"; + HasErrors = true; + } + } + for (unsigned Idx : MissingLines.set_bits()) + errs() << "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(&I); + if (!DVI) + continue; + + unsigned VarNum; + bool Valid = to_integer(DVI->getVariable()->getName(), VarNum, 10); + assert(Valid && "Unexpected name for DILocalVariable"); + (void)Valid; + MissingVars.reset(VarNum - 1); + } + } + for (unsigned Idx : MissingVars.set_bits()) + errs() << "ERROR: Missing variable " << Idx + 1 << "\n"; + HasErrors |= MissingVars.count() > 0; + + errs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n"; +} + +struct DebugifyLegacyPass : public ModulePass { + bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); } + + DebugifyLegacyPass() : ModulePass(ID) { + initializeDebugifyLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + // Pass identification. + static char ID; +}; + +struct CheckDebugifyLegacyPass : public ModulePass { + bool runOnModule(Module &M) override { + checkDebugifyMetadata(M); + return false; + } + + CheckDebugifyLegacyPass() : ModulePass(ID) { + initializeCheckDebugifyLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + // Pass identification. + static char ID; +}; + +} // end anonymous namespace + +char DebugifyLegacyPass::ID = 0; + +INITIALIZE_PASS(DebugifyLegacyPass, "debugify", + "Attach debug info to everything", false, false) + +ModulePass *llvm::createDebugifyPass() { return new DebugifyLegacyPass(); } + +char CheckDebugifyLegacyPass::ID = 0; + +INITIALIZE_PASS(CheckDebugifyLegacyPass, "check-debugify", + "Check debug info from -debugify", false, false) + +ModulePass *llvm::createCheckDebugifyPass() { + return new CheckDebugifyLegacyPass(); +} + +PreservedAnalyses DebugifyPass::run(Module &M, ModuleAnalysisManager &) { + applyDebugifyMetadata(M); + return PreservedAnalyses::all(); +} + +PreservedAnalyses CheckDebugifyPass::run(Module &M, ModuleAnalysisManager &) { + checkDebugifyMetadata(M); + return PreservedAnalyses::all(); +} Index: lib/Transforms/Utils/Utils.cpp =================================================================== --- lib/Transforms/Utils/Utils.cpp +++ lib/Transforms/Utils/Utils.cpp @@ -23,6 +23,8 @@ void llvm::initializeTransformUtils(PassRegistry &Registry) { initializeAddDiscriminatorsLegacyPassPass(Registry); initializeBreakCriticalEdgesPass(Registry); + initializeCheckDebugifyLegacyPassPass(Registry); + initializeDebugifyLegacyPassPass(Registry); initializeInstNamerPass(Registry); initializeLCSSAWrapperPassPass(Registry); initializeLibCallsShrinkWrapLegacyPassPass(Registry); Index: test/Transforms/Debugify/debugify.ll =================================================================== --- /dev/null +++ test/Transforms/Debugify/debugify.ll @@ -0,0 +1,50 @@ +; RUN: opt -debugify -S -o - < %s | FileCheck %s +; RUN: opt -passes=debugify -S -o - < %s | FileCheck %s +; +; RUN: opt -debugify -debugify -S -o - < %s | FileCheck %s +; RUN: opt -passes=debugify,debugify -S -o - < %s | FileCheck %s +; +; RUN: opt -debugify -check-debugify -S -o - < %s 2>&1 | \ +; RUN: FileCheck %s -implicit-check-not="CheckDebugify: FAIL" +; RUN: opt -passes=debugify,check-debugify -S -o - < %s 2>&1 | \ +; RUN: FileCheck %s -implicit-check-not="CheckDebugify: 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: "", 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} Index: test/Transforms/LoopStrengthReduce/debug-info.ll =================================================================== --- /dev/null +++ test/Transforms/LoopStrengthReduce/debug-info.ll @@ -0,0 +1,36 @@ +; XFAIL: * +; RUN: opt < %s -debugify -loop-reduce -gvn -check-debugify -S 2>&1 | FileCheck %s +; ERROR: Instruction with empty DebugLoc -- %lsr.iv1 = bitcast float* %lsr.iv to i1* +; CHECK: CheckDebugify: PASS + +target datalayout = "e-i64:64-v16:16-v32:32-n16:32:64" + +define void @_Z3fooPfll(float* nocapture readonly %input, i64 %n, i64 %s) { +entry: + %mul = shl nsw i64 %s, 2 + tail call void @_Z3bazl(i64 %mul) #2 + %cmp.5 = icmp sgt i64 %n, 0 + br i1 %cmp.5, label %for.body.preheader, label %for.cond.cleanup + +for.body.preheader: ; preds = %entry + br label %for.body + +for.cond.cleanup.loopexit: ; preds = %for.body + br label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond.cleanup.loopexit, %entry + ret void + +for.body: ; preds = %for.body.preheader, %for.body + %i.06 = phi i64 [ %add, %for.body ], [ 0, %for.body.preheader ] + %arrayidx = getelementptr inbounds float, float* %input, i64 %i.06 + %0 = load float, float* %arrayidx, align 4 + tail call void @_Z3barf(float %0) #2 + %add = add nsw i64 %i.06, %s + %cmp = icmp slt i64 %add, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup.loopexit +} + +declare void @_Z3bazl(i64) + +declare void @_Z3barf(float) Index: test/Transforms/Mem2Reg/PromoteMemToRegister.ll =================================================================== --- test/Transforms/Mem2Reg/PromoteMemToRegister.ll +++ test/Transforms/Mem2Reg/PromoteMemToRegister.ll @@ -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 2>&1 | FileCheck %s + +; CHECK-NOT: alloca +; CHECK: CheckDebugify: PASS define double @testfunc(i32 %i, double %j) { %I = alloca i32 ; [#uses=4]