Skip to content

Commit 9d85ba5

Browse files
authored
[SandboxIR] Preserve the order of switch cases after revert. (#115577)
Preserving the case order is not strictly necessary to preserve semantics (for example, operations like SwitchInst::removeCase will happily swap cases around). However, I'm planning to introduce an optional verification step for SandboxIR that will use StructuralHash to compare IR after a revert to the original IR to help catch tracker bugs, and the order difference triggers a difference there.
1 parent a204252 commit 9d85ba5

File tree

4 files changed

+108
-8
lines changed

4 files changed

+108
-8
lines changed

llvm/include/llvm/SandboxIR/Tracker.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,15 @@ class SwitchAddCase : public IRChangeBase {
315315

316316
class SwitchRemoveCase : public IRChangeBase {
317317
SwitchInst *Switch;
318-
ConstantInt *Val;
319-
BasicBlock *Dest;
318+
struct Case {
319+
ConstantInt *Val;
320+
BasicBlock *Dest;
321+
};
322+
SmallVector<Case> Cases;
320323

321324
public:
322-
SwitchRemoveCase(SwitchInst *Switch, ConstantInt *Val, BasicBlock *Dest)
323-
: Switch(Switch), Val(Val), Dest(Dest) {}
325+
SwitchRemoveCase(SwitchInst *Switch);
326+
324327
void revert(Tracker &Tracker) final;
325328
void accept() final {}
326329
#ifndef NDEBUG

llvm/lib/SandboxIR/Instruction.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,9 +1131,7 @@ void SwitchInst::addCase(ConstantInt *OnVal, BasicBlock *Dest) {
11311131
}
11321132

11331133
SwitchInst::CaseIt SwitchInst::removeCase(CaseIt It) {
1134-
auto &Case = *It;
1135-
Ctx.getTracker().emplaceIfTracking<SwitchRemoveCase>(
1136-
this, Case.getCaseValue(), Case.getCaseSuccessor());
1134+
Ctx.getTracker().emplaceIfTracking<SwitchRemoveCase>(this);
11371135

11381136
auto *LLVMSwitch = cast<llvm::SwitchInst>(Val);
11391137
unsigned CaseNum = It - case_begin();

llvm/lib/SandboxIR/Tracker.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,24 @@ void CatchSwitchAddHandler::revert(Tracker &Tracker) {
170170
LLVMCSI->removeHandler(LLVMCSI->handler_begin() + HandlerIdx);
171171
}
172172

173-
void SwitchRemoveCase::revert(Tracker &Tracker) { Switch->addCase(Val, Dest); }
173+
SwitchRemoveCase::SwitchRemoveCase(SwitchInst *Switch) : Switch(Switch) {
174+
for (const auto &C : Switch->cases())
175+
Cases.push_back({C.getCaseValue(), C.getCaseSuccessor()});
176+
}
177+
178+
void SwitchRemoveCase::revert(Tracker &Tracker) {
179+
// SwitchInst::removeCase doesn't provide any guarantees about the order of
180+
// cases after removal. In order to preserve the original ordering, we save
181+
// all of them and, when reverting, clear them all then insert them in the
182+
// desired order. This still relies on the fact that `addCase` will insert
183+
// them at the end, but it is documented to invalidate `case_end()` so it's
184+
// probably okay.
185+
unsigned NumCases = Switch->getNumCases();
186+
for (unsigned I = 0; I < NumCases; ++I)
187+
Switch->removeCase(Switch->case_begin());
188+
for (auto &Case : Cases)
189+
Switch->addCase(Case.Val, Case.Dest);
190+
}
174191

175192
#ifndef NDEBUG
176193
void SwitchRemoveCase::dump() const {

llvm/unittests/SandboxIR/TrackerTest.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,88 @@ define void @foo(i32 %cond0, i32 %cond1) {
965965
EXPECT_EQ(Switch->findCaseDest(BB1), One);
966966
}
967967

968+
TEST_F(TrackerTest, SwitchInstPreservesSuccesorOrder) {
969+
parseIR(C, R"IR(
970+
define void @foo(i32 %cond0) {
971+
entry:
972+
switch i32 %cond0, label %default [ i32 0, label %bb0
973+
i32 1, label %bb1
974+
i32 2, label %bb2 ]
975+
bb0:
976+
ret void
977+
bb1:
978+
ret void
979+
bb2:
980+
ret void
981+
default:
982+
ret void
983+
}
984+
)IR");
985+
Function &LLVMF = *M->getFunction("foo");
986+
auto *LLVMEntry = getBasicBlockByName(LLVMF, "entry");
987+
988+
sandboxir::Context Ctx(C);
989+
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
990+
auto *Entry = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMEntry));
991+
auto *BB0 = cast<sandboxir::BasicBlock>(
992+
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
993+
auto *BB1 = cast<sandboxir::BasicBlock>(
994+
Ctx.getValue(getBasicBlockByName(LLVMF, "bb1")));
995+
auto *BB2 = cast<sandboxir::BasicBlock>(
996+
Ctx.getValue(getBasicBlockByName(LLVMF, "bb2")));
997+
auto *Switch = cast<sandboxir::SwitchInst>(&*Entry->begin());
998+
999+
auto *DefaultDest = Switch->getDefaultDest();
1000+
auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0);
1001+
auto *One = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 1);
1002+
auto *Two = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 2);
1003+
1004+
// Check that we can properly revert a removeCase multiple positions apart
1005+
// from the end of the operand list.
1006+
Ctx.save();
1007+
Switch->removeCase(Switch->findCaseValue(Zero));
1008+
EXPECT_EQ(Switch->getNumCases(), 2u);
1009+
Ctx.revert();
1010+
EXPECT_EQ(Switch->getNumCases(), 3u);
1011+
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
1012+
EXPECT_EQ(Switch->findCaseDest(BB1), One);
1013+
EXPECT_EQ(Switch->findCaseDest(BB2), Two);
1014+
EXPECT_EQ(Switch->getSuccessor(0), DefaultDest);
1015+
EXPECT_EQ(Switch->getSuccessor(1), BB0);
1016+
EXPECT_EQ(Switch->getSuccessor(2), BB1);
1017+
EXPECT_EQ(Switch->getSuccessor(3), BB2);
1018+
1019+
// Check that we can properly revert a removeCase of the last case.
1020+
Ctx.save();
1021+
Switch->removeCase(Switch->findCaseValue(Two));
1022+
EXPECT_EQ(Switch->getNumCases(), 2u);
1023+
Ctx.revert();
1024+
EXPECT_EQ(Switch->getNumCases(), 3u);
1025+
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
1026+
EXPECT_EQ(Switch->findCaseDest(BB1), One);
1027+
EXPECT_EQ(Switch->findCaseDest(BB2), Two);
1028+
EXPECT_EQ(Switch->getSuccessor(0), DefaultDest);
1029+
EXPECT_EQ(Switch->getSuccessor(1), BB0);
1030+
EXPECT_EQ(Switch->getSuccessor(2), BB1);
1031+
EXPECT_EQ(Switch->getSuccessor(3), BB2);
1032+
1033+
// Check order is preserved after reverting multiple removeCase invocations.
1034+
Ctx.save();
1035+
Switch->removeCase(Switch->findCaseValue(One));
1036+
Switch->removeCase(Switch->findCaseValue(Zero));
1037+
Switch->removeCase(Switch->findCaseValue(Two));
1038+
EXPECT_EQ(Switch->getNumCases(), 0u);
1039+
Ctx.revert();
1040+
EXPECT_EQ(Switch->getNumCases(), 3u);
1041+
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
1042+
EXPECT_EQ(Switch->findCaseDest(BB1), One);
1043+
EXPECT_EQ(Switch->findCaseDest(BB2), Two);
1044+
EXPECT_EQ(Switch->getSuccessor(0), DefaultDest);
1045+
EXPECT_EQ(Switch->getSuccessor(1), BB0);
1046+
EXPECT_EQ(Switch->getSuccessor(2), BB1);
1047+
EXPECT_EQ(Switch->getSuccessor(3), BB2);
1048+
}
1049+
9681050
TEST_F(TrackerTest, SelectInst) {
9691051
parseIR(C, R"IR(
9701052
define void @foo(i1 %c0, i8 %v0, i8 %v1) {

0 commit comments

Comments
 (0)