1 //===- Tracker.h ------------------------------------------------*- 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 is the component of SandboxIR that tracks all changes made to its 10 // state, such that we can revert the state when needed. 11 // 12 // Tracking changes 13 // ---------------- 14 // The user needs to call `Tracker::save()` to enable tracking changes 15 // made to SandboxIR. From that point on, any change made to SandboxIR, will 16 // automatically create a change tracking object and register it with the 17 // tracker. IR-change objects are subclasses of `IRChangeBase` and get 18 // registered with the `Tracker::track()` function. The change objects 19 // are saved in the order they are registered with the tracker and are stored in 20 // the `Tracker::Changes` vector. All of this is done transparently to 21 // the user. 22 // 23 // Reverting changes 24 // ----------------- 25 // Calling `Tracker::revert()` will restore the state saved when 26 // `Tracker::save()` was called. Internally this goes through the 27 // change objects in `Tracker::Changes` in reverse order, calling their 28 // `IRChangeBase::revert()` function one by one. 29 // 30 // Accepting changes 31 // ----------------- 32 // The user needs to either revert or accept changes before the tracker object 33 // is destroyed. This is enforced in the tracker's destructor. 34 // This is the job of `Tracker::accept()`. Internally this will go 35 // through the change objects in `Tracker::Changes` in order, calling 36 // `IRChangeBase::accept()`. 37 // 38 //===----------------------------------------------------------------------===// 39 40 #ifndef LLVM_SANDBOXIR_TRACKER_H 41 #define LLVM_SANDBOXIR_TRACKER_H 42 43 #include "llvm/ADT/PointerUnion.h" 44 #include "llvm/ADT/SmallVector.h" 45 #include "llvm/IR/IRBuilder.h" 46 #include "llvm/IR/Instruction.h" 47 #include "llvm/IR/Module.h" 48 #include "llvm/SandboxIR/Use.h" 49 #include "llvm/Support/Debug.h" 50 #include <memory> 51 #include <regex> 52 53 namespace llvm::sandboxir { 54 55 class BasicBlock; 56 class Instruction; 57 class Tracker; 58 59 /// The base class for IR Change classes. 60 class IRChangeBase { 61 protected: 62 Tracker &Parent; 63 64 public: 65 IRChangeBase(Tracker &Parent); 66 /// This runs when changes get reverted. 67 virtual void revert() = 0; 68 /// This runs when changes get accepted. 69 virtual void accept() = 0; 70 virtual ~IRChangeBase() = default; 71 #ifndef NDEBUG 72 /// \Returns the index of this change by iterating over all changes in the 73 /// tracker. This is only used for debugging. 74 unsigned getIdx() const; dumpCommon(raw_ostream & OS)75 void dumpCommon(raw_ostream &OS) const { OS << getIdx() << ". "; } 76 virtual void dump(raw_ostream &OS) const = 0; 77 LLVM_DUMP_METHOD virtual void dump() const = 0; 78 friend raw_ostream &operator<<(raw_ostream &OS, const IRChangeBase &C) { 79 C.dump(OS); 80 return OS; 81 } 82 #endif 83 }; 84 85 /// Tracks the change of the source Value of a sandboxir::Use. 86 class UseSet : public IRChangeBase { 87 Use U; 88 Value *OrigV = nullptr; 89 90 public: UseSet(const Use & U,Tracker & Tracker)91 UseSet(const Use &U, Tracker &Tracker) 92 : IRChangeBase(Tracker), U(U), OrigV(U.get()) {} revert()93 void revert() final { U.set(OrigV); } accept()94 void accept() final {} 95 #ifndef NDEBUG dump(raw_ostream & OS)96 void dump(raw_ostream &OS) const final { 97 dumpCommon(OS); 98 OS << "UseSet"; 99 } 100 LLVM_DUMP_METHOD void dump() const final; 101 #endif 102 }; 103 104 class EraseFromParent : public IRChangeBase { 105 /// Contains all the data we need to restore an "erased" (i.e., detached) 106 /// instruction: the instruction itself and its operands in order. 107 struct InstrAndOperands { 108 /// The operands that got dropped. 109 SmallVector<llvm::Value *> Operands; 110 /// The instruction that got "erased". 111 llvm::Instruction *LLVMI; 112 }; 113 /// The instruction data is in reverse program order, which helps create the 114 /// original program order during revert(). 115 SmallVector<InstrAndOperands> InstrData; 116 /// This is either the next Instruction in the stream, or the parent 117 /// BasicBlock if at the end of the BB. 118 PointerUnion<llvm::Instruction *, llvm::BasicBlock *> NextLLVMIOrBB; 119 /// We take ownership of the "erased" instruction. 120 std::unique_ptr<sandboxir::Value> ErasedIPtr; 121 122 public: 123 EraseFromParent(std::unique_ptr<sandboxir::Value> &&IPtr, Tracker &Tracker); 124 void revert() final; 125 void accept() final; 126 #ifndef NDEBUG dump(raw_ostream & OS)127 void dump(raw_ostream &OS) const final { 128 dumpCommon(OS); 129 OS << "EraseFromParent"; 130 } 131 LLVM_DUMP_METHOD void dump() const final; 132 friend raw_ostream &operator<<(raw_ostream &OS, const EraseFromParent &C) { 133 C.dump(OS); 134 return OS; 135 } 136 #endif 137 }; 138 139 class RemoveFromParent : public IRChangeBase { 140 /// The instruction that is about to get removed. 141 Instruction *RemovedI = nullptr; 142 /// This is either the next instr, or the parent BB if at the end of the BB. 143 PointerUnion<Instruction *, BasicBlock *> NextInstrOrBB; 144 145 public: 146 RemoveFromParent(Instruction *RemovedI, Tracker &Tracker); 147 void revert() final; accept()148 void accept() final {}; getInstruction()149 Instruction *getInstruction() const { return RemovedI; } 150 #ifndef NDEBUG dump(raw_ostream & OS)151 void dump(raw_ostream &OS) const final { 152 dumpCommon(OS); 153 OS << "RemoveFromParent"; 154 } 155 LLVM_DUMP_METHOD void dump() const final; 156 #endif // NDEBUG 157 }; 158 159 class MoveInstr : public IRChangeBase { 160 /// The instruction that moved. 161 Instruction *MovedI; 162 /// This is either the next instruction in the block, or the parent BB if at 163 /// the end of the BB. 164 PointerUnion<Instruction *, BasicBlock *> NextInstrOrBB; 165 166 public: 167 MoveInstr(sandboxir::Instruction *I, Tracker &Tracker); 168 void revert() final; accept()169 void accept() final {} 170 #ifndef NDEBUG dump(raw_ostream & OS)171 void dump(raw_ostream &OS) const final { 172 dumpCommon(OS); 173 OS << "MoveInstr"; 174 } 175 LLVM_DUMP_METHOD void dump() const final; 176 #endif // NDEBUG 177 }; 178 179 /// The tracker collects all the change objects and implements the main API for 180 /// saving / reverting / accepting. 181 class Tracker { 182 public: 183 enum class TrackerState { 184 Disabled, ///> Tracking is disabled 185 Record, ///> Tracking changes 186 }; 187 188 private: 189 /// The list of changes that are being tracked. 190 SmallVector<std::unique_ptr<IRChangeBase>> Changes; 191 #ifndef NDEBUG 192 friend unsigned IRChangeBase::getIdx() const; // For accessing `Changes`. 193 #endif 194 /// The current state of the tracker. 195 TrackerState State = TrackerState::Disabled; 196 Context &Ctx; 197 198 public: 199 #ifndef NDEBUG 200 /// Helps catch bugs where we are creating new change objects while in the 201 /// middle of creating other change objects. 202 bool InMiddleOfCreatingChange = false; 203 #endif // NDEBUG 204 Tracker(Context & Ctx)205 explicit Tracker(Context &Ctx) : Ctx(Ctx) {} 206 ~Tracker(); getContext()207 Context &getContext() const { return Ctx; } 208 /// Record \p Change and take ownership. This is the main function used to 209 /// track Sandbox IR changes. 210 void track(std::unique_ptr<IRChangeBase> &&Change); 211 /// \Returns true if the tracker is recording changes. isTracking()212 bool isTracking() const { return State == TrackerState::Record; } 213 /// \Returns the current state of the tracker. getState()214 TrackerState getState() const { return State; } 215 /// Turns on IR tracking. 216 void save(); 217 /// Stops tracking and accept changes. 218 void accept(); 219 /// Stops tracking and reverts to saved state. 220 void revert(); 221 222 #ifndef NDEBUG 223 void dump(raw_ostream &OS) const; 224 LLVM_DUMP_METHOD void dump() const; 225 friend raw_ostream &operator<<(raw_ostream &OS, const Tracker &Tracker) { 226 Tracker.dump(OS); 227 return OS; 228 } 229 #endif // NDEBUG 230 }; 231 232 } // namespace llvm::sandboxir 233 234 #endif // LLVM_SANDBOXIR_TRACKER_H 235