Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:26

0001 //===- Tracker.h ------------------------------------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file is the component of SandboxIR that tracks all changes made to its
0010 // state, such that we can revert the state when needed.
0011 //
0012 // Tracking changes
0013 // ----------------
0014 // The user needs to call `Tracker::save()` to enable tracking changes
0015 // made to SandboxIR. From that point on, any change made to SandboxIR, will
0016 // automatically create a change tracking object and register it with the
0017 // tracker. IR-change objects are subclasses of `IRChangeBase` and get
0018 // registered with the `Tracker::track()` function. The change objects
0019 // are saved in the order they are registered with the tracker and are stored in
0020 // the `Tracker::Changes` vector. All of this is done transparently to
0021 // the user.
0022 //
0023 // Reverting changes
0024 // -----------------
0025 // Calling `Tracker::revert()` will restore the state saved when
0026 // `Tracker::save()` was called. Internally this goes through the
0027 // change objects in `Tracker::Changes` in reverse order, calling their
0028 // `IRChangeBase::revert()` function one by one.
0029 //
0030 // Accepting changes
0031 // -----------------
0032 // The user needs to either revert or accept changes before the tracker object
0033 // is destroyed. This is enforced in the tracker's destructor.
0034 // This is the job of `Tracker::accept()`. Internally this will go
0035 // through the change objects in `Tracker::Changes` in order, calling
0036 // `IRChangeBase::accept()`.
0037 //
0038 //===----------------------------------------------------------------------===//
0039 
0040 #ifndef LLVM_SANDBOXIR_TRACKER_H
0041 #define LLVM_SANDBOXIR_TRACKER_H
0042 
0043 #include "llvm/ADT/PointerUnion.h"
0044 #include "llvm/ADT/SmallVector.h"
0045 #include "llvm/ADT/StableHashing.h"
0046 #include "llvm/IR/IRBuilder.h"
0047 #include "llvm/IR/Instruction.h"
0048 #include "llvm/SandboxIR/Use.h"
0049 #include "llvm/Support/Debug.h"
0050 #include <memory>
0051 
0052 namespace llvm::sandboxir {
0053 
0054 class BasicBlock;
0055 class CallBrInst;
0056 class LoadInst;
0057 class StoreInst;
0058 class Instruction;
0059 class Tracker;
0060 class AllocaInst;
0061 class CatchSwitchInst;
0062 class SwitchInst;
0063 class ConstantInt;
0064 class ShuffleVectorInst;
0065 class CmpInst;
0066 class GlobalVariable;
0067 
0068 #ifndef NDEBUG
0069 
0070 /// A class that saves hashes and textual IR snapshots of functions in a
0071 /// SandboxIR Context, and does hash comparison when `expectNoDiff` is called.
0072 /// If hashes differ, it prints textual IR for both old and new versions to
0073 /// aid debugging.
0074 ///
0075 /// This is used as an additional debug check when reverting changes to
0076 /// SandboxIR, to verify the reverted state matches the initial state.
0077 class IRSnapshotChecker {
0078   Context &Ctx;
0079 
0080   // A snapshot of textual IR for a function, with a hash for quick comparison.
0081   struct FunctionSnapshot {
0082     llvm::stable_hash Hash;
0083     std::string TextualIR;
0084   };
0085 
0086   // A snapshot for each llvm::Function found in every module in the SandboxIR
0087   // Context. In practice there will always be one module, but sandbox IR
0088   // save/restore ops work at the Context level, so we must take the full state
0089   // into account.
0090   using ContextSnapshot = DenseMap<const llvm::Function *, FunctionSnapshot>;
0091 
0092   ContextSnapshot OrigContextSnapshot;
0093 
0094   // Dumps to a string the textual IR for a single Function.
0095   std::string dumpIR(const llvm::Function &F) const;
0096 
0097   // Returns a snapshot of all the modules in the sandbox IR context.
0098   ContextSnapshot takeSnapshot() const;
0099 
0100   // Compares two snapshots and returns true if they differ.
0101   bool diff(const ContextSnapshot &Orig, const ContextSnapshot &Curr) const;
0102 
0103 public:
0104   IRSnapshotChecker(Context &Ctx) : Ctx(Ctx) {}
0105 
0106   /// Saves a snapshot of the current state. If there was any previous snapshot,
0107   /// it will be replaced with the new one.
0108   void save();
0109 
0110   /// Checks current state against saved state, crashes if different.
0111   void expectNoDiff();
0112 };
0113 
0114 #endif // NDEBUG
0115 
0116 /// The base class for IR Change classes.
0117 class IRChangeBase {
0118 protected:
0119   friend class Tracker; // For Parent.
0120 
0121 public:
0122   /// This runs when changes get reverted.
0123   virtual void revert(Tracker &Tracker) = 0;
0124   /// This runs when changes get accepted.
0125   virtual void accept() = 0;
0126   virtual ~IRChangeBase() = default;
0127 #ifndef NDEBUG
0128   virtual void dump(raw_ostream &OS) const = 0;
0129   LLVM_DUMP_METHOD virtual void dump() const = 0;
0130   friend raw_ostream &operator<<(raw_ostream &OS, const IRChangeBase &C) {
0131     C.dump(OS);
0132     return OS;
0133   }
0134 #endif
0135 };
0136 
0137 /// Tracks the change of the source Value of a sandboxir::Use.
0138 class UseSet : public IRChangeBase {
0139   Use U;
0140   Value *OrigV = nullptr;
0141 
0142 public:
0143   UseSet(const Use &U) : U(U), OrigV(U.get()) {}
0144   void revert(Tracker &Tracker) final { U.set(OrigV); }
0145   void accept() final {}
0146 #ifndef NDEBUG
0147   void dump(raw_ostream &OS) const final { OS << "UseSet"; }
0148   LLVM_DUMP_METHOD void dump() const final;
0149 #endif
0150 };
0151 
0152 class PHIRemoveIncoming : public IRChangeBase {
0153   PHINode *PHI;
0154   unsigned RemovedIdx;
0155   Value *RemovedV;
0156   BasicBlock *RemovedBB;
0157 
0158 public:
0159   PHIRemoveIncoming(PHINode *PHI, unsigned RemovedIdx);
0160   void revert(Tracker &Tracker) final;
0161   void accept() final {}
0162 #ifndef NDEBUG
0163   void dump(raw_ostream &OS) const final { OS << "PHISetIncoming"; }
0164   LLVM_DUMP_METHOD void dump() const final;
0165 #endif
0166 };
0167 
0168 class PHIAddIncoming : public IRChangeBase {
0169   PHINode *PHI;
0170   unsigned Idx;
0171 
0172 public:
0173   PHIAddIncoming(PHINode *PHI);
0174   void revert(Tracker &Tracker) final;
0175   void accept() final {}
0176 #ifndef NDEBUG
0177   void dump(raw_ostream &OS) const final { OS << "PHISetIncoming"; }
0178   LLVM_DUMP_METHOD void dump() const final;
0179 #endif
0180 };
0181 
0182 class CmpSwapOperands : public IRChangeBase {
0183   CmpInst *Cmp;
0184 
0185 public:
0186   CmpSwapOperands(CmpInst *Cmp);
0187   void revert(Tracker &Tracker) final;
0188   void accept() final {}
0189 #ifndef NDEBUG
0190   void dump(raw_ostream &OS) const final { OS << "CmpSwapOperands"; }
0191   LLVM_DUMP_METHOD void dump() const final;
0192 #endif
0193 };
0194 
0195 /// Tracks swapping a Use with another Use.
0196 class UseSwap : public IRChangeBase {
0197   Use ThisUse;
0198   Use OtherUse;
0199 
0200 public:
0201   UseSwap(const Use &ThisUse, const Use &OtherUse)
0202       : ThisUse(ThisUse), OtherUse(OtherUse) {
0203     assert(ThisUse.getUser() == OtherUse.getUser() && "Expected same user!");
0204   }
0205   void revert(Tracker &Tracker) final { ThisUse.swap(OtherUse); }
0206   void accept() final {}
0207 #ifndef NDEBUG
0208   void dump(raw_ostream &OS) const final { OS << "UseSwap"; }
0209   LLVM_DUMP_METHOD void dump() const final;
0210 #endif
0211 };
0212 
0213 class EraseFromParent : public IRChangeBase {
0214   /// Contains all the data we need to restore an "erased" (i.e., detached)
0215   /// instruction: the instruction itself and its operands in order.
0216   struct InstrAndOperands {
0217     /// The operands that got dropped.
0218     SmallVector<llvm::Value *> Operands;
0219     /// The instruction that got "erased".
0220     llvm::Instruction *LLVMI;
0221   };
0222   /// The instruction data is in reverse program order, which helps create the
0223   /// original program order during revert().
0224   SmallVector<InstrAndOperands> InstrData;
0225   /// This is either the next Instruction in the stream, or the parent
0226   /// BasicBlock if at the end of the BB.
0227   PointerUnion<llvm::Instruction *, llvm::BasicBlock *> NextLLVMIOrBB;
0228   /// We take ownership of the "erased" instruction.
0229   std::unique_ptr<sandboxir::Value> ErasedIPtr;
0230 
0231 public:
0232   EraseFromParent(std::unique_ptr<sandboxir::Value> &&IPtr);
0233   void revert(Tracker &Tracker) final;
0234   void accept() final;
0235 #ifndef NDEBUG
0236   void dump(raw_ostream &OS) const final { OS << "EraseFromParent"; }
0237   LLVM_DUMP_METHOD void dump() const final;
0238   friend raw_ostream &operator<<(raw_ostream &OS, const EraseFromParent &C) {
0239     C.dump(OS);
0240     return OS;
0241   }
0242 #endif
0243 };
0244 
0245 class RemoveFromParent : public IRChangeBase {
0246   /// The instruction that is about to get removed.
0247   Instruction *RemovedI = nullptr;
0248   /// This is either the next instr, or the parent BB if at the end of the BB.
0249   PointerUnion<Instruction *, BasicBlock *> NextInstrOrBB;
0250 
0251 public:
0252   RemoveFromParent(Instruction *RemovedI);
0253   void revert(Tracker &Tracker) final;
0254   void accept() final {};
0255   Instruction *getInstruction() const { return RemovedI; }
0256 #ifndef NDEBUG
0257   void dump(raw_ostream &OS) const final { OS << "RemoveFromParent"; }
0258   LLVM_DUMP_METHOD void dump() const final;
0259 #endif // NDEBUG
0260 };
0261 
0262 /// This class can be used for tracking most instruction setters.
0263 /// The two template arguments are:
0264 /// - GetterFn: The getter member function pointer (e.g., `&Foo::get`)
0265 /// - SetterFn: The setter member function pointer (e.g., `&Foo::set`)
0266 /// Upon construction, it saves a copy of the original value by calling the
0267 /// getter function. Revert sets the value back to the one saved, using the
0268 /// setter function provided.
0269 ///
0270 /// Example:
0271 ///  Tracker.track(std::make_unique<
0272 ///                GenericSetter<&FooInst::get, &FooInst::set>>(I, Tracker));
0273 ///
0274 template <auto GetterFn, auto SetterFn>
0275 class GenericSetter final : public IRChangeBase {
0276   /// Traits for getting the class type from GetterFn type.
0277   template <typename> struct GetClassTypeFromGetter;
0278   template <typename RetT, typename ClassT>
0279   struct GetClassTypeFromGetter<RetT (ClassT::*)() const> {
0280     using ClassType = ClassT;
0281   };
0282   using InstrT = typename GetClassTypeFromGetter<decltype(GetterFn)>::ClassType;
0283   using SavedValT = std::invoke_result_t<decltype(GetterFn), InstrT>;
0284   InstrT *I;
0285   SavedValT OrigVal;
0286 
0287 public:
0288   GenericSetter(InstrT *I) : I(I), OrigVal((I->*GetterFn)()) {}
0289   void revert(Tracker &Tracker) final { (I->*SetterFn)(OrigVal); }
0290   void accept() final {}
0291 #ifndef NDEBUG
0292   void dump(raw_ostream &OS) const final { OS << "GenericSetter"; }
0293   LLVM_DUMP_METHOD void dump() const final {
0294     dump(dbgs());
0295     dbgs() << "\n";
0296   }
0297 #endif
0298 };
0299 
0300 /// Similar to GenericSetter but the setters/getters have an index as their
0301 /// first argument. This is commont in cases like: getOperand(unsigned Idx)
0302 template <auto GetterFn, auto SetterFn>
0303 class GenericSetterWithIdx final : public IRChangeBase {
0304   /// Helper for getting the class type from the getter
0305   template <typename ClassT, typename RetT>
0306   static ClassT getClassTypeFromGetter(RetT (ClassT::*Fn)(unsigned) const);
0307   template <typename ClassT, typename RetT>
0308   static ClassT getClassTypeFromGetter(RetT (ClassT::*Fn)(unsigned));
0309 
0310   using InstrT = decltype(getClassTypeFromGetter(GetterFn));
0311   using SavedValT = std::invoke_result_t<decltype(GetterFn), InstrT, unsigned>;
0312   InstrT *I;
0313   SavedValT OrigVal;
0314   unsigned Idx;
0315 
0316 public:
0317   GenericSetterWithIdx(InstrT *I, unsigned Idx)
0318       : I(I), OrigVal((I->*GetterFn)(Idx)), Idx(Idx) {}
0319   void revert(Tracker &Tracker) final { (I->*SetterFn)(Idx, OrigVal); }
0320   void accept() final {}
0321 #ifndef NDEBUG
0322   void dump(raw_ostream &OS) const final { OS << "GenericSetterWithIdx"; }
0323   LLVM_DUMP_METHOD void dump() const final {
0324     dump(dbgs());
0325     dbgs() << "\n";
0326   }
0327 #endif
0328 };
0329 
0330 class CatchSwitchAddHandler : public IRChangeBase {
0331   CatchSwitchInst *CSI;
0332   unsigned HandlerIdx;
0333 
0334 public:
0335   CatchSwitchAddHandler(CatchSwitchInst *CSI);
0336   void revert(Tracker &Tracker) final;
0337   void accept() final {}
0338 #ifndef NDEBUG
0339   void dump(raw_ostream &OS) const final { OS << "CatchSwitchAddHandler"; }
0340   LLVM_DUMP_METHOD void dump() const final {
0341     dump(dbgs());
0342     dbgs() << "\n";
0343   }
0344 #endif // NDEBUG
0345 };
0346 
0347 class SwitchAddCase : public IRChangeBase {
0348   SwitchInst *Switch;
0349   ConstantInt *Val;
0350 
0351 public:
0352   SwitchAddCase(SwitchInst *Switch, ConstantInt *Val)
0353       : Switch(Switch), Val(Val) {}
0354   void revert(Tracker &Tracker) final;
0355   void accept() final {}
0356 #ifndef NDEBUG
0357   void dump(raw_ostream &OS) const final { OS << "SwitchAddCase"; }
0358   LLVM_DUMP_METHOD void dump() const final;
0359 #endif // NDEBUG
0360 };
0361 
0362 class SwitchRemoveCase : public IRChangeBase {
0363   SwitchInst *Switch;
0364   struct Case {
0365     ConstantInt *Val;
0366     BasicBlock *Dest;
0367   };
0368   SmallVector<Case> Cases;
0369 
0370 public:
0371   SwitchRemoveCase(SwitchInst *Switch);
0372 
0373   void revert(Tracker &Tracker) final;
0374   void accept() final {}
0375 #ifndef NDEBUG
0376   void dump(raw_ostream &OS) const final { OS << "SwitchRemoveCase"; }
0377   LLVM_DUMP_METHOD void dump() const final;
0378 #endif // NDEBUG
0379 };
0380 
0381 class MoveInstr : public IRChangeBase {
0382   /// The instruction that moved.
0383   Instruction *MovedI;
0384   /// This is either the next instruction in the block, or the parent BB if at
0385   /// the end of the BB.
0386   PointerUnion<Instruction *, BasicBlock *> NextInstrOrBB;
0387 
0388 public:
0389   MoveInstr(sandboxir::Instruction *I);
0390   void revert(Tracker &Tracker) final;
0391   void accept() final {}
0392 #ifndef NDEBUG
0393   void dump(raw_ostream &OS) const final { OS << "MoveInstr"; }
0394   LLVM_DUMP_METHOD void dump() const final;
0395 #endif // NDEBUG
0396 };
0397 
0398 class InsertIntoBB final : public IRChangeBase {
0399   Instruction *InsertedI = nullptr;
0400 
0401 public:
0402   InsertIntoBB(Instruction *InsertedI);
0403   void revert(Tracker &Tracker) final;
0404   void accept() final {}
0405 #ifndef NDEBUG
0406   void dump(raw_ostream &OS) const final { OS << "InsertIntoBB"; }
0407   LLVM_DUMP_METHOD void dump() const final;
0408 #endif // NDEBUG
0409 };
0410 
0411 class CreateAndInsertInst final : public IRChangeBase {
0412   Instruction *NewI = nullptr;
0413 
0414 public:
0415   CreateAndInsertInst(Instruction *NewI) : NewI(NewI) {}
0416   void revert(Tracker &Tracker) final;
0417   void accept() final {}
0418 #ifndef NDEBUG
0419   void dump(raw_ostream &OS) const final { OS << "CreateAndInsertInst"; }
0420   LLVM_DUMP_METHOD void dump() const final;
0421 #endif
0422 };
0423 
0424 class ShuffleVectorSetMask final : public IRChangeBase {
0425   ShuffleVectorInst *SVI;
0426   SmallVector<int, 8> PrevMask;
0427 
0428 public:
0429   ShuffleVectorSetMask(ShuffleVectorInst *SVI);
0430   void revert(Tracker &Tracker) final;
0431   void accept() final {}
0432 #ifndef NDEBUG
0433   void dump(raw_ostream &OS) const final { OS << "ShuffleVectorSetMask"; }
0434   LLVM_DUMP_METHOD void dump() const final;
0435 #endif
0436 };
0437 
0438 /// The tracker collects all the change objects and implements the main API for
0439 /// saving / reverting / accepting.
0440 class Tracker {
0441 public:
0442   enum class TrackerState {
0443     Disabled, ///> Tracking is disabled
0444     Record,   ///> Tracking changes
0445   };
0446 
0447 private:
0448   /// The list of changes that are being tracked.
0449   SmallVector<std::unique_ptr<IRChangeBase>> Changes;
0450   /// The current state of the tracker.
0451   TrackerState State = TrackerState::Disabled;
0452   Context &Ctx;
0453 
0454 #ifndef NDEBUG
0455   IRSnapshotChecker SnapshotChecker;
0456 #endif
0457 
0458 public:
0459 #ifndef NDEBUG
0460   /// Helps catch bugs where we are creating new change objects while in the
0461   /// middle of creating other change objects.
0462   bool InMiddleOfCreatingChange = false;
0463 #endif // NDEBUG
0464 
0465   explicit Tracker(Context &Ctx)
0466       : Ctx(Ctx)
0467 #ifndef NDEBUG
0468         ,
0469         SnapshotChecker(Ctx)
0470 #endif
0471   {
0472   }
0473 
0474   ~Tracker();
0475   Context &getContext() const { return Ctx; }
0476   /// Record \p Change and take ownership. This is the main function used to
0477   /// track Sandbox IR changes.
0478   void track(std::unique_ptr<IRChangeBase> &&Change) {
0479     assert(State == TrackerState::Record && "The tracker should be tracking!");
0480 #ifndef NDEBUG
0481     assert(!InMiddleOfCreatingChange &&
0482            "We are in the middle of creating another change!");
0483     if (isTracking())
0484       InMiddleOfCreatingChange = true;
0485 #endif // NDEBUG
0486     Changes.push_back(std::move(Change));
0487 
0488 #ifndef NDEBUG
0489     InMiddleOfCreatingChange = false;
0490 #endif
0491   }
0492   /// A convenience wrapper for `track()` that constructs and tracks the Change
0493   /// object if tracking is enabled. \Returns true if tracking is enabled.
0494   template <typename ChangeT, typename... ArgsT>
0495   bool emplaceIfTracking(ArgsT... Args) {
0496     if (!isTracking())
0497       return false;
0498     track(std::make_unique<ChangeT>(Args...));
0499     return true;
0500   }
0501   /// \Returns true if the tracker is recording changes.
0502   bool isTracking() const { return State == TrackerState::Record; }
0503   /// \Returns the current state of the tracker.
0504   TrackerState getState() const { return State; }
0505   /// Turns on IR tracking.
0506   void save();
0507   /// Stops tracking and accept changes.
0508   void accept();
0509   /// Stops tracking and reverts to saved state.
0510   void revert();
0511 
0512 #ifndef NDEBUG
0513   void dump(raw_ostream &OS) const;
0514   LLVM_DUMP_METHOD void dump() const;
0515   friend raw_ostream &operator<<(raw_ostream &OS, const Tracker &Tracker) {
0516     Tracker.dump(OS);
0517     return OS;
0518   }
0519 #endif // NDEBUG
0520 };
0521 
0522 } // namespace llvm::sandboxir
0523 
0524 #endif // LLVM_SANDBOXIR_TRACKER_H