blob: d016c7793a52c081a313a243d37fb44fd7f5fb78 [file] [log] [blame] [edit]
//===- TrackerTest.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Module.h"
#include "llvm/SandboxIR/SandboxIR.h"
#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
using namespace llvm;
struct TrackerTest : public testing::Test {
LLVMContext C;
std::unique_ptr<Module> M;
void parseIR(LLVMContext &C, const char *IR) {
SMDiagnostic Err;
M = parseAssemblyString(IR, Err, C);
if (!M)
Err.print("TrackerTest", errs());
}
BasicBlock *getBasicBlockByName(Function &F, StringRef Name) {
for (BasicBlock &BB : F)
if (BB.getName() == Name)
return &BB;
llvm_unreachable("Expected to find basic block!");
}
};
TEST_F(TrackerTest, SetOperand) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
%gep0 = getelementptr float, ptr %ptr, i32 0
%gep1 = getelementptr float, ptr %ptr, i32 1
%ld0 = load float, ptr %gep0
store float undef, ptr %gep0
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
auto *BB = &*F->begin();
auto &Tracker = Ctx.getTracker();
Tracker.save();
auto It = BB->begin();
auto *Gep0 = &*It++;
auto *Gep1 = &*It++;
auto *Ld = &*It++;
auto *St = &*It++;
St->setOperand(0, Ld);
St->setOperand(1, Gep1);
Ld->setOperand(0, Gep1);
EXPECT_EQ(St->getOperand(0), Ld);
EXPECT_EQ(St->getOperand(1), Gep1);
EXPECT_EQ(Ld->getOperand(0), Gep1);
Ctx.getTracker().revert();
EXPECT_NE(St->getOperand(0), Ld);
EXPECT_EQ(St->getOperand(1), Gep0);
EXPECT_EQ(Ld->getOperand(0), Gep0);
}
TEST_F(TrackerTest, SetUse) {
parseIR(C, R"IR(
define void @foo(ptr %ptr, i8 %arg) {
%ld = load i8, ptr %ptr
%add = add i8 %ld, %arg
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
unsigned ArgIdx = 0;
auto *Arg0 = F->getArg(ArgIdx++);
auto *BB = &*F->begin();
auto &Tracker = Ctx.getTracker();
Tracker.save();
auto It = BB->begin();
auto *Ld = &*It++;
auto *Add = &*It++;
Ctx.save();
sandboxir::Use Use = Add->getOperandUse(0);
Use.set(Arg0);
EXPECT_EQ(Add->getOperand(0), Arg0);
Ctx.revert();
EXPECT_EQ(Add->getOperand(0), Ld);
}
TEST_F(TrackerTest, SwapOperands) {
parseIR(C, R"IR(
define void @foo(i1 %cond) {
bb0:
br i1 %cond, label %bb1, label %bb2
bb1:
ret void
bb2:
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
Ctx.createFunction(&LLVMF);
auto *BB0 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
auto *BB1 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb1")));
auto *BB2 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb2")));
auto &Tracker = Ctx.getTracker();
Tracker.save();
auto It = BB0->begin();
auto *Br = cast<sandboxir::BranchInst>(&*It++);
unsigned SuccIdx = 0;
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB2, BB1});
for (auto *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
// This calls User::swapOperandsInternal() internally.
Br->swapSuccessors();
SuccIdx = 0;
for (auto *Succ : reverse(Br->successors()))
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
Ctx.getTracker().revert();
SuccIdx = 0;
for (auto *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
}
TEST_F(TrackerTest, RUWIf_RAUW_RUOW) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
%ld0 = load float, ptr %ptr
%ld1 = load float, ptr %ptr
store float %ld0, ptr %ptr
store float %ld0, ptr %ptr
ret void
}
)IR");
llvm::Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
llvm::BasicBlock *LLVMBB = &*LLVMF.begin();
Ctx.createFunction(&LLVMF);
auto *BB = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB));
auto It = BB->begin();
sandboxir::Instruction *Ld0 = &*It++;
sandboxir::Instruction *Ld1 = &*It++;
sandboxir::Instruction *St0 = &*It++;
sandboxir::Instruction *St1 = &*It++;
Ctx.save();
// Check RUWIf when the lambda returns false.
Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return false; });
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUWIf when the lambda returns true.
Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return true; });
EXPECT_EQ(St0->getOperand(0), Ld1);
EXPECT_EQ(St1->getOperand(0), Ld1);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUWIf user == St0.
Ctx.save();
Ld0->replaceUsesWithIf(
Ld1, [St0](const sandboxir::Use &Use) { return Use.getUser() == St0; });
EXPECT_EQ(St0->getOperand(0), Ld1);
EXPECT_EQ(St1->getOperand(0), Ld0);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUWIf user == St1.
Ctx.save();
Ld0->replaceUsesWithIf(
Ld1, [St1](const sandboxir::Use &Use) { return Use.getUser() == St1; });
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld1);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RAUW.
Ctx.save();
Ld1->replaceAllUsesWith(Ld0);
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUOW.
Ctx.save();
St0->replaceUsesOfWith(Ld0, Ld1);
EXPECT_EQ(St0->getOperand(0), Ld1);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
// Check accept().
Ctx.save();
St0->replaceUsesOfWith(Ld0, Ld1);
EXPECT_EQ(St0->getOperand(0), Ld1);
Ctx.accept();
EXPECT_EQ(St0->getOperand(0), Ld1);
}
// TODO: Test multi-instruction patterns.
TEST_F(TrackerTest, EraseFromParent) {
parseIR(C, R"IR(
define void @foo(i32 %v1) {
%add0 = add i32 %v1, %v1
%add1 = add i32 %add0, %v1
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
auto *BB = &*F->begin();
auto It = BB->begin();
sandboxir::Instruction *Add0 = &*It++;
sandboxir::Instruction *Add1 = &*It++;
sandboxir::Instruction *Ret = &*It++;
Ctx.save();
// Check erase.
Add1->eraseFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Add0->getNumUses(), 0u);
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Add1->getOperand(0), Add0);
// Same for the last instruction in the block.
Ctx.save();
Ret->eraseFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
}
// TODO: Test multi-instruction patterns.
TEST_F(TrackerTest, RemoveFromParent) {
parseIR(C, R"IR(
define i32 @foo(i32 %arg) {
%add0 = add i32 %arg, %arg
%add1 = add i32 %add0, %arg
ret i32 %add1
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
auto *Arg = F->getArg(0);
auto *BB = &*F->begin();
auto It = BB->begin();
sandboxir::Instruction *Add0 = &*It++;
sandboxir::Instruction *Add1 = &*It++;
sandboxir::Instruction *Ret = &*It++;
Ctx.save();
// Check removeFromParent().
Add1->removeFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Removed instruction still be connected to operands and users.
EXPECT_EQ(Add1->getOperand(0), Add0);
EXPECT_EQ(Add1->getOperand(1), Arg);
EXPECT_EQ(Add0->getNumUses(), 1u);
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Add1->getOperand(0), Add0);
// Same for the last instruction in the block.
Ctx.save();
Ret->removeFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Ret->getOperand(0), Add1);
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
}
// TODO: Test multi-instruction patterns.
TEST_F(TrackerTest, MoveInstr) {
parseIR(C, R"IR(
define i32 @foo(i32 %arg) {
%add0 = add i32 %arg, %arg
%add1 = add i32 %add0, %arg
ret i32 %add1
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
auto *BB = &*F->begin();
auto It = BB->begin();
sandboxir::Instruction *Add0 = &*It++;
sandboxir::Instruction *Add1 = &*It++;
sandboxir::Instruction *Ret = &*It++;
// Check moveBefore(Instruction *) with tracking enabled.
Ctx.save();
Add1->moveBefore(Add0);
It = BB->begin();
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Same for the last instruction in the block.
Ctx.save();
Ret->moveBefore(Add0);
It = BB->begin();
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check moveBefore(BasicBlock &, BasicBlock::iterator) with tracking enabled.
Ctx.save();
Add1->moveBefore(*BB, Add0->getIterator());
It = BB->begin();
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Same for the last instruction in the block.
Ctx.save();
Ret->moveBefore(*BB, Add0->getIterator());
It = BB->begin();
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check moveAfter(Instruction *) with tracking enabled.
Ctx.save();
Add0->moveAfter(Add1);
It = BB->begin();
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Same for the last instruction in the block.
Ctx.save();
Ret->moveAfter(Add0);
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
}
TEST_F(TrackerTest, CallBaseSetters) {
parseIR(C, R"IR(
declare void @bar1(i8)
declare void @bar2(i8)
define void @foo(i8 %arg0, i8 %arg1) {
call void @bar1(i8 %arg0)
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
unsigned ArgIdx = 0;
auto *Arg0 = F->getArg(ArgIdx++);
auto *Arg1 = F->getArg(ArgIdx++);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Call = cast<sandboxir::CallBase>(&*It++);
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check setArgOperand().
Ctx.save();
Call->setArgOperand(0, Arg1);
EXPECT_EQ(Call->getArgOperand(0), Arg1);
Ctx.revert();
EXPECT_EQ(Call->getArgOperand(0), Arg0);
auto *Bar1F = Call->getCalledFunction();
auto *Bar2F = Ctx.createFunction(M->getFunction("bar2"));
// Check setCalledOperand().
Ctx.save();
Call->setCalledOperand(Bar2F);
EXPECT_EQ(Call->getCalledOperand(), Bar2F);
Ctx.revert();
EXPECT_EQ(Call->getCalledOperand(), Bar1F);
// Check setCalledFunction().
Ctx.save();
Call->setCalledFunction(Bar2F);
EXPECT_EQ(Call->getCalledFunction(), Bar2F);
Ctx.revert();
EXPECT_EQ(Call->getCalledFunction(), Bar1F);
}
TEST_F(TrackerTest, InvokeSetters) {
parseIR(C, R"IR(
define void @foo(i8 %arg) {
bb0:
invoke i8 @foo(i8 %arg) to label %normal_bb
unwind label %exception_bb
normal_bb:
ret void
exception_bb:
ret void
other_bb:
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *BB0 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
auto *NormalBB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "normal_bb")));
auto *ExceptionBB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "exception_bb")));
auto *OtherBB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "other_bb")));
auto It = BB0->begin();
auto *Invoke = cast<sandboxir::InvokeInst>(&*It++);
// Check setNormalDest().
Ctx.save();
Invoke->setNormalDest(OtherBB);
EXPECT_EQ(Invoke->getNormalDest(), OtherBB);
Ctx.revert();
EXPECT_EQ(Invoke->getNormalDest(), NormalBB);
// Check setUnwindDest().
Ctx.save();
Invoke->setUnwindDest(OtherBB);
EXPECT_EQ(Invoke->getUnwindDest(), OtherBB);
Ctx.revert();
EXPECT_EQ(Invoke->getUnwindDest(), ExceptionBB);
// Check setSuccessor().
Ctx.save();
Invoke->setSuccessor(0, OtherBB);
EXPECT_EQ(Invoke->getSuccessor(0), OtherBB);
Ctx.revert();
EXPECT_EQ(Invoke->getSuccessor(0), NormalBB);
Ctx.save();
Invoke->setSuccessor(1, OtherBB);
EXPECT_EQ(Invoke->getSuccessor(1), OtherBB);
Ctx.revert();
EXPECT_EQ(Invoke->getSuccessor(1), ExceptionBB);
}
TEST_F(TrackerTest, CallBrSetters) {
parseIR(C, R"IR(
define void @foo(i8 %arg) {
bb0:
callbr void @foo(i8 %arg)
to label %bb1 [label %bb2]
bb1:
ret void
bb2:
ret void
other_bb:
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *BB0 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
auto *OtherBB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "other_bb")));
auto It = BB0->begin();
auto *CallBr = cast<sandboxir::CallBrInst>(&*It++);
// Check setDefaultDest().
Ctx.save();
auto *OrigDefaultDest = CallBr->getDefaultDest();
CallBr->setDefaultDest(OtherBB);
EXPECT_EQ(CallBr->getDefaultDest(), OtherBB);
Ctx.revert();
EXPECT_EQ(CallBr->getDefaultDest(), OrigDefaultDest);
// Check setIndirectDest().
Ctx.save();
auto *OrigIndirectDest = CallBr->getIndirectDest(0);
CallBr->setIndirectDest(0, OtherBB);
EXPECT_EQ(CallBr->getIndirectDest(0), OtherBB);
Ctx.revert();
EXPECT_EQ(CallBr->getIndirectDest(0), OrigIndirectDest);
}
TEST_F(TrackerTest, PHINodeSetters) {
parseIR(C, R"IR(
define void @foo(i8 %arg0, i8 %arg1, i8 %arg2) {
bb0:
br label %bb2
bb1:
%phi = phi i8 [ %arg0, %bb0 ], [ %arg1, %bb1 ]
br label %bb1
bb2:
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
unsigned ArgIdx = 0;
auto *Arg0 = F.getArg(ArgIdx++);
auto *Arg1 = F.getArg(ArgIdx++);
auto *Arg2 = F.getArg(ArgIdx++);
auto *BB0 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
auto *BB1 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb1")));
auto *BB2 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb2")));
auto *PHI = cast<sandboxir::PHINode>(&*BB1->begin());
// Check setIncomingValue().
Ctx.save();
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
PHI->setIncomingValue(0, Arg2);
EXPECT_EQ(PHI->getIncomingValue(0), Arg2);
Ctx.revert();
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
EXPECT_EQ(PHI->getIncomingValue(1), Arg1);
// Check setIncomingBlock().
Ctx.save();
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
PHI->setIncomingBlock(0, BB2);
EXPECT_EQ(PHI->getIncomingBlock(0), BB2);
Ctx.revert();
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
EXPECT_EQ(PHI->getIncomingValue(1), Arg1);
// Check addIncoming().
Ctx.save();
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
PHI->addIncoming(Arg1, BB2);
EXPECT_EQ(PHI->getNumIncomingValues(), 3u);
EXPECT_EQ(PHI->getIncomingBlock(2), BB2);
EXPECT_EQ(PHI->getIncomingValue(2), Arg1);
Ctx.revert();
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
EXPECT_EQ(PHI->getIncomingValue(1), Arg1);
// Check removeIncomingValue(1).
Ctx.save();
PHI->removeIncomingValue(1);
EXPECT_EQ(PHI->getNumIncomingValues(), 1u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
Ctx.revert();
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
EXPECT_EQ(PHI->getIncomingValue(1), Arg1);
// Check removeIncomingValue(0).
Ctx.save();
PHI->removeIncomingValue(0u);
EXPECT_EQ(PHI->getNumIncomingValues(), 1u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB1);
EXPECT_EQ(PHI->getIncomingValue(0), Arg1);
Ctx.revert();
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
EXPECT_EQ(PHI->getIncomingValue(1), Arg1);
// Check removeIncomingValue() remove all.
Ctx.save();
PHI->removeIncomingValue(0u);
EXPECT_EQ(PHI->getNumIncomingValues(), 1u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB1);
EXPECT_EQ(PHI->getIncomingValue(0), Arg1);
PHI->removeIncomingValue(0u);
EXPECT_EQ(PHI->getNumIncomingValues(), 0u);
Ctx.revert();
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
EXPECT_EQ(PHI->getIncomingValue(1), Arg1);
// Check removeIncomingValue(BasicBlock *).
Ctx.save();
PHI->removeIncomingValue(BB1);
EXPECT_EQ(PHI->getNumIncomingValues(), 1u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
Ctx.revert();
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
EXPECT_EQ(PHI->getIncomingValue(0), Arg0);
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
EXPECT_EQ(PHI->getIncomingValue(1), Arg1);
}