Index: llvm/docs/HowToUpdateDebugInfo.rst =================================================================== --- llvm/docs/HowToUpdateDebugInfo.rst +++ llvm/docs/HowToUpdateDebugInfo.rst @@ -218,11 +218,20 @@ mutated to test debug info handling within that transformation. This is a simple way to test for proper debug info handling. -The ``debugify`` utility -^^^^^^^^^^^^^^^^^^^^^^^^ +The ``VerifyDIPreserve`` utility passes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The ``debugify`` testing utility is just a pair of passes: ``debugify`` and -``check-debugify``. +The ``VerifyDIPreserve`` testing utility is just a pair of passes: +``prepare-debuginfo-for-verify`` and ``check-debuginfo``. It works in two modes: + +* `debugify` - attaches synthetic debug info to everything and tests its + preservation (default mode) + +* `original-di-check` - collects and checks the preservation of real debug info + metadata + +The ``debugify`` 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 @@ -244,7 +253,7 @@ ret void } -and after running ``opt -debugify``: +and after running ``opt -prepare-debuginfo-for-verify``: .. code-block:: llvm @@ -281,50 +290,68 @@ !15 = !DILocation(line: 4, column: 1, scope: !6) !16 = !DILocation(line: 5, column: 1, scope: !6) -Using ``debugify`` -^^^^^^^^^^^^^^^^^^ +The ``original-di-check`` mode +****************************** + +The ``VerifyDIPreserve`` utility can operate in the `original-di-check` 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 -verify-debuginfo-preserve -pass-to-test sample.ll + +Using ``VerifyDIPreserve`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ -A simple way to use ``debugify`` is as follows: +A simple way to use ``VerifyDIPreserve`` in ``debugify`` mode is as follows: .. code-block:: bash - $ opt -debugify -pass-to-test -check-debugify sample.ll + $ opt -prepare-debuginfo-for-verify -pass-to-test -check-debuginfo sample.ll This will inject synthetic DI to ``sample.ll`` run the ``pass-to-test`` and -then check for missing DI. The ``-check-debugify`` step can of course be +then check for missing DI. The ``-check-debuginfo`` step can of course be omitted in favor of more customizable FileCheck directives. -Some other ways to run debugify are available: +Some other ways to run the verifier are available: .. code-block:: bash # Same as the above example. $ opt -enable-debugify -pass-to-test sample.ll + # Run the pass in original-di-check mode. + $ opt -verify-debuginfo-preserve -pass-to-test sample.ll + # Suppresses verbose debugify output. $ opt -enable-debugify -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). + # Prepend -prepare-debuginfo-for-verify before and append -check-debuginfo + # -strip after each pass on the pipeline (similar to -verify-each). $ opt -debugify-each -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. + # Check the preservation of original Debug Info after each pass. + $ opt -verify-each-debuginfo-preserve -O2 sample.ll + +In order for ``check-debuginfo`` in `debugify` mode to work, the DI must be +coming from ``debugify``. Thus, modules with existing DI will be skipped. -``debugify`` can be used to test a backend, e.g: +``debugify`` mode can be used to test a backend, e.g: .. code-block:: bash - $ opt -debugify < sample.ll | llc -o - + $ opt -prepare-debuginfo-for-verify < sample.ll | llc -o - There is also a MIR-level debugify pass that can be run before each backend pass, see: :ref:`Mutation testing for MIR-level transformations`. -``debugify`` in regression tests -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``debugify`` mode in regression tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The output of the ``debugify`` pass must be stable enough to use in regression +The output of the ``debugify`` must be stable enough to use in regression tests. Changes to this pass are not allowed to break existing tests. .. note:: Index: llvm/include/llvm/Transforms/Utils/Debugify.h =================================================================== --- llvm/include/llvm/Transforms/Utils/Debugify.h +++ /dev/null @@ -1,146 +0,0 @@ -//===- Debugify.h - Attach synthetic debug info to everything -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file Interface to the `debugify` synthetic debug info testing utility. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TRANSFORM_UTILS_DEBUGIFY_H -#define LLVM_TRANSFORM_UTILS_DEBUGIFY_H - -#include "llvm/ADT/MapVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Bitcode/BitcodeWriterPass.h" -#include "llvm/IR/IRPrintingPasses.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/PassManager.h" - -namespace llvm { -class DIBuilder; - -/// Add synthesized debug information to a module. -/// -/// \param M The module to add debug information to. -/// \param Functions A range of functions to add debug information to. -/// \param Banner A prefix string to add to debug/error messages. -/// \param ApplyToMF A call back that will add debug information to the -/// MachineFunction for a Function. If nullptr, then the -/// MachineFunction (if any) will not be modified. -bool applyDebugifyMetadata( - Module &M, iterator_range Functions, StringRef Banner, - std::function ApplyToMF); - -/// Strip out all of the metadata and debug info inserted by debugify. If no -/// llvm.debugify module-level named metadata is present, this is a no-op. -/// Returns true if any change was made. -bool stripDebugifyMetadata(Module &M); - -} // namespace llvm - -llvm::ModulePass *createDebugifyModulePass(); -llvm::FunctionPass *createDebugifyFunctionPass(); - -struct NewPMDebugifyPass : public llvm::PassInfoMixin { - llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); -}; - -/// Track how much `debugify` information has been lost. -struct DebugifyStatistics { - /// Number of missing dbg.values. - unsigned NumDbgValuesMissing = 0; - - /// Number of dbg.values expected. - unsigned NumDbgValuesExpected = 0; - - /// Number of instructions with empty debug locations. - unsigned NumDbgLocsMissing = 0; - - /// Number of instructions expected to have debug locations. - unsigned NumDbgLocsExpected = 0; - - /// Get the ratio of missing/expected dbg.values. - float getMissingValueRatio() const { - return float(NumDbgValuesMissing) / float(NumDbgLocsExpected); - } - - /// Get the ratio of missing/expected instructions with locations. - float getEmptyLocationRatio() const { - return float(NumDbgLocsMissing) / float(NumDbgLocsExpected); - } -}; - -/// 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); - -struct NewPMCheckDebugifyPass - : public llvm::PassInfoMixin { - llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); -}; - -namespace llvm { -/// DebugifyCustomPassManager wraps each pass with the debugify passes if -/// needed. -/// NOTE: We support legacy custom pass manager only. -/// TODO: Add New PM support for custom pass manager. -class DebugifyCustomPassManager : public legacy::PassManager { - DebugifyStatsMap DIStatsMap; - bool EnableDebugifyEach = false; - -public: - using super = legacy::PassManager; - - 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 = EnableDebugifyEach && !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. - PassKind Kind = P->getPassKind(); - StringRef Name = P->getPassName(); - - // TODO: Implement Debugify for LoopPass. - switch (Kind) { - case PT_Function: - super::add(createDebugifyFunctionPass()); - super::add(P); - super::add(createCheckDebugifyFunctionPass(true, Name, &DIStatsMap)); - break; - case PT_Module: - super::add(createDebugifyModulePass()); - super::add(P); - super::add(createCheckDebugifyModulePass(true, Name, &DIStatsMap)); - break; - default: - super::add(P); - break; - } - } - - void enableDebugifyEach() { EnableDebugifyEach = true; } - - const DebugifyStatsMap &getDebugifyStatsMap() const { return DIStatsMap; } -}; -} // namespace llvm - -#endif // LLVM_TRANSFORM_UTILS_DEBUGIFY_H Index: llvm/include/llvm/Transforms/Utils/VerifyDIPreserve.h =================================================================== --- /dev/null +++ llvm/include/llvm/Transforms/Utils/VerifyDIPreserve.h @@ -0,0 +1,226 @@ +//===- VerifyDIPreserve.h - Check debug info preservation -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file Interface to the debug info testing utility pass. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORM_UTILS_VDIP_H +#define LLVM_TRANSFORM_UTILS_VDIP_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/ValueHandle.h" + +using DebugFnMap = llvm::DenseMap; +using DebugInstMap = llvm::DenseMap; +using WeakInstValueMap = + llvm::DenseMap; + +/// Used to track the Debug Info Metadata information. +struct DebugInfoPerPass { + // This maps a function name to its associated DISubprogram. + DebugFnMap DIFunctions; + // This maps an instruction and the info about whether it has !dbg attached. + DebugInstMap DILocations; + // This tracks value (instruction) deletion. If an instruction gets deleted, + // WeakVH nulls itself. + WeakInstValueMap InstToDelete; +}; + +/// Map pass names to a per-pass DebugInfoPerPass instance. +using DebugInfoPerPassMap = llvm::MapVector; + +namespace llvm { +class DIBuilder; + +/// Add synthesized debug information to a module. +/// +/// \param M The module to add debug information to. +/// \param Functions A range of functions to add debug information to. +/// \param Banner A prefix string to add to debug/error messages. +/// \param ApplyToMF A call back that will add debug information to the +/// MachineFunction for a Function. If nullptr, then the +/// MachineFunction (if any) will not be modified. +bool applyDebugifyMetadata( + Module &M, iterator_range Functions, StringRef Banner, + std::function ApplyToMF); + +/// Strip out all of the metadata and debug info inserted by debugify. If no +/// llvm.debugify module-level named metadata is present, this is a no-op. +/// 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 + +/// Used to check whether we track synthetic or original debug info. +enum class VerifyDIPreserveMode { + NoVerify, + Debugify, + OriginalDebugInfo +}; + +llvm::ModulePass *createPrepareDIModulePass( + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + llvm::StringRef NameOfWrappedPass = "", + DebugInfoPerPassMap *DIPreservationMap = nullptr); +llvm::FunctionPass *createPrepareDIFunctionPass( + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + 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. +struct DebugifyStatistics { + /// Number of missing dbg.values. + unsigned NumDbgValuesMissing = 0; + + /// Number of dbg.values expected. + unsigned NumDbgValuesExpected = 0; + + /// Number of instructions with empty debug locations. + unsigned NumDbgLocsMissing = 0; + + /// Number of instructions expected to have debug locations. + unsigned NumDbgLocsExpected = 0; + + /// Get the ratio of missing/expected dbg.values. + float getMissingValueRatio() const { + return float(NumDbgValuesMissing) / float(NumDbgLocsExpected); + } + + /// Get the ratio of missing/expected instructions with locations. + float getEmptyLocationRatio() const { + return float(NumDbgLocsMissing) / float(NumDbgLocsExpected); + } +}; + +/// Map pass names to a per-pass DebugifyStatistics instance. +using DebugifyStatsMap = llvm::MapVector; + +llvm::ModulePass *createCheckDIModulePass( + bool Strip = false, llvm::StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + DebugInfoPerPassMap *DIPreservationMap = nullptr); + +llvm::FunctionPass *createCheckDIFunctionPass( + bool Strip = false, llvm::StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + DebugInfoPerPassMap *DIPreservationMap = nullptr); + +struct NewPMCheckDebugifyPass + : public llvm::PassInfoMixin { + llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); +}; + +namespace llvm { +/// VerifyDIPreserveCustomPassManager wraps each pass with +/// the verify-di-preservation passes if needed. +/// NOTE: We support legacy custom pass manager only. +/// TODO: Add New PM support for custom pass manager. +class VerifyDIPreserveCustomPassManager : public legacy::PassManager { + DebugifyStatsMap *DIStatsMap = nullptr; + DebugInfoPerPassMap *DIPreservationMap = nullptr; + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::NoVerify; + +public: + using super = legacy::PassManager; + + void add(Pass *P) override { + // Wrap each pass with the debugify passes if requested, making + // exceptions for passes which shouldn't see the debugify instrumentation. + bool WrapWithVerifyDIPreservation = + Mode != VerifyDIPreserveMode::NoVerify && !P->getAsImmutablePass() && + !isIRPrintingPass(P) && !isBitcodeWriterPass(P); + if (!WrapWithVerifyDIPreservation) { + super::add(P); + return; + } + + // Either apply the 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 the debug info verifiers for LoopPass. + switch (Kind) { + case PT_Function: + super::add(createPrepareDIFunctionPass(Mode, Name, DIPreservationMap)); + super::add(P); + super::add(createCheckDIFunctionPass(isDebugify(), Name, DIStatsMap, Mode, + DIPreservationMap)); + break; + case PT_Module: + super::add(createPrepareDIModulePass(Mode, Name, DIPreservationMap)); + super::add(P); + super::add(createCheckDIModulePass(isDebugify(), Name, DIStatsMap, Mode, + DIPreservationMap)); + break; + default: + super::add(P); + break; + } + } + + // Used within Debugify mode. + void setDIStatsMap(DebugifyStatsMap &StatMap) { + DIStatsMap = &StatMap; + } + // Used within original di preservation checking mode. + void setDIPreservationMap(DebugInfoPerPassMap &PerPassMap) { + DIPreservationMap = &PerPassMap; + } + void setVerifyDIPreserveMode(enum VerifyDIPreserveMode M) { + Mode = M; + } + + bool isDebugify() const { return Mode == VerifyDIPreserveMode::Debugify; } + bool isOriginalDebugInfoPreserveVerify() const { + return Mode == VerifyDIPreserveMode::OriginalDebugInfo; + } + + const DebugifyStatsMap &getDebugifyStatsMap() const { return *DIStatsMap; } + DebugInfoPerPassMap &getDebugInfoPerPassMap() { return *DIPreservationMap; } +}; +} // namespace llvm + +#endif // LLVM_TRANSFORM_UTILS_VDIP_H Index: llvm/lib/CodeGen/MachineDebugify.cpp =================================================================== --- llvm/lib/CodeGen/MachineDebugify.cpp +++ llvm/lib/CodeGen/MachineDebugify.cpp @@ -25,7 +25,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/InitializePasses.h" -#include "llvm/Transforms/Utils/Debugify.h" +#include "llvm/Transforms/Utils/VerifyDIPreserve.h" #define DEBUG_TYPE "mir-debugify" Index: llvm/lib/CodeGen/MachineStripDebug.cpp =================================================================== --- llvm/lib/CodeGen/MachineStripDebug.cpp +++ llvm/lib/CodeGen/MachineStripDebug.cpp @@ -16,7 +16,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/InitializePasses.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Transforms/Utils/Debugify.h" +#include "llvm/Transforms/Utils/VerifyDIPreserve.h" #define DEBUG_TYPE "mir-strip-debug" Index: llvm/lib/Transforms/Utils/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/Utils/CMakeLists.txt +++ llvm/lib/Transforms/Utils/CMakeLists.txt @@ -16,7 +16,6 @@ CodeExtractor.cpp CodeMoverUtils.cpp CtorUtils.cpp - Debugify.cpp DemoteRegToStack.cpp EntryExitInstrumenter.cpp EscapeEnumerator.cpp @@ -71,6 +70,7 @@ UniqueInternalLinkageNames.cpp Utils.cpp ValueMapper.cpp + VerifyDIPreserve.cpp VNCoercion.cpp ADDITIONAL_HEADER_DIRS Index: llvm/lib/Transforms/Utils/Debugify.cpp =================================================================== --- llvm/lib/Transforms/Utils/Debugify.cpp +++ /dev/null @@ -1,520 +0,0 @@ -//===- Debugify.cpp - Attach synthetic debug info to everything -----------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file 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/DIBuilder.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Support/CommandLine.h" - -using namespace llvm; - -namespace { - -cl::opt Quiet("debugify-quiet", - cl::desc("Suppress verbose debugify output")); - -enum class Level { - Locations, - LocationsAndVariables -}; -cl::opt DebugifyLevel( - "debugify-level", cl::desc("Kind of debug info to add"), - cl::values(clEnumValN(Level::Locations, "locations", "Locations only"), - clEnumValN(Level::LocationsAndVariables, "location+variables", - "Locations and Variables")), - cl::init(Level::LocationsAndVariables)); - -raw_ostream &dbg() { return Quiet ? nulls() : errs(); } - -uint64_t getAllocSizeInBits(Module &M, Type *Ty) { - return Ty->isSized() ? M.getDataLayout().getTypeAllocSizeInBits(Ty) : 0; -} - -bool isFunctionSkipped(Function &F) { - return F.isDeclaration() || !F.hasExactDefinition(); -} - -/// Find the basic block's terminating instruction. -/// -/// Special care is needed to handle musttail and deopt calls, as these behave -/// like (but are in fact not) terminators. -Instruction *findTerminatingInstruction(BasicBlock &BB) { - if (auto *I = BB.getTerminatingMustTailCall()) - return I; - if (auto *I = BB.getTerminatingDeoptimizeCall()) - return I; - return BB.getTerminator(); -} -} // end anonymous namespace - -bool llvm::applyDebugifyMetadata( - Module &M, iterator_range Functions, StringRef Banner, - std::function ApplyToMF) { - // Skip modules with debug info. - if (M.getNamedMetadata("llvm.dbg.cu")) { - dbg() << Banner << "Skipping module with debug info\n"; - return false; - } - - DIBuilder DIB(M); - LLVMContext &Ctx = M.getContext(); - auto *Int32Ty = Type::getInt32Ty(Ctx); - - // Get a DIType which corresponds to Ty. - DenseMap TypeCache; - auto getCachedDIType = [&](Type *Ty) -> DIType * { - uint64_t Size = getAllocSizeInBits(M, 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, File, "debugify", - /*isOptimized=*/true, "", 0); - - // Visit each instruction. - for (Function &F : Functions) { - if (isFunctionSkipped(F)) - continue; - - bool InsertedDbgVal = false; - auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); - DISubprogram::DISPFlags SPFlags = - DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized; - if (F.hasPrivateLinkage() || F.hasInternalLinkage()) - SPFlags |= DISubprogram::SPFlagLocalToUnit; - auto SP = DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, - SPType, NextLine, DINode::FlagZero, SPFlags); - F.setSubprogram(SP); - - // Helper that inserts a dbg.value before \p InsertBefore, copying the - // location (and possibly the type, if it's non-void) from \p TemplateInst. - auto insertDbgVal = [&](Instruction &TemplateInst, - Instruction *InsertBefore) { - std::string Name = utostr(NextVar++); - Value *V = &TemplateInst; - if (TemplateInst.getType()->isVoidTy()) - V = ConstantInt::get(Int32Ty, 0); - const DILocation *Loc = TemplateInst.getDebugLoc().get(); - auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(), - getCachedDIType(V->getType()), - /*AlwaysPreserve=*/true); - DIB.insertDbgValueIntrinsic(V, LocalVar, DIB.createExpression(), Loc, - InsertBefore); - }; - - for (BasicBlock &BB : F) { - // Attach debug locations. - for (Instruction &I : BB) - I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP)); - - if (DebugifyLevel < Level::LocationsAndVariables) - continue; - - // Inserting debug values into EH pads can break IR invariants. - if (BB.isEHPad()) - continue; - - // Find the terminating instruction, after which no debug values are - // attached. - Instruction *LastInst = findTerminatingInstruction(BB); - assert(LastInst && "Expected basic block with a terminator"); - - // Maintain an insertion point which can't be invalidated when updates - // are made. - BasicBlock::iterator InsertPt = BB.getFirstInsertionPt(); - assert(InsertPt != BB.end() && "Expected to find an insertion point"); - Instruction *InsertBefore = &*InsertPt; - - // Attach debug values. - for (Instruction *I = &*BB.begin(); I != LastInst; I = I->getNextNode()) { - // Skip void-valued instructions. - if (I->getType()->isVoidTy()) - continue; - - // Phis and EH pads must be grouped at the beginning of the block. - // Only advance the insertion point when we finish visiting these. - if (!isa(I) && !I->isEHPad()) - InsertBefore = I->getNextNode(); - - insertDbgVal(*I, InsertBefore); - InsertedDbgVal = true; - } - } - // Make sure we emit at least one dbg.value, otherwise MachineDebugify may - // not have anything to work with as it goes about inserting DBG_VALUEs. - // (It's common for MIR tests to be written containing skeletal IR with - // empty functions -- we're still interested in debugifying the MIR within - // those tests, and this helps with that.) - if (DebugifyLevel == Level::LocationsAndVariables && !InsertedDbgVal) { - auto *Term = findTerminatingInstruction(F.getEntryBlock()); - insertDbgVal(*Term, Term); - } - if (ApplyToMF) - ApplyToMF(DIB, F); - DIB.finalizeSubprogram(SP); - } - DIB.finalize(); - - // Track the number of distinct lines and variables. - NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify"); - auto addDebugifyOperand = [&](unsigned N) { - NMD->addOperand(MDNode::get( - Ctx, ValueAsMetadata::getConstant(ConstantInt::get(Int32Ty, N)))); - }; - 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!"); - - // Claim that this synthetic debug info is valid. - StringRef DIVersionKey = "Debug Info Version"; - if (!M.getModuleFlag(DIVersionKey)) - M.addModuleFlag(Module::Warning, DIVersionKey, DEBUG_METADATA_VERSION); - - return true; -} - -bool llvm::stripDebugifyMetadata(Module &M) { - bool Changed = false; - - // Remove the llvm.debugify module-level named metadata. - NamedMDNode *DebugifyMD = M.getNamedMetadata("llvm.debugify"); - if (DebugifyMD) { - M.eraseNamedMetadata(DebugifyMD); - Changed = true; - } - - // Strip out all debug intrinsics and supporting metadata (subprograms, types, - // variables, etc). - Changed |= StripDebugInfo(M); - - // Strip out the dead dbg.value prototype. - Function *DbgValF = M.getFunction("llvm.dbg.value"); - if (DbgValF) { - assert(DbgValF->isDeclaration() && DbgValF->use_empty() && - "Not all debug info stripped?"); - DbgValF->eraseFromParent(); - Changed = true; - } - - // Strip out the module-level Debug Info Version metadata. - // FIXME: There must be an easier way to remove an operand from a NamedMDNode. - NamedMDNode *NMD = M.getModuleFlagsMetadata(); - if (!NMD) - return Changed; - SmallVector Flags; - for (MDNode *Flag : NMD->operands()) - Flags.push_back(Flag); - NMD->clearOperands(); - for (MDNode *Flag : Flags) { - MDString *Key = dyn_cast_or_null(Flag->getOperand(1)); - if (Key->getString() == "Debug Info Version") { - Changed = true; - continue; - } - NMD->addOperand(Flag); - } - // If we left it empty we might as well remove it. - if (NMD->getNumOperands() == 0) - NMD->eraseFromParent(); - - return Changed; -} - -namespace { -/// Return true if a mis-sized diagnostic is issued for \p DVI. -bool diagnoseMisSizedDbgValue(Module &M, DbgValueInst *DVI) { - // The size of a dbg.value's value operand should match the size of the - // variable it corresponds to. - // - // TODO: This, along with a check for non-null value operands, should be - // promoted to verifier failures. - Value *V = DVI->getValue(); - if (!V) - return false; - - // For now, don't try to interpret anything more complicated than an empty - // DIExpression. Eventually we should try to handle OP_deref and fragments. - if (DVI->getExpression()->getNumElements()) - return false; - - Type *Ty = V->getType(); - uint64_t ValueOperandSize = getAllocSizeInBits(M, Ty); - Optional DbgVarSize = DVI->getFragmentSizeInBits(); - if (!ValueOperandSize || !DbgVarSize) - return false; - - bool HasBadSize = false; - if (Ty->isIntegerTy()) { - auto Signedness = DVI->getVariable()->getSignedness(); - if (Signedness && *Signedness == DIBasicType::Signedness::Signed) - HasBadSize = ValueOperandSize < *DbgVarSize; - } else { - HasBadSize = ValueOperandSize != *DbgVarSize; - } - - if (HasBadSize) { - dbg() << "ERROR: dbg.value operand has size " << ValueOperandSize - << ", but its variable has size " << *DbgVarSize << ": "; - DVI->print(dbg()); - dbg() << "\n"; - } - return HasBadSize; -} - -bool checkDebugifyMetadata(Module &M, - iterator_range Functions, - StringRef NameOfWrappedPass, StringRef Banner, - bool Strip, DebugifyStatsMap *StatsMap) { - // Skip modules without debugify metadata. - NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify"); - if (!NMD) { - dbg() << Banner << ": Skipping module without debugify metadata\n"; - return false; - } - - auto getDebugifyOperand = [&](unsigned Idx) -> unsigned { - return mdconst::extract(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; - - // Track debug info loss statistics if able. - DebugifyStatistics *Stats = nullptr; - if (StatsMap && !NameOfWrappedPass.empty()) - Stats = &StatsMap->operator[](NameOfWrappedPass); - - BitVector MissingLines{OriginalNumLines, true}; - BitVector MissingVars{OriginalNumVars, true}; - for (Function &F : Functions) { - if (isFunctionSkipped(F)) - continue; - - // Find missing lines. - for (Instruction &I : instructions(F)) { - if (isa(&I) || isa(&I)) - continue; - - auto DL = I.getDebugLoc(); - if (DL && DL.getLine() != 0) { - MissingLines.reset(DL.getLine() - 1); - continue; - } - - if (!DL) { - dbg() << "WARNING: Instruction with empty DebugLoc in function "; - dbg() << F.getName() << " --"; - I.print(dbg()); - dbg() << "\n"; - } - } - - // Find missing variables and mis-sized debug values. - for (Instruction &I : instructions(F)) { - auto *DVI = dyn_cast(&I); - if (!DVI) - continue; - - unsigned Var = ~0U; - (void)to_integer(DVI->getVariable()->getName(), Var, 10); - assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable"); - bool HasBadSize = diagnoseMisSizedDbgValue(M, DVI); - if (!HasBadSize) - MissingVars.reset(Var - 1); - HasErrors |= HasBadSize; - } - } - - // Print the results. - for (unsigned Idx : MissingLines.set_bits()) - dbg() << "WARNING: Missing line " << Idx + 1 << "\n"; - - for (unsigned Idx : MissingVars.set_bits()) - dbg() << "WARNING: Missing variable " << Idx + 1 << "\n"; - - // Update DI loss statistics. - if (Stats) { - Stats->NumDbgLocsExpected += OriginalNumLines; - Stats->NumDbgLocsMissing += MissingLines.count(); - Stats->NumDbgValuesExpected += OriginalNumVars; - Stats->NumDbgValuesMissing += MissingVars.count(); - } - - dbg() << Banner; - if (!NameOfWrappedPass.empty()) - dbg() << " [" << NameOfWrappedPass << "]"; - dbg() << ": " << (HasErrors ? "FAIL" : "PASS") << '\n'; - - // Strip debugify metadata if required. - if (Strip) - return stripDebugifyMetadata(M); - - return false; -} - -/// 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: ", /*ApplyToMF*/ nullptr); - } - - 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: ", /*ApplyToMF*/ nullptr); - } - - DebugifyFunctionPass() : FunctionPass(ID) {} - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - - static char ID; // Pass identification. -}; - -/// 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); - } - - CheckDebugifyModulePass(bool Strip = false, StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr) - : ModulePass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), - StatsMap(StatsMap) {} - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - - static char ID; // Pass identification. - -private: - bool Strip; - StringRef NameOfWrappedPass; - DebugifyStatsMap *StatsMap; -}; - -/// 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)), - NameOfWrappedPass, "CheckFunctionDebugify", - Strip, StatsMap); - } - - CheckDebugifyFunctionPass(bool Strip = false, - StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr) - : FunctionPass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), - StatsMap(StatsMap) {} - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - - static char ID; // Pass identification. - -private: - bool Strip; - StringRef NameOfWrappedPass; - DebugifyStatsMap *StatsMap; -}; - -} // end anonymous namespace - -ModulePass *createDebugifyModulePass() { return new DebugifyModulePass(); } - -FunctionPass *createDebugifyFunctionPass() { - return new DebugifyFunctionPass(); -} - -PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) { - applyDebugifyMetadata(M, M.functions(), - "ModuleDebugify: ", /*ApplyToMF*/ nullptr); - return PreservedAnalyses::all(); -} - -ModulePass *createCheckDebugifyModulePass(bool Strip, - StringRef NameOfWrappedPass, - DebugifyStatsMap *StatsMap) { - return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap); -} - -FunctionPass *createCheckDebugifyFunctionPass(bool Strip, - StringRef NameOfWrappedPass, - DebugifyStatsMap *StatsMap) { - return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap); -} - -PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, - ModuleAnalysisManager &) { - checkDebugifyMetadata(M, M.functions(), "", "CheckModuleDebugify", false, - nullptr); - return PreservedAnalyses::all(); -} - -char DebugifyModulePass::ID = 0; -static RegisterPass DM("debugify", - "Attach debug info to everything"); - -char CheckDebugifyModulePass::ID = 0; -static RegisterPass - CDM("check-debugify", "Check debug info from -debugify"); - -char DebugifyFunctionPass::ID = 0; -static RegisterPass DF("debugify-function", - "Attach debug info to a function"); - -char CheckDebugifyFunctionPass::ID = 0; -static RegisterPass - CDF("check-debugify-function", "Check debug info from -debugify-function"); Index: llvm/lib/Transforms/Utils/VerifyDIPreserve.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/Utils/VerifyDIPreserve.cpp @@ -0,0 +1,816 @@ +//===- VerifyDIPreserve.cpp - Check debug info preservation ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file Verify Debug Info Preserve contains two modes: +/// 1) debugify - works with synthetic debug info +/// 2) original-di-check - works with original debug info +/// The debugify attaches synthetic debug info to everything. It can be used +/// to create targeted tests for debug info preservation. +/// The original-di-check mode checks original debug info (-g +/// generated) preservation. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/VerifyDIPreserve.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" + +#define DEBUG_TYPE "verify-di-preserve" + +using namespace llvm; + +namespace { + +cl::opt Quiet("debugify-quiet", + cl::desc("Suppress verbose debugify output")); + +enum class Level { + Locations, + LocationsAndVariables +}; +cl::opt DebugifyLevel( + "debugify-level", cl::desc("Kind of debug info to add"), + cl::values(clEnumValN(Level::Locations, "locations", "Locations only"), + clEnumValN(Level::LocationsAndVariables, "location+variables", + "Locations and Variables")), + cl::init(Level::LocationsAndVariables)); + +raw_ostream &dbg() { return Quiet ? nulls() : errs(); } + +uint64_t getAllocSizeInBits(Module &M, Type *Ty) { + return Ty->isSized() ? M.getDataLayout().getTypeAllocSizeInBits(Ty) : 0; +} + +bool isFunctionSkipped(Function &F) { + return F.isDeclaration() || !F.hasExactDefinition(); +} + +/// Find the basic block's terminating instruction. +/// +/// Special care is needed to handle musttail and deopt calls, as these behave +/// like (but are in fact not) terminators. +Instruction *findTerminatingInstruction(BasicBlock &BB) { + if (auto *I = BB.getTerminatingMustTailCall()) + return I; + if (auto *I = BB.getTerminatingDeoptimizeCall()) + return I; + return BB.getTerminator(); +} +} // end anonymous namespace + +bool llvm::applyDebugifyMetadata( + Module &M, iterator_range Functions, StringRef Banner, + std::function ApplyToMF) { + // Skip modules with debug info. + if (M.getNamedMetadata("llvm.dbg.cu")) { + dbg() << Banner << "Skipping module with debug info\n"; + return false; + } + + DIBuilder DIB(M); + LLVMContext &Ctx = M.getContext(); + auto *Int32Ty = Type::getInt32Ty(Ctx); + + // Get a DIType which corresponds to Ty. + DenseMap TypeCache; + auto getCachedDIType = [&](Type *Ty) -> DIType * { + uint64_t Size = getAllocSizeInBits(M, 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, File, "debugify", + /*isOptimized=*/true, "", 0); + + // Visit each instruction. + for (Function &F : Functions) { + if (isFunctionSkipped(F)) + continue; + + bool InsertedDbgVal = false; + auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); + DISubprogram::DISPFlags SPFlags = + DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized; + if (F.hasPrivateLinkage() || F.hasInternalLinkage()) + SPFlags |= DISubprogram::SPFlagLocalToUnit; + auto SP = DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, + SPType, NextLine, DINode::FlagZero, SPFlags); + F.setSubprogram(SP); + + // Helper that inserts a dbg.value before \p InsertBefore, copying the + // location (and possibly the type, if it's non-void) from \p TemplateInst. + auto insertDbgVal = [&](Instruction &TemplateInst, + Instruction *InsertBefore) { + std::string Name = utostr(NextVar++); + Value *V = &TemplateInst; + if (TemplateInst.getType()->isVoidTy()) + V = ConstantInt::get(Int32Ty, 0); + const DILocation *Loc = TemplateInst.getDebugLoc().get(); + auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(), + getCachedDIType(V->getType()), + /*AlwaysPreserve=*/true); + DIB.insertDbgValueIntrinsic(V, LocalVar, DIB.createExpression(), Loc, + InsertBefore); + }; + + for (BasicBlock &BB : F) { + // Attach debug locations. + for (Instruction &I : BB) + I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP)); + + if (DebugifyLevel < Level::LocationsAndVariables) + continue; + + // Inserting debug values into EH pads can break IR invariants. + if (BB.isEHPad()) + continue; + + // Find the terminating instruction, after which no debug values are + // attached. + Instruction *LastInst = findTerminatingInstruction(BB); + assert(LastInst && "Expected basic block with a terminator"); + + // Maintain an insertion point which can't be invalidated when updates + // are made. + BasicBlock::iterator InsertPt = BB.getFirstInsertionPt(); + assert(InsertPt != BB.end() && "Expected to find an insertion point"); + Instruction *InsertBefore = &*InsertPt; + + // Attach debug values. + for (Instruction *I = &*BB.begin(); I != LastInst; I = I->getNextNode()) { + // Skip void-valued instructions. + if (I->getType()->isVoidTy()) + continue; + + // Phis and EH pads must be grouped at the beginning of the block. + // Only advance the insertion point when we finish visiting these. + if (!isa(I) && !I->isEHPad()) + InsertBefore = I->getNextNode(); + + insertDbgVal(*I, InsertBefore); + InsertedDbgVal = true; + } + } + // Make sure we emit at least one dbg.value, otherwise MachineDebugify may + // not have anything to work with as it goes about inserting DBG_VALUEs. + // (It's common for MIR tests to be written containing skeletal IR with + // empty functions -- we're still interested in debugifying the MIR within + // those tests, and this helps with that.) + if (DebugifyLevel == Level::LocationsAndVariables && !InsertedDbgVal) { + auto *Term = findTerminatingInstruction(F.getEntryBlock()); + insertDbgVal(*Term, Term); + } + if (ApplyToMF) + ApplyToMF(DIB, F); + DIB.finalizeSubprogram(SP); + } + DIB.finalize(); + + // Track the number of distinct lines and variables. + NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify"); + auto addDebugifyOperand = [&](unsigned N) { + NMD->addOperand(MDNode::get( + Ctx, ValueAsMetadata::getConstant(ConstantInt::get(Int32Ty, N)))); + }; + 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!"); + + // Claim that this synthetic debug info is valid. + StringRef DIVersionKey = "Debug Info Version"; + if (!M.getModuleFlag(DIVersionKey)) + M.addModuleFlag(Module::Warning, DIVersionKey, DEBUG_METADATA_VERSION); + + return true; +} + +bool llvm::stripDebugifyMetadata(Module &M) { + bool Changed = false; + + // Remove the llvm.debugify module-level named metadata. + NamedMDNode *DebugifyMD = M.getNamedMetadata("llvm.debugify"); + if (DebugifyMD) { + M.eraseNamedMetadata(DebugifyMD); + Changed = true; + } + + // Strip out all debug intrinsics and supporting metadata (subprograms, types, + // variables, etc). + Changed |= StripDebugInfo(M); + + // Strip out the dead dbg.value prototype. + Function *DbgValF = M.getFunction("llvm.dbg.value"); + if (DbgValF) { + assert(DbgValF->isDeclaration() && DbgValF->use_empty() && + "Not all debug info stripped?"); + DbgValF->eraseFromParent(); + Changed = true; + } + + // Strip out the module-level Debug Info Version metadata. + // FIXME: There must be an easier way to remove an operand from a NamedMDNode. + NamedMDNode *NMD = M.getModuleFlagsMetadata(); + if (!NMD) + return Changed; + SmallVector Flags; + for (MDNode *Flag : NMD->operands()) + Flags.push_back(Flag); + NMD->clearOperands(); + for (MDNode *Flag : Flags) { + MDString *Key = dyn_cast_or_null(Flag->getOperand(1)); + if (Key->getString() == "Debug Info Version") { + Changed = true; + continue; + } + NMD->addOperand(Flag); + } + // If we left it empty we might as well remove it. + if (NMD->getNumOperands() == 0) + NMD->eraseFromParent(); + + 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. + auto *SP = F.getSubprogram(); + DIPreservationMap[NameOfWrappedPass].DIFunctions.insert( + {F.getName(), SP}); + if (SP) + LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n'); + + for (BasicBlock &BB : F) { + // Collect debug locations (!dbg). + // 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'); + DIPreservationMap[NameOfWrappedPass].InstToDelete.insert({&I, &I}); + + 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(const DebugFnMap &DIFunctionsBefore, + const DebugFnMap &DIFunctionsAfter, + StringRef NameOfWrappedPass, + StringRef FileNameFromCU) { + bool Preserved = true; + for (const auto &F : DIFunctionsAfter) { + if (F.second) + continue; + auto SPIt = DIFunctionsBefore.find(F.first); + if (SPIt == DIFunctionsBefore.end()) { + dbg() << "ERROR: " << NameOfWrappedPass + << " did not generate DISubprogram for " << F.first << " from " + << FileNameFromCU << '\n'; + Preserved = false; + } else { + auto SP = SPIt->second; + if (!SP) + continue; + // If the function had the SP attached before the pass, consider it as + // a debug info bug. + dbg() << "ERROR: " << NameOfWrappedPass << " dropped DISubprogram of " + << F.first << " from " << FileNameFromCU << '\n'; + Preserved = false; + } + } + + return Preserved; +} + +// This checks the preservation of the original debug info attached to +// instructions. +static bool checkInstructions(const DebugInstMap &DILocsBefore, + const DebugInstMap &DILocsAfter, + const WeakInstValueMap &InstToDelete, + StringRef NameOfWrappedPass, + StringRef FileNameFromCU) { + bool Preserved = true; + for (const auto &L : DILocsAfter) { + if (L.second) + continue; + auto Instr = L.first; + + // In order to avoid pointer reuse/recycling, skip the values that might + // have been deleted during a pass. + auto WeakInstrPtr = InstToDelete.find(Instr); + if (WeakInstrPtr != InstToDelete.end() && !WeakInstrPtr->second) + continue; + + 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() << "WARNING: " << NameOfWrappedPass + << " did not generate DILocation for " << *Instr + << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; + Preserved = false; + } else { + if (!InstrIt->second) + continue; + // If the instr had the !dbg attached before the pass, consider it as + // a debug info issue. + dbg() << "WARNING: " << NameOfWrappedPass << " dropped DILocation of " + << *Instr << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; + 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. + auto *SP = F.getSubprogram(); + DIPreservationMap[NameOfWrappedPass].DIFunctions.insert( + {F.getName(), SP}); + if (SP) + LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n'); + + 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; + + auto InstToDelete = DIPreservationAfter[NameOfWrappedPass].InstToDelete; + + bool Result = checkFunctions(DIFunctionsBefore, DIFunctionsAfter, + NameOfWrappedPass, FileNameFromCU) && + checkInstructions(DILocsBefore, DILocsAfter, InstToDelete, + 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) { + // The size of a dbg.value's value operand should match the size of the + // variable it corresponds to. + // + // TODO: This, along with a check for non-null value operands, should be + // promoted to verifier failures. + Value *V = DVI->getValue(); + if (!V) + return false; + + // For now, don't try to interpret anything more complicated than an empty + // DIExpression. Eventually we should try to handle OP_deref and fragments. + if (DVI->getExpression()->getNumElements()) + return false; + + Type *Ty = V->getType(); + uint64_t ValueOperandSize = getAllocSizeInBits(M, Ty); + Optional DbgVarSize = DVI->getFragmentSizeInBits(); + if (!ValueOperandSize || !DbgVarSize) + return false; + + bool HasBadSize = false; + if (Ty->isIntegerTy()) { + auto Signedness = DVI->getVariable()->getSignedness(); + if (Signedness && *Signedness == DIBasicType::Signedness::Signed) + HasBadSize = ValueOperandSize < *DbgVarSize; + } else { + HasBadSize = ValueOperandSize != *DbgVarSize; + } + + if (HasBadSize) { + dbg() << "ERROR: dbg.value operand has size " << ValueOperandSize + << ", but its variable has size " << *DbgVarSize << ": "; + DVI->print(dbg()); + dbg() << "\n"; + } + return HasBadSize; +} + +bool checkDebugifyMetadata(Module &M, + iterator_range Functions, + StringRef NameOfWrappedPass, StringRef Banner, + bool Strip, DebugifyStatsMap *StatsMap) { + // Skip modules without debugify metadata. + NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify"); + if (!NMD) { + dbg() << Banner << ": Skipping module without debugify metadata\n"; + return false; + } + + auto getDebugifyOperand = [&](unsigned Idx) -> unsigned { + return mdconst::extract(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; + + // Track debug info loss statistics if able. + DebugifyStatistics *Stats = nullptr; + if (StatsMap && !NameOfWrappedPass.empty()) + Stats = &StatsMap->operator[](NameOfWrappedPass); + + BitVector MissingLines{OriginalNumLines, true}; + BitVector MissingVars{OriginalNumVars, true}; + for (Function &F : Functions) { + if (isFunctionSkipped(F)) + continue; + + // Find missing lines. + for (Instruction &I : instructions(F)) { + if (isa(&I) || isa(&I)) + continue; + + auto DL = I.getDebugLoc(); + if (DL && DL.getLine() != 0) { + MissingLines.reset(DL.getLine() - 1); + continue; + } + + if (!DL) { + dbg() << "WARNING: Instruction with empty DebugLoc in function "; + dbg() << F.getName() << " --"; + I.print(dbg()); + dbg() << "\n"; + } + } + + // Find missing variables and mis-sized debug values. + for (Instruction &I : instructions(F)) { + auto *DVI = dyn_cast(&I); + if (!DVI) + continue; + + unsigned Var = ~0U; + (void)to_integer(DVI->getVariable()->getName(), Var, 10); + assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable"); + bool HasBadSize = diagnoseMisSizedDbgValue(M, DVI); + if (!HasBadSize) + MissingVars.reset(Var - 1); + HasErrors |= HasBadSize; + } + } + + // Print the results. + for (unsigned Idx : MissingLines.set_bits()) + dbg() << "WARNING: Missing line " << Idx + 1 << "\n"; + + for (unsigned Idx : MissingVars.set_bits()) + dbg() << "WARNING: Missing variable " << Idx + 1 << "\n"; + + // Update DI loss statistics. + if (Stats) { + Stats->NumDbgLocsExpected += OriginalNumLines; + Stats->NumDbgLocsMissing += MissingLines.count(); + Stats->NumDbgValuesExpected += OriginalNumVars; + Stats->NumDbgValuesMissing += MissingVars.count(); + } + + dbg() << Banner; + if (!NameOfWrappedPass.empty()) + dbg() << " [" << NameOfWrappedPass << "]"; + dbg() << ": " << (HasErrors ? "FAIL" : "PASS") << '\n'; + + // Strip debugify metadata if required. + if (Strip) + return stripDebugifyMetadata(M); + + return false; +} + +/// ModulePass for attaching synthetic debug info to everything, used with the +/// legacy module pass manager, if the mode is set to Debugify. If the mode is +/// set to OriginalDebugInfo, it just collects debug info before a pass. +struct PrepareDIModulePass : public ModulePass { + bool runOnModule(Module &M) override { + if (Mode == VerifyDIPreserveMode::Debugify) + return applyDebugifyMetadata( + M, M.functions(), "ModuleVerifyDIPreserve: ", /*ApplyToMF*/ nullptr); + return collectDebugInfoMetadata( + M, M.functions(), *DIPreservationMap, + "ModuleVerifyDIPreserve(original-di-check): ", NameOfWrappedPass); + } + + PrepareDIModulePass( + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + 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 VerifyDIPreserveMode Mode; +}; + +/// FunctionPass for attaching synthetic debug info to instructions within a +/// single function, used with the legacy module pass manager, if the mode is +/// set to Debugify. If the mode is set to OriginalDebugInfo, it just +/// collects debug info before a pass. +struct PrepareDIFunctionPass : public FunctionPass { + bool runOnFunction(Function &F) override { + Module &M = *F.getParent(); + auto FuncIt = F.getIterator(); + if (Mode == VerifyDIPreserveMode::Debugify) + return applyDebugifyMetadata( + M, make_range(FuncIt, std::next(FuncIt)), + "FunctionVerifyDIPreserve: ", /*ApplyToMF*/ nullptr); + return collectDebugInfoMetadata( + M, M.functions(), *DIPreservationMap, + "FunctionVerifyDIPreserve(original-di-check): ", NameOfWrappedPass); + } + + PrepareDIFunctionPass( + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + 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 VerifyDIPreserveMode Mode; +}; + +/// ModulePass for checking debug info inserted by debugify, used with the +/// legacy module pass manager. In the case of the di-preservation-verify, +/// it just checks if a pass preserved the debug info. +struct CheckDIModulePass : public ModulePass { + bool runOnModule(Module &M) override { + if (Mode == VerifyDIPreserveMode::Debugify) + return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass, + "CheckModuleVerifyDIPreserve", Strip, + StatsMap); + return checkDebugInfoMetadata( + M, M.functions(), *DIPreservationMap, + "CheckModuleVerifyDIPreserve(original-di-check): ", NameOfWrappedPass); + } + + CheckDIModulePass( + bool Strip = false, StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + DebugInfoPerPassMap *DIPreservationMap = nullptr) + : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass), + StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), + Strip(Strip) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + static char ID; // Pass identification. + +private: + StringRef NameOfWrappedPass; + DebugifyStatsMap *StatsMap; + DebugInfoPerPassMap *DIPreservationMap; + enum VerifyDIPreserveMode Mode; + bool Strip; +}; + +/// FunctionPass for checking debug info inserted by debugify-function, used +/// with the legacy module pass manager. In the case of the +/// di-preservation-verify, it just checks if a pass preserved the debug +/// info. +struct CheckDIFunctionPass : public FunctionPass { + bool runOnFunction(Function &F) override { + Module &M = *F.getParent(); + auto FuncIt = F.getIterator(); + if (Mode == VerifyDIPreserveMode::Debugify) + return checkDebugifyMetadata( + M, make_range(FuncIt, std::next(FuncIt)), NameOfWrappedPass, + "CheckFunctionVerifyDIPreserve", Strip, StatsMap); + return checkDebugInfoMetadata( + M, make_range(FuncIt, std::next(FuncIt)), *DIPreservationMap, + "CheckFunctionVerifyDIPreserve(original-di-check): ", + NameOfWrappedPass); + } + + CheckDIFunctionPass( + bool Strip = false, StringRef NameOfWrappedPass = "", + DebugifyStatsMap *StatsMap = nullptr, + enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, + DebugInfoPerPassMap *DIPreservationMap = nullptr) + : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass), + StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), + Strip(Strip) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + static char ID; // Pass identification. + +private: + StringRef NameOfWrappedPass; + DebugifyStatsMap *StatsMap; + DebugInfoPerPassMap *DIPreservationMap; + enum VerifyDIPreserveMode Mode; + bool Strip; +}; + +} // end anonymous namespace + +ModulePass *createPrepareDIModulePass(enum VerifyDIPreserveMode Mode, + llvm::StringRef NameOfWrappedPass, + DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == VerifyDIPreserveMode::Debugify) + return new PrepareDIModulePass(); + assert(Mode == VerifyDIPreserveMode::OriginalDebugInfo && + "Must be original-di-check mode"); + return new PrepareDIModulePass(Mode, NameOfWrappedPass, DIPreservationMap); +} + +FunctionPass * +createPrepareDIFunctionPass(enum VerifyDIPreserveMode Mode, + llvm::StringRef NameOfWrappedPass, + DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == VerifyDIPreserveMode::Debugify) + return new PrepareDIFunctionPass(); + assert(Mode == VerifyDIPreserveMode::OriginalDebugInfo && + "Must be original-di-check mode"); + return new PrepareDIFunctionPass(Mode, NameOfWrappedPass, DIPreservationMap); +} + +PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) { + applyDebugifyMetadata(M, M.functions(), + "ModuleVerifyDIPreserve: ", /*ApplyToMF*/ nullptr); + return PreservedAnalyses::all(); +} + +ModulePass *createCheckDIModulePass(bool Strip, StringRef NameOfWrappedPass, + DebugifyStatsMap *StatsMap, + enum VerifyDIPreserveMode Mode, + DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == VerifyDIPreserveMode::Debugify) + return new CheckDIModulePass(Strip, NameOfWrappedPass, StatsMap); + assert(Mode == VerifyDIPreserveMode::OriginalDebugInfo && + "Must be original-di-check mode"); + return new CheckDIModulePass(false, NameOfWrappedPass, nullptr, Mode, + DIPreservationMap); +} + +FunctionPass *createCheckDIFunctionPass( + bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap, + enum VerifyDIPreserveMode Mode, DebugInfoPerPassMap *DIPreservationMap) { + if (Mode == VerifyDIPreserveMode::Debugify) + return new CheckDIFunctionPass(Strip, NameOfWrappedPass, StatsMap); + assert(Mode == VerifyDIPreserveMode::OriginalDebugInfo && + "Must be original-di-check mode"); + return new CheckDIFunctionPass(false, NameOfWrappedPass, nullptr, Mode, + DIPreservationMap); +} + +PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, + ModuleAnalysisManager &) { + checkDebugifyMetadata(M, M.functions(), "", "CheckModuleVerifyDIPreserve", + false, nullptr); + return PreservedAnalyses::all(); +} + +char PrepareDIModulePass::ID = 0; +static RegisterPass + DM("prepare-debuginfo-for-verify", "Prepare debug info for the analysis"); + +char CheckDIModulePass::ID = 0; +static RegisterPass + CDM("check-debuginfo", "Check debug info from -prepare-debuginfo"); + +char PrepareDIFunctionPass::ID = 0; +static RegisterPass + DF("prepare-debuginfo-for-verify-function", + "Prepare a function debug info for the analysis"); + +char CheckDIFunctionPass::ID = 0; +static RegisterPass + CDF("check-debuginfo-function", + "Check debug info from -prepare-debuginfo-function"); Index: llvm/test/CodeGen/X86/block-placement.ll =================================================================== --- llvm/test/CodeGen/X86/block-placement.ll +++ llvm/test/CodeGen/X86/block-placement.ll @@ -1,5 +1,5 @@ ; RUN: llc -mtriple=i686-linux -pre-RA-sched=source < %s | FileCheck %s -; RUN: opt -disable-output -debugify < %s +; RUN: opt -disable-output -prepare-debuginfo-for-verify < %s declare void @error(i32 %i, i32 %a, i32 %b) Index: llvm/test/CodeGen/X86/deopt-intrinsic-cconv.ll =================================================================== --- llvm/test/CodeGen/X86/deopt-intrinsic-cconv.ll +++ llvm/test/CodeGen/X86/deopt-intrinsic-cconv.ll @@ -1,6 +1,6 @@ ; RUN: llc < %s | FileCheck %s ; RUN: llc -debug-only=stackmaps < %s 2>&1 | FileCheck --check-prefix=STACKMAPS %s -; RUN: opt -disable-output -debugify < %s +; RUN: opt -disable-output -prepare-debuginfo-for-verify < %s ; REQUIRES: asserts target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" Index: llvm/test/DebugInfo/X86/bitcast-di.ll =================================================================== --- llvm/test/DebugInfo/X86/bitcast-di.ll +++ llvm/test/DebugInfo/X86/bitcast-di.ll @@ -1,4 +1,4 @@ -; RUN: opt -mtriple=x86_64-unknown-linux-gnu -S -debugify -codegenprepare < %s | FileCheck %s +; RUN: opt -mtriple=x86_64-unknown-linux-gnu -S -prepare-debuginfo-for-verify -codegenprepare < %s | FileCheck %s 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" target triple = "x86_64-unknown-linux-gnu" Index: llvm/test/DebugInfo/debugify-bogus-dbg-value.ll =================================================================== --- llvm/test/DebugInfo/debugify-bogus-dbg-value.ll +++ llvm/test/DebugInfo/debugify-bogus-dbg-value.ll @@ -1,4 +1,4 @@ -; RUN: opt -check-debugify < %s 2>&1 | FileCheck %s +; RUN: opt -check-debuginfo < %s 2>&1 | FileCheck %s define <2 x i64> @test-fun(<2 x i64> %A) !dbg !6 { %and = and <2 x i64> %A, , !dbg !14 @@ -22,7 +22,7 @@ ret <2 x i64> %and, !dbg !16 } -; CHECK: CheckModuleDebugify: FAIL +; CHECK: CheckModuleVerifyDIPreserve: FAIL ; Function Attrs: nounwind readnone speculatable declare void @llvm.dbg.value(metadata, metadata, metadata) #0 Index: llvm/test/DebugInfo/debugify-each.ll =================================================================== --- llvm/test/DebugInfo/debugify-each.ll +++ llvm/test/DebugInfo/debugify-each.ll @@ -38,10 +38,10 @@ ; Verify that the module & function (check-)debugify passes run at least twice. -; MODULE-PASS: CheckModuleDebugify [{{.*}}] -; MODULE-PASS: CheckModuleDebugify [{{.*}}] +; MODULE-PASS: CheckModuleVerifyDIPreserve [{{.*}}] +; MODULE-PASS: CheckModuleVerifyDIPreserve [{{.*}}] -; FUNCTION-PASS: CheckFunctionDebugify [{{.*}}] -; FUNCTION-PASS: CheckFunctionDebugify [{{.*}}] -; FUNCTION-PASS: CheckFunctionDebugify [{{.*}}] -; FUNCTION-PASS: CheckFunctionDebugify [{{.*}}] +; FUNCTION-PASS: CheckFunctionVerifyDIPreserve [{{.*}}] +; FUNCTION-PASS: CheckFunctionVerifyDIPreserve [{{.*}}] +; FUNCTION-PASS: CheckFunctionVerifyDIPreserve [{{.*}}] +; FUNCTION-PASS: CheckFunctionVerifyDIPreserve [{{.*}}] Index: llvm/test/DebugInfo/debugify-report-missing-locs-only.ll =================================================================== --- llvm/test/DebugInfo/debugify-report-missing-locs-only.ll +++ llvm/test/DebugInfo/debugify-report-missing-locs-only.ll @@ -1,4 +1,4 @@ -; RUN: opt -check-debugify < %s -S -o - 2>&1 | FileCheck %s -implicit-check-not "WARNING: Instruction with empty DebugLoc in function bar" +; RUN: opt -check-debuginfo < %s -S -o - 2>&1 | FileCheck %s -implicit-check-not "WARNING: Instruction with empty DebugLoc in function bar" ; CHECK: WARNING: Instruction with empty DebugLoc in function foo -- ret void define void @foo() !dbg !6 { Index: llvm/test/DebugInfo/debugify.ll =================================================================== --- llvm/test/DebugInfo/debugify.ll +++ llvm/test/DebugInfo/debugify.ll @@ -1,19 +1,19 @@ -; RUN: opt -debugify -S -o - < %s | FileCheck %s -; RUN: opt -passes=debugify -S -o - < %s | FileCheck %s +; RUN: opt -prepare-debuginfo-for-verify -S -o - < %s | FileCheck %s +; RUN: opt -passes=prepare-debuginfo-for-verify -S -o - < %s | FileCheck %s -; RUN: opt -debugify -debugify -S -o - < %s 2>&1 | \ +; RUN: opt -prepare-debuginfo-for-verify -prepare-debuginfo-for-verify -S -o - < %s 2>&1 | \ ; RUN: FileCheck %s -check-prefix=CHECK-REPEAT -; RUN: opt -passes=debugify,debugify -S -o - < %s 2>&1 | \ +; RUN: opt -passes=prepare-debuginfo-for-verify,prepare-debuginfo-for-verify -S -o - < %s 2>&1 | \ ; RUN: FileCheck %s -check-prefix=CHECK-REPEAT -; RUN: opt -debugify -check-debugify -S -o - < %s | \ -; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" -; RUN: opt -passes=debugify,check-debugify -S -o - < %s | \ -; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" +; RUN: opt -prepare-debuginfo-for-verify -check-debuginfo -S -o - < %s | \ +; RUN: FileCheck %s -implicit-check-not="CheckModuleVerifyDIPreserve: FAIL" +; RUN: opt -passes=prepare-debuginfo-for-verify,check-debuginfo -S -o - < %s | \ +; RUN: FileCheck %s -implicit-check-not="CheckModuleVerifyDIPreserve: FAIL" ; RUN: opt -enable-debugify -passes=verify -S -o - < %s | \ -; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" +; RUN: FileCheck %s -implicit-check-not="CheckModuleVerifyDIPreserve: FAIL" -; RUN: opt -debugify -strip -check-debugify -S -o - < %s 2>&1 | \ +; RUN: opt -prepare-debuginfo-for-verify -strip -check-debuginfo -S -o - < %s 2>&1 | \ ; RUN: FileCheck %s -check-prefix=CHECK-WARN ; RUN: opt -enable-debugify -strip -S -o - < %s 2>&1 | \ @@ -23,7 +23,7 @@ ; Verify that debugify can be safely used with piping ; RUN: opt -enable-debugify -O1 < %s | opt -O2 -o /dev/null -; RUN: opt -debugify -mem2reg -check-debugify < %s | opt -O2 -o /dev/null +; RUN: opt -prepare-debuginfo-for-verify -mem2reg -check-debuginfo < %s | opt -O2 -o /dev/null ; CHECK-LABEL: define void @foo define void @foo() { @@ -85,7 +85,7 @@ ; CHECK-DAG: ![[NUM_VARS]] = !{i32 3} ; --- Repeat case -; CHECK-REPEAT: ModuleDebugify: Skipping module with debug info +; CHECK-REPEAT: ModuleVerifyDIPreserve: Skipping module with debug info ; --- Failure case ; CHECK-WARN: WARNING: Instruction with empty DebugLoc in function foo -- ret void @@ -97,6 +97,6 @@ ; CHECK-WARN: WARNING: Missing line 3 ; CHECK-WARN: WARNING: Missing line 4 ; CHECK-WARN: WARNING: Missing variable 1 -; CHECK-WARN: CheckModuleDebugify: PASS +; CHECK-WARN: CheckModuleVerifyDIPreserve: PASS -; PASS: CheckModuleDebugify: PASS +; PASS: CheckModuleVerifyDIPreserve: PASS Index: llvm/test/DebugInfo/di-preserve-verify-no-dbg-info.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/di-preserve-verify-no-dbg-info.ll @@ -0,0 +1,23 @@ +; RUN: opt -verify-debuginfo-preserve -instcombine -S -o - < %s 2>&1 | FileCheck %s + +; CHECK: ModuleVerifyDIPreserve(original-di-check): Skipping module without debug info +; CHECK-NEXT: CheckModuleVerifyDIPreserve(original-di-check): 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/di-preserve-verify.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/di-preserve-verify.ll @@ -0,0 +1,73 @@ +; Check the preservation of original debug info. + +; RUN: opt -verify-debuginfo-preserve -instcombine -S -o - < %s 2>&1 | FileCheck %s + +; CHECK: CheckModuleVerifyDIPreserve(original-di-check): : 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/orig-di-preserve-verify-each-dbginfo.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/orig-di-preserve-verify-each-dbginfo.ll @@ -0,0 +1,96 @@ +; Check the preservation of original debug info. + +; RUN: opt -O2 -verify-each-debuginfo-preserve -S -o - < %s 2>&1 | FileCheck %s + +; CHECK: WARNING: 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/pr37964.ll =================================================================== --- llvm/test/DebugInfo/pr37964.ll +++ llvm/test/DebugInfo/pr37964.ll @@ -1,7 +1,7 @@ ; RUN: opt -disable-output -debugify-each -gvn < %s 2>&1 | FileCheck %s ; CHECK-NOT: ERROR: Instruction with empty DebugLoc in function _Z3bazv -- {{%.*}} = phi -; CHECK: CheckFunctionDebugify [Global Value Numbering]: PASS +; CHECK: CheckFunctionVerifyDIPreserve [Global Value Numbering]: PASS @foo = dso_local local_unnamed_addr global i32 0, align 4 @x = global i8 17 Index: llvm/test/DebugInfo/salvage-cast-debug-info.ll =================================================================== --- llvm/test/DebugInfo/salvage-cast-debug-info.ll +++ llvm/test/DebugInfo/salvage-cast-debug-info.ll @@ -1,4 +1,4 @@ -; RUN: opt %s -debugify -early-cse -earlycse-debug-hash -S | FileCheck %s +; RUN: opt %s -prepare-debuginfo-for-verify -early-cse -earlycse-debug-hash -S | FileCheck %s define i32 @foo(i64 %nose, i32 %more) { ; CHECK-LABEL: @foo( ; CHECK: call void @llvm.dbg.value(metadata i64 %nose, metadata [[V1:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned Index: llvm/test/Transforms/ArgumentPromotion/pr27568.ll =================================================================== --- llvm/test/Transforms/ArgumentPromotion/pr27568.ll +++ llvm/test/Transforms/ArgumentPromotion/pr27568.ll @@ -1,7 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes ; RUN: opt -S -argpromotion < %s | FileCheck %s ; RUN: opt -S -passes=argpromotion < %s | FileCheck %s -; RUN: opt -S -debugify -o /dev/null < %s +; RUN: opt -S -prepare-debuginfo-for-verify -o /dev/null < %s target triple = "x86_64-pc-windows-msvc" define internal void @callee(i8*) { Index: llvm/test/Transforms/BDCE/basic.ll =================================================================== --- llvm/test/Transforms/BDCE/basic.ll +++ llvm/test/Transforms/BDCE/basic.ll @@ -1,6 +1,6 @@ ; RUN: opt -S -bdce -instsimplify < %s | FileCheck %s ; RUN: opt -S -instsimplify < %s | FileCheck %s -check-prefix=CHECK-IO -; RUN: opt -S -debugify -bdce < %s | FileCheck %s -check-prefix=DEBUGIFY +; RUN: opt -S -prepare-debuginfo-for-verify -bdce < %s | FileCheck %s -check-prefix=DEBUGIFY target datalayout = "E-m:e-i64:64-n32:64" target triple = "powerpc64-unknown-linux-gnu" Index: llvm/test/Transforms/CodeExtractor/LoopExtractor_alloca.ll =================================================================== --- llvm/test/Transforms/CodeExtractor/LoopExtractor_alloca.ll +++ llvm/test/Transforms/CodeExtractor/LoopExtractor_alloca.ll @@ -1,4 +1,4 @@ -; RUN: opt -debugify -loop-extract -S < %s | FileCheck %s +; RUN: opt -prepare-debuginfo-for-verify -loop-extract -S < %s | FileCheck %s ; This tests 2 cases: ; 1. loop1 should be extracted into a function, without extracting %v1 alloca. 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 @@ -168,4 +168,4 @@ } ; Check that every instruction inserted by -codegenprepare has a debug location. -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS 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 @@ -120,4 +120,4 @@ } ; Check that every instruction inserted by -codegenprepare has a debug location. -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS 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 @@ -637,5 +637,5 @@ } ; Check that every instruction inserted by -codegenprepare has a debug location. -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS Index: llvm/test/Transforms/CodeGenPrepare/X86/select.ll =================================================================== --- llvm/test/Transforms/CodeGenPrepare/X86/select.ll +++ llvm/test/Transforms/CodeGenPrepare/X86/select.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 -debugify -codegenprepare -S < %s | FileCheck %s -check-prefix=DEBUG +; RUN: opt -prepare-debuginfo-for-verify -codegenprepare -S < %s | FileCheck %s -check-prefix=DEBUG target triple = "x86_64-unknown-unknown" 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 @@ -403,4 +403,4 @@ declare <8 x i32> @llvm.fshl.v8i32(<8 x i32>, <8 x i32>, <8 x i32>) #1 ; Check that every instruction inserted by -codegenprepare has a debug location. -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS Index: llvm/test/Transforms/CodeGenPrepare/X86/widen_switch.ll =================================================================== --- llvm/test/Transforms/CodeGenPrepare/X86/widen_switch.ll +++ llvm/test/Transforms/CodeGenPrepare/X86/widen_switch.ll @@ -1,7 +1,7 @@ ;; x86 is chosen to show the transform when 8-bit and 16-bit registers are available. ; RUN: opt < %s -codegenprepare -S -mtriple=x86_64-unknown-unknown | FileCheck %s --check-prefix=X86 -; RUN: opt < %s -debugify -codegenprepare -S -mtriple=x86_64-unknown-unknown | FileCheck %s --check-prefix=DEBUG +; RUN: opt < %s -prepare-debuginfo-for-verify -codegenprepare -S -mtriple=x86_64-unknown-unknown | FileCheck %s --check-prefix=DEBUG ; No change for x86 because 16-bit registers are part of the architecture. Index: llvm/test/Transforms/CorrelatedValuePropagation/ashr.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/ashr.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/ashr.ll @@ -4,7 +4,7 @@ ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors ; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS ; CHECK-LABEL: @test1 define void @test1(i32 %n) { Index: llvm/test/Transforms/CorrelatedValuePropagation/overflows.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/overflows.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/overflows.ll @@ -5,7 +5,7 @@ ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors ; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) Index: llvm/test/Transforms/CorrelatedValuePropagation/sext.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/sext.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/sext.ll @@ -5,7 +5,7 @@ ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors ; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS declare void @use64(i64) Index: llvm/test/Transforms/CorrelatedValuePropagation/udiv.ll =================================================================== --- llvm/test/Transforms/CorrelatedValuePropagation/udiv.ll +++ llvm/test/Transforms/CorrelatedValuePropagation/udiv.ll @@ -4,7 +4,7 @@ ; https://llvm.org/docs/SourceLevelDebugging.html#fixing-errors ; RUN: opt < %s -enable-debugify -correlated-propagation -S 2>&1 | \ ; RUN: FileCheck %s -check-prefix=DEBUG -; DEBUG: CheckModuleDebugify: PASS +; DEBUG: CheckModuleVerifyDIPreserve: PASS ; CHECK-LABEL: @test_nop define void @test_nop(i32 %n) { Index: llvm/test/Transforms/DCE/basic.ll =================================================================== --- llvm/test/Transforms/DCE/basic.ll +++ llvm/test/Transforms/DCE/basic.ll @@ -1,5 +1,5 @@ -; RUN: opt -debugify -dce -S < %s | FileCheck %s -; RUN: opt -passes='module(debugify),function(dce)' -S < %s | FileCheck %s +; RUN: opt -prepare-debuginfo-for-verify -dce -S < %s | FileCheck %s +; RUN: opt -passes='module(prepare-debuginfo-for-verify),function(dce)' -S < %s | FileCheck %s ; CHECK-LABEL: @test define void @test() { Index: llvm/test/Transforms/DeadStoreElimination/MSSA/debuginfo.ll =================================================================== --- llvm/test/Transforms/DeadStoreElimination/MSSA/debuginfo.ll +++ llvm/test/Transforms/DeadStoreElimination/MSSA/debuginfo.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify -basic-aa -dse -enable-dse-memoryssa -S | FileCheck %s +; RUN: opt < %s -prepare-debuginfo-for-verify -basic-aa -dse -enable-dse-memoryssa -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" Index: llvm/test/Transforms/DeadStoreElimination/MemDepAnalysis/debuginfo.ll =================================================================== --- llvm/test/Transforms/DeadStoreElimination/MemDepAnalysis/debuginfo.ll +++ llvm/test/Transforms/DeadStoreElimination/MemDepAnalysis/debuginfo.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify -basic-aa -dse -enable-dse-memoryssa=false -S | FileCheck %s +; RUN: opt < %s -prepare-debuginfo-for-verify -basic-aa -dse -enable-dse-memoryssa=false -S | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" Index: llvm/test/Transforms/GVN/PRE/phi-translate-2.ll =================================================================== --- llvm/test/Transforms/GVN/PRE/phi-translate-2.ll +++ llvm/test/Transforms/GVN/PRE/phi-translate-2.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify -gvn -S | FileCheck %s +; RUN: opt < %s -prepare-debuginfo-for-verify -gvn -S | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" @a = common global [100 x i64] zeroinitializer, align 16 Index: llvm/test/Transforms/GlobalOpt/shrink-global-to-bool-check-debug.ll =================================================================== --- llvm/test/Transforms/GlobalOpt/shrink-global-to-bool-check-debug.ll +++ llvm/test/Transforms/GlobalOpt/shrink-global-to-bool-check-debug.ll @@ -1,4 +1,4 @@ -;RUN: opt -S -debugify -globalopt -f %s | FileCheck %s +;RUN: opt -S -prepare-debuginfo-for-verify -globalopt -f %s | FileCheck %s @foo = internal global i32 0, align 4 Index: llvm/test/Transforms/InstCombine/cast-mul-select.ll =================================================================== --- llvm/test/Transforms/InstCombine/cast-mul-select.ll +++ llvm/test/Transforms/InstCombine/cast-mul-select.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -instcombine -S | FileCheck %s -; RUN: opt -debugify -instcombine -S < %s | FileCheck %s -check-prefix DBGINFO +; RUN: opt -prepare-debuginfo-for-verify -instcombine -S < %s | FileCheck %s -check-prefix DBGINFO 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" Index: llvm/test/Transforms/InstCombine/debuginfo-variables.ll =================================================================== --- llvm/test/Transforms/InstCombine/debuginfo-variables.ll +++ llvm/test/Transforms/InstCombine/debuginfo-variables.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify -instcombine -S | FileCheck %s +; RUN: opt < %s -prepare-debuginfo-for-verify -instcombine -S | FileCheck %s declare void @escape32(i32) 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 @@ -737,4 +737,4 @@ ret float %F } -; DBG-VALID: CheckModuleDebugify: PASS +; DBG-VALID: CheckModuleVerifyDIPreserve: PASS Index: llvm/test/Transforms/InstCombine/storemerge-dbg.ll =================================================================== --- llvm/test/Transforms/InstCombine/storemerge-dbg.ll +++ llvm/test/Transforms/InstCombine/storemerge-dbg.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify -instcombine -S | FileCheck %s +; RUN: opt < %s -prepare-debuginfo-for-verify -instcombine -S | FileCheck %s declare i32 @escape(i32) Index: llvm/test/Transforms/InstMerge/st_sink_check_debug.ll =================================================================== --- llvm/test/Transforms/InstMerge/st_sink_check_debug.ll +++ llvm/test/Transforms/InstMerge/st_sink_check_debug.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -S -debugify -mldst-motion -o - | FileCheck %s +; RUN: opt < %s -S -prepare-debuginfo-for-verify -mldst-motion -o - | FileCheck %s %struct.S = type { i32 } Index: llvm/test/Transforms/JumpThreading/branch-debug-info.ll =================================================================== --- llvm/test/Transforms/JumpThreading/branch-debug-info.ll +++ llvm/test/Transforms/JumpThreading/branch-debug-info.ll @@ -1,4 +1,4 @@ -; RUN: opt %s -debugify -jump-threading -S | FileCheck %s +; RUN: opt %s -prepare-debuginfo-for-verify -jump-threading -S | FileCheck %s ; Tests Bug 37966 define void @test0(i32 %i) { Index: llvm/test/Transforms/LCSSA/avoid-intrinsics-in-catchswitch.ll =================================================================== --- llvm/test/Transforms/LCSSA/avoid-intrinsics-in-catchswitch.ll +++ llvm/test/Transforms/LCSSA/avoid-intrinsics-in-catchswitch.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify -licm -S -o /dev/null +; RUN: opt < %s -prepare-debuginfo-for-verify -licm -S -o /dev/null ; ; The following test is from https://bugs.llvm.org/show_bug.cgi?id=36238 ; This test should pass (not assert or fault). The error that originally Index: llvm/test/Transforms/LCSSA/basictest.ll =================================================================== --- llvm/test/Transforms/LCSSA/basictest.ll +++ llvm/test/Transforms/LCSSA/basictest.ll @@ -1,6 +1,6 @@ ; RUN: opt < %s -lcssa -S | FileCheck %s ; RUN: opt < %s -passes=lcssa -S | FileCheck %s -; RUN: opt < %s -debugify -lcssa -S | FileCheck -check-prefix=DEBUGIFY %s +; RUN: opt < %s -prepare-debuginfo-for-verify -lcssa -S | FileCheck -check-prefix=DEBUGIFY %s define void @lcssa(i1 %S2) { ; CHECK-LABEL: @lcssa Index: llvm/test/Transforms/LICM/sinking.ll =================================================================== --- llvm/test/Transforms/LICM/sinking.ll +++ llvm/test/Transforms/LICM/sinking.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -basic-aa -licm -S | FileCheck %s -; RUN: opt < %s -debugify -basic-aa -licm -S | FileCheck %s -check-prefix=DEBUGIFY +; RUN: opt < %s -prepare-debuginfo-for-verify -basic-aa -licm -S | FileCheck %s -check-prefix=DEBUGIFY ; RUN: opt < %s -basic-aa -licm -S -enable-mssa-loop-dependency=true -verify-memoryssa | FileCheck %s Index: llvm/test/Transforms/LoopIdiom/memcpy-debugify-remarks.ll =================================================================== --- llvm/test/Transforms/LoopIdiom/memcpy-debugify-remarks.ll +++ llvm/test/Transforms/LoopIdiom/memcpy-debugify-remarks.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -basic-aa -debugify -loop-idiom -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -verify -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 | FileCheck %s +; RUN: opt -basic-aa -prepare-debuginfo-for-verify -loop-idiom -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -verify -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" Index: llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll =================================================================== --- llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll +++ llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -basic-aa -debugify -loop-idiom -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -verify -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 | FileCheck %s +; RUN: opt -basic-aa -prepare-debuginfo-for-verify -loop-idiom -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -verify -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" Index: llvm/test/Transforms/LoopVectorize/i8-induction.ll =================================================================== --- llvm/test/Transforms/LoopVectorize/i8-induction.ll +++ llvm/test/Transforms/LoopVectorize/i8-induction.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -loop-vectorize -force-vector-interleave=1 -force-vector-width=4 -dce -instcombine -S -; RUN: opt < %s -debugify -loop-vectorize -S | FileCheck %s --check-prefix=DEBUGLOC +; RUN: opt < %s -prepare-debuginfo-for-verify -loop-vectorize -S | FileCheck %s --check-prefix=DEBUGLOC 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-S128" Index: llvm/test/Transforms/LoopVectorize/preserve-dbg-loc-and-loop-metadata.ll =================================================================== --- llvm/test/Transforms/LoopVectorize/preserve-dbg-loc-and-loop-metadata.ll +++ llvm/test/Transforms/LoopVectorize/preserve-dbg-loc-and-loop-metadata.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -loop-vectorize -S 2>&1 | FileCheck %s -; RUN: opt < %s -debugify -loop-vectorize -S | FileCheck %s -check-prefix DEBUGLOC +; RUN: opt < %s -prepare-debuginfo-for-verify -loop-vectorize -S | FileCheck %s -check-prefix DEBUGLOC target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" ; This test makes sure we don't duplicate the loop vectorizer's metadata Index: llvm/test/Transforms/Mem2Reg/PromoteMemToRegister.ll =================================================================== --- llvm/test/Transforms/Mem2Reg/PromoteMemToRegister.ll +++ llvm/test/Transforms/Mem2Reg/PromoteMemToRegister.ll @@ -1,8 +1,8 @@ ; Simple sanity check testcase. Both alloca's should be eliminated. -; RUN: opt < %s -debugify -mem2reg -check-debugify -S 2>&1 | FileCheck %s +; RUN: opt < %s -prepare-debuginfo-for-verify -mem2reg -check-debuginfo -S 2>&1 | FileCheck %s ; CHECK-NOT: alloca -; CHECK: CheckModuleDebugify: PASS +; CHECK: CheckModuleVerifyDIPreserve: PASS define double @testfunc(i32 %i, double %j) { %I = alloca i32 ; [#uses=4] Index: llvm/test/Transforms/MemCpyOpt/pr37967.ll =================================================================== --- llvm/test/Transforms/MemCpyOpt/pr37967.ll +++ llvm/test/Transforms/MemCpyOpt/pr37967.ll @@ -1,6 +1,6 @@ -; RUN: opt -debugify -memcpyopt -check-debugify -S < %s 2>&1 | FileCheck %s +; RUN: opt -prepare-debuginfo-for-verify -memcpyopt -check-debuginfo -S < %s 2>&1 | FileCheck %s -; CHECK: CheckModuleDebugify: PASS +; CHECK: CheckModuleVerifyDIPreserve: PASS ; CHECK-LABEL: define {{.*}} @_Z3bar3Foo ; CHECK: [[target:%.*]] = load i8*, i8** bitcast (%struct.Foo** @a to i8**), align 8, !dbg Index: llvm/test/Transforms/SCCP/loadtest.ll =================================================================== --- llvm/test/Transforms/SCCP/loadtest.ll +++ llvm/test/Transforms/SCCP/loadtest.ll @@ -1,8 +1,8 @@ ; This test makes sure that these instructions are properly constant propagated. -; RUN: opt < %s -data-layout="e-p:32:32" -debugify -sccp -S | FileCheck %s -; RUN: opt < %s -data-layout="E-p:32:32" -debugify -sccp -S | FileCheck %s -; RUN: opt < %s -data-layout="E-p:32:32" -debugify -ipsccp -S | FileCheck %s +; RUN: opt < %s -data-layout="e-p:32:32" -prepare-debuginfo-for-verify -sccp -S | FileCheck %s +; RUN: opt < %s -data-layout="E-p:32:32" -prepare-debuginfo-for-verify -sccp -S | FileCheck %s +; RUN: opt < %s -data-layout="E-p:32:32" -prepare-debuginfo-for-verify -ipsccp -S | FileCheck %s @X = constant i32 42 ; [#uses=1] @Y = constant [2 x { i32, float }] [ { i32, float } { i32 12, float 1.000000e+00 }, { i32, float } { i32 37, float 0x3FF3B2FEC0000000 } ] ; <[2 x { i32, float }]*> [#uses=2] Index: llvm/test/Transforms/SROA/alignment.ll =================================================================== --- llvm/test/Transforms/SROA/alignment.ll +++ llvm/test/Transforms/SROA/alignment.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -sroa -S | FileCheck %s -; RUN: opt -debugify -sroa -S < %s | FileCheck %s -check-prefix DEBUGLOC +; RUN: opt -prepare-debuginfo-for-verify -sroa -S < %s | FileCheck %s -check-prefix DEBUGLOC target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64" Index: llvm/test/Transforms/SimplifyCFG/debug-info-thread-phi.ll =================================================================== --- llvm/test/Transforms/SimplifyCFG/debug-info-thread-phi.ll +++ llvm/test/Transforms/SimplifyCFG/debug-info-thread-phi.ll @@ -1,4 +1,4 @@ -; RUN: opt %s -debugify -simplifycfg -S | FileCheck %s +; RUN: opt %s -prepare-debuginfo-for-verify -simplifycfg -S | FileCheck %s ; Tests Bug 37966 define void @bar(i32 %aa) { Index: llvm/test/Transforms/TailCallElim/debugloc.ll =================================================================== --- llvm/test/Transforms/TailCallElim/debugloc.ll +++ llvm/test/Transforms/TailCallElim/debugloc.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -debugify -tailcallelim -S | FileCheck %s +; RUN: opt < %s -prepare-debuginfo-for-verify -tailcallelim -S | FileCheck %s define void @foo() { entry: Index: llvm/test/Transforms/Util/Debugify/loc-only.ll =================================================================== --- llvm/test/Transforms/Util/Debugify/loc-only.ll +++ llvm/test/Transforms/Util/Debugify/loc-only.ll @@ -1,5 +1,5 @@ -; RUN: opt -debugify -S < %s | FileCheck --check-prefixes=ALL,VALUE %s -; RUN: opt -debugify -debugify-level=locations -S < %s | FileCheck --check-prefixes=ALL --implicit-check-not=dbg.value %s +; RUN: opt -prepare-debuginfo-for-verify -S < %s | FileCheck --check-prefixes=ALL,VALUE %s +; RUN: opt -prepare-debuginfo-for-verify -debugify-level=locations -S < %s | FileCheck --check-prefixes=ALL --implicit-check-not=dbg.value %s ; ALL-LABEL: @test define void @test() { Index: llvm/tools/opt/NewPMDriver.cpp =================================================================== --- llvm/tools/opt/NewPMDriver.cpp +++ llvm/tools/opt/NewPMDriver.cpp @@ -37,7 +37,7 @@ #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Transforms/Scalar/LoopPassManager.h" -#include "llvm/Transforms/Utils/Debugify.h" +#include "llvm/Transforms/Utils/VerifyDIPreserve.h" using namespace llvm; using namespace opt_tool; @@ -292,10 +292,10 @@ PB.registerPipelineParsingCallback( [](StringRef Name, ModulePassManager &MPM, ArrayRef) { - if (Name == "debugify") { + if (Name == "prepare-debuginfo-for-verify") { MPM.addPass(NewPMDebugifyPass()); return true; - } else if (Name == "check-debugify") { + } else if (Name == "check-debuginfo") { MPM.addPass(NewPMCheckDebugifyPass()); return true; } Index: llvm/tools/opt/opt.cpp =================================================================== --- llvm/tools/opt/opt.cpp +++ llvm/tools/opt/opt.cpp @@ -55,7 +55,7 @@ #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/IPO/WholeProgramDevirt.h" #include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/Transforms/Utils/Debugify.h" +#include "llvm/Transforms/Utils/VerifyDIPreserve.h" #include #include using namespace llvm; @@ -207,16 +207,26 @@ static cl::opt EnableDebugify( "enable-debugify", cl::desc( - "Start the pipeline with debugify and end it with check-debugify")); + "Start the pipeline with debugify and end it with check-debugify.")); + +static cl::opt VerifyDebugInfoPreserve( + "verify-debuginfo-preserve", + cl::desc("Start the pipeline with collecting and end it with checking of " + "debug info preservation.")); 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.")); + +static cl::opt VerifyEachDebugInfoPreserve( + "verify-each-debuginfo-preserve", + cl::desc("Start each pass with collecting and end it with checking of " + "debug info preservation.")); 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 @@ -799,12 +809,22 @@ // Create a PassManager to hold and optimize the collection of passes we are // about to build. If the -debugify-each option is set, wrap each pass with - // the (-check)-debugify passes. - DebugifyCustomPassManager Passes; - if (DebugifyEach) - Passes.enableDebugifyEach(); + // the debugify passes. It also applies to -verify-each-debuginfo-preserve + // as well, which performs the analysis on original debug info. + VerifyDIPreserveCustomPassManager Passes; + DebugifyStatsMap DIStatsMap; + DebugInfoPerPassMap DIPreservationMap; + if (DebugifyEach) { + Passes.setVerifyDIPreserveMode(VerifyDIPreserveMode::Debugify); + Passes.setDIStatsMap(DIStatsMap); + } else if (VerifyEachDebugInfoPreserve) { + Passes.setVerifyDIPreserveMode(VerifyDIPreserveMode::OriginalDebugInfo); + Passes.setDIPreservationMap(DIPreservationMap); + } - bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach; + bool AddOneTimeDebugifyPasses = + (EnableDebugify && !DebugifyEach) || + (VerifyDebugInfoPreserve && !VerifyEachDebugInfoPreserve); Passes.add(new TargetLibraryInfoWrapperPass(TLII)); @@ -812,8 +832,17 @@ Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); - if (AddOneTimeDebugifyPasses) - Passes.add(createDebugifyModulePass()); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify) { + Passes.setDIStatsMap(DIStatsMap); + Passes.add(createPrepareDIModulePass()); + } else if (VerifyDebugInfoPreserve) { + Passes.setDIPreservationMap(DIPreservationMap); + Passes.add(createPrepareDIModulePass( + VerifyDIPreserveMode::OriginalDebugInfo, "", + &(Passes.getDebugInfoPerPassMap()))); + } + } std::unique_ptr FPasses; if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || @@ -957,8 +986,14 @@ if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); - if (AddOneTimeDebugifyPasses) - Passes.add(createCheckDebugifyModulePass(false)); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify) + Passes.add(createCheckDIModulePass(false)); + else if (VerifyDebugInfoPreserve) + Passes.add(createCheckDIModulePass( + false, "", nullptr, VerifyDIPreserveMode::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