|
| 1 | +//===- DebugSSAUpdater.h - Debug SSA Update Tool ----------------*- C++ -*-===// |
| 2 | +// |
| 3 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | +// See https://llvm.org/LICENSE.txt for license information. |
| 5 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | +// |
| 7 | +//===----------------------------------------------------------------------===// |
| 8 | +// |
| 9 | +// This file declares the DebugSSAUpdater class, which is used to evaluate the |
| 10 | +// live values of debug variables in IR. This uses SSA construction, treating |
| 11 | +// debug value records as definitions, to determine at each point in the program |
| 12 | +// which definition(s) are live at a given point. This is useful for analysis of |
| 13 | +// the state of debug variables, such as measuring the change in values of a |
| 14 | +// variable over time, or calculating coverage stats. |
| 15 | +// |
| 16 | +//===----------------------------------------------------------------------===// |
| 17 | + |
| 18 | +#ifndef LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H |
| 19 | +#define LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H |
| 20 | + |
| 21 | +#include "llvm/IR/BasicBlock.h" |
| 22 | +#include "llvm/IR/CFG.h" |
| 23 | +#include "llvm/IR/DebugInfoMetadata.h" |
| 24 | +#include "llvm/IR/DebugProgramInstruction.h" |
| 25 | +#include "llvm/IR/Instruction.h" |
| 26 | + |
| 27 | +namespace llvm { |
| 28 | + |
| 29 | +//////////////////////////////////////// |
| 30 | +// SSAUpdater specialization classes |
| 31 | + |
| 32 | +class DbgSSAPhi; |
| 33 | +template <typename T> class SmallVectorImpl; |
| 34 | +template <typename T> class SSAUpdaterTraits; |
| 35 | + |
| 36 | +/// A definition of a variable; can represent either a debug value, no |
| 37 | +/// definition (the variable has not yet been defined), or a phi value*. |
| 38 | +/// *Meaning multiple definitions that are live-in to a block from different |
| 39 | +/// predecessors, not a debug value that uses an IR PHINode. |
| 40 | +struct DbgValueDef { |
| 41 | + DbgSSAPhi *Phi; |
| 42 | + bool IsUndef; |
| 43 | + bool IsMemory; |
| 44 | + Metadata *Locations; |
| 45 | + DIExpression *Expression; |
| 46 | + |
| 47 | + DbgValueDef() |
| 48 | + : Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr), |
| 49 | + Expression(nullptr) {} |
| 50 | + DbgValueDef(int) |
| 51 | + : Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr), |
| 52 | + Expression(nullptr) {} |
| 53 | + DbgValueDef(bool IsMemory, Metadata *Locations, DIExpression *Expression) |
| 54 | + : Phi(nullptr), IsUndef(false), IsMemory(IsMemory), Locations(Locations), |
| 55 | + Expression(Expression) {} |
| 56 | + DbgValueDef(DbgVariableRecord *DVR) : Phi(nullptr) { |
| 57 | + assert(!DVR->isDbgAssign() && "#dbg_assign not yet supported"); |
| 58 | + IsUndef = DVR->isKillLocation(); |
| 59 | + IsMemory = DVR->isAddressOfVariable(); |
| 60 | + Locations = DVR->getRawLocation(); |
| 61 | + Expression = DVR->getExpression(); |
| 62 | + } |
| 63 | + DbgValueDef(DbgSSAPhi *Phi) |
| 64 | + : Phi(Phi), IsUndef(false), IsMemory(false), Locations(nullptr), |
| 65 | + Expression(nullptr) {} |
| 66 | + |
| 67 | + bool agreesWith(DbgValueDef Other) const { |
| 68 | + if (IsUndef && Other.IsUndef) |
| 69 | + return true; |
| 70 | + return std::tie(Phi, IsUndef, IsMemory, Locations, Expression) == |
| 71 | + std::tie(Other.Phi, Other.IsUndef, Other.IsMemory, Other.Locations, |
| 72 | + Other.Expression); |
| 73 | + } |
| 74 | + |
| 75 | + operator bool() const { return !IsUndef; } |
| 76 | + bool operator==(DbgValueDef Other) const { return agreesWith(Other); } |
| 77 | + bool operator!=(DbgValueDef Other) const { return !agreesWith(Other); } |
| 78 | + |
| 79 | + void print(raw_ostream &OS) const; |
| 80 | +}; |
| 81 | + |
| 82 | +class DbgSSABlock; |
| 83 | +class DebugSSAUpdater; |
| 84 | + |
| 85 | +/// Represents the live-in definitions of a variable to a block with multiple |
| 86 | +/// predecessors. |
| 87 | +class DbgSSAPhi { |
| 88 | +public: |
| 89 | + SmallVector<std::pair<DbgSSABlock *, DbgValueDef>, 4> IncomingValues; |
| 90 | + DbgSSABlock *ParentBlock; |
| 91 | + DbgSSAPhi(DbgSSABlock *ParentBlock) : ParentBlock(ParentBlock) {} |
| 92 | + |
| 93 | + DbgSSABlock *getParent() { return ParentBlock; } |
| 94 | + unsigned getNumIncomingValues() const { return IncomingValues.size(); } |
| 95 | + DbgSSABlock *getIncomingBlock(size_t Idx) { |
| 96 | + return IncomingValues[Idx].first; |
| 97 | + } |
| 98 | + DbgValueDef getIncomingValue(size_t Idx) { |
| 99 | + return IncomingValues[Idx].second; |
| 100 | + } |
| 101 | + void addIncoming(DbgSSABlock *BB, DbgValueDef DV) { |
| 102 | + IncomingValues.push_back({BB, DV}); |
| 103 | + } |
| 104 | + |
| 105 | + void print(raw_ostream &OS) const; |
| 106 | +}; |
| 107 | + |
| 108 | +inline raw_ostream &operator<<(raw_ostream &OS, const DbgValueDef &DV) { |
| 109 | + DV.print(OS); |
| 110 | + return OS; |
| 111 | +} |
| 112 | +inline raw_ostream &operator<<(raw_ostream &OS, const DbgSSAPhi &PHI) { |
| 113 | + PHI.print(OS); |
| 114 | + return OS; |
| 115 | +} |
| 116 | + |
| 117 | +/// Thin wrapper around a block successor iterator. |
| 118 | +class DbgSSABlockSuccIterator { |
| 119 | +public: |
| 120 | + succ_iterator SuccIt; |
| 121 | + DebugSSAUpdater &Updater; |
| 122 | + |
| 123 | + DbgSSABlockSuccIterator(succ_iterator SuccIt, DebugSSAUpdater &Updater) |
| 124 | + : SuccIt(SuccIt), Updater(Updater) {} |
| 125 | + |
| 126 | + bool operator!=(const DbgSSABlockSuccIterator &OtherIt) const { |
| 127 | + return OtherIt.SuccIt != SuccIt; |
| 128 | + } |
| 129 | + |
| 130 | + DbgSSABlockSuccIterator &operator++() { |
| 131 | + ++SuccIt; |
| 132 | + return *this; |
| 133 | + } |
| 134 | + |
| 135 | + DbgSSABlock *operator*(); |
| 136 | +}; |
| 137 | + |
| 138 | +/// Thin wrapper around a block successor iterator. |
| 139 | +class DbgSSABlockPredIterator { |
| 140 | +public: |
| 141 | + pred_iterator PredIt; |
| 142 | + DebugSSAUpdater &Updater; |
| 143 | + |
| 144 | + DbgSSABlockPredIterator(pred_iterator PredIt, DebugSSAUpdater &Updater) |
| 145 | + : PredIt(PredIt), Updater(Updater) {} |
| 146 | + |
| 147 | + bool operator!=(const DbgSSABlockPredIterator &OtherIt) const { |
| 148 | + return OtherIt.PredIt != PredIt; |
| 149 | + } |
| 150 | + |
| 151 | + DbgSSABlockPredIterator &operator++() { |
| 152 | + ++PredIt; |
| 153 | + return *this; |
| 154 | + } |
| 155 | + |
| 156 | + DbgSSABlock *operator*(); |
| 157 | +}; |
| 158 | + |
| 159 | +class DbgSSABlock { |
| 160 | +public: |
| 161 | + BasicBlock &BB; |
| 162 | + DebugSSAUpdater &Updater; |
| 163 | + using PHIListT = SmallVector<DbgSSAPhi, 1>; |
| 164 | + /// List of PHIs in this block. There should only ever be one, but this needs |
| 165 | + /// to be a list for the SSAUpdater. |
| 166 | + PHIListT PHIList; |
| 167 | + |
| 168 | + DbgSSABlock(BasicBlock &BB, DebugSSAUpdater &Updater) |
| 169 | + : BB(BB), Updater(Updater) {} |
| 170 | + |
| 171 | + DbgSSABlockPredIterator pred_begin() { |
| 172 | + return DbgSSABlockPredIterator(llvm::pred_begin(&BB), Updater); |
| 173 | + } |
| 174 | + |
| 175 | + DbgSSABlockPredIterator pred_end() { |
| 176 | + return DbgSSABlockPredIterator(llvm::pred_end(&BB), Updater); |
| 177 | + } |
| 178 | + |
| 179 | + iterator_range<DbgSSABlockPredIterator> predecessors() { |
| 180 | + return iterator_range(pred_begin(), pred_end()); |
| 181 | + } |
| 182 | + |
| 183 | + DbgSSABlockSuccIterator succ_begin() { |
| 184 | + return DbgSSABlockSuccIterator(llvm::succ_begin(&BB), Updater); |
| 185 | + } |
| 186 | + |
| 187 | + DbgSSABlockSuccIterator succ_end() { |
| 188 | + return DbgSSABlockSuccIterator(llvm::succ_end(&BB), Updater); |
| 189 | + } |
| 190 | + |
| 191 | + iterator_range<DbgSSABlockSuccIterator> successors() { |
| 192 | + return iterator_range(succ_begin(), succ_end()); |
| 193 | + } |
| 194 | + |
| 195 | + /// SSAUpdater has requested a PHI: create that within this block record. |
| 196 | + DbgSSAPhi *newPHI() { |
| 197 | + assert(PHIList.empty() && |
| 198 | + "Only one PHI should exist per-block per-variable"); |
| 199 | + PHIList.emplace_back(this); |
| 200 | + return &PHIList.back(); |
| 201 | + } |
| 202 | + |
| 203 | + /// SSAUpdater wishes to know what PHIs already exist in this block. |
| 204 | + PHIListT &phis() { return PHIList; } |
| 205 | +}; |
| 206 | + |
| 207 | +/// Class used to determine the live ranges of debug variables in IR using |
| 208 | +/// SSA construction (via the SSAUpdaterImpl class), used for analysis purposes. |
| 209 | +class DebugSSAUpdater { |
| 210 | + friend class SSAUpdaterTraits<DebugSSAUpdater>; |
| 211 | + |
| 212 | +private: |
| 213 | + /// This keeps track of which value to use on a per-block basis. When we |
| 214 | + /// insert PHI nodes, we keep track of them here. |
| 215 | + void *AV = nullptr; |
| 216 | + |
| 217 | + SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs; |
| 218 | + |
| 219 | + DenseMap<BasicBlock *, DbgSSABlock *> BlockMap; |
| 220 | + |
| 221 | +public: |
| 222 | + /// If InsertedPHIs is specified, it will be filled |
| 223 | + /// in with all PHI Nodes created by rewriting. |
| 224 | + explicit DebugSSAUpdater( |
| 225 | + SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs = nullptr); |
| 226 | + DebugSSAUpdater(const DebugSSAUpdater &) = delete; |
| 227 | + DebugSSAUpdater &operator=(const DebugSSAUpdater &) = delete; |
| 228 | + ~DebugSSAUpdater(); |
| 229 | + |
| 230 | + void reset() { |
| 231 | + for (auto &Block : BlockMap) |
| 232 | + delete Block.second; |
| 233 | + |
| 234 | + if (InsertedPHIs) |
| 235 | + InsertedPHIs->clear(); |
| 236 | + BlockMap.clear(); |
| 237 | + } |
| 238 | + |
| 239 | + void initialize(); |
| 240 | + |
| 241 | + /// For a given BB, create a wrapper block for it. Stores it in the |
| 242 | + /// DebugSSAUpdater block map. |
| 243 | + DbgSSABlock *getDbgSSABlock(BasicBlock *BB) { |
| 244 | + auto it = BlockMap.find(BB); |
| 245 | + if (it == BlockMap.end()) { |
| 246 | + BlockMap[BB] = new DbgSSABlock(*BB, *this); |
| 247 | + it = BlockMap.find(BB); |
| 248 | + } |
| 249 | + return it->second; |
| 250 | + } |
| 251 | + |
| 252 | + /// Indicate that a rewritten value is available in the specified block |
| 253 | + /// with the specified value. |
| 254 | + void addAvailableValue(DbgSSABlock *BB, DbgValueDef DV); |
| 255 | + |
| 256 | + /// Return true if the DebugSSAUpdater already has a value for the specified |
| 257 | + /// block. |
| 258 | + bool hasValueForBlock(DbgSSABlock *BB) const; |
| 259 | + |
| 260 | + /// Return the value for the specified block if the DebugSSAUpdater has one, |
| 261 | + /// otherwise return nullptr. |
| 262 | + DbgValueDef findValueForBlock(DbgSSABlock *BB) const; |
| 263 | + |
| 264 | + /// Construct SSA form, materializing a value that is live at the end |
| 265 | + /// of the specified block. |
| 266 | + DbgValueDef getValueAtEndOfBlock(DbgSSABlock *BB); |
| 267 | + |
| 268 | + /// Construct SSA form, materializing a value that is live in the |
| 269 | + /// middle of the specified block. |
| 270 | + /// |
| 271 | + /// \c getValueInMiddleOfBlock is the same as \c GetValueAtEndOfBlock except |
| 272 | + /// in one important case: if there is a definition of the rewritten value |
| 273 | + /// after the 'use' in BB. Consider code like this: |
| 274 | + /// |
| 275 | + /// \code |
| 276 | + /// X1 = ... |
| 277 | + /// SomeBB: |
| 278 | + /// use(X) |
| 279 | + /// X2 = ... |
| 280 | + /// br Cond, SomeBB, OutBB |
| 281 | + /// \endcode |
| 282 | + /// |
| 283 | + /// In this case, there are two values (X1 and X2) added to the AvailableVals |
| 284 | + /// set by the client of the rewriter, and those values are both live out of |
| 285 | + /// their respective blocks. However, the use of X happens in the *middle* of |
| 286 | + /// a block. Because of this, we need to insert a new PHI node in SomeBB to |
| 287 | + /// merge the appropriate values, and this value isn't live out of the block. |
| 288 | + DbgValueDef getValueInMiddleOfBlock(DbgSSABlock *BB); |
| 289 | + |
| 290 | +private: |
| 291 | + DbgValueDef getValueAtEndOfBlockInternal(DbgSSABlock *BB); |
| 292 | +}; |
| 293 | + |
| 294 | +struct DbgRangeEntry { |
| 295 | + BasicBlock::iterator Start; |
| 296 | + BasicBlock::iterator End; |
| 297 | + // Should be non-PHI. |
| 298 | + DbgValueDef Value; |
| 299 | +}; |
| 300 | + |
| 301 | +class DbgValueRangeTable { |
| 302 | + DenseMap<DebugVariableAggregate, SmallVector<DbgRangeEntry>> |
| 303 | + OrigVariableValueRangeTable; |
| 304 | + DenseMap<DebugVariableAggregate, DbgValueDef> OrigSingleLocVariableValueTable; |
| 305 | + // For the only initial user of this class, the mappings below are useful and |
| 306 | + // are used in conjunction with the variable value ranges above, thus we track |
| 307 | + // them as part of the same class. If we have more uses for variable value |
| 308 | + // range tracking, then the line/variable name mapping should be moved out to |
| 309 | + // a separate class. |
| 310 | + DenseMap<BasicBlock::iterator, uint64_t> LineMapping; |
| 311 | + DenseMap<uint64_t, std::string> VariableNameMapping; |
| 312 | + |
| 313 | + using VarMapping = DenseMap<Value *, uint64_t>; |
| 314 | + VarMapping VariableMapping; |
| 315 | + uint64_t KeyIndex = 0; |
| 316 | + |
| 317 | +public: |
| 318 | + void addVariable(Function *F, DebugVariableAggregate DVA); |
| 319 | + bool hasVariableEntry(DebugVariableAggregate DVA) const { |
| 320 | + return OrigVariableValueRangeTable.contains(DVA) || |
| 321 | + OrigSingleLocVariableValueTable.contains(DVA); |
| 322 | + } |
| 323 | + bool hasSingleLocEntry(DebugVariableAggregate DVA) const { |
| 324 | + return OrigSingleLocVariableValueTable.contains(DVA); |
| 325 | + } |
| 326 | + ArrayRef<DbgRangeEntry> getVariableRanges(DebugVariableAggregate DVA) { |
| 327 | + return OrigVariableValueRangeTable[DVA]; |
| 328 | + } |
| 329 | + DbgValueDef getSingleLoc(DebugVariableAggregate DVA) { |
| 330 | + return OrigSingleLocVariableValueTable[DVA]; |
| 331 | + } |
| 332 | + |
| 333 | + void addLine(BasicBlock::iterator I, uint64_t LineAddr) { |
| 334 | + LineMapping[I] = LineAddr; |
| 335 | + } |
| 336 | + uint64_t getLine(BasicBlock::iterator I) { |
| 337 | + return LineMapping.contains(I) ? LineMapping[I] : (uint64_t)-1; |
| 338 | + } |
| 339 | + |
| 340 | + uint64_t addVariableName(Value *V, uint64_t Size); |
| 341 | + std::string getVariableName(uint64_t Key) { |
| 342 | + assert(VariableNameMapping.contains(Key) && "Why not here?"); |
| 343 | + return VariableNameMapping[Key]; |
| 344 | + } |
| 345 | + |
| 346 | + void printValues(DebugVariableAggregate DVA, raw_ostream &OS); |
| 347 | +}; |
| 348 | + |
| 349 | +} // end namespace llvm |
| 350 | + |
| 351 | +#endif // LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H |
0 commit comments