File indexing completed on 2026-05-10 08:44:26
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
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
0071
0072
0073
0074
0075
0076
0077 class IRSnapshotChecker {
0078 Context &Ctx;
0079
0080
0081 struct FunctionSnapshot {
0082 llvm::stable_hash Hash;
0083 std::string TextualIR;
0084 };
0085
0086
0087
0088
0089
0090 using ContextSnapshot = DenseMap<const llvm::Function *, FunctionSnapshot>;
0091
0092 ContextSnapshot OrigContextSnapshot;
0093
0094
0095 std::string dumpIR(const llvm::Function &F) const;
0096
0097
0098 ContextSnapshot takeSnapshot() const;
0099
0100
0101 bool diff(const ContextSnapshot &Orig, const ContextSnapshot &Curr) const;
0102
0103 public:
0104 IRSnapshotChecker(Context &Ctx) : Ctx(Ctx) {}
0105
0106
0107
0108 void save();
0109
0110
0111 void expectNoDiff();
0112 };
0113
0114 #endif
0115
0116
0117 class IRChangeBase {
0118 protected:
0119 friend class Tracker;
0120
0121 public:
0122
0123 virtual void revert(Tracker &Tracker) = 0;
0124
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
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
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
0215
0216 struct InstrAndOperands {
0217
0218 SmallVector<llvm::Value *> Operands;
0219
0220 llvm::Instruction *LLVMI;
0221 };
0222
0223
0224 SmallVector<InstrAndOperands> InstrData;
0225
0226
0227 PointerUnion<llvm::Instruction *, llvm::BasicBlock *> NextLLVMIOrBB;
0228
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
0247 Instruction *RemovedI = nullptr;
0248
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
0260 };
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274 template <auto GetterFn, auto SetterFn>
0275 class GenericSetter final : public IRChangeBase {
0276
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
0301
0302 template <auto GetterFn, auto SetterFn>
0303 class GenericSetterWithIdx final : public IRChangeBase {
0304
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
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
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
0379 };
0380
0381 class MoveInstr : public IRChangeBase {
0382
0383 Instruction *MovedI;
0384
0385
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
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
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
0439
0440 class Tracker {
0441 public:
0442 enum class TrackerState {
0443 Disabled,
0444 Record,
0445 };
0446
0447 private:
0448
0449 SmallVector<std::unique_ptr<IRChangeBase>> Changes;
0450
0451 TrackerState State = TrackerState::Disabled;
0452 Context &Ctx;
0453
0454 #ifndef NDEBUG
0455 IRSnapshotChecker SnapshotChecker;
0456 #endif
0457
0458 public:
0459 #ifndef NDEBUG
0460
0461
0462 bool InMiddleOfCreatingChange = false;
0463 #endif
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
0477
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
0486 Changes.push_back(std::move(Change));
0487
0488 #ifndef NDEBUG
0489 InMiddleOfCreatingChange = false;
0490 #endif
0491 }
0492
0493
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
0502 bool isTracking() const { return State == TrackerState::Record; }
0503
0504 TrackerState getState() const { return State; }
0505
0506 void save();
0507
0508 void accept();
0509
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
0520 };
0521
0522 }
0523
0524 #endif