Skip to content

Commit c5432d3

Browse files
authored
[SandboxIR][Tracker] Track eraseFromParent() (#99431)
This patch adds tracking support for Instruction::eraseFromParent(). The Instruction is not actually being erased, but instead it is detached from the instruction list and drops its Use edges. The original instruction position and Use edges are saved in the `EraseFromParent` change object, and are being used during `revert()` to restore the original state.
1 parent 78e3bfc commit c5432d3

File tree

5 files changed

+173
-7
lines changed

5 files changed

+173
-7
lines changed

llvm/include/llvm/SandboxIR/SandboxIR.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ class Instruction : public sandboxir::User {
493493
/// \Returns the LLVM IR Instructions that this SandboxIR maps to in program
494494
/// order.
495495
virtual SmallVector<llvm::Instruction *, 1> getLLVMInstrs() const = 0;
496+
friend class EraseFromParent; // For getLLVMInstrs().
496497

497498
public:
498499
static const char *getOpcodeName(Opcode Opc);
@@ -658,6 +659,7 @@ class Context {
658659
friend void Instruction::eraseFromParent(); // For detach().
659660
/// Take ownership of VPtr and store it in `LLVMValueToValueMap`.
660661
Value *registerValue(std::unique_ptr<Value> &&VPtr);
662+
friend class EraseFromParent; // For registerValue().
661663
/// This is the actual function that creates sandboxir values for \p V,
662664
/// and among others handles all instruction types.
663665
Value *getOrCreateValueInternal(llvm::Value *V, llvm::User *U = nullptr);
@@ -682,7 +684,7 @@ class Context {
682684
friend class BasicBlock; // For getOrCreateValue().
683685

684686
public:
685-
Context(LLVMContext &LLVMCtx) : LLVMCtx(LLVMCtx) {}
687+
Context(LLVMContext &LLVMCtx) : LLVMCtx(LLVMCtx), IRTracker(*this) {}
686688

687689
Tracker &getTracker() { return IRTracker; }
688690
/// Convenience function for `getTracker().save()`

llvm/include/llvm/SandboxIR/Tracker.h

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#ifndef LLVM_SANDBOXIR_TRACKER_H
4141
#define LLVM_SANDBOXIR_TRACKER_H
4242

43+
#include "llvm/ADT/PointerUnion.h"
4344
#include "llvm/ADT/SmallVector.h"
4445
#include "llvm/IR/IRBuilder.h"
4546
#include "llvm/IR/Instruction.h"
@@ -99,6 +100,41 @@ class UseSet : public IRChangeBase {
99100
#endif
100101
};
101102

103+
class EraseFromParent : public IRChangeBase {
104+
/// Contains all the data we need to restore an "erased" (i.e., detached)
105+
/// instruction: the instruction itself and its operands in order.
106+
struct InstrAndOperands {
107+
/// The operands that got dropped.
108+
SmallVector<llvm::Value *> Operands;
109+
/// The instruction that got "erased".
110+
llvm::Instruction *LLVMI;
111+
};
112+
/// The instruction data is in reverse program order, which helps create the
113+
/// original program order during revert().
114+
SmallVector<InstrAndOperands> InstrData;
115+
/// This is either the next Instruction in the stream, or the parent
116+
/// BasicBlock if at the end of the BB.
117+
PointerUnion<llvm::Instruction *, llvm::BasicBlock *> NextLLVMIOrBB;
118+
/// We take ownership of the "erased" instruction.
119+
std::unique_ptr<sandboxir::Value> ErasedIPtr;
120+
121+
public:
122+
EraseFromParent(std::unique_ptr<sandboxir::Value> &&IPtr, Tracker &Tracker);
123+
void revert() final;
124+
void accept() final;
125+
#ifndef NDEBUG
126+
void dump(raw_ostream &OS) const final {
127+
dumpCommon(OS);
128+
OS << "EraseFromParent";
129+
}
130+
LLVM_DUMP_METHOD void dump() const final;
131+
friend raw_ostream &operator<<(raw_ostream &OS, const EraseFromParent &C) {
132+
C.dump(OS);
133+
return OS;
134+
}
135+
#endif
136+
};
137+
102138
/// The tracker collects all the change objects and implements the main API for
103139
/// saving / reverting / accepting.
104140
class Tracker {
@@ -116,6 +152,7 @@ class Tracker {
116152
#endif
117153
/// The current state of the tracker.
118154
TrackerState State = TrackerState::Disabled;
155+
Context &Ctx;
119156

120157
public:
121158
#ifndef NDEBUG
@@ -124,8 +161,9 @@ class Tracker {
124161
bool InMiddleOfCreatingChange = false;
125162
#endif // NDEBUG
126163

127-
Tracker() = default;
164+
explicit Tracker(Context &Ctx) : Ctx(Ctx) {}
128165
~Tracker();
166+
Context &getContext() const { return Ctx; }
129167
/// Record \p Change and take ownership. This is the main function used to
130168
/// track Sandbox IR changes.
131169
void track(std::unique_ptr<IRChangeBase> &&Change);

llvm/lib/SandboxIR/SandboxIR.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -341,11 +341,26 @@ void Instruction::removeFromParent() {
341341
void Instruction::eraseFromParent() {
342342
assert(users().empty() && "Still connected to users, can't erase!");
343343
std::unique_ptr<Value> Detached = Ctx.detach(this);
344-
// We don't have Tracking yet, so just erase the LLVM IR instructions.
345-
// Erase in reverse to avoid erasing nstructions with attached uses.
346-
auto Instrs = getLLVMInstrs();
347-
for (llvm::Instruction *I : reverse(Instrs))
348-
I->eraseFromParent();
344+
auto LLVMInstrs = getLLVMInstrs();
345+
346+
auto &Tracker = Ctx.getTracker();
347+
if (Tracker.isTracking()) {
348+
Tracker.track(
349+
std::make_unique<EraseFromParent>(std::move(Detached), Tracker));
350+
// We don't actually delete the IR instruction, because then it would be
351+
// impossible to bring it back from the dead at the same memory location.
352+
// Instead we remove it from its BB and track its current location.
353+
for (llvm::Instruction *I : LLVMInstrs)
354+
I->removeFromParent();
355+
// TODO: Multi-instructions need special treatment because some of the
356+
// references are internal to the instruction.
357+
for (llvm::Instruction *I : LLVMInstrs)
358+
I->dropAllReferences();
359+
} else {
360+
// Erase in reverse to avoid erasing nstructions with attached uses.
361+
for (llvm::Instruction *I : reverse(LLVMInstrs))
362+
I->eraseFromParent();
363+
}
349364
}
350365

351366
void Instruction::moveBefore(BasicBlock &BB, const BBIterator &WhereIt) {

llvm/lib/SandboxIR/Tracker.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,65 @@ Tracker::~Tracker() {
4141
assert(Changes.empty() && "You must accept or revert changes!");
4242
}
4343

44+
EraseFromParent::EraseFromParent(std::unique_ptr<sandboxir::Value> &&ErasedIPtr,
45+
Tracker &Tracker)
46+
: IRChangeBase(Tracker), ErasedIPtr(std::move(ErasedIPtr)) {
47+
auto *I = cast<Instruction>(this->ErasedIPtr.get());
48+
auto LLVMInstrs = I->getLLVMInstrs();
49+
// Iterate in reverse program order.
50+
for (auto *LLVMI : reverse(LLVMInstrs)) {
51+
SmallVector<llvm::Value *> Operands;
52+
Operands.reserve(LLVMI->getNumOperands());
53+
for (auto [OpNum, Use] : enumerate(LLVMI->operands()))
54+
Operands.push_back(Use.get());
55+
InstrData.push_back({Operands, LLVMI});
56+
}
57+
assert(is_sorted(InstrData,
58+
[](const auto &D0, const auto &D1) {
59+
return D0.LLVMI->comesBefore(D1.LLVMI);
60+
}) &&
61+
"Expected reverse program order!");
62+
auto *BotLLVMI = cast<llvm::Instruction>(I->Val);
63+
if (BotLLVMI->getNextNode() != nullptr)
64+
NextLLVMIOrBB = BotLLVMI->getNextNode();
65+
else
66+
NextLLVMIOrBB = BotLLVMI->getParent();
67+
}
68+
69+
void EraseFromParent::accept() {
70+
for (const auto &IData : InstrData)
71+
IData.LLVMI->deleteValue();
72+
}
73+
74+
void EraseFromParent::revert() {
75+
// Place the bottom-most instruction first.
76+
auto [Operands, BotLLVMI] = InstrData[0];
77+
if (auto *NextLLVMI = NextLLVMIOrBB.dyn_cast<llvm::Instruction *>()) {
78+
BotLLVMI->insertBefore(NextLLVMI);
79+
} else {
80+
auto *LLVMBB = NextLLVMIOrBB.get<llvm::BasicBlock *>();
81+
BotLLVMI->insertInto(LLVMBB, LLVMBB->end());
82+
}
83+
for (auto [OpNum, Op] : enumerate(Operands))
84+
BotLLVMI->setOperand(OpNum, Op);
85+
86+
// Go over the rest of the instructions and stack them on top.
87+
for (auto [Operands, LLVMI] : drop_begin(InstrData)) {
88+
LLVMI->insertBefore(BotLLVMI);
89+
for (auto [OpNum, Op] : enumerate(Operands))
90+
LLVMI->setOperand(OpNum, Op);
91+
BotLLVMI = LLVMI;
92+
}
93+
Parent.getContext().registerValue(std::move(ErasedIPtr));
94+
}
95+
96+
#ifndef NDEBUG
97+
void EraseFromParent::dump() const {
98+
dump(dbgs());
99+
dbgs() << "\n";
100+
}
101+
#endif
102+
44103
void Tracker::track(std::unique_ptr<IRChangeBase> &&Change) {
45104
assert(State == TrackerState::Record && "The tracker should be tracking!");
46105
Changes.push_back(std::move(Change));

llvm/unittests/SandboxIR/TrackerTest.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,55 @@ define void @foo(ptr %ptr) {
146146
Ctx.accept();
147147
EXPECT_EQ(St0->getOperand(0), Ld1);
148148
}
149+
150+
// TODO: Test multi-instruction patterns.
151+
TEST_F(TrackerTest, EraseFromParent) {
152+
parseIR(C, R"IR(
153+
define void @foo(i32 %v1) {
154+
%add0 = add i32 %v1, %v1
155+
%add1 = add i32 %add0, %v1
156+
ret void
157+
}
158+
)IR");
159+
Function &LLVMF = *M->getFunction("foo");
160+
sandboxir::Context Ctx(C);
161+
162+
auto *F = Ctx.createFunction(&LLVMF);
163+
auto *BB = &*F->begin();
164+
auto It = BB->begin();
165+
sandboxir::Instruction *Add0 = &*It++;
166+
sandboxir::Instruction *Add1 = &*It++;
167+
sandboxir::Instruction *Ret = &*It++;
168+
169+
Ctx.save();
170+
// Check erase.
171+
Add1->eraseFromParent();
172+
It = BB->begin();
173+
EXPECT_EQ(&*It++, Add0);
174+
EXPECT_EQ(&*It++, Ret);
175+
EXPECT_EQ(It, BB->end());
176+
EXPECT_EQ(Add0->getNumUses(), 0u);
177+
178+
// Check revert().
179+
Ctx.revert();
180+
It = BB->begin();
181+
EXPECT_EQ(&*It++, Add0);
182+
EXPECT_EQ(&*It++, Add1);
183+
EXPECT_EQ(&*It++, Ret);
184+
EXPECT_EQ(It, BB->end());
185+
EXPECT_EQ(Add1->getOperand(0), Add0);
186+
187+
// Same for the last instruction in the block.
188+
Ctx.save();
189+
Ret->eraseFromParent();
190+
It = BB->begin();
191+
EXPECT_EQ(&*It++, Add0);
192+
EXPECT_EQ(&*It++, Add1);
193+
EXPECT_EQ(It, BB->end());
194+
Ctx.revert();
195+
It = BB->begin();
196+
EXPECT_EQ(&*It++, Add0);
197+
EXPECT_EQ(&*It++, Add1);
198+
EXPECT_EQ(&*It++, Ret);
199+
EXPECT_EQ(It, BB->end());
200+
}

0 commit comments

Comments
 (0)