xref: /freebsd/contrib/llvm-project/llvm/include/llvm/SandboxIR/Tracker.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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