Index: llvm/docs/HowToUpdateDebugInfo.rst =================================================================== --- llvm/docs/HowToUpdateDebugInfo.rst +++ llvm/docs/HowToUpdateDebugInfo.rst @@ -222,7 +222,16 @@ ^^^^^^^^^^^^^^^^^^^^^^^^ The ``debugify`` testing utility is just a pair of passes: ``debugify`` and -``check-debugify``. +``check-debugify``. It works in two modes: + +* `synthetic` - attaches synthetic debug info to everything and tests its + preservation + +* `original` - collects and checks the preservation of real debug info + metadata + +The ``debugify`` in ``synthetic`` mode +************************************** The first applies synthetic debug information to every instruction of the module, and the second checks that this DI is still available after an @@ -281,6 +290,18 @@ !15 = !DILocation(line: 4, column: 1, scope: !6) !16 = !DILocation(line: 5, column: 1, scope: !6) +The ``debugify`` in ``original`` mode +************************************* + +The debugify utility can operate in the `original` mode when it checks +the preservation of the original/real Debug Info metadata in optimizations. +Before a pass it collects the metadata, and after the pass it checks if it +preserved the Debug Info metadata. It could be run as follows: + +.. code-block:: bash + + $ opt -enable-debugify=original -pass-to-test sample.ll + Using ``debugify`` ^^^^^^^^^^^^^^^^^^ @@ -299,17 +320,24 @@ .. code-block:: bash # Same as the above example. - $ opt -enable-debugify -pass-to-test sample.ll + $ opt -enable-debugify=synthetic -pass-to-test sample.ll + + # Run the debugify in original mode. + $ opt -enable-debugify=original -pass-to-test sample.ll # Suppresses verbose debugify output. - $ opt -enable-debugify -debugify-quiet -pass-to-test sample.ll + $ opt -enable-debugify=synthetic/original -debugify-quiet -pass-to-test sample.ll # Prepend -debugify before and append -check-debugify -strip after # each pass on the pipeline (similar to -verify-each). - $ opt -debugify-each -O2 sample.ll + $ opt -debugify-each=synthetic -O2 sample.ll + + # Check the preservation of original Debug Info after each pass. + $ opt -debugify-each=original -O2 sample.ll -In order for ``check-debugify`` to work, the DI must be coming from -``debugify``. Thus, modules with existing DI will be skipped. +In order for ``check-debugify`` in `synthetic` mode to work, the DI must be +coming from ``debugify`` (from the same mode). Thus, modules with existing +DI will be skipped. ``debugify`` can be used to test a backend, e.g: Index: llvm/include/llvm/Transforms/Utils/Debugify.h =================================================================== --- llvm/include/llvm/Transforms/Utils/Debugify.h +++ llvm/include/llvm/Transforms/Utils/Debugify.h @@ -1,4 +1,4 @@ -//===- Debugify.h - Attach synthetic debug info to everything -------------===// +//===- Debugify.h - Check debug info preservation in optimizations --------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,8 @@ // //===----------------------------------------------------------------------===// /// -/// \file Interface to the `debugify` synthetic debug info testing utility. +/// \file Interface to the `debugify` synthetic/original debug info testing +/// utility. /// //===----------------------------------------------------------------------===// @@ -17,6 +18,23 @@ #include "llvm/ADT/MapVector.h" #include "llvm/IR/PassManager.h" +#include + +using DebugFnMap = std::map; +using DebugInstMap = std::map; + +/// Used to track the Debug Info Metadata information. +struct DebugInfoPerPass { + // This maps a function name and its DISubprogram. + DebugFnMap DIFunctions; + // This maps an instruction and the info whether it has !dbg attached. + DebugInstMap DILocations; +}; + +/// Map pass names to a per-pass DebugInfoPerPass instance. +using DebugInfoPerPassMap = + llvm::MapVector; + namespace llvm { class DIBuilder; @@ -37,16 +55,51 @@ /// Returns true if any change was made. bool stripDebugifyMetadata(Module &M); +/// Collect original debug information before a pass. +/// +/// \param M The module to collect debug information from. +/// \param Functions A range of functions to collect debug information from. +/// \param DIPreservationMap A map to collect the DI metadata. +/// \param Banner A prefix string to add to debug/error messages. +/// \param NameOfWrappedPass A name of a pass to add to debug/error messages. +bool collectDebugInfoMetadata(Module &M, + iterator_range Functions, + DebugInfoPerPassMap &DIPreservationMap, + StringRef Banner, + StringRef NameOfWrappedPass); + +/// Check original debug information after a pass. +/// +/// \param M The module to collect debug information from. +/// \param Functions A range of functions to collect debug information from. +/// \param DIPreservationMap A map used to check collected the DI metadata. +/// \param Banner A prefix string to add to debug/error messages. +/// \param NameOfWrappedPass A name of a pass to add to debug/error messages. +bool checkDebugInfoMetadata(Module &M, + iterator_range Functions, + DebugInfoPerPassMap &DIPreservationMap, + StringRef Banner, + StringRef NameOfWrappedPass); } // namespace llvm -llvm::ModulePass *createDebugifyModulePass(); -llvm::FunctionPass *createDebugifyFunctionPass(); +/// Used to check whether we track synthetic or original debug info. +enum class DebugifyMode { NoDebugify, SyntheticDebugInfo, OriginalDebugInfo }; + +llvm::ModulePass *createDebugifyModulePass( + enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + llvm::StringRef NameOfWrappedPass = "", + DebugInfoPerPassMap *DIPreservationMap = nullptr); +llvm::FunctionPass *createDebugifyFunctionPass( + enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + llvm::StringRef NameOfWrappedPass = "", + DebugInfoPerPassMap *DIPreservationMap = nullptr); struct NewPMDebugifyPass : public llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); }; -/// Track how much `debugify` information has been lost. +/// Track how much `debugify` information (in the `synthetic` mode only) +/// has been lost. struct DebugifyStatistics { /// Number of missing dbg.values. unsigned NumDbgValuesMissing = 0; @@ -74,15 +127,17 @@ /// Map pass names to a per-pass DebugifyStatistics instance. using DebugifyStatsMap = llvm::MapVector; -llvm::ModulePass * -createCheckDebugifyModulePass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr); - -llvm::FunctionPass * -createCheckDebugifyFunctionPass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr); +llvm::ModulePass *createCheckDebugifyModulePass( + bool Strip = false, llvm::StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + DebugInfoPerPassMap *DIPreservationMap = nullptr); + +llvm::FunctionPass *createCheckDebugifyFunctionPass( + bool Strip = false, llvm::StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + DebugInfoPerPassMap *DIPreservationMap = nullptr); struct NewPMCheckDebugifyPass : public llvm::PassInfoMixin { Index: llvm/lib/Transforms/Utils/Debugify.cpp =================================================================== --- llvm/lib/Transforms/Utils/Debugify.cpp +++ llvm/lib/Transforms/Utils/Debugify.cpp @@ -1,4 +1,4 @@ -//===- Debugify.cpp - Attach synthetic debug info to everything -----------===// +//===- Debugify.cpp - Check debug info preservation in optimizations ------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,10 @@ // //===----------------------------------------------------------------------===// /// -/// \file This pass attaches synthetic debug info to everything. It can be used -/// to create targeted tests for debug info preservation. +/// \file In the `synthetic` mode, the `-debugify` attaches synthetic debug info +/// to everything. It can be used to create targeted tests for debug info +/// preservation. In addition, when using the `original` mode, it can check +/// original debug info preservation. /// //===----------------------------------------------------------------------===// @@ -23,6 +25,8 @@ #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" +#define DEBUG_TYPE "debugify" + using namespace llvm; namespace { @@ -34,6 +38,8 @@ Locations, LocationsAndVariables }; + +// Used for the synthetic mode only. cl::opt DebugifyLevel( "debugify-level", cl::desc("Kind of debug info to add"), cl::values(clEnumValN(Level::Locations, "locations", "Locations only"), @@ -245,6 +251,213 @@ return Changed; } +bool llvm::collectDebugInfoMetadata(Module &M, + iterator_range Functions, + DebugInfoPerPassMap &DIPreservationMap, + StringRef Banner, + StringRef NameOfWrappedPass) { + LLVM_DEBUG(dbgs() << Banner << " (before) " << NameOfWrappedPass << '\n'); + + // Clear the map with the debug info before every single pass. + DIPreservationMap.clear(); + + if (!M.getNamedMetadata("llvm.dbg.cu")) { + dbg() << Banner << " Skipping module without debug info\n"; + return false; + } + + // Visit each instruction. + for (Function &F : Functions) { + if (isFunctionSkipped(F)) + continue; + + // Collect the DISubprogram. + if (auto *SP = F.getSubprogram()) { + LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n'); + DIPreservationMap[NameOfWrappedPass].DIFunctions.insert( + {F.getName(), SP}); + } else { + LLVM_DEBUG(dbgs() << " Collecting subprogram: nullptr\n"); + DIPreservationMap[NameOfWrappedPass].DIFunctions.insert( + {F.getName(), nullptr}); + } + + for (BasicBlock &BB : F) { + // Collect debug locations (!dbg) and dbg.values. + for (Instruction &I : BB) { + // Skip PHIs. + if (isa(I)) + continue; + + // Skip debug instructions. + if (isa(&I)) + continue; + + LLVM_DEBUG(dbgs() << " Collecting info for inst: " << I << '\n'); + + const DILocation *Loc = I.getDebugLoc().get(); + bool HasLoc = Loc != nullptr; + + DIPreservationMap[NameOfWrappedPass].DILocations.insert({&I, HasLoc}); + } + } + } + + return true; +} + +// This checks the preservation of original debug info attached to functions. +static bool checkFunctions(DebugFnMap &DIFunctionsBefore, + DebugFnMap &DIFunctionsAfter, + StringRef NameOfWrappedPass, + StringRef FileNameFromCU) { + bool Preserved = true; + for (const auto &F : DIFunctionsAfter) { + if (!F.second) { + auto SPIt = DIFunctionsBefore.find(F.first); + if (SPIt == DIFunctionsBefore.end()) { + dbg() << "ERROR: " << NameOfWrappedPass + << " did not generate DISubprogram for " << F.first << " from " + << FileNameFromCU << '\n'; + if (Preserved) + Preserved = false; + } else { + auto SP = SPIt->second; + // If the function had the SP attacched before the pass, consider it as + // a debug info bug. + if (SP) { + dbg() << "ERROR: " << NameOfWrappedPass << " dropped DISubprogram of " + << F.first << " from " << FileNameFromCU << '\n'; + if (Preserved) + Preserved = false; + } + } + } + } + + return Preserved; +} + +// This checks the preservation of the original debug info attached to +// instructions. +static bool checkInstructions(DebugInstMap &DILocsBefore, + DebugInstMap &DILocsAfter, + StringRef NameOfWrappedPass, + StringRef FileNameFromCU) { + bool Preserved = true; + for (const auto &L : DILocsAfter) { + if (!L.second) { + auto Instr = L.first; + auto FnName = Instr->getFunction()->getName(); + auto BB = Instr->getParent(); + auto BBName = BB->hasName() ? BB->getName() : "no-name"; + + auto InstrIt = DILocsBefore.find(Instr); + if (InstrIt == DILocsBefore.end()) { + dbg() << "ERROR: " << NameOfWrappedPass + << " did not generate DILocation for " << *Instr + << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; + if (Preserved) + Preserved = false; + } else { + auto hadLoc = InstrIt->second; + // If the instr had the !dbg attacched before the pass, consider it as + // a debug info bug. + if (hadLoc) { + dbg() << "ERROR: " << NameOfWrappedPass << " dropped DILocation of " + << *Instr << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; + if (Preserved) + Preserved = false; + } + } + } + } + + return Preserved; +} + +bool llvm::checkDebugInfoMetadata(Module &M, + iterator_range Functions, + DebugInfoPerPassMap &DIPreservationMap, + StringRef Banner, + StringRef NameOfWrappedPass) { + LLVM_DEBUG(dbgs() << Banner << " (after) " << NameOfWrappedPass << '\n'); + + if (!M.getNamedMetadata("llvm.dbg.cu")) { + dbg() << Banner << " Skipping module without debug info\n"; + return false; + } + + // Map the debug info holding DIs after a pass. + DebugInfoPerPassMap DIPreservationAfter; + + // Visit each instruction. + for (Function &F : Functions) { + if (isFunctionSkipped(F)) + continue; + + // TODO: Collect metadata other than DISubprograms. + // Collect the DISubprogram. + if (auto *SP = F.getSubprogram()) { + LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n'); + DIPreservationAfter[NameOfWrappedPass].DIFunctions.insert( + {F.getName(), SP}); + } else { + LLVM_DEBUG(dbgs() << " Collecting subprogram: nullptr\n"); + DIPreservationAfter[NameOfWrappedPass].DIFunctions.insert( + {F.getName(), nullptr}); + } + + for (BasicBlock &BB : F) { + // Collect debug locations (!dbg attachments). + // TODO: Collect dbg.values. + for (Instruction &I : BB) { + // Skip PHIs. + if (isa(I)) + continue; + + // Skip debug instructions. + if (isa(&I)) + continue; + + LLVM_DEBUG(dbgs() << " Collecting info for inst: " << I << '\n'); + + const DILocation *Loc = I.getDebugLoc().get(); + bool HasLoc = Loc != nullptr; + + DIPreservationAfter[NameOfWrappedPass].DILocations.insert({&I, HasLoc}); + } + } + } + + // TODO: The name of the module could be read better? + StringRef FileNameFromCU = + (cast(M.getNamedMetadata("llvm.dbg.cu")->getOperand(0))) + ->getFilename(); + + auto DIFunctionsBefore = DIPreservationMap[NameOfWrappedPass].DIFunctions; + auto DIFunctionsAfter = DIPreservationAfter[NameOfWrappedPass].DIFunctions; + + auto DILocsBefore = DIPreservationMap[NameOfWrappedPass].DILocations; + auto DILocsAfter = DIPreservationAfter[NameOfWrappedPass].DILocations; + + bool Result = checkFunctions(DIFunctionsBefore, DIFunctionsAfter, + NameOfWrappedPass, FileNameFromCU) && + checkInstructions(DILocsBefore, DILocsAfter, NameOfWrappedPass, + FileNameFromCU); + + StringRef ResultBanner = NameOfWrappedPass != "" ? NameOfWrappedPass : Banner; + if (Result) + dbg() << ResultBanner << ": PASS\n"; + else + dbg() << ResultBanner << ": FAIL\n"; + + LLVM_DEBUG(dbgs() << "\n\n"); + return Result; +} + namespace { /// Return true if a mis-sized diagnostic is issued for \p DVI. bool diagnoseMisSizedDbgValue(Module &M, DbgValueInst *DVI) { @@ -385,17 +598,30 @@ /// legacy module pass manager. struct DebugifyModulePass : public ModulePass { bool runOnModule(Module &M) override { - return applyDebugifyMetadata(M, M.functions(), - "ModuleDebugify: ", /*ApplyToMF*/ nullptr); + if (Mode == DebugifyMode::SyntheticDebugInfo) + return applyDebugifyMetadata(M, M.functions(), + "ModuleDebugify: ", /*ApplyToMF*/ nullptr); + return collectDebugInfoMetadata( + M, M.functions(), *DIPreservationMap, + "ModuleDebugify (original): ", NameOfWrappedPass); } - DebugifyModulePass() : ModulePass(ID) {} + DebugifyModulePass(enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + StringRef NameOfWrappedPass = "", + DebugInfoPerPassMap *DIPreservationMap = nullptr) + : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass), + DIPreservationMap(DIPreservationMap), Mode(Mode) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); } static char ID; // Pass identification. + +private: + StringRef NameOfWrappedPass; + DebugInfoPerPassMap *DIPreservationMap; + enum DebugifyMode Mode; }; /// FunctionPass for attaching synthetic debug info to instructions within a @@ -404,31 +630,53 @@ bool runOnFunction(Function &F) override { Module &M = *F.getParent(); auto FuncIt = F.getIterator(); - return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)), - "FunctionDebugify: ", /*ApplyToMF*/ nullptr); + if (Mode == DebugifyMode::SyntheticDebugInfo) + return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)), + "FunctionDebugify: ", /*ApplyToMF*/ nullptr); + return collectDebugInfoMetadata( + M, M.functions(), *DIPreservationMap, + "FunctionDebugify (original): ", NameOfWrappedPass); } - DebugifyFunctionPass() : FunctionPass(ID) {} + DebugifyFunctionPass( + enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + StringRef NameOfWrappedPass = "", + DebugInfoPerPassMap *DIPreservationMap = nullptr) + : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass), + DIPreservationMap(DIPreservationMap), Mode(Mode) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); } static char ID; // Pass identification. + +private: + StringRef NameOfWrappedPass; + DebugInfoPerPassMap *DIPreservationMap; + enum DebugifyMode Mode; }; /// ModulePass for checking debug info inserted by -debugify, used with the /// legacy module pass manager. struct CheckDebugifyModulePass : public ModulePass { bool runOnModule(Module &M) override { - return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass, - "CheckModuleDebugify", Strip, StatsMap); + if (Mode == DebugifyMode::SyntheticDebugInfo) + return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass, + "CheckModuleDebugify", Strip, StatsMap); + return checkDebugInfoMetadata( + M, M.functions(), *DIPreservationMap, + "CheckModuleDebugify(original): ", NameOfWrappedPass); } - CheckDebugifyModulePass(bool Strip = false, StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr) - : ModulePass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), - StatsMap(StatsMap) {} + CheckDebugifyModulePass( + bool Strip = false, StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + DebugInfoPerPassMap *DIPreservationMap = nullptr) + : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass), + StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), + Strip(Strip) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); @@ -437,9 +685,11 @@ static char ID; // Pass identification. private: - bool Strip; StringRef NameOfWrappedPass; DebugifyStatsMap *StatsMap; + DebugInfoPerPassMap *DIPreservationMap; + enum DebugifyMode Mode; + bool Strip; }; /// FunctionPass for checking debug info inserted by -debugify-function, used @@ -448,16 +698,23 @@ bool runOnFunction(Function &F) override { Module &M = *F.getParent(); auto FuncIt = F.getIterator(); - return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)), - NameOfWrappedPass, "CheckFunctionDebugify", - Strip, StatsMap); + if (Mode == DebugifyMode::SyntheticDebugInfo) + return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)), + NameOfWrappedPass, "CheckFunctionDebugify", + Strip, StatsMap); + return checkDebugInfoMetadata( + M, make_range(FuncIt, std::next(FuncIt)), *DIPreservationMap, + "CheckFunctionDebugify(original): ", NameOfWrappedPass); } - CheckDebugifyFunctionPass(bool Strip = false, - StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr) - : FunctionPass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), - StatsMap(StatsMap) {} + CheckDebugifyFunctionPass( + bool Strip = false, StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, + DebugInfoPerPassMap *DIPreservationMap = nullptr) + : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass), + StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), + Strip(Strip) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); @@ -466,17 +723,32 @@ static char ID; // Pass identification. private: - bool Strip; StringRef NameOfWrappedPass; DebugifyStatsMap *StatsMap; + DebugInfoPerPassMap *DIPreservationMap; + enum DebugifyMode Mode; + bool Strip; }; } // end anonymous namespace -ModulePass *createDebugifyModulePass() { return new DebugifyModulePass(); } +ModulePass *createDebugifyModulePass(enum DebugifyMode Mode, + llvm::StringRef NameOfWrappedPass, + DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == DebugifyMode::SyntheticDebugInfo) + return new DebugifyModulePass(); + assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode"); + return new DebugifyModulePass(Mode, NameOfWrappedPass, DIPreservationMap); +} -FunctionPass *createDebugifyFunctionPass() { - return new DebugifyFunctionPass(); +FunctionPass * +createDebugifyFunctionPass(enum DebugifyMode Mode, + llvm::StringRef NameOfWrappedPass, + DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == DebugifyMode::SyntheticDebugInfo) + return new DebugifyFunctionPass(); + assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode"); + return new DebugifyFunctionPass(Mode, NameOfWrappedPass, DIPreservationMap); } PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) { @@ -485,16 +757,24 @@ return PreservedAnalyses::all(); } -ModulePass *createCheckDebugifyModulePass(bool Strip, - StringRef NameOfWrappedPass, - DebugifyStatsMap *StatsMap) { - return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap); +ModulePass *createCheckDebugifyModulePass( + bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap, + enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == DebugifyMode::SyntheticDebugInfo) + return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap); + assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode"); + return new CheckDebugifyModulePass(false, NameOfWrappedPass, nullptr, Mode, + DIPreservationMap); } -FunctionPass *createCheckDebugifyFunctionPass(bool Strip, - StringRef NameOfWrappedPass, - DebugifyStatsMap *StatsMap) { - return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap); +FunctionPass *createCheckDebugifyFunctionPass( + bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap, + enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == DebugifyMode::SyntheticDebugInfo) + return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap); + assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode"); + return new CheckDebugifyFunctionPass(false, NameOfWrappedPass, nullptr, Mode, + DIPreservationMap); } PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, Index: llvm/test/DebugInfo/check-debugify-preserves-analyses.ll =================================================================== --- llvm/test/DebugInfo/check-debugify-preserves-analyses.ll +++ llvm/test/DebugInfo/check-debugify-preserves-analyses.ll @@ -1,7 +1,7 @@ ; RUN: opt < %s -globals-aa -functionattrs | \ ; RUN: opt -S -strip -strip-dead-prototypes -strip-named-metadata > %t.no_dbg -; RUN: opt < %s -debugify-each -globals-aa -functionattrs | \ +; RUN: opt < %s -debugify-each=synthetic -globals-aa -functionattrs | \ ; RUN: opt -S -strip -strip-dead-prototypes -strip-named-metadata > %t.with_dbg ; RUN: diff %t.no_dbg %t.with_dbg Index: llvm/test/DebugInfo/debugify-each-original-dbginfo.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/debugify-each-original-dbginfo.ll @@ -0,0 +1,96 @@ +; Check the preservation of original debug info. + +; RUN: opt -O2 -debugify-each=original -S -o - < %s 2>&1 | FileCheck %s + +; CHECK: ERROR: Combine redundant instructions {{.*}} +; CHECK-NEXT: Combine redundant instructions: FAIL + +; ModuleID = 'simple.c' +source_filename = "simple.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define dso_local i32 @fn1(i32 %arg1) !dbg !12 { +entry: + %retval = alloca i32, align 4 + %arg1.addr = alloca i32, align 4 + store i32 %arg1, i32* %arg1.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %arg1.addr, metadata !16, metadata !DIExpression()), !dbg !21 + %0 = load i32, i32* %arg1.addr, align 4, !dbg !22 + %rem = srem i32 %0, 2, !dbg !24 + %tobool = icmp ne i32 %rem, 0, !dbg !24 + br i1 %tobool, label %if.then, label %if.end, !dbg !25 + +if.then: ; preds = %entry + %1 = load i32, i32* %arg1.addr, align 4, !dbg !24 + %inc = add nsw i32 %1, 1, !dbg !24 + store i32 %inc, i32* %arg1.addr, align 4, !dbg !24 + store i32 %inc, i32* %retval, align 4, !dbg !24 + br label %return, !dbg !24 + +if.end: ; preds = %entry + %2 = load i32, i32* %arg1.addr, align 4, !dbg !25 + store i32 %2, i32* %retval, align 4, !dbg !25 + br label %return, !dbg !25 + +return: ; preds = %if.end, %if.then + %3 = load i32, i32* %retval, align 4, !dbg !25 + ret i32 %3, !dbg !25 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +; Function Attrs: nounwind uwtable +define dso_local i32 @fn2() !dbg !31 { +entry: + %local1 = alloca i32, align 4 + %0 = bitcast i32* %local1 to i8*, !dbg !36 + call void @llvm.dbg.declare(metadata i32* %local1, metadata !35, metadata !DIExpression()), !dbg !36 + %call = call i32 (...) @fn3(), !dbg !36 + store i32 %call, i32* %local1, align 4, !dbg !36 + %1 = load i32, i32* %local1, align 4, !dbg !36 + %call1 = call i32 @fn1(i32 %1), !dbg !36 + %2 = load i32, i32* %local1, align 4 + %add = add nsw i32 %2, %call1 + store i32 %add, i32* %local1, align 4 + %3 = load i32, i32* %local1, align 4, !dbg !36 + %4 = bitcast i32* %local1 to i8*, !dbg !36 + ret i32 %3, !dbg !36 +} + +declare !dbg !4 dso_local i32 @fn3(...) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "simple.c", directory: "/dir") +!2 = !{} +!3 = !{!4} +!4 = !DISubprogram(name: "fn3", scope: !1, file: !1, line: 1, type: !5, spFlags: DISPFlagOptimized, retainedNodes: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7, null} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 7, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 11.0.0"} +!12 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 3, type: !13, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7, !7} +!15 = !{!16} +!16 = !DILocalVariable(name: "arg1", arg: 1, scope: !12, file: !1, line: 3, type: !7) +!21 = !DILocation(line: 3, column: 13, scope: !12) +!22 = !DILocation(line: 4, column: 7, scope: !23) +!23 = distinct !DILexicalBlock(scope: !12, file: !1, line: 4, column: 7) +!24 = !DILocation(line: 4, column: 12, scope: !23) +!25 = !DILocation(line: 4, column: 7, scope: !12) +!31 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 10, type: !32, scopeLine: 10, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !34) +!32 = !DISubroutineType(types: !33) +!33 = !{!7} +!34 = !{!35} +!35 = !DILocalVariable(name: "local1", scope: !31, file: !1, line: 11, type: !7) +!36 = !DILocation(line: 11, column: 3, scope: !31) Index: llvm/test/DebugInfo/debugify-each.ll =================================================================== --- llvm/test/DebugInfo/debugify-each.ll +++ llvm/test/DebugInfo/debugify-each.ll @@ -1,30 +1,30 @@ -; RUN: opt -debugify-each -O3 -S -o /dev/null < %s 2> %t +; RUN: opt -debugify-each=synthetic -O3 -S -o /dev/null < %s 2> %t ; RUN: FileCheck %s -input-file=%t -check-prefix=MODULE-PASS ; RUN: FileCheck %s -input-file=%t -check-prefix=FUNCTION-PASS -; RUN: opt -enable-debugify -debugify-each -O3 -S -o /dev/null < %s 2> %t +; RUN: opt -enable-debugify=synthetic -debugify-each=synthetic -O3 -S -o /dev/null < %s 2> %t ; RUN: FileCheck %s -input-file=%t -check-prefix=MODULE-PASS ; RUN: FileCheck %s -input-file=%t -check-prefix=FUNCTION-PASS -; RUN: opt -debugify-each -instrprof -instrprof -sroa -sccp -S -o /dev/null < %s 2> %t +; RUN: opt -debugify-each=synthetic -instrprof -instrprof -sroa -sccp -S -o /dev/null < %s 2> %t ; RUN: FileCheck %s -input-file=%t -check-prefix=MODULE-PASS ; RUN: FileCheck %s -input-file=%t -check-prefix=FUNCTION-PASS ; Verify that debugify each can be safely used with piping -; RUN: opt -debugify-each -O1 < %s | opt -O2 -o /dev/null +; RUN: opt -debugify-each=synthetic -O1 < %s | opt -O2 -o /dev/null ; Check that the quiet mode emits no messages. -; RUN: opt -disable-output -debugify-quiet -debugify-each -O1 < %s 2>&1 | count 0 +; RUN: opt -disable-output -debugify-quiet -debugify-each=synthetic -O1 < %s 2>&1 | count 0 ; Check that stripped textual IR compares equal before and after applying ; debugify. ; RUN: opt -O1 < %s -S -o %t.before -; RUN: opt -O1 -debugify-each < %s -S -o %t.after +; RUN: opt -O1 -debugify-each=synthetic < %s -S -o %t.after ; RUN: diff %t.before %t.after ; Check that stripped IR compares equal before and after applying debugify. ; RUN: opt -O1 < %s | llvm-dis -o %t.before -; RUN: opt -O1 -debugify-each < %s | llvm-dis -o %t.after +; RUN: opt -O1 -debugify-each=synthetic < %s | llvm-dis -o %t.after ; RUN: diff %t.before %t.after define void @foo(i32 %arg) { Index: llvm/test/DebugInfo/debugify-export.ll =================================================================== --- llvm/test/DebugInfo/debugify-export.ll +++ llvm/test/DebugInfo/debugify-export.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify-each -debugify-quiet -debugify-export - -o /dev/null | FileCheck %s +; RUN: opt < %s -debugify-each=synthetic -debugify-quiet -debugify-export - -o /dev/null | FileCheck %s ; CHECK: Pass Name ; CHECK-SAME: # of missing debug values Index: llvm/test/DebugInfo/debugify-original-dbginfo.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/debugify-original-dbginfo.ll @@ -0,0 +1,73 @@ +; Check the preservation of original debug info. + +; RUN: opt -enable-debugify=original -instcombine -S -o - < %s 2>&1 | FileCheck %s + +; CHECK: CheckModuleDebugify(original): : PASS + +; ModuleID = 'test' +source_filename = "test.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: norecurse nounwind readnone uwtable +define dso_local i32 @fn1(i32 %0) local_unnamed_addr !dbg !12 { + call void @llvm.dbg.value(metadata i32 %0, metadata !16, metadata !DIExpression()), !dbg !17 + %2 = and i32 %0, 1, !dbg !18 + %.0 = add nsw i32 %2, %0, !dbg !20 + ret i32 %.0, !dbg !21 +} + +; Function Attrs: nounwind uwtable +define dso_local i32 @fn2() local_unnamed_addr !dbg !22 { + %1 = tail call i32 (...) @fn3(), !dbg !27 + call void @llvm.dbg.value(metadata i32 %1, metadata !26, metadata !DIExpression()), !dbg !28 + call void @llvm.dbg.value(metadata i32 %1, metadata !16, metadata !DIExpression()), !dbg !29 + %2 = and i32 %1, 1, !dbg !31 + %reass.add = shl i32 %1, 1 + %3 = or i32 %2, %reass.add + call void @llvm.dbg.value(metadata i32 %3, metadata !26, metadata !DIExpression()), !dbg !28 + ret i32 %3, !dbg !32 +} + +declare !dbg !4 dso_local i32 @fn3(...) local_unnamed_addr + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{} +!3 = !{!4} +!4 = !DISubprogram(name: "fn3", scope: !1, file: !1, line: 1, type: !5, spFlags: DISPFlagOptimized, retainedNodes: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7, null} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 7, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 11.0.0"} +!12 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 2, type: !13, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7, !7} +!15 = !{!16} +!16 = !DILocalVariable(name: "arg1", arg: 1, scope: !12, file: !1, line: 2, type: !7) +!17 = !DILocation(line: 0, scope: !12) +!18 = !DILocation(line: 3, column: 12, scope: !19) +!19 = distinct !DILexicalBlock(scope: !12, file: !1, line: 3, column: 7) +!20 = !DILocation(line: 3, column: 7, scope: !12) +!21 = !DILocation(line: 6, column: 1, scope: !12) +!22 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 7, type: !23, scopeLine: 7, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !25) +!23 = !DISubroutineType(types: !24) +!24 = !{!7} +!25 = !{!26} +!26 = !DILocalVariable(name: "local1", scope: !22, file: !1, line: 8, type: !7) +!27 = !DILocation(line: 8, column: 16, scope: !22) +!28 = !DILocation(line: 0, scope: !22) +!29 = !DILocation(line: 0, scope: !12, inlinedAt: !30) +!30 = distinct !DILocation(line: 9, column: 13, scope: !22) +!31 = !DILocation(line: 3, column: 12, scope: !19, inlinedAt: !30) +!32 = !DILocation(line: 10, column: 3, scope: !22) Index: llvm/test/DebugInfo/debugify-original-no-dbg-info.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/debugify-original-no-dbg-info.ll @@ -0,0 +1,23 @@ +; RUN: opt -enable-debugify=original -instcombine -S -o - < %s 2>&1 | FileCheck %s + +; CHECK: ModuleDebugify (original): Skipping module without debug info +; CHECK-NEXT: CheckModuleDebugify(original): Skipping module without debug info + +; ModuleID = 'no-dbg-info.c' +source_filename = "no-dbg-info.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define dso_local i32 @fn() { + %1 = call i32 (...) @fn2() + ret i32 %1 +} + +declare dso_local i32 @fn2(...) + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 11.0.0"} Index: llvm/test/DebugInfo/debugify.ll =================================================================== --- llvm/test/DebugInfo/debugify.ll +++ llvm/test/DebugInfo/debugify.ll @@ -10,19 +10,19 @@ ; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" ; RUN: opt -passes=debugify,check-debugify -S -o - < %s | \ ; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" -; RUN: opt -enable-debugify -passes=verify -S -o - < %s | \ +; RUN: opt -enable-debugify=synthetic -passes=verify -S -o - < %s | \ ; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" ; RUN: opt -debugify -strip -check-debugify -S -o - < %s 2>&1 | \ ; RUN: FileCheck %s -check-prefix=CHECK-FAIL -; RUN: opt -enable-debugify -strip -S -o - < %s 2>&1 | \ +; RUN: opt -enable-debugify=synthetic -strip -S -o - < %s 2>&1 | \ ; RUN: FileCheck %s -check-prefix=CHECK-FAIL -; RUN: opt -enable-debugify -S -o - < %s 2>&1 | FileCheck %s -check-prefix=PASS +; RUN: opt -enable-debugify=synthetic -S -o - < %s 2>&1 | FileCheck %s -check-prefix=PASS ; Verify that debugify can be safely used with piping -; RUN: opt -enable-debugify -O1 < %s | opt -O2 -o /dev/null +; RUN: opt -enable-debugify=synthetic -O1 < %s | opt -O2 -o /dev/null ; RUN: opt -debugify -mem2reg -check-debugify < %s | opt -O2 -o /dev/null ; CHECK-LABEL: define void @foo Index: llvm/test/DebugInfo/pr37964.ll =================================================================== --- llvm/test/DebugInfo/pr37964.ll +++ llvm/test/DebugInfo/pr37964.ll @@ -1,4 +1,4 @@ -; RUN: opt -disable-output -debugify-each -gvn < %s 2>&1 | FileCheck %s +; RUN: opt -disable-output -debugify-each=synthetic -gvn < %s 2>&1 | FileCheck %s ; CHECK-NOT: ERROR: Instruction with empty DebugLoc in function _Z3bazv -- {{%.*}} = phi ; CHECK: CheckFunctionDebugify [Global Value Numbering]: PASS Index: llvm/test/Transforms/CodeGenPrepare/AArch64/overflow-intrinsics.ll =================================================================== --- llvm/test/Transforms/CodeGenPrepare/AArch64/overflow-intrinsics.ll +++ llvm/test/Transforms/CodeGenPrepare/AArch64/overflow-intrinsics.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -codegenprepare -S < %s | FileCheck %s -; RUN: opt -enable-debugify -codegenprepare -S < %s 2>&1 | FileCheck %s -check-prefix=DEBUG +; RUN: opt -enable-debugify=synthetic -codegenprepare -S < %s 2>&1 | FileCheck %s -check-prefix=DEBUG ; Subset of tests from llvm/tests/Transforms/CodeGenPrepare/X86/overflow-intrinsics.ll ; to test shouldFormOverflowOp on SPARC, where it is not profitable to create Index: llvm/test/Transforms/CodeGenPrepare/SPARC/overflow-intrinsics.ll =================================================================== --- llvm/test/Transforms/CodeGenPrepare/SPARC/overflow-intrinsics.ll +++ llvm/test/Transforms/CodeGenPrepare/SPARC/overflow-intrinsics.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -codegenprepare -S < %s | FileCheck %s -; RUN: opt -enable-debugify -codegenprepare -S < %s 2>&1 | FileCheck %s -check-prefix=DEBUG +; RUN: opt -enable-debugify=synthetic -codegenprepare -S < %s 2>&1 | FileCheck %s -check-prefix=DEBUG ; Subset of tests from llvm/tests/Transforms/CodeGenPrepare/X86/overflow-intrinsics.ll ; to test shouldFormOverflowOp on SPARC, where it is not profitable to create Index: llvm/test/Transforms/CodeGenPrepare/X86/overflow-intrinsics.ll =================================================================== --- llvm/test/Transforms/CodeGenPrepare/X86/overflow-intrinsics.ll +++ llvm/test/Transforms/CodeGenPrepare/X86/overflow-intrinsics.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -codegenprepare -S < %s | FileCheck %s -; RUN: opt -enable-debugify -codegenprepare -S < %s 2>&1 | FileCheck %s -check-prefix=DEBUG +; RUN: opt -enable-debugify=synthetic -codegenprepare -S < %s 2>&1 | FileCheck %s -check-prefix=DEBUG target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" Index: llvm/test/Transforms/CodeGenPrepare/X86/vec-shift.ll =================================================================== --- llvm/test/Transforms/CodeGenPrepare/X86/vec-shift.ll +++ llvm/test/Transforms/CodeGenPrepare/X86/vec-shift.ll @@ -4,7 +4,7 @@ ; RUN: opt -codegenprepare -mtriple=x86_64-- -mattr=+avx512bw -S < %s | FileCheck %s --check-prefixes=ALL,AVX,AVX512BW ; RUN: opt -codegenprepare -mtriple=x86_64-- -mattr=+avx,+xop -S < %s | FileCheck %s --check-prefixes=ALL,XOP,XOPAVX1 ; RUN: opt -codegenprepare -mtriple=x86_64-- -mattr=+avx2,+xop -S < %s | FileCheck %s --check-prefixes=ALL,XOP,XOPAVX2 -; RUN: opt -codegenprepare -mtriple=x86_64-- -mattr=+avx -S -enable-debugify < %s 2>&1 | FileCheck %s -check-prefix=DEBUG +; RUN: opt -codegenprepare -mtriple=x86_64-- -mattr=+avx -S -enable-debugify=synthetic < %s 2>&1 | FileCheck %s -check-prefix=DEBUG define <4 x i32> @vector_variable_shift_right_v4i32(<4 x i1> %cond, <4 x i32> %x, <4 x i32> %y, <4 x i32> %z) { ; AVX1-LABEL: @vector_variable_shift_right_v4i32( Index: llvm/test/Transforms/CorrelatedValuePropagation/ashr.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/ashr.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/ashr.ll @@ -2,7 +2,7 @@ ; Check that debug locations are preserved. For more info see: ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors -; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ +; RUN: opt < %s -enable-debugify=synthetic -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG ; DEBUG: CheckModuleDebugify: PASS Index: llvm/test/Transforms/CorrelatedValuePropagation/overflows.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/overflows.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/overflows.ll @@ -3,7 +3,7 @@ ; Check that debug locations are preserved. For more info see: ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors -; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ +; RUN: opt < %s -enable-debugify=synthetic -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG ; DEBUG: CheckModuleDebugify: PASS Index: llvm/test/Transforms/CorrelatedValuePropagation/sext.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/sext.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/sext.ll @@ -3,7 +3,7 @@ ; Check that debug locations are preserved. For more info see: ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors -; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ +; RUN: opt < %s -enable-debugify=synthetic -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG ; DEBUG: CheckModuleDebugify: PASS Index: llvm/test/Transforms/CorrelatedValuePropagation/udiv.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/udiv.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/udiv.ll @@ -2,7 +2,7 @@ ; Check that debug locations are preserved. For more info see: ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors -; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ +; RUN: opt < %s -enable-debugify=synthetic -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG ; DEBUG: CheckModuleDebugify: PASS Index: llvm/test/Transforms/InstCombine/call-guard.ll =================================================================== --- llvm/test/Transforms/InstCombine/call-guard.ll +++ llvm/test/Transforms/InstCombine/call-guard.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -instcombine -instcombine-infinite-loop-threshold=2 -S | FileCheck %s -; RUN: opt < %s -instcombine -S -debugify-each | FileCheck %s +; RUN: opt < %s -instcombine -S -debugify-each=synthetic | FileCheck %s declare void @llvm.experimental.guard(i1, ...) Index: llvm/test/Transforms/InstCombine/double-float-shrink-2.ll =================================================================== --- llvm/test/Transforms/InstCombine/double-float-shrink-2.ll +++ llvm/test/Transforms/InstCombine/double-float-shrink-2.ll @@ -5,7 +5,7 @@ ; RUN: opt < %s -instcombine -S -mtriple "i386-pc-mingw32" | FileCheck %s ; RUN: opt < %s -instcombine -S -mtriple "x86_64-pc-mingw32" | FileCheck %s ; RUN: opt < %s -instcombine -S -mtriple "sparc-sun-solaris" | FileCheck %s -; RUN: opt < %s -instcombine -S -mtriple "x86_64-pc-win32" -enable-debugify 2>&1 | FileCheck --check-prefix=DBG-VALID %s +; RUN: opt < %s -instcombine -S -mtriple "x86_64-pc-win32" -enable-debugify=synthetic 2>&1 | FileCheck --check-prefix=DBG-VALID %s declare double @floor(double) declare double @ceil(double) Index: llvm/test/Transforms/InstCombine/malloc-free-delete-dbginvar.ll =================================================================== --- llvm/test/Transforms/InstCombine/malloc-free-delete-dbginvar.ll +++ llvm/test/Transforms/InstCombine/malloc-free-delete-dbginvar.ll @@ -2,7 +2,7 @@ ; This is a regression test for a function taken from malloc-free-delete.ll. ; RUN: opt < %s -instcombine -S > %t.no_dbg.ll -; RUN: opt < %s -debugify-each -instcombine -S > %t.ll +; RUN: opt < %s -debugify-each=synthetic -instcombine -S > %t.ll ; RUN: diff %t.no_dbg.ll %t.ll declare void @free(i8*) Index: llvm/test/Transforms/InstCombine/musttail-thunk.ll =================================================================== --- llvm/test/Transforms/InstCombine/musttail-thunk.ll +++ llvm/test/Transforms/InstCombine/musttail-thunk.ll @@ -1,5 +1,5 @@ ; RUN: opt -instcombine -S < %s | FileCheck %s -; RUN: opt -debugify-each -instcombine -S < %s | FileCheck %s +; RUN: opt -debugify-each=synthetic -instcombine -S < %s | FileCheck %s ; These are both direct calls, but make sure instcombine leaves the casts ; alone. Index: llvm/test/Transforms/SCCP/ipsccp-basic.ll =================================================================== --- llvm/test/Transforms/SCCP/ipsccp-basic.ll +++ llvm/test/Transforms/SCCP/ipsccp-basic.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -ipsccp -S | FileCheck %s -; RUN: opt < %s -enable-debugify -ipsccp -debugify-quiet -disable-output +; RUN: opt < %s -enable-debugify=synthetic -ipsccp -debugify-quiet -disable-output ;;======================== test1 Index: llvm/tools/opt/opt.cpp =================================================================== --- llvm/tools/opt/opt.cpp +++ llvm/tools/opt/opt.cpp @@ -211,19 +211,34 @@ static cl::opt AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization")); -static cl::opt EnableDebugify( +static cl::opt EnableDebugify( "enable-debugify", - cl::desc( - "Start the pipeline with debugify and end it with check-debugify")); - -static cl::opt DebugifyEach( + cl::desc("Start the pipeline with debugify and end it with check-debugify " + "by processing either synthetic or original debug info"), + cl::values(clEnumValN(DebugifyMode::NoDebugify, "no-debugify", + "Do not use debugify"), + clEnumValN(DebugifyMode::SyntheticDebugInfo, "synthetic", + "Synthetic Debug Info"), + clEnumValN(DebugifyMode::OriginalDebugInfo, "original", + "Original Debug Info")), + cl::init(DebugifyMode::NoDebugify)); + +static cl::opt DebugifyEach( "debugify-each", - cl::desc( - "Start each pass with debugify and end it with check-debugify")); + cl::desc("Start each pass with debugify and end it with check-debugify by " + "processing either synthetic or original debug info."), + cl::values(clEnumValN(DebugifyMode::NoDebugify, "no-debugify", + "Do not use debugify"), + clEnumValN(DebugifyMode::SyntheticDebugInfo, "synthetic", + "Synthetic Debug Info"), + clEnumValN(DebugifyMode::OriginalDebugInfo, "original", + "Original Debug Info")), + cl::init(DebugifyMode::NoDebugify)); static cl::opt DebugifyExport("debugify-export", - cl::desc("Export per-pass debugify statistics to this file"), + cl::desc("Export per-pass debugify (in synthetic mode) " + "statistics to this file"), cl::value_desc("filename"), cl::init("")); static cl::opt @@ -332,6 +347,7 @@ class OptCustomPassManager : public legacy::PassManager { DebugifyStatsMap DIStatsMap; + DebugInfoPerPassMap DIPreservationMap; public: using super = legacy::PassManager; @@ -339,37 +355,65 @@ void add(Pass *P) override { // Wrap each pass with (-check)-debugify passes if requested, making // exceptions for passes which shouldn't see -debugify instrumentation. - bool WrapWithDebugify = DebugifyEach && !P->getAsImmutablePass() && + bool WrapWithDebugify = DebugifyEach != DebugifyMode::NoDebugify && + !P->getAsImmutablePass() && !isIRPrintingPass(P) && !isBitcodeWriterPass(P); + if (!WrapWithDebugify) { super::add(P); return; } - // Apply -debugify/-check-debugify before/after each pass and collect - // debug info loss statistics. + // Either apply -debugify/-check-debugify before/after each pass and collect + // debug info loss statistics, or collect and check original debug info in + // the optimizations. PassKind Kind = P->getPassKind(); StringRef Name = P->getPassName(); // TODO: Implement Debugify for LoopPass. switch (Kind) { - case PT_Function: + case PT_Function: + if (DebugifyEach == DebugifyMode::SyntheticDebugInfo) { super::add(createDebugifyFunctionPass()); super::add(P); super::add(createCheckDebugifyFunctionPass(true, Name, &DIStatsMap)); - break; - case PT_Module: + } else { + assert(DebugifyEach == DebugifyMode::OriginalDebugInfo && + "Must be original mode"); + super::add(createDebugifyFunctionPass(DebugifyMode::OriginalDebugInfo, + Name, &DIPreservationMap)); + super::add(P); + super::add(createCheckDebugifyFunctionPass( + false, Name, nullptr, DebugifyMode::OriginalDebugInfo, + &DIPreservationMap)); + } + break; + case PT_Module: + if (DebugifyEach == DebugifyMode::SyntheticDebugInfo) { super::add(createDebugifyModulePass()); super::add(P); super::add(createCheckDebugifyModulePass(true, Name, &DIStatsMap)); - break; - default: + } else { + assert(DebugifyEach == DebugifyMode::OriginalDebugInfo && + "Must be original mode"); + super::add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, + Name, &DIPreservationMap)); super::add(P); - break; + super::add(createCheckDebugifyModulePass( + false, Name, nullptr, DebugifyMode::OriginalDebugInfo, + &DIPreservationMap)); + } + break; + default: + super::add(P); + break; } } const DebugifyStatsMap &getDebugifyStatsMap() const { return DIStatsMap; } + DebugInfoPerPassMap &getDebugInfoPerPassMap() { + return DIPreservationMap; + } }; static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { @@ -754,7 +798,9 @@ RemarksFile.get(), PassPipeline, OK, VK, PreserveAssemblyUseListOrder, PreserveBitcodeUseListOrder, EmitSummaryIndex, - EmitModuleHash, EnableDebugify, Coroutines) + EmitModuleHash, + EnableDebugify == DebugifyMode::SyntheticDebugInfo, + Coroutines) ? 0 : 1; } @@ -762,7 +808,8 @@ // Create a PassManager to hold and optimize the collection of passes we are // about to build. OptCustomPassManager Passes; - bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach; + bool AddOneTimeDebugifyPasses = EnableDebugify != DebugifyMode::NoDebugify && + DebugifyEach == DebugifyMode::NoDebugify; // Add an appropriate TargetLibraryInfo pass for the module's triple. TargetLibraryInfoImpl TLII(ModuleTriple); @@ -789,8 +836,13 @@ Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); - if (AddOneTimeDebugifyPasses) - Passes.add(createDebugifyModulePass()); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify == DebugifyMode::SyntheticDebugInfo) + Passes.add(createDebugifyModulePass()); + else + Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "", + &(Passes.getDebugInfoPerPassMap()))); + } std::unique_ptr FPasses; if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || @@ -934,8 +986,14 @@ if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); - if (AddOneTimeDebugifyPasses) - Passes.add(createCheckDebugifyModulePass(false)); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify == DebugifyMode::SyntheticDebugInfo) + Passes.add(createCheckDebugifyModulePass(false)); + else + Passes.add(createCheckDebugifyModulePass( + false, "", nullptr, DebugifyMode::OriginalDebugInfo, + &(Passes.getDebugInfoPerPassMap()))); + } // 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 @@ -1009,7 +1067,8 @@ Out->os() << BOS->str(); } - if (DebugifyEach && !DebugifyExport.empty()) + if (DebugifyEach == DebugifyMode::SyntheticDebugInfo && + !DebugifyExport.empty()) exportDebugifyStats(DebugifyExport, Passes.getDebugifyStatsMap()); // Declare success.