Skip to content

Commit 387c241

Browse files
committed
[SandboxIR][Tracker] Track eraseFromParent()
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 5338bd3 commit 387c241

File tree

5 files changed

+174
-5
lines changed

5 files changed

+174
-5
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: 44 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,46 @@ 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 the operands data.
106+
struct InstrData {
107+
/// The operand and the corresponding operand number.
108+
struct OpData {
109+
llvm::Value *Op;
110+
unsigned long OpNum;
111+
};
112+
/// The operands that got dropped.
113+
SmallVector<OpData> OpDataVec;
114+
/// The instruction that got "erased".
115+
llvm::Instruction *LLVMI;
116+
};
117+
/// The instruction data is in revere program order, which helps create the
118+
/// original program order during revert().
119+
SmallVector<InstrData> InstrData;
120+
/// This is either the next Instruction in the stream, or the parent
121+
/// BasicBlock if at the end of the BB.
122+
PointerUnion<llvm::Instruction *, llvm::BasicBlock *> NextLLVMIOrBB;
123+
/// We take ownership of the "erased" instruction.
124+
std::unique_ptr<sandboxir::Value> ErasedIPtr;
125+
126+
public:
127+
EraseFromParent(std::unique_ptr<sandboxir::Value> &&IPtr, Tracker &Tracker);
128+
void revert() final;
129+
void accept() final;
130+
#ifndef NDEBUG
131+
void dump(raw_ostream &OS) const final {
132+
dumpCommon(OS);
133+
OS << "EraseFromParent";
134+
}
135+
LLVM_DUMP_METHOD void dump() const final;
136+
friend raw_ostream &operator<<(raw_ostream &OS, const EraseFromParent &C) {
137+
C.dump(OS);
138+
return OS;
139+
}
140+
#endif
141+
};
142+
102143
/// The tracker collects all the change objects and implements the main API for
103144
/// saving / reverting / accepting.
104145
class Tracker {
@@ -116,6 +157,7 @@ class Tracker {
116157
#endif
117158
/// The current state of the tracker.
118159
TrackerState State = TrackerState::Disabled;
160+
Context &Ctx;
119161

120162
public:
121163
#ifndef NDEBUG
@@ -124,8 +166,9 @@ class Tracker {
124166
bool InMiddleOfCreatingChange = false;
125167
#endif // NDEBUG
126168

127-
Tracker() = default;
169+
explicit Tracker(Context &Ctx) : Ctx(Ctx) {}
128170
~Tracker();
171+
Context &getContext() const { return Ctx; }
129172
/// Record \p Change and take ownership. This is the main function used to
130173
/// track Sandbox IR changes.
131174
void track(std::unique_ptr<IRChangeBase> &&Change);

llvm/lib/SandboxIR/SandboxIR.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +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.
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+
return;
360+
}
361+
345362
// Erase in reverse to avoid erasing nstructions with attached uses.
346-
auto Instrs = getLLVMInstrs();
347-
for (llvm::Instruction *I : reverse(Instrs))
363+
for (llvm::Instruction *I : reverse(LLVMInstrs))
348364
I->eraseFromParent();
349365
}
350366

llvm/lib/SandboxIR/Tracker.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,62 @@ 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<InstrData::OpData> OpDataVec;
52+
for (auto [OpNum, Use] : enumerate(LLVMI->operands()))
53+
OpDataVec.push_back({Use.get(), OpNum});
54+
InstrData.push_back({OpDataVec, LLVMI});
55+
}
56+
#ifndef NDEBUG
57+
for (auto Idx : seq<unsigned>(1, InstrData.size()))
58+
assert(InstrData[Idx].LLVMI->comesBefore(InstrData[Idx - 1].LLVMI) &&
59+
"Expected reverse program order!");
60+
#endif
61+
auto *BotLLVMI = cast<llvm::Instruction>(I->Val);
62+
if (BotLLVMI->getNextNode() != nullptr)
63+
NextLLVMIOrBB = BotLLVMI->getNextNode();
64+
else
65+
NextLLVMIOrBB = BotLLVMI->getParent();
66+
}
67+
68+
void EraseFromParent::accept() {
69+
for (const auto &IData : InstrData)
70+
IData.LLVMI->deleteValue();
71+
}
72+
73+
void EraseFromParent::revert() {
74+
auto [OpData, BotLLVMI] = InstrData[0];
75+
if (auto *NextLLVMI = NextLLVMIOrBB.dyn_cast<llvm::Instruction *>()) {
76+
BotLLVMI->insertBefore(NextLLVMI);
77+
} else {
78+
auto *LLVMBB = NextLLVMIOrBB.get<llvm::BasicBlock *>();
79+
BotLLVMI->insertInto(LLVMBB, LLVMBB->end());
80+
}
81+
for (auto [Op, OpNum] : OpData)
82+
BotLLVMI->setOperand(OpNum, Op);
83+
84+
for (auto [OpData, LLVMI] : drop_begin(InstrData)) {
85+
LLVMI->insertBefore(BotLLVMI);
86+
for (auto [Op, OpNum] : OpData)
87+
LLVMI->setOperand(OpNum, Op);
88+
BotLLVMI = LLVMI;
89+
}
90+
Parent.getContext().registerValue(std::move(ErasedIPtr));
91+
}
92+
93+
#ifndef NDEBUG
94+
void EraseFromParent::dump() const {
95+
dump(dbgs());
96+
dbgs() << "\n";
97+
}
98+
#endif
99+
44100
void Tracker::track(std::unique_ptr<IRChangeBase> &&Change) {
45101
assert(State == TrackerState::Record && "The tracker should be tracking!");
46102
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+
TEST_F(TrackerTest, EraseFromParent) {
151+
parseIR(C, R"IR(
152+
define void @foo(i32 %v1) {
153+
%add0 = add i32 %v1, %v1
154+
%add1 = add i32 %add0, %v1
155+
ret void
156+
}
157+
)IR");
158+
Function &LLVMF = *M->getFunction("foo");
159+
sandboxir::Context Ctx(C);
160+
161+
auto *F = Ctx.createFunction(&LLVMF);
162+
auto *BB = &*F->begin();
163+
auto It = BB->begin();
164+
sandboxir::Instruction *Add0 = &*It++;
165+
sandboxir::Instruction *Add1 = &*It++;
166+
sandboxir::Instruction *Ret = &*It++;
167+
168+
Ctx.save();
169+
auto &Tracker = Ctx.getTracker();
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)