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