//===- DebugSSAUpdater.h - Debug SSA Update Tool ----------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// // // This file declares the DebugSSAUpdater class, which is used to evaluate the // live values of debug variables in IR. This uses SSA construction, treating // debug value records as definitions, to determine at each point in the program // which definition(s) are live at a given point. This is useful for analysis of // the state of debug variables, such as measuring the change in values of a // variable over time, or calculating coverage stats. // // NB: This is an expensive analysis that is generally not suitable for use in // LLVM passes, but may be useful for standalone tools. // //===----------------------------------------------------------------------===// #ifndef LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H #define LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H #include "llvm/ADT/SmallVector.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugProgramInstruction.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/ValueHandle.h" #include "llvm/IR/ValueMap.h" #include "llvm/Support/Compiler.h" #include namespace llvm { //////////////////////////////////////// // SSAUpdater specialization classes class DbgSSAPhi; template class SSAUpdaterTraits; /// A definition of a variable; can represent either a debug value, no /// definition (the variable has not yet been defined), or a phi value*. /// *Meaning multiple definitions that are live-in to a block from different /// predecessors, not a debug value that uses an IR PHINode. struct DbgValueDef { DbgSSAPhi *Phi; bool IsUndef; bool IsMemory; Metadata *Locations; DIExpression *Expression; DbgValueDef() : Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr), Expression(nullptr) {} DbgValueDef(int) : Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr), Expression(nullptr) {} DbgValueDef(bool IsMemory, Metadata *Locations, DIExpression *Expression) : Phi(nullptr), IsUndef(false), IsMemory(IsMemory), Locations(Locations), Expression(Expression) {} DbgValueDef(DbgVariableRecord *DVR) : Phi(nullptr) { assert(!DVR->isDbgAssign() && "#dbg_assign not yet supported"); IsUndef = DVR->isKillLocation(); IsMemory = DVR->isAddressOfVariable(); Locations = DVR->getRawLocation(); Expression = DVR->getExpression(); } DbgValueDef(DbgSSAPhi *Phi) : Phi(Phi), IsUndef(false), IsMemory(false), Locations(nullptr), Expression(nullptr) {} bool agreesWith(DbgValueDef Other) const { if (IsUndef && Other.IsUndef) return true; return std::tie(Phi, IsUndef, IsMemory, Locations, Expression) == std::tie(Other.Phi, Other.IsUndef, Other.IsMemory, Other.Locations, Other.Expression); } operator bool() const { return !IsUndef; } bool operator==(DbgValueDef Other) const { return agreesWith(Other); } bool operator!=(DbgValueDef Other) const { return !agreesWith(Other); } void print(raw_ostream &OS) const; }; class DbgSSABlock; class DebugSSAUpdater; /// Represents the live-in definitions of a variable to a block with multiple /// predecessors. class DbgSSAPhi { public: SmallVector, 4> IncomingValues; DbgSSABlock *ParentBlock; DbgSSAPhi(DbgSSABlock *ParentBlock) : ParentBlock(ParentBlock) {} DbgSSABlock *getParent() { return ParentBlock; } unsigned getNumIncomingValues() const { return IncomingValues.size(); } DbgSSABlock *getIncomingBlock(size_t Idx) { return IncomingValues[Idx].first; } DbgValueDef getIncomingValue(size_t Idx) { return IncomingValues[Idx].second; } void addIncoming(DbgSSABlock *BB, DbgValueDef DV) { IncomingValues.push_back({BB, DV}); } void print(raw_ostream &OS) const; }; inline raw_ostream &operator<<(raw_ostream &OS, const DbgValueDef &DV) { DV.print(OS); return OS; } inline raw_ostream &operator<<(raw_ostream &OS, const DbgSSAPhi &PHI) { PHI.print(OS); return OS; } /// Thin wrapper around a block successor iterator. class DbgSSABlockSuccIterator { public: succ_iterator SuccIt; DebugSSAUpdater &Updater; DbgSSABlockSuccIterator(succ_iterator SuccIt, DebugSSAUpdater &Updater) : SuccIt(SuccIt), Updater(Updater) {} bool operator!=(const DbgSSABlockSuccIterator &OtherIt) const { return OtherIt.SuccIt != SuccIt; } DbgSSABlockSuccIterator &operator++() { ++SuccIt; return *this; } DbgSSABlock *operator*(); }; /// Thin wrapper around a block successor iterator. class DbgSSABlockPredIterator { public: pred_iterator PredIt; DebugSSAUpdater &Updater; DbgSSABlockPredIterator(pred_iterator PredIt, DebugSSAUpdater &Updater) : PredIt(PredIt), Updater(Updater) {} bool operator!=(const DbgSSABlockPredIterator &OtherIt) const { return OtherIt.PredIt != PredIt; } DbgSSABlockPredIterator &operator++() { ++PredIt; return *this; } DbgSSABlock *operator*(); }; class DbgSSABlock { public: BasicBlock &BB; DebugSSAUpdater &Updater; using PHIListT = SmallVector; /// List of PHIs in this block. There should only ever be one, but this needs /// to be a list for the SSAUpdater. PHIListT PHIList; DbgSSABlock(BasicBlock &BB, DebugSSAUpdater &Updater) : BB(BB), Updater(Updater) {} DbgSSABlockPredIterator pred_begin() { return DbgSSABlockPredIterator(llvm::pred_begin(&BB), Updater); } DbgSSABlockPredIterator pred_end() { return DbgSSABlockPredIterator(llvm::pred_end(&BB), Updater); } iterator_range predecessors() { return iterator_range(pred_begin(), pred_end()); } DbgSSABlockSuccIterator succ_begin() { return DbgSSABlockSuccIterator(llvm::succ_begin(&BB), Updater); } DbgSSABlockSuccIterator succ_end() { return DbgSSABlockSuccIterator(llvm::succ_end(&BB), Updater); } iterator_range successors() { return iterator_range(succ_begin(), succ_end()); } /// SSAUpdater has requested a PHI: create that within this block record. DbgSSAPhi *newPHI() { assert(PHIList.empty() && "Only one PHI should exist per-block per-variable"); PHIList.emplace_back(this); return &PHIList.back(); } /// SSAUpdater wishes to know what PHIs already exist in this block. PHIListT &phis() { return PHIList; } }; /// Class used to determine the live ranges of debug variables in IR using /// SSA construction (via the SSAUpdaterImpl class), used for analysis purposes. class DebugSSAUpdater { friend class SSAUpdaterTraits; using AvailableValsTy = DenseMap; private: /// This keeps track of which value to use on a per-block basis. When we /// insert PHI nodes, we keep track of them here. AvailableValsTy AV; /// Pointer to an optionally-passed vector into which, if it is non-null, /// the PHIs that describe ambiguous variable locations will be inserted. SmallVectorImpl *InsertedPHIs; DenseMap BlockMap; public: /// If InsertedPHIs is specified, it will be filled /// in with all PHI Nodes created by rewriting. explicit DebugSSAUpdater( SmallVectorImpl *InsertedPHIs = nullptr); DebugSSAUpdater(const DebugSSAUpdater &) = delete; DebugSSAUpdater &operator=(const DebugSSAUpdater &) = delete; ~DebugSSAUpdater() { for (auto &Block : BlockMap) delete Block.second; } void reset() { for (auto &Block : BlockMap) delete Block.second; if (InsertedPHIs) InsertedPHIs->clear(); BlockMap.clear(); } void initialize(); /// For a given BB, create a wrapper block for it. Stores it in the /// DebugSSAUpdater block map. DbgSSABlock *getDbgSSABlock(BasicBlock *BB) { auto it = BlockMap.find(BB); if (it == BlockMap.end()) { BlockMap[BB] = new DbgSSABlock(*BB, *this); it = BlockMap.find(BB); } return it->second; } /// Indicate that a rewritten value is available in the specified block /// with the specified value. void addAvailableValue(DbgSSABlock *BB, DbgValueDef DV); /// Return true if the DebugSSAUpdater already has a value for the specified /// block. bool hasValueForBlock(DbgSSABlock *BB) const; /// Return the value for the specified block if the DebugSSAUpdater has one, /// otherwise return nullptr. DbgValueDef findValueForBlock(DbgSSABlock *BB) const; /// Construct SSA form, materializing a value that is live at the end /// of the specified block. DbgValueDef getValueAtEndOfBlock(DbgSSABlock *BB); /// Construct SSA form, materializing a value that is live in the /// middle of the specified block. /// /// \c getValueInMiddleOfBlock is the same as \c GetValueAtEndOfBlock except /// in one important case: if there is a definition of the rewritten value /// after the 'use' in BB. Consider code like this: /// /// \code /// X1 = ... /// SomeBB: /// use(X) /// X2 = ... /// br Cond, SomeBB, OutBB /// \endcode /// /// In this case, there are two values (X1 and X2) added to the AvailableVals /// set by the client of the rewriter, and those values are both live out of /// their respective blocks. However, the use of X happens in the *middle* of /// a block. Because of this, we need to insert a new PHI node in SomeBB to /// merge the appropriate values, and this value isn't live out of the block. DbgValueDef getValueInMiddleOfBlock(DbgSSABlock *BB); private: DbgValueDef getValueAtEndOfBlockInternal(DbgSSABlock *BB); }; struct DbgRangeEntry { BasicBlock::iterator Start; BasicBlock::iterator End; // Should be non-PHI. DbgValueDef Value; }; /// Utility class used to store the names of SSA values after their owning /// modules have been destroyed. Values are added via \c addValue to receive a /// corresponding ID, which can then be used to retrieve the name of the SSA /// value via \c getName at any point. Adding the same value multiple times /// returns the same ID, making \c addValue idempotent. class SSAValueNameMap { struct Config : ValueMapConfig { enum { FollowRAUW = false }; }; public: using ValueID = uint64_t; ValueID addValue(Value *V); std::string getName(ValueID ID) { return ValueIDToNameMap[ID]; } private: DenseMap ValueIDToNameMap; ValueMap ValueToIDMap; ValueID NextID = 0; }; /// Utility class used to find and store the live debug ranges for variables in /// a module. This class uses the DebugSSAUpdater for each variable added with /// \c addVariable to find either a single-location value, e.g. #dbg_declare, or /// a set of live value ranges corresponding to the set of #dbg_value records. class DbgValueRangeTable { DenseMap> OrigVariableValueRangeTable; DenseMap OrigSingleLocVariableValueTable; public: LLVM_ABI_FOR_TEST void addVariable(Function *F, DebugVariableAggregate DVA); bool hasVariableEntry(DebugVariableAggregate DVA) const { return OrigVariableValueRangeTable.contains(DVA) || OrigSingleLocVariableValueTable.contains(DVA); } bool hasSingleLocEntry(DebugVariableAggregate DVA) const { return OrigSingleLocVariableValueTable.contains(DVA); } ArrayRef getVariableRanges(DebugVariableAggregate DVA) { return OrigVariableValueRangeTable[DVA]; } DbgValueDef getSingleLoc(DebugVariableAggregate DVA) { return OrigSingleLocVariableValueTable[DVA]; } void printValues(DebugVariableAggregate DVA, raw_ostream &OS); }; } // end namespace llvm #endif // LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H