LLVM 20.0.0git
Tracker.cpp
Go to the documentation of this file.
1//===- Tracker.cpp --------------------------------------------------------===//
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
10#include "llvm/ADT/STLExtras.h"
11#include "llvm/IR/BasicBlock.h"
12#include "llvm/IR/Instruction.h"
13#include "llvm/IR/Module.h"
16#include <sstream>
17
18using namespace llvm::sandboxir;
19
20#ifndef NDEBUG
21
22std::string IRSnapshotChecker::dumpIR(const llvm::Function &F) const {
23 std::string Result;
24 raw_string_ostream SS(Result);
25 F.print(SS, /*AssemblyAnnotationWriter=*/nullptr);
26 return Result;
27}
28
29IRSnapshotChecker::ContextSnapshot IRSnapshotChecker::takeSnapshot() const {
30 ContextSnapshot Result;
31 for (const auto &Entry : Ctx.LLVMModuleToModuleMap)
32 for (const auto &F : *Entry.first) {
33 FunctionSnapshot Snapshot;
34 Snapshot.Hash = StructuralHash(F, /*DetailedHash=*/true);
35 Snapshot.TextualIR = dumpIR(F);
36 Result[&F] = Snapshot;
37 }
38 return Result;
39}
40
41bool IRSnapshotChecker::diff(const ContextSnapshot &Orig,
42 const ContextSnapshot &Curr) const {
43 bool DifferenceFound = false;
44 for (const auto &[F, OrigFS] : Orig) {
45 auto CurrFSIt = Curr.find(F);
46 if (CurrFSIt == Curr.end()) {
47 DifferenceFound = true;
48 dbgs() << "Function " << F->getName() << " not found in current IR.\n";
49 dbgs() << OrigFS.TextualIR << "\n";
50 continue;
51 }
52 const FunctionSnapshot &CurrFS = CurrFSIt->second;
53 if (OrigFS.Hash != CurrFS.Hash) {
54 DifferenceFound = true;
55 dbgs() << "Found IR difference in Function " << F->getName() << "\n";
56 dbgs() << "Original:\n" << OrigFS.TextualIR << "\n";
57 dbgs() << "Current:\n" << CurrFS.TextualIR << "\n";
58 }
59 }
60 // Check that Curr doesn't contain any new functions.
61 for (const auto &[F, CurrFS] : Curr) {
62 if (!Orig.contains(F)) {
63 DifferenceFound = true;
64 dbgs() << "Function " << F->getName()
65 << " found in current IR but not in original snapshot.\n";
66 dbgs() << CurrFS.TextualIR << "\n";
67 }
68 }
69 return DifferenceFound;
70}
71
72void IRSnapshotChecker::save() { OrigContextSnapshot = takeSnapshot(); }
73
75 ContextSnapshot CurrContextSnapshot = takeSnapshot();
76 if (diff(OrigContextSnapshot, CurrContextSnapshot)) {
78 "Original and current IR differ! Probably a checkpointing bug.");
79 }
80}
81
82void UseSet::dump() const {
83 dump(dbgs());
84 dbgs() << "\n";
85}
86
87void UseSwap::dump() const {
88 dump(dbgs());
89 dbgs() << "\n";
90}
91#endif // NDEBUG
92
94 : PHI(PHI), RemovedIdx(RemovedIdx) {
95 RemovedV = PHI->getIncomingValue(RemovedIdx);
96 RemovedBB = PHI->getIncomingBlock(RemovedIdx);
97}
98
100 // Special case: if the PHI is now empty, as we don't need to care about the
101 // order of the incoming values.
102 unsigned NumIncoming = PHI->getNumIncomingValues();
103 if (NumIncoming == 0) {
104 PHI->addIncoming(RemovedV, RemovedBB);
105 return;
106 }
107 // Shift all incoming values by one starting from the end until `Idx`.
108 // Start by adding a copy of the last incoming values.
109 unsigned LastIdx = NumIncoming - 1;
110 PHI->addIncoming(PHI->getIncomingValue(LastIdx),
111 PHI->getIncomingBlock(LastIdx));
112 for (unsigned Idx = LastIdx; Idx > RemovedIdx; --Idx) {
113 auto *PrevV = PHI->getIncomingValue(Idx - 1);
114 auto *PrevBB = PHI->getIncomingBlock(Idx - 1);
115 PHI->setIncomingValue(Idx, PrevV);
116 PHI->setIncomingBlock(Idx, PrevBB);
117 }
118 PHI->setIncomingValue(RemovedIdx, RemovedV);
119 PHI->setIncomingBlock(RemovedIdx, RemovedBB);
120}
121
122#ifndef NDEBUG
124 dump(dbgs());
125 dbgs() << "\n";
126}
127#endif // NDEBUG
128
130 : PHI(PHI), Idx(PHI->getNumIncomingValues()) {}
131
133
134#ifndef NDEBUG
136 dump(dbgs());
137 dbgs() << "\n";
138}
139#endif // NDEBUG
140
142 assert(Changes.empty() && "You must accept or revert changes!");
143}
144
145EraseFromParent::EraseFromParent(std::unique_ptr<sandboxir::Value> &&ErasedIPtr)
146 : ErasedIPtr(std::move(ErasedIPtr)) {
147 auto *I = cast<Instruction>(this->ErasedIPtr.get());
148 auto LLVMInstrs = I->getLLVMInstrs();
149 // Iterate in reverse program order.
150 for (auto *LLVMI : reverse(LLVMInstrs)) {
152 Operands.reserve(LLVMI->getNumOperands());
153 for (auto [OpNum, Use] : enumerate(LLVMI->operands()))
154 Operands.push_back(Use.get());
155 InstrData.push_back({Operands, LLVMI});
156 }
157 assert(is_sorted(InstrData,
158 [](const auto &D0, const auto &D1) {
159 return D0.LLVMI->comesBefore(D1.LLVMI);
160 }) &&
161 "Expected reverse program order!");
162 auto *BotLLVMI = cast<llvm::Instruction>(I->Val);
163 if (BotLLVMI->getNextNode() != nullptr)
164 NextLLVMIOrBB = BotLLVMI->getNextNode();
165 else
166 NextLLVMIOrBB = BotLLVMI->getParent();
167}
168
170 for (const auto &IData : InstrData)
171 IData.LLVMI->deleteValue();
172}
173
175 // Place the bottom-most instruction first.
176 auto [Operands, BotLLVMI] = InstrData[0];
177 if (auto *NextLLVMI = dyn_cast<llvm::Instruction *>(NextLLVMIOrBB)) {
178 BotLLVMI->insertBefore(NextLLVMI);
179 } else {
180 auto *LLVMBB = cast<llvm::BasicBlock *>(NextLLVMIOrBB);
181 BotLLVMI->insertInto(LLVMBB, LLVMBB->end());
182 }
183 for (auto [OpNum, Op] : enumerate(Operands))
184 BotLLVMI->setOperand(OpNum, Op);
185
186 // Go over the rest of the instructions and stack them on top.
187 for (auto [Operands, LLVMI] : drop_begin(InstrData)) {
188 LLVMI->insertBefore(BotLLVMI);
189 for (auto [OpNum, Op] : enumerate(Operands))
190 LLVMI->setOperand(OpNum, Op);
191 BotLLVMI = LLVMI;
192 }
193 Tracker.getContext().registerValue(std::move(ErasedIPtr));
194}
195
196#ifndef NDEBUG
198 dump(dbgs());
199 dbgs() << "\n";
200}
201#endif // NDEBUG
202
203RemoveFromParent::RemoveFromParent(Instruction *RemovedI) : RemovedI(RemovedI) {
204 if (auto *NextI = RemovedI->getNextNode())
205 NextInstrOrBB = NextI;
206 else
207 NextInstrOrBB = RemovedI->getParent();
208}
209
211 if (auto *NextI = dyn_cast<Instruction *>(NextInstrOrBB)) {
212 RemovedI->insertBefore(NextI);
213 } else {
214 auto *BB = cast<BasicBlock *>(NextInstrOrBB);
215 RemovedI->insertInto(BB, BB->end());
216 }
217}
218
219#ifndef NDEBUG
221 dump(dbgs());
222 dbgs() << "\n";
223}
224#endif
225
227 : CSI(CSI), HandlerIdx(CSI->getNumHandlers()) {}
228
230 // TODO: This should ideally use sandboxir::CatchSwitchInst::removeHandler()
231 // once it gets implemented.
232 auto *LLVMCSI = cast<llvm::CatchSwitchInst>(CSI->Val);
233 LLVMCSI->removeHandler(LLVMCSI->handler_begin() + HandlerIdx);
234}
235
237 for (const auto &C : Switch->cases())
238 Cases.push_back({C.getCaseValue(), C.getCaseSuccessor()});
239}
240
242 // SwitchInst::removeCase doesn't provide any guarantees about the order of
243 // cases after removal. In order to preserve the original ordering, we save
244 // all of them and, when reverting, clear them all then insert them in the
245 // desired order. This still relies on the fact that `addCase` will insert
246 // them at the end, but it is documented to invalidate `case_end()` so it's
247 // probably okay.
248 unsigned NumCases = Switch->getNumCases();
249 for (unsigned I = 0; I < NumCases; ++I)
250 Switch->removeCase(Switch->case_begin());
251 for (auto &Case : Cases)
252 Switch->addCase(Case.Val, Case.Dest);
253}
254
255#ifndef NDEBUG
257 dump(dbgs());
258 dbgs() << "\n";
259}
260#endif // NDEBUG
261
263 auto It = Switch->findCaseValue(Val);
264 Switch->removeCase(It);
265}
266
267#ifndef NDEBUG
269 dump(dbgs());
270 dbgs() << "\n";
271}
272#endif // NDEBUG
273
274MoveInstr::MoveInstr(Instruction *MovedI) : MovedI(MovedI) {
275 if (auto *NextI = MovedI->getNextNode())
276 NextInstrOrBB = NextI;
277 else
278 NextInstrOrBB = MovedI->getParent();
279}
280
282 if (auto *NextI = dyn_cast<Instruction *>(NextInstrOrBB)) {
283 MovedI->moveBefore(NextI);
284 } else {
285 auto *BB = cast<BasicBlock *>(NextInstrOrBB);
286 MovedI->moveBefore(*BB, BB->end());
287 }
288}
289
290#ifndef NDEBUG
291void MoveInstr::dump() const {
292 dump(dbgs());
293 dbgs() << "\n";
294}
295#endif
296
298
299InsertIntoBB::InsertIntoBB(Instruction *InsertedI) : InsertedI(InsertedI) {}
300
301#ifndef NDEBUG
302void InsertIntoBB::dump() const {
303 dump(dbgs());
304 dbgs() << "\n";
305}
306#endif
307
309
310#ifndef NDEBUG
312 dump(dbgs());
313 dbgs() << "\n";
314}
315#endif
316
318 : SVI(SVI), PrevMask(SVI->getShuffleMask()) {}
319
321 SVI->setShuffleMask(PrevMask);
322}
323
324#ifndef NDEBUG
326 dump(dbgs());
327 dbgs() << "\n";
328}
329#endif
330
332
334#ifndef NDEBUG
336 dump(dbgs());
337 dbgs() << "\n";
338}
339#endif
340
342 State = TrackerState::Record;
343#if !defined(NDEBUG) && defined(EXPENSIVE_CHECKS)
344 SnapshotChecker.save();
345#endif
346}
347
349 assert(State == TrackerState::Record && "Forgot to save()!");
351 for (auto &Change : reverse(Changes))
352 Change->revert(*this);
353 Changes.clear();
354#if !defined(NDEBUG) && defined(EXPENSIVE_CHECKS)
355 SnapshotChecker.expectNoDiff();
356#endif
357}
358
360 assert(State == TrackerState::Record && "Forgot to save()!");
362 for (auto &Change : Changes)
363 Change->accept();
364 Changes.clear();
365}
366
367#ifndef NDEBUG
369 for (auto [Idx, ChangePtr] : enumerate(Changes)) {
370 OS << Idx << ". ";
371 ChangePtr->dump(OS);
372 OS << "\n";
373 }
374}
375void Tracker::dump() const {
376 dump(dbgs());
377 dbgs() << "\n";
378}
379#endif // NDEBUG
Rewrite undef for PHI
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
mir Rename Register Operands
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file contains some templates that are useful if you are working with the STL at all.
raw_pwrite_stream & OS
This class represents an Operation in the Expression.
void push_back(const T &Elt)
Definition: SmallVector.h:413
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
A raw_ostream that writes to an std::string.
Definition: raw_ostream.h:661
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:229
CatchSwitchAddHandler(CatchSwitchInst *CSI)
Definition: Tracker.cpp:226
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:333
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:335
DenseMap< llvm::Module *, std::unique_ptr< Module > > LLVMModuleToModuleMap
Maps an LLVM Module to the corresponding sandboxir::Module.
Definition: Context.h:61
Value * registerValue(std::unique_ptr< Value > &&VPtr)
Take ownership of VPtr and store it in LLVMValueToValueMap.
Definition: Context.cpp:34
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:311
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:308
void accept() final
This runs when changes get accepted.
Definition: Tracker.cpp:169
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:197
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:174
EraseFromParent(std::unique_ptr< sandboxir::Value > &&IPtr)
Definition: Tracker.cpp:145
void expectNoDiff()
Checks current state against saved state, crashes if different.
Definition: Tracker.cpp:74
void save()
Saves a snapshot of the current state.
Definition: Tracker.cpp:72
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:302
InsertIntoBB(Instruction *InsertedI)
Definition: Tracker.cpp:299
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:297
A sandboxir::User with operands, opcode and linked with previous/next instructions in an instruction ...
Definition: Instruction.h:42
void moveBefore(BasicBlock &BB, const BBIterator &WhereIt)
Move this instruction to WhereIt.
void insertInto(BasicBlock *BB, const BBIterator &WhereIt)
Insert this detached instruction into BB at WhereIt.
Instruction * getNextNode() const
\Returns the next sandboxir::Instruction in the block, or nullptr if at the end of the block.
Definition: Instruction.cpp:43
void removeFromParent()
Detach this from its parent BasicBlock without deleting it.
Definition: Instruction.cpp:66
void insertBefore(Instruction *BeforeI)
Insert this detached instruction before BeforeI.
void eraseFromParent()
Detach this Value from its parent and delete it.
Definition: Instruction.cpp:74
BasicBlock * getParent() const
\Returns the BasicBlock containing this Instruction, or null if it is detached.
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:291
MoveInstr(sandboxir::Instruction *I)
Definition: Tracker.cpp:274
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:281
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:132
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:135
unsigned getNumIncomingValues() const
Definition: Instruction.h:2417
Value * getIncomingValue(unsigned Idx) const
void setIncomingBlock(unsigned Idx, BasicBlock *BB)
Value * removeIncomingValue(unsigned Idx)
void setIncomingValue(unsigned Idx, Value *V)
BasicBlock * getIncomingBlock(unsigned Idx) const
void addIncoming(Value *V, BasicBlock *BB)
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:123
PHIRemoveIncoming(PHINode *PHI, unsigned RemovedIdx)
Definition: Tracker.cpp:93
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:99
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:210
RemoveFromParent(Instruction *RemovedI)
Definition: Tracker.cpp:203
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:220
void setShuffleMask(ArrayRef< int > Mask)
ShuffleVectorSetMask(ShuffleVectorInst *SVI)
Definition: Tracker.cpp:317
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:325
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:320
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:262
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:268
CaseIt findCaseValue(const ConstantInt *C)
Definition: Instruction.h:1903
void addCase(ConstantInt *OnVal, BasicBlock *Dest)
CaseIt case_begin()
Returns a read/write iterator that points to the first case in the SwitchInst.
Definition: Instruction.h:1886
unsigned getNumCases() const
Definition: Instruction.h:1872
CaseIt removeCase(CaseIt It)
This method removes the specified case and its successor from the switch instruction.
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:256
SwitchRemoveCase(SwitchInst *Switch)
Definition: Tracker.cpp:236
void revert(Tracker &Tracker) final
This runs when changes get reverted.
Definition: Tracker.cpp:241
The tracker collects all the change objects and implements the main API for saving / reverting / acce...
Definition: Tracker.h:440
@ Record
ā€¨Tracking is disabled
void revert()
Stops tracking and reverts to saved state.
Definition: Tracker.cpp:348
void save()
Turns on IR tracking.
Definition: Tracker.cpp:341
Context & getContext() const
Definition: Tracker.h:475
void accept()
Stops tracking and accept changes.
Definition: Tracker.cpp:359
LLVM_DUMP_METHOD void dump() const
Definition: Tracker.cpp:375
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:82
LLVM_DUMP_METHOD void dump() const final
Definition: Tracker.cpp:87
Represents a Def-use/Use-def edge in SandboxIR.
Definition: Use.h:32
Value * get() const
Definition: Use.cpp:15
llvm::Value * Val
The LLVM Value that corresponds to this SandboxIR Value.
Definition: Value.h:103
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ Entry
Definition: COFF.h:844
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
@ SS
Definition: X86.h:212
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition: STLExtras.h:329
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition: STLExtras.h:2448
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:420
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
bool is_sorted(R &&Range, Compare C)
Wrapper function around std::is_sorted to check if elements in a range R are sorted with respect to a...
Definition: STLExtras.h:1926
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1873
stable_hash StructuralHash(const Function &F, bool DetailedHash=false)
Returns a hash of the function F.
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858