xref: /llvm-project/llvm/lib/SandboxIR/Tracker.cpp (revision 749443a307e8e47a25a5552cbeb27f69845e6ce8)
15338bd3cSvporpo //===- Tracker.cpp --------------------------------------------------------===//
25338bd3cSvporpo //
35338bd3cSvporpo // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45338bd3cSvporpo // See https://llvm.org/LICENSE.txt for license information.
55338bd3cSvporpo // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65338bd3cSvporpo //
75338bd3cSvporpo //===----------------------------------------------------------------------===//
85338bd3cSvporpo 
95338bd3cSvporpo #include "llvm/SandboxIR/Tracker.h"
105338bd3cSvporpo #include "llvm/ADT/STLExtras.h"
115338bd3cSvporpo #include "llvm/IR/BasicBlock.h"
125338bd3cSvporpo #include "llvm/IR/Instruction.h"
133b8606beSJorge Gorbe Moya #include "llvm/IR/Module.h"
143b8606beSJorge Gorbe Moya #include "llvm/IR/StructuralHash.h"
15eba106d4Svporpo #include "llvm/SandboxIR/Instruction.h"
165338bd3cSvporpo #include <sstream>
175338bd3cSvporpo 
185338bd3cSvporpo using namespace llvm::sandboxir;
195338bd3cSvporpo 
205338bd3cSvporpo #ifndef NDEBUG
213b8606beSJorge Gorbe Moya 
223b8606beSJorge Gorbe Moya std::string IRSnapshotChecker::dumpIR(const llvm::Function &F) const {
233b8606beSJorge Gorbe Moya   std::string Result;
243b8606beSJorge Gorbe Moya   raw_string_ostream SS(Result);
253b8606beSJorge Gorbe Moya   F.print(SS, /*AssemblyAnnotationWriter=*/nullptr);
263b8606beSJorge Gorbe Moya   return Result;
273b8606beSJorge Gorbe Moya }
283b8606beSJorge Gorbe Moya 
293b8606beSJorge Gorbe Moya IRSnapshotChecker::ContextSnapshot IRSnapshotChecker::takeSnapshot() const {
303b8606beSJorge Gorbe Moya   ContextSnapshot Result;
313b8606beSJorge Gorbe Moya   for (const auto &Entry : Ctx.LLVMModuleToModuleMap)
323b8606beSJorge Gorbe Moya     for (const auto &F : *Entry.first) {
333b8606beSJorge Gorbe Moya       FunctionSnapshot Snapshot;
343b8606beSJorge Gorbe Moya       Snapshot.Hash = StructuralHash(F, /*DetailedHash=*/true);
353b8606beSJorge Gorbe Moya       Snapshot.TextualIR = dumpIR(F);
363b8606beSJorge Gorbe Moya       Result[&F] = Snapshot;
373b8606beSJorge Gorbe Moya     }
383b8606beSJorge Gorbe Moya   return Result;
393b8606beSJorge Gorbe Moya }
403b8606beSJorge Gorbe Moya 
413b8606beSJorge Gorbe Moya bool IRSnapshotChecker::diff(const ContextSnapshot &Orig,
423b8606beSJorge Gorbe Moya                              const ContextSnapshot &Curr) const {
433b8606beSJorge Gorbe Moya   bool DifferenceFound = false;
443b8606beSJorge Gorbe Moya   for (const auto &[F, OrigFS] : Orig) {
453b8606beSJorge Gorbe Moya     auto CurrFSIt = Curr.find(F);
463b8606beSJorge Gorbe Moya     if (CurrFSIt == Curr.end()) {
473b8606beSJorge Gorbe Moya       DifferenceFound = true;
483b8606beSJorge Gorbe Moya       dbgs() << "Function " << F->getName() << " not found in current IR.\n";
493b8606beSJorge Gorbe Moya       dbgs() << OrigFS.TextualIR << "\n";
503b8606beSJorge Gorbe Moya       continue;
513b8606beSJorge Gorbe Moya     }
523b8606beSJorge Gorbe Moya     const FunctionSnapshot &CurrFS = CurrFSIt->second;
533b8606beSJorge Gorbe Moya     if (OrigFS.Hash != CurrFS.Hash) {
543b8606beSJorge Gorbe Moya       DifferenceFound = true;
553b8606beSJorge Gorbe Moya       dbgs() << "Found IR difference in Function " << F->getName() << "\n";
563b8606beSJorge Gorbe Moya       dbgs() << "Original:\n" << OrigFS.TextualIR << "\n";
573b8606beSJorge Gorbe Moya       dbgs() << "Current:\n" << CurrFS.TextualIR << "\n";
583b8606beSJorge Gorbe Moya     }
593b8606beSJorge Gorbe Moya   }
603b8606beSJorge Gorbe Moya   // Check that Curr doesn't contain any new functions.
613b8606beSJorge Gorbe Moya   for (const auto &[F, CurrFS] : Curr) {
623b8606beSJorge Gorbe Moya     if (!Orig.contains(F)) {
633b8606beSJorge Gorbe Moya       DifferenceFound = true;
643b8606beSJorge Gorbe Moya       dbgs() << "Function " << F->getName()
653b8606beSJorge Gorbe Moya              << " found in current IR but not in original snapshot.\n";
663b8606beSJorge Gorbe Moya       dbgs() << CurrFS.TextualIR << "\n";
673b8606beSJorge Gorbe Moya     }
683b8606beSJorge Gorbe Moya   }
693b8606beSJorge Gorbe Moya   return DifferenceFound;
703b8606beSJorge Gorbe Moya }
713b8606beSJorge Gorbe Moya 
723b8606beSJorge Gorbe Moya void IRSnapshotChecker::save() { OrigContextSnapshot = takeSnapshot(); }
733b8606beSJorge Gorbe Moya 
743b8606beSJorge Gorbe Moya void IRSnapshotChecker::expectNoDiff() {
753b8606beSJorge Gorbe Moya   ContextSnapshot CurrContextSnapshot = takeSnapshot();
763b8606beSJorge Gorbe Moya   if (diff(OrigContextSnapshot, CurrContextSnapshot)) {
773b8606beSJorge Gorbe Moya     llvm_unreachable(
783b8606beSJorge Gorbe Moya         "Original and current IR differ! Probably a checkpointing bug.");
793b8606beSJorge Gorbe Moya   }
803b8606beSJorge Gorbe Moya }
813b8606beSJorge Gorbe Moya 
825338bd3cSvporpo void UseSet::dump() const {
835338bd3cSvporpo   dump(dbgs());
845338bd3cSvporpo   dbgs() << "\n";
855338bd3cSvporpo }
86c444548bSVasileios Porpodas 
87c444548bSVasileios Porpodas void UseSwap::dump() const {
88c444548bSVasileios Porpodas   dump(dbgs());
89c444548bSVasileios Porpodas   dbgs() << "\n";
90c444548bSVasileios Porpodas }
915338bd3cSvporpo #endif // NDEBUG
925338bd3cSvporpo 
9344f30c80SVasileios Porpodas PHIRemoveIncoming::PHIRemoveIncoming(PHINode *PHI, unsigned RemovedIdx)
94f7ad495aSvporpo     : PHI(PHI), RemovedIdx(RemovedIdx) {
9544f30c80SVasileios Porpodas   RemovedV = PHI->getIncomingValue(RemovedIdx);
9644f30c80SVasileios Porpodas   RemovedBB = PHI->getIncomingBlock(RemovedIdx);
973403b593SSterling-Augustine }
983403b593SSterling-Augustine 
99f7ad495aSvporpo void PHIRemoveIncoming::revert(Tracker &Tracker) {
1003403b593SSterling-Augustine   // Special case: if the PHI is now empty, as we don't need to care about the
1013403b593SSterling-Augustine   // order of the incoming values.
10244f30c80SVasileios Porpodas   unsigned NumIncoming = PHI->getNumIncomingValues();
1033403b593SSterling-Augustine   if (NumIncoming == 0) {
10444f30c80SVasileios Porpodas     PHI->addIncoming(RemovedV, RemovedBB);
1053403b593SSterling-Augustine     return;
1063403b593SSterling-Augustine   }
1073403b593SSterling-Augustine   // Shift all incoming values by one starting from the end until `Idx`.
1083403b593SSterling-Augustine   // Start by adding a copy of the last incoming values.
1093403b593SSterling-Augustine   unsigned LastIdx = NumIncoming - 1;
11044f30c80SVasileios Porpodas   PHI->addIncoming(PHI->getIncomingValue(LastIdx),
11144f30c80SVasileios Porpodas                    PHI->getIncomingBlock(LastIdx));
1123403b593SSterling-Augustine   for (unsigned Idx = LastIdx; Idx > RemovedIdx; --Idx) {
11344f30c80SVasileios Porpodas     auto *PrevV = PHI->getIncomingValue(Idx - 1);
11444f30c80SVasileios Porpodas     auto *PrevBB = PHI->getIncomingBlock(Idx - 1);
11544f30c80SVasileios Porpodas     PHI->setIncomingValue(Idx, PrevV);
11644f30c80SVasileios Porpodas     PHI->setIncomingBlock(Idx, PrevBB);
1173403b593SSterling-Augustine   }
11844f30c80SVasileios Porpodas   PHI->setIncomingValue(RemovedIdx, RemovedV);
11944f30c80SVasileios Porpodas   PHI->setIncomingBlock(RemovedIdx, RemovedBB);
1203403b593SSterling-Augustine }
1213403b593SSterling-Augustine 
1223403b593SSterling-Augustine #ifndef NDEBUG
1233403b593SSterling-Augustine void PHIRemoveIncoming::dump() const {
1243403b593SSterling-Augustine   dump(dbgs());
1253403b593SSterling-Augustine   dbgs() << "\n";
1263403b593SSterling-Augustine }
1273403b593SSterling-Augustine #endif // NDEBUG
1283403b593SSterling-Augustine 
12944f30c80SVasileios Porpodas PHIAddIncoming::PHIAddIncoming(PHINode *PHI)
13044f30c80SVasileios Porpodas     : PHI(PHI), Idx(PHI->getNumIncomingValues()) {}
1313403b593SSterling-Augustine 
13244f30c80SVasileios Porpodas void PHIAddIncoming::revert(Tracker &Tracker) { PHI->removeIncomingValue(Idx); }
1333403b593SSterling-Augustine 
1343403b593SSterling-Augustine #ifndef NDEBUG
1353403b593SSterling-Augustine void PHIAddIncoming::dump() const {
1363403b593SSterling-Augustine   dump(dbgs());
1373403b593SSterling-Augustine   dbgs() << "\n";
1383403b593SSterling-Augustine }
1393403b593SSterling-Augustine #endif // NDEBUG
1403403b593SSterling-Augustine 
1415338bd3cSvporpo Tracker::~Tracker() {
1425338bd3cSvporpo   assert(Changes.empty() && "You must accept or revert changes!");
1435338bd3cSvporpo }
1445338bd3cSvporpo 
145f7ad495aSvporpo EraseFromParent::EraseFromParent(std::unique_ptr<sandboxir::Value> &&ErasedIPtr)
146f7ad495aSvporpo     : ErasedIPtr(std::move(ErasedIPtr)) {
147c5432d31Svporpo   auto *I = cast<Instruction>(this->ErasedIPtr.get());
148c5432d31Svporpo   auto LLVMInstrs = I->getLLVMInstrs();
149c5432d31Svporpo   // Iterate in reverse program order.
150c5432d31Svporpo   for (auto *LLVMI : reverse(LLVMInstrs)) {
151c5432d31Svporpo     SmallVector<llvm::Value *> Operands;
152c5432d31Svporpo     Operands.reserve(LLVMI->getNumOperands());
153c5432d31Svporpo     for (auto [OpNum, Use] : enumerate(LLVMI->operands()))
154c5432d31Svporpo       Operands.push_back(Use.get());
155c5432d31Svporpo     InstrData.push_back({Operands, LLVMI});
156c5432d31Svporpo   }
157c5432d31Svporpo   assert(is_sorted(InstrData,
158c5432d31Svporpo                    [](const auto &D0, const auto &D1) {
159c5432d31Svporpo                      return D0.LLVMI->comesBefore(D1.LLVMI);
160c5432d31Svporpo                    }) &&
161c5432d31Svporpo          "Expected reverse program order!");
162c5432d31Svporpo   auto *BotLLVMI = cast<llvm::Instruction>(I->Val);
163c5432d31Svporpo   if (BotLLVMI->getNextNode() != nullptr)
164c5432d31Svporpo     NextLLVMIOrBB = BotLLVMI->getNextNode();
165c5432d31Svporpo   else
166c5432d31Svporpo     NextLLVMIOrBB = BotLLVMI->getParent();
167c5432d31Svporpo }
168c5432d31Svporpo 
169c5432d31Svporpo void EraseFromParent::accept() {
170c5432d31Svporpo   for (const auto &IData : InstrData)
171c5432d31Svporpo     IData.LLVMI->deleteValue();
172c5432d31Svporpo }
173c5432d31Svporpo 
174f7ad495aSvporpo void EraseFromParent::revert(Tracker &Tracker) {
175c5432d31Svporpo   // Place the bottom-most instruction first.
176c5432d31Svporpo   auto [Operands, BotLLVMI] = InstrData[0];
1775b19ed8bSKazu Hirata   if (auto *NextLLVMI = dyn_cast<llvm::Instruction *>(NextLLVMIOrBB)) {
178*749443a3SJeremy Morse     BotLLVMI->insertBefore(NextLLVMI->getIterator());
179c5432d31Svporpo   } else {
1805b19ed8bSKazu Hirata     auto *LLVMBB = cast<llvm::BasicBlock *>(NextLLVMIOrBB);
181c5432d31Svporpo     BotLLVMI->insertInto(LLVMBB, LLVMBB->end());
182c5432d31Svporpo   }
183c5432d31Svporpo   for (auto [OpNum, Op] : enumerate(Operands))
184c5432d31Svporpo     BotLLVMI->setOperand(OpNum, Op);
185c5432d31Svporpo 
186c5432d31Svporpo   // Go over the rest of the instructions and stack them on top.
187c5432d31Svporpo   for (auto [Operands, LLVMI] : drop_begin(InstrData)) {
188*749443a3SJeremy Morse     LLVMI->insertBefore(BotLLVMI->getIterator());
189c5432d31Svporpo     for (auto [OpNum, Op] : enumerate(Operands))
190c5432d31Svporpo       LLVMI->setOperand(OpNum, Op);
191c5432d31Svporpo     BotLLVMI = LLVMI;
192c5432d31Svporpo   }
193f7ad495aSvporpo   Tracker.getContext().registerValue(std::move(ErasedIPtr));
194c5432d31Svporpo }
195c5432d31Svporpo 
196c5432d31Svporpo #ifndef NDEBUG
197c5432d31Svporpo void EraseFromParent::dump() const {
198c5432d31Svporpo   dump(dbgs());
199c5432d31Svporpo   dbgs() << "\n";
200c5432d31Svporpo }
2014f786c68Svporpo #endif // NDEBUG
2024f786c68Svporpo 
203f7ad495aSvporpo RemoveFromParent::RemoveFromParent(Instruction *RemovedI) : RemovedI(RemovedI) {
2044f786c68Svporpo   if (auto *NextI = RemovedI->getNextNode())
2054f786c68Svporpo     NextInstrOrBB = NextI;
2064f786c68Svporpo   else
2074f786c68Svporpo     NextInstrOrBB = RemovedI->getParent();
2084f786c68Svporpo }
2094f786c68Svporpo 
210f7ad495aSvporpo void RemoveFromParent::revert(Tracker &Tracker) {
2115b19ed8bSKazu Hirata   if (auto *NextI = dyn_cast<Instruction *>(NextInstrOrBB)) {
2124f786c68Svporpo     RemovedI->insertBefore(NextI);
2134f786c68Svporpo   } else {
2145b19ed8bSKazu Hirata     auto *BB = cast<BasicBlock *>(NextInstrOrBB);
2154f786c68Svporpo     RemovedI->insertInto(BB, BB->end());
2164f786c68Svporpo   }
2174f786c68Svporpo }
2184f786c68Svporpo 
2194f786c68Svporpo #ifndef NDEBUG
2204f786c68Svporpo void RemoveFromParent::dump() const {
2214f786c68Svporpo   dump(dbgs());
2224f786c68Svporpo   dbgs() << "\n";
2234f786c68Svporpo }
224c5432d31Svporpo #endif
225c5432d31Svporpo 
2266e8c9703Svporpo CatchSwitchAddHandler::CatchSwitchAddHandler(CatchSwitchInst *CSI)
2276e8c9703Svporpo     : CSI(CSI), HandlerIdx(CSI->getNumHandlers()) {}
2286e8c9703Svporpo 
2296e8c9703Svporpo void CatchSwitchAddHandler::revert(Tracker &Tracker) {
2306e8c9703Svporpo   // TODO: This should ideally use sandboxir::CatchSwitchInst::removeHandler()
2316e8c9703Svporpo   // once it gets implemented.
2326e8c9703Svporpo   auto *LLVMCSI = cast<llvm::CatchSwitchInst>(CSI->Val);
2336e8c9703Svporpo   LLVMCSI->removeHandler(LLVMCSI->handler_begin() + HandlerIdx);
2346e8c9703Svporpo }
2356e8c9703Svporpo 
2369d85ba57SJorge Gorbe Moya SwitchRemoveCase::SwitchRemoveCase(SwitchInst *Switch) : Switch(Switch) {
2379d85ba57SJorge Gorbe Moya   for (const auto &C : Switch->cases())
2389d85ba57SJorge Gorbe Moya     Cases.push_back({C.getCaseValue(), C.getCaseSuccessor()});
2399d85ba57SJorge Gorbe Moya }
2409d85ba57SJorge Gorbe Moya 
2419d85ba57SJorge Gorbe Moya void SwitchRemoveCase::revert(Tracker &Tracker) {
2429d85ba57SJorge Gorbe Moya   // SwitchInst::removeCase doesn't provide any guarantees about the order of
2439d85ba57SJorge Gorbe Moya   // cases after removal. In order to preserve the original ordering, we save
2449d85ba57SJorge Gorbe Moya   // all of them and, when reverting, clear them all then insert them in the
2459d85ba57SJorge Gorbe Moya   // desired order. This still relies on the fact that `addCase` will insert
2469d85ba57SJorge Gorbe Moya   // them at the end, but it is documented to invalidate `case_end()` so it's
2479d85ba57SJorge Gorbe Moya   // probably okay.
2489d85ba57SJorge Gorbe Moya   unsigned NumCases = Switch->getNumCases();
2499d85ba57SJorge Gorbe Moya   for (unsigned I = 0; I < NumCases; ++I)
2509d85ba57SJorge Gorbe Moya     Switch->removeCase(Switch->case_begin());
2519d85ba57SJorge Gorbe Moya   for (auto &Case : Cases)
2529d85ba57SJorge Gorbe Moya     Switch->addCase(Case.Val, Case.Dest);
2539d85ba57SJorge Gorbe Moya }
254516c1a0eSvporpo 
255516c1a0eSvporpo #ifndef NDEBUG
256516c1a0eSvporpo void SwitchRemoveCase::dump() const {
257516c1a0eSvporpo   dump(dbgs());
258516c1a0eSvporpo   dbgs() << "\n";
259516c1a0eSvporpo }
260516c1a0eSvporpo #endif // NDEBUG
261516c1a0eSvporpo 
262516c1a0eSvporpo void SwitchAddCase::revert(Tracker &Tracker) {
263516c1a0eSvporpo   auto It = Switch->findCaseValue(Val);
264516c1a0eSvporpo   Switch->removeCase(It);
265516c1a0eSvporpo }
266516c1a0eSvporpo 
267516c1a0eSvporpo #ifndef NDEBUG
268516c1a0eSvporpo void SwitchAddCase::dump() const {
269516c1a0eSvporpo   dump(dbgs());
270516c1a0eSvporpo   dbgs() << "\n";
271516c1a0eSvporpo }
272516c1a0eSvporpo #endif // NDEBUG
273516c1a0eSvporpo 
274f7ad495aSvporpo MoveInstr::MoveInstr(Instruction *MovedI) : MovedI(MovedI) {
275cbbd1532Svporpo   if (auto *NextI = MovedI->getNextNode())
276cbbd1532Svporpo     NextInstrOrBB = NextI;
277cbbd1532Svporpo   else
278cbbd1532Svporpo     NextInstrOrBB = MovedI->getParent();
279cbbd1532Svporpo }
280cbbd1532Svporpo 
281f7ad495aSvporpo void MoveInstr::revert(Tracker &Tracker) {
2825b19ed8bSKazu Hirata   if (auto *NextI = dyn_cast<Instruction *>(NextInstrOrBB)) {
283cbbd1532Svporpo     MovedI->moveBefore(NextI);
284cbbd1532Svporpo   } else {
2855b19ed8bSKazu Hirata     auto *BB = cast<BasicBlock *>(NextInstrOrBB);
286cbbd1532Svporpo     MovedI->moveBefore(*BB, BB->end());
287cbbd1532Svporpo   }
288cbbd1532Svporpo }
289cbbd1532Svporpo 
290cbbd1532Svporpo #ifndef NDEBUG
291cbbd1532Svporpo void MoveInstr::dump() const {
292cbbd1532Svporpo   dump(dbgs());
293cbbd1532Svporpo   dbgs() << "\n";
294cbbd1532Svporpo }
295cbbd1532Svporpo #endif
296cbbd1532Svporpo 
297f7ad495aSvporpo void InsertIntoBB::revert(Tracker &Tracker) { InsertedI->removeFromParent(); }
29885da9611Svporpo 
299f7ad495aSvporpo InsertIntoBB::InsertIntoBB(Instruction *InsertedI) : InsertedI(InsertedI) {}
30085da9611Svporpo 
30185da9611Svporpo #ifndef NDEBUG
30285da9611Svporpo void InsertIntoBB::dump() const {
30385da9611Svporpo   dump(dbgs());
30485da9611Svporpo   dbgs() << "\n";
30585da9611Svporpo }
30685da9611Svporpo #endif
30785da9611Svporpo 
308f7ad495aSvporpo void CreateAndInsertInst::revert(Tracker &Tracker) { NewI->eraseFromParent(); }
3090959e58fSvporpo 
3100959e58fSvporpo #ifndef NDEBUG
3110959e58fSvporpo void CreateAndInsertInst::dump() const {
3120959e58fSvporpo   dump(dbgs());
3130959e58fSvporpo   dbgs() << "\n";
3140959e58fSvporpo }
3150959e58fSvporpo #endif
3160959e58fSvporpo 
317b5ba7265SJorge Gorbe Moya ShuffleVectorSetMask::ShuffleVectorSetMask(ShuffleVectorInst *SVI)
318b5ba7265SJorge Gorbe Moya     : SVI(SVI), PrevMask(SVI->getShuffleMask()) {}
319b5ba7265SJorge Gorbe Moya 
320b5ba7265SJorge Gorbe Moya void ShuffleVectorSetMask::revert(Tracker &Tracker) {
321b5ba7265SJorge Gorbe Moya   SVI->setShuffleMask(PrevMask);
322b5ba7265SJorge Gorbe Moya }
323b5ba7265SJorge Gorbe Moya 
324b5ba7265SJorge Gorbe Moya #ifndef NDEBUG
325b5ba7265SJorge Gorbe Moya void ShuffleVectorSetMask::dump() const {
326b5ba7265SJorge Gorbe Moya   dump(dbgs());
327b5ba7265SJorge Gorbe Moya   dbgs() << "\n";
328b5ba7265SJorge Gorbe Moya }
329b5ba7265SJorge Gorbe Moya #endif
330b5ba7265SJorge Gorbe Moya 
331840da2e8SSterling-Augustine CmpSwapOperands::CmpSwapOperands(CmpInst *Cmp) : Cmp(Cmp) {}
332840da2e8SSterling-Augustine 
333840da2e8SSterling-Augustine void CmpSwapOperands::revert(Tracker &Tracker) { Cmp->swapOperands(); }
334b30880e9SVasileios Porpodas #ifndef NDEBUG
335840da2e8SSterling-Augustine void CmpSwapOperands::dump() const {
336840da2e8SSterling-Augustine   dump(dbgs());
337840da2e8SSterling-Augustine   dbgs() << "\n";
338840da2e8SSterling-Augustine }
339b30880e9SVasileios Porpodas #endif
340840da2e8SSterling-Augustine 
3413b8606beSJorge Gorbe Moya void Tracker::save() {
3423b8606beSJorge Gorbe Moya   State = TrackerState::Record;
3433b8606beSJorge Gorbe Moya #if !defined(NDEBUG) && defined(EXPENSIVE_CHECKS)
3443b8606beSJorge Gorbe Moya   SnapshotChecker.save();
3453b8606beSJorge Gorbe Moya #endif
3463b8606beSJorge Gorbe Moya }
3475338bd3cSvporpo 
3485338bd3cSvporpo void Tracker::revert() {
3495338bd3cSvporpo   assert(State == TrackerState::Record && "Forgot to save()!");
3505338bd3cSvporpo   State = TrackerState::Disabled;
3515338bd3cSvporpo   for (auto &Change : reverse(Changes))
352f7ad495aSvporpo     Change->revert(*this);
3535338bd3cSvporpo   Changes.clear();
3543b8606beSJorge Gorbe Moya #if !defined(NDEBUG) && defined(EXPENSIVE_CHECKS)
3553b8606beSJorge Gorbe Moya   SnapshotChecker.expectNoDiff();
3563b8606beSJorge Gorbe Moya #endif
3575338bd3cSvporpo }
3585338bd3cSvporpo 
3595338bd3cSvporpo void Tracker::accept() {
3605338bd3cSvporpo   assert(State == TrackerState::Record && "Forgot to save()!");
3615338bd3cSvporpo   State = TrackerState::Disabled;
3625338bd3cSvporpo   for (auto &Change : Changes)
3635338bd3cSvporpo     Change->accept();
3645338bd3cSvporpo   Changes.clear();
3655338bd3cSvporpo }
3665338bd3cSvporpo 
3675338bd3cSvporpo #ifndef NDEBUG
3685338bd3cSvporpo void Tracker::dump(raw_ostream &OS) const {
369f7ad495aSvporpo   for (auto [Idx, ChangePtr] : enumerate(Changes)) {
370f7ad495aSvporpo     OS << Idx << ". ";
3715338bd3cSvporpo     ChangePtr->dump(OS);
3725338bd3cSvporpo     OS << "\n";
3735338bd3cSvporpo   }
3745338bd3cSvporpo }
3755338bd3cSvporpo void Tracker::dump() const {
3765338bd3cSvporpo   dump(dbgs());
3775338bd3cSvporpo   dbgs() << "\n";
3785338bd3cSvporpo }
3795338bd3cSvporpo #endif // NDEBUG
380