Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -109,6 +109,8 @@ void initializeDataFlowSanitizerPass(PassRegistry&); void initializeDeadInstEliminationPass(PassRegistry&); void initializeDeadMachineInstructionElimPass(PassRegistry&); +void initializeDebugifyPass(PassRegistry&); +void initializeCheckDebugifyPass(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: 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,226 @@ +//===- 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/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/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 { + +struct Debugify : public ModulePass { + bool runOnModule(Module &M) override { + // 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_plus_plus_11, + 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)); + auto SP = + DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, + SPType, isLocalToUnit(F), 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; + } + + /// Determine whether \p GV can only be referenced within its module. + static bool isLocalToUnit(GlobalValue &GV) { + return GV.hasPrivateLinkage() || GV.hasInternalLinkage(); + } + + Debugify() : ModulePass(ID) { + initializeDebugifyPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + // Pass identification. + static char ID; +}; + +struct CheckDebugify : public ModulePass { + bool runOnModule(Module &M) override { + // Skip modules without debugify metadata. + NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify"); + if (!NMD) + return false; + + 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 (BasicBlock &BB : F) { + for (Instruction &I : BB) { + 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 (BasicBlock &BB : F) { + for (Instruction &I : BB) { + 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"; + return false; + } + + CheckDebugify() : ModulePass(ID) { + initializeCheckDebugifyPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + // Pass identification. + static char ID; +}; + +} // end anonymous namespace + +char Debugify::ID = 0; + +INITIALIZE_PASS_BEGIN(Debugify, "debugify", "Attach debug info to everything", + false, false) +INITIALIZE_PASS_END(Debugify, "debugify", "Attach debug info to everything", + false, false) + +ModulePass *llvm::createDebugifyPass() { return new Debugify(); } + +char CheckDebugify::ID = 0; + +INITIALIZE_PASS_BEGIN(CheckDebugify, "check-debugify", + "Check debug info from -debugify", false, false) +INITIALIZE_PASS_END(CheckDebugify, "check-debugify", + "Check debug info from -debugify", false, false) + +ModulePass *llvm::createCheckDebugifyPass() { return new CheckDebugify(); } 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); + initializeDebugifyPass(Registry); + initializeCheckDebugifyPass(Registry); initializeInstNamerPass(Registry); initializeLCSSAWrapperPassPass(Registry); initializeLibCallsShrinkWrapLegacyPassPass(Registry); Index: test/Transforms/Debugify/debugify.ll =================================================================== --- /dev/null +++ test/Transforms/Debugify/debugify.ll @@ -0,0 +1,46 @@ +; RUN: opt -debugify -S -o - < %s | FileCheck %s +; +; RUN: opt -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" + +; 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_plus_plus_11, 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]