This patch implements the unconditional and conditional branch instructions mirroring the newly added LLVM IR instructions. So now we have two new classes UncondBrInst and CondBrInst inheriting from BranchInst. The original Br opcode has been removed in favor of UncondBr and CondBr.
1921 lines
56 KiB
C++
1921 lines
56 KiB
C++
//===- 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/Function.h"
|
|
#include "llvm/SandboxIR/Instruction.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "gmock/gmock-matchers.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();
|
|
// Check empty().
|
|
EXPECT_TRUE(Ctx.getTracker().empty());
|
|
|
|
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);
|
|
|
|
// Check empty().
|
|
EXPECT_FALSE(Ctx.getTracker().empty());
|
|
|
|
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::CondBrInst>(&*It++);
|
|
|
|
unsigned SuccIdx = 0;
|
|
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB1, BB2});
|
|
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());
|
|
}
|
|
|
|
// TODO: Test multi-instruction patterns.
|
|
TEST_F(TrackerTest, InsertIntoBB) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i32 %arg) {
|
|
%add0 = add i32 %arg, %arg
|
|
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 *Ret = &*It++;
|
|
// Detach `Add0` before we save.
|
|
Add0->removeFromParent();
|
|
|
|
// Check insertBefore(Instruction *) with tracking enabled.
|
|
Ctx.save();
|
|
Add0->insertBefore(Ret);
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, Add0);
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(It, BB->end());
|
|
// Check revert().
|
|
Ctx.revert();
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(It, BB->end());
|
|
|
|
// Check insertAfter(Instruction *) with tracking enabled.
|
|
Ctx.save();
|
|
Add0->insertAfter(Ret);
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(&*It++, Add0);
|
|
EXPECT_EQ(It, BB->end());
|
|
// Check revert().
|
|
Ctx.revert();
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(It, BB->end());
|
|
|
|
// Check insertInto(BasicBlock *, BasicBlock::iterator) with tracking enabled.
|
|
Ctx.save();
|
|
Add0->insertInto(BB, Ret->getIterator());
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, Add0);
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(It, BB->end());
|
|
// Check revert().
|
|
Ctx.revert();
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(It, BB->end());
|
|
|
|
// To make sure we don't leak memory insert `Add0` back into the BB before the
|
|
// end of the test.
|
|
Add0->insertBefore(Ret);
|
|
}
|
|
|
|
// TODO: Test multi-instruction patterns.
|
|
TEST_F(TrackerTest, CreateAndInsertInst) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr) {
|
|
%ld = load i8, ptr %ptr, align 64
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
|
|
auto *F = Ctx.createFunction(&LLVMF);
|
|
auto *Ptr = F->getArg(0);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
auto *Ld = cast<sandboxir::LoadInst>(&*It++);
|
|
auto *Ret = &*It++;
|
|
|
|
Ctx.save();
|
|
// Check create(InsertBefore) with tracking enabled.
|
|
sandboxir::LoadInst *NewLd = sandboxir::LoadInst::create(
|
|
Ld->getType(), Ptr, Align(8),
|
|
/*InsertBefore=*/Ld->getIterator(), Ctx, "NewLd");
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, NewLd);
|
|
EXPECT_EQ(&*It++, Ld);
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(It, BB->end());
|
|
// Check revert().
|
|
Ctx.revert();
|
|
It = BB->begin();
|
|
EXPECT_EQ(&*It++, Ld);
|
|
EXPECT_EQ(&*It++, Ret);
|
|
EXPECT_EQ(It, BB->end());
|
|
}
|
|
|
|
TEST_F(TrackerTest, FenceInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo() {
|
|
fence syncscope("singlethread") seq_cst
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
sandboxir::Function *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
auto *Fence = cast<sandboxir::FenceInst>(&*It++);
|
|
|
|
// Check setOrdering().
|
|
auto OrigOrdering = Fence->getOrdering();
|
|
auto NewOrdering = AtomicOrdering::Release;
|
|
EXPECT_NE(NewOrdering, OrigOrdering);
|
|
Ctx.save();
|
|
Fence->setOrdering(NewOrdering);
|
|
EXPECT_EQ(Fence->getOrdering(), NewOrdering);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Fence->getOrdering(), OrigOrdering);
|
|
// Check setSyncScopeID().
|
|
auto OrigSSID = Fence->getSyncScopeID();
|
|
auto NewSSID = SyncScope::System;
|
|
EXPECT_NE(NewSSID, OrigSSID);
|
|
Ctx.save();
|
|
Fence->setSyncScopeID(NewSSID);
|
|
EXPECT_EQ(Fence->getSyncScopeID(), NewSSID);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Fence->getSyncScopeID(), OrigSSID);
|
|
}
|
|
|
|
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, CatchSwitchInst) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i32 %cond0, i32 %cond1) {
|
|
bb0:
|
|
%cs0 = catchswitch within none [label %handler0, label %handler1] unwind to caller
|
|
bb1:
|
|
%cs1 = catchswitch within %cs0 [label %handler0, label %handler1] unwind label %cleanup
|
|
handler0:
|
|
ret void
|
|
handler1:
|
|
ret void
|
|
cleanup:
|
|
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 *BB1 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "bb1")));
|
|
auto *Handler0 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "handler0")));
|
|
auto *Handler1 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "handler1")));
|
|
auto *CS0 = cast<sandboxir::CatchSwitchInst>(&*BB0->begin());
|
|
auto *CS1 = cast<sandboxir::CatchSwitchInst>(&*BB1->begin());
|
|
|
|
// Check setParentPad().
|
|
auto *OrigPad = CS0->getParentPad();
|
|
auto *NewPad = CS1;
|
|
EXPECT_NE(NewPad, OrigPad);
|
|
Ctx.save();
|
|
CS0->setParentPad(NewPad);
|
|
EXPECT_EQ(CS0->getParentPad(), NewPad);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CS0->getParentPad(), OrigPad);
|
|
// Check setUnwindDest().
|
|
auto *OrigUnwindDest = CS1->getUnwindDest();
|
|
auto *NewUnwindDest = BB0;
|
|
EXPECT_NE(NewUnwindDest, OrigUnwindDest);
|
|
Ctx.save();
|
|
CS1->setUnwindDest(NewUnwindDest);
|
|
EXPECT_EQ(CS1->getUnwindDest(), NewUnwindDest);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CS1->getUnwindDest(), OrigUnwindDest);
|
|
// Check setSuccessor().
|
|
auto *OrigSuccessor = CS0->getSuccessor(0);
|
|
auto *NewSuccessor = BB0;
|
|
EXPECT_NE(NewSuccessor, OrigSuccessor);
|
|
Ctx.save();
|
|
CS0->setSuccessor(0, NewSuccessor);
|
|
EXPECT_EQ(CS0->getSuccessor(0), NewSuccessor);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CS0->getSuccessor(0), OrigSuccessor);
|
|
// Check addHandler().
|
|
Ctx.save();
|
|
CS0->addHandler(BB0);
|
|
EXPECT_EQ(CS0->getNumHandlers(), 3u);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CS0->getNumHandlers(), 2u);
|
|
auto HIt = CS0->handler_begin();
|
|
EXPECT_EQ(*HIt++, Handler0);
|
|
EXPECT_EQ(*HIt++, Handler1);
|
|
}
|
|
|
|
TEST_F(TrackerTest, LandingPadInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo() {
|
|
entry:
|
|
invoke void @foo()
|
|
to label %bb unwind label %unwind
|
|
unwind:
|
|
%lpad = landingpad { ptr, i32 }
|
|
catch ptr null
|
|
ret void
|
|
bb:
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
auto *LLVMUnwind = getBasicBlockByName(LLVMF, "unwind");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
|
|
auto *Unwind = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMUnwind));
|
|
auto It = Unwind->begin();
|
|
auto *LPad = cast<sandboxir::LandingPadInst>(&*It++);
|
|
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
// Check setCleanup().
|
|
auto OrigIsCleanup = LPad->isCleanup();
|
|
auto NewIsCleanup = true;
|
|
EXPECT_NE(NewIsCleanup, OrigIsCleanup);
|
|
Ctx.save();
|
|
LPad->setCleanup(NewIsCleanup);
|
|
EXPECT_EQ(LPad->isCleanup(), NewIsCleanup);
|
|
Ctx.revert();
|
|
EXPECT_EQ(LPad->isCleanup(), OrigIsCleanup);
|
|
}
|
|
|
|
TEST_F(TrackerTest, CatchReturnInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo() {
|
|
dispatch:
|
|
%cs = catchswitch within none [label %catch] unwind to caller
|
|
catch:
|
|
%catchpad = catchpad within %cs [ptr @foo]
|
|
catchret from %catchpad to label %continue
|
|
continue:
|
|
ret void
|
|
catch2:
|
|
%catchpad2 = catchpad within %cs [ptr @foo]
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
BasicBlock *LLVMCatch = getBasicBlockByName(LLVMF, "catch");
|
|
auto LLVMIt = LLVMCatch->begin();
|
|
[[maybe_unused]] auto *LLVMCP = cast<llvm::CatchPadInst>(&*LLVMIt++);
|
|
|
|
sandboxir::Context Ctx(C);
|
|
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
|
|
auto *Catch = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMCatch));
|
|
auto *Catch2 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "catch2")));
|
|
auto It = Catch->begin();
|
|
[[maybe_unused]] auto *CP = cast<sandboxir::CatchPadInst>(&*It++);
|
|
auto *CR = cast<sandboxir::CatchReturnInst>(&*It++);
|
|
auto *CP2 = cast<sandboxir::CatchPadInst>(&*Catch2->begin());
|
|
|
|
// Check setCatchPad().
|
|
auto *OrigCP = CR->getCatchPad();
|
|
auto *NewCP = CP2;
|
|
EXPECT_NE(NewCP, OrigCP);
|
|
Ctx.save();
|
|
CR->setCatchPad(NewCP);
|
|
EXPECT_EQ(CR->getCatchPad(), NewCP);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CR->getCatchPad(), OrigCP);
|
|
// Check setSuccessor().
|
|
auto *OrigSucc = CR->getSuccessor();
|
|
auto *NewSucc = Catch;
|
|
EXPECT_NE(NewSucc, OrigSucc);
|
|
Ctx.save();
|
|
CR->setSuccessor(NewSucc);
|
|
EXPECT_EQ(CR->getSuccessor(), NewSucc);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CR->getSuccessor(), OrigSucc);
|
|
}
|
|
|
|
TEST_F(TrackerTest, CleanupReturnInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo() {
|
|
dispatch:
|
|
invoke void @foo()
|
|
to label %throw unwind label %cleanup
|
|
throw:
|
|
ret void
|
|
cleanup:
|
|
%cleanuppad = cleanuppad within none []
|
|
cleanupret from %cleanuppad unwind label %cleanup2
|
|
cleanup2:
|
|
%cleanuppad2 = cleanuppad within none []
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
BasicBlock *LLVMCleanup = getBasicBlockByName(LLVMF, "cleanup");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
|
|
auto *Throw = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "throw")));
|
|
auto *Cleanup = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMCleanup));
|
|
auto *Cleanup2 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "cleanup2")));
|
|
auto It = Cleanup->begin();
|
|
[[maybe_unused]] auto *CP = cast<sandboxir::CleanupPadInst>(&*It++);
|
|
auto *CRI = cast<sandboxir::CleanupReturnInst>(&*It++);
|
|
auto *CP2 = cast<sandboxir::CleanupPadInst>(&*Cleanup2->begin());
|
|
|
|
// Check setCleanupPad().
|
|
auto *OrigCleanupPad = CRI->getCleanupPad();
|
|
auto *NewCleanupPad = CP2;
|
|
EXPECT_NE(NewCleanupPad, OrigCleanupPad);
|
|
Ctx.save();
|
|
CRI->setCleanupPad(NewCleanupPad);
|
|
EXPECT_EQ(CRI->getCleanupPad(), NewCleanupPad);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CRI->getCleanupPad(), OrigCleanupPad);
|
|
// Check setUnwindDest().
|
|
auto *OrigUnwindDest = CRI->getUnwindDest();
|
|
auto *NewUnwindDest = Throw;
|
|
EXPECT_NE(NewUnwindDest, OrigUnwindDest);
|
|
Ctx.save();
|
|
CRI->setUnwindDest(NewUnwindDest);
|
|
EXPECT_EQ(CRI->getUnwindDest(), NewUnwindDest);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CRI->getUnwindDest(), OrigUnwindDest);
|
|
}
|
|
|
|
TEST_F(TrackerTest, SwitchInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i32 %cond0, i32 %cond1) {
|
|
entry:
|
|
switch i32 %cond0, label %default [ i32 0, label %bb0
|
|
i32 1, label %bb1 ]
|
|
bb0:
|
|
ret void
|
|
bb1:
|
|
ret void
|
|
default:
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
auto *LLVMEntry = getBasicBlockByName(LLVMF, "entry");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
auto &F = *Ctx.createFunction(&LLVMF);
|
|
auto *Cond1 = F.getArg(1);
|
|
auto *Entry = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMEntry));
|
|
auto *BB0 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
|
|
auto *BB1 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "bb1")));
|
|
auto *Switch = cast<sandboxir::SwitchInst>(&*Entry->begin());
|
|
|
|
// Check setCondition().
|
|
auto *OrigCond = Switch->getCondition();
|
|
auto *NewCond = Cond1;
|
|
EXPECT_NE(NewCond, OrigCond);
|
|
Ctx.save();
|
|
Switch->setCondition(NewCond);
|
|
EXPECT_EQ(Switch->getCondition(), NewCond);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getCondition(), OrigCond);
|
|
// Check setDefaultDest().
|
|
auto *OrigDefaultDest = Switch->getDefaultDest();
|
|
auto *NewDefaultDest = Entry;
|
|
EXPECT_NE(NewDefaultDest, OrigDefaultDest);
|
|
Ctx.save();
|
|
Switch->setDefaultDest(NewDefaultDest);
|
|
EXPECT_EQ(Switch->getDefaultDest(), NewDefaultDest);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getDefaultDest(), OrigDefaultDest);
|
|
// Check setSuccessor().
|
|
auto *OrigSucc = Switch->getSuccessor(0);
|
|
auto *NewSucc = Entry;
|
|
EXPECT_NE(NewSucc, OrigSucc);
|
|
Ctx.save();
|
|
Switch->setSuccessor(0, NewSucc);
|
|
EXPECT_EQ(Switch->getSuccessor(0), NewSucc);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getSuccessor(0), OrigSucc);
|
|
// Check addCase().
|
|
auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0);
|
|
auto *One = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 1);
|
|
auto *FortyTwo =
|
|
sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42);
|
|
Ctx.save();
|
|
Switch->addCase(FortyTwo, Entry);
|
|
EXPECT_EQ(Switch->getNumCases(), 3u);
|
|
EXPECT_EQ(Switch->findCaseDest(Entry), FortyTwo);
|
|
EXPECT_EQ(Switch->findCaseValue(FortyTwo)->getCaseSuccessor(), Entry);
|
|
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
|
|
EXPECT_EQ(Switch->findCaseDest(BB1), One);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getNumCases(), 2u);
|
|
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
|
|
EXPECT_EQ(Switch->findCaseDest(BB1), One);
|
|
// Check removeCase().
|
|
Ctx.save();
|
|
Switch->removeCase(Switch->findCaseValue(Zero));
|
|
EXPECT_EQ(Switch->getNumCases(), 1u);
|
|
EXPECT_EQ(Switch->findCaseDest(BB1), One);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getNumCases(), 2u);
|
|
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
|
|
EXPECT_EQ(Switch->findCaseDest(BB1), One);
|
|
}
|
|
|
|
TEST_F(TrackerTest, SwitchInstPreservesSuccesorOrder) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i32 %cond0) {
|
|
entry:
|
|
switch i32 %cond0, label %default [ i32 0, label %bb0
|
|
i32 1, label %bb1
|
|
i32 2, label %bb2 ]
|
|
bb0:
|
|
ret void
|
|
bb1:
|
|
ret void
|
|
bb2:
|
|
ret void
|
|
default:
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
auto *LLVMEntry = getBasicBlockByName(LLVMF, "entry");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
|
|
auto *Entry = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMEntry));
|
|
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 *Switch = cast<sandboxir::SwitchInst>(&*Entry->begin());
|
|
|
|
auto *DefaultDest = Switch->getDefaultDest();
|
|
auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0);
|
|
auto *One = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 1);
|
|
auto *Two = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 2);
|
|
|
|
// Check that we can properly revert a removeCase multiple positions apart
|
|
// from the end of the operand list.
|
|
Ctx.save();
|
|
Switch->removeCase(Switch->findCaseValue(Zero));
|
|
EXPECT_EQ(Switch->getNumCases(), 2u);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getNumCases(), 3u);
|
|
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
|
|
EXPECT_EQ(Switch->findCaseDest(BB1), One);
|
|
EXPECT_EQ(Switch->findCaseDest(BB2), Two);
|
|
EXPECT_EQ(Switch->getSuccessor(0), DefaultDest);
|
|
EXPECT_EQ(Switch->getSuccessor(1), BB0);
|
|
EXPECT_EQ(Switch->getSuccessor(2), BB1);
|
|
EXPECT_EQ(Switch->getSuccessor(3), BB2);
|
|
|
|
// Check that we can properly revert a removeCase of the last case.
|
|
Ctx.save();
|
|
Switch->removeCase(Switch->findCaseValue(Two));
|
|
EXPECT_EQ(Switch->getNumCases(), 2u);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getNumCases(), 3u);
|
|
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
|
|
EXPECT_EQ(Switch->findCaseDest(BB1), One);
|
|
EXPECT_EQ(Switch->findCaseDest(BB2), Two);
|
|
EXPECT_EQ(Switch->getSuccessor(0), DefaultDest);
|
|
EXPECT_EQ(Switch->getSuccessor(1), BB0);
|
|
EXPECT_EQ(Switch->getSuccessor(2), BB1);
|
|
EXPECT_EQ(Switch->getSuccessor(3), BB2);
|
|
|
|
// Check order is preserved after reverting multiple removeCase invocations.
|
|
Ctx.save();
|
|
Switch->removeCase(Switch->findCaseValue(One));
|
|
Switch->removeCase(Switch->findCaseValue(Zero));
|
|
Switch->removeCase(Switch->findCaseValue(Two));
|
|
EXPECT_EQ(Switch->getNumCases(), 0u);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Switch->getNumCases(), 3u);
|
|
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
|
|
EXPECT_EQ(Switch->findCaseDest(BB1), One);
|
|
EXPECT_EQ(Switch->findCaseDest(BB2), Two);
|
|
EXPECT_EQ(Switch->getSuccessor(0), DefaultDest);
|
|
EXPECT_EQ(Switch->getSuccessor(1), BB0);
|
|
EXPECT_EQ(Switch->getSuccessor(2), BB1);
|
|
EXPECT_EQ(Switch->getSuccessor(3), BB2);
|
|
}
|
|
|
|
TEST_F(TrackerTest, SelectInst) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i1 %c0, i8 %v0, i8 %v1) {
|
|
%sel = select i1 %c0, i8 %v0, i8 %v1
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
sandboxir::Function *F = Ctx.createFunction(LLVMF);
|
|
auto *V0 = F->getArg(1);
|
|
auto *V1 = F->getArg(2);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
auto *Select = cast<sandboxir::SelectInst>(&*It++);
|
|
|
|
// Check tracking for swapValues.
|
|
Ctx.save();
|
|
Select->swapValues();
|
|
EXPECT_EQ(Select->getTrueValue(), V1);
|
|
EXPECT_EQ(Select->getFalseValue(), V0);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Select->getTrueValue(), V0);
|
|
EXPECT_EQ(Select->getFalseValue(), V1);
|
|
}
|
|
|
|
TEST_F(TrackerTest, ShuffleVectorInst) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(<2 x i8> %v1, <2 x i8> %v2) {
|
|
%shuf = shufflevector <2 x i8> %v1, <2 x i8> %v2, <2 x i32> <i32 1, i32 2>
|
|
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();
|
|
auto *SVI = cast<sandboxir::ShuffleVectorInst>(&*It++);
|
|
|
|
// Check setShuffleMask.
|
|
SmallVector<int, 2> OrigMask(SVI->getShuffleMask());
|
|
Ctx.save();
|
|
SVI->setShuffleMask(ArrayRef<int>({0, 0}));
|
|
EXPECT_NE(SVI->getShuffleMask(), ArrayRef<int>(OrigMask));
|
|
Ctx.revert();
|
|
EXPECT_EQ(SVI->getShuffleMask(), ArrayRef<int>(OrigMask));
|
|
|
|
// Check commute.
|
|
auto *Op0 = SVI->getOperand(0);
|
|
auto *Op1 = SVI->getOperand(1);
|
|
Ctx.save();
|
|
SVI->commute();
|
|
EXPECT_EQ(SVI->getOperand(0), Op1);
|
|
EXPECT_EQ(SVI->getOperand(1), Op0);
|
|
EXPECT_NE(SVI->getShuffleMask(), ArrayRef<int>(OrigMask));
|
|
Ctx.revert();
|
|
EXPECT_EQ(SVI->getOperand(0), Op0);
|
|
EXPECT_EQ(SVI->getOperand(1), Op1);
|
|
EXPECT_EQ(SVI->getShuffleMask(), ArrayRef<int>(OrigMask));
|
|
}
|
|
|
|
TEST_F(TrackerTest, PossiblyDisjointInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i8 %arg0, i8 %arg1) {
|
|
%or = or i8 %arg0, %arg1
|
|
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();
|
|
auto *PDI = cast<sandboxir::PossiblyDisjointInst>(&*It++);
|
|
|
|
// Check setIsDisjoint().
|
|
auto OrigIsDisjoint = PDI->isDisjoint();
|
|
auto NewIsDisjoint = true;
|
|
EXPECT_NE(NewIsDisjoint, OrigIsDisjoint);
|
|
Ctx.save();
|
|
PDI->setIsDisjoint(NewIsDisjoint);
|
|
EXPECT_EQ(PDI->isDisjoint(), NewIsDisjoint);
|
|
Ctx.revert();
|
|
EXPECT_EQ(PDI->isDisjoint(), OrigIsDisjoint);
|
|
}
|
|
|
|
TEST_F(TrackerTest, PossiblyNonNegInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i32 %arg) {
|
|
%zext = zext i32 %arg to i64
|
|
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();
|
|
auto *PNNI = cast<sandboxir::PossiblyNonNegInst>(&*It++);
|
|
|
|
// Check setNonNeg().
|
|
auto OrigNonNeg = PNNI->hasNonNeg();
|
|
auto NewNonNeg = true;
|
|
EXPECT_NE(NewNonNeg, OrigNonNeg);
|
|
Ctx.save();
|
|
PNNI->setNonNeg(NewNonNeg);
|
|
EXPECT_EQ(PNNI->hasNonNeg(), NewNonNeg);
|
|
Ctx.revert();
|
|
EXPECT_EQ(PNNI->hasNonNeg(), OrigNonNeg);
|
|
}
|
|
|
|
TEST_F(TrackerTest, AtomicRMWSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %arg) {
|
|
%atomicrmw = atomicrmw add ptr %ptr, i8 %arg acquire, align 128
|
|
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();
|
|
auto *RMW = cast<sandboxir::AtomicRMWInst>(&*It++);
|
|
|
|
// Check setAlignment().
|
|
Ctx.save();
|
|
auto OrigAlign = RMW->getAlign();
|
|
Align NewAlign(1024);
|
|
EXPECT_NE(NewAlign, OrigAlign);
|
|
RMW->setAlignment(NewAlign);
|
|
EXPECT_EQ(RMW->getAlign(), NewAlign);
|
|
Ctx.revert();
|
|
EXPECT_EQ(RMW->getAlign(), OrigAlign);
|
|
|
|
// Check setVolatile().
|
|
Ctx.save();
|
|
auto OrigIsVolatile = RMW->isVolatile();
|
|
bool NewIsVolatile = true;
|
|
EXPECT_NE(NewIsVolatile, OrigIsVolatile);
|
|
RMW->setVolatile(NewIsVolatile);
|
|
EXPECT_EQ(RMW->isVolatile(), NewIsVolatile);
|
|
Ctx.revert();
|
|
EXPECT_EQ(RMW->isVolatile(), OrigIsVolatile);
|
|
|
|
// Check setOrdering().
|
|
Ctx.save();
|
|
auto OrigOrdering = RMW->getOrdering();
|
|
auto NewOrdering = AtomicOrdering::SequentiallyConsistent;
|
|
EXPECT_NE(NewOrdering, OrigOrdering);
|
|
RMW->setOrdering(NewOrdering);
|
|
EXPECT_EQ(RMW->getOrdering(), NewOrdering);
|
|
Ctx.revert();
|
|
EXPECT_EQ(RMW->getOrdering(), OrigOrdering);
|
|
|
|
// Check setSyncScopeID().
|
|
Ctx.save();
|
|
auto OrigSSID = RMW->getSyncScopeID();
|
|
auto NewSSID = SyncScope::SingleThread;
|
|
EXPECT_NE(NewSSID, OrigSSID);
|
|
RMW->setSyncScopeID(NewSSID);
|
|
EXPECT_EQ(RMW->getSyncScopeID(), NewSSID);
|
|
Ctx.revert();
|
|
EXPECT_EQ(RMW->getSyncScopeID(), OrigSSID);
|
|
}
|
|
|
|
TEST_F(TrackerTest, AtomicCmpXchgSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %cmp, i8 %new) {
|
|
%cmpxchg = cmpxchg ptr %ptr, i8 %cmp, i8 %new monotonic monotonic, align 128
|
|
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();
|
|
auto *CmpXchg = cast<sandboxir::AtomicCmpXchgInst>(&*It++);
|
|
|
|
// Check setAlignment().
|
|
Ctx.save();
|
|
auto OrigAlign = CmpXchg->getAlign();
|
|
Align NewAlign(1024);
|
|
EXPECT_NE(NewAlign, OrigAlign);
|
|
CmpXchg->setAlignment(NewAlign);
|
|
EXPECT_EQ(CmpXchg->getAlign(), NewAlign);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CmpXchg->getAlign(), OrigAlign);
|
|
|
|
// Check setVolatile().
|
|
Ctx.save();
|
|
auto OrigIsVolatile = CmpXchg->isVolatile();
|
|
bool NewIsVolatile = true;
|
|
EXPECT_NE(NewIsVolatile, OrigIsVolatile);
|
|
CmpXchg->setVolatile(NewIsVolatile);
|
|
EXPECT_EQ(CmpXchg->isVolatile(), NewIsVolatile);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CmpXchg->isVolatile(), OrigIsVolatile);
|
|
|
|
// Check setWeak().
|
|
Ctx.save();
|
|
auto OrigIsWeak = CmpXchg->isWeak();
|
|
bool NewIsWeak = true;
|
|
EXPECT_NE(NewIsWeak, OrigIsWeak);
|
|
CmpXchg->setWeak(NewIsWeak);
|
|
EXPECT_EQ(CmpXchg->isWeak(), NewIsWeak);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CmpXchg->isWeak(), OrigIsWeak);
|
|
|
|
// Check setSuccessOrdering().
|
|
Ctx.save();
|
|
auto OrigSuccessOrdering = CmpXchg->getSuccessOrdering();
|
|
auto NewSuccessOrdering = AtomicOrdering::SequentiallyConsistent;
|
|
EXPECT_NE(NewSuccessOrdering, OrigSuccessOrdering);
|
|
CmpXchg->setSuccessOrdering(NewSuccessOrdering);
|
|
EXPECT_EQ(CmpXchg->getSuccessOrdering(), NewSuccessOrdering);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CmpXchg->getSuccessOrdering(), OrigSuccessOrdering);
|
|
|
|
// Check setFailureOrdering().
|
|
Ctx.save();
|
|
auto OrigFailureOrdering = CmpXchg->getFailureOrdering();
|
|
auto NewFailureOrdering = AtomicOrdering::SequentiallyConsistent;
|
|
EXPECT_NE(NewFailureOrdering, OrigFailureOrdering);
|
|
CmpXchg->setFailureOrdering(NewFailureOrdering);
|
|
EXPECT_EQ(CmpXchg->getFailureOrdering(), NewFailureOrdering);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CmpXchg->getFailureOrdering(), OrigFailureOrdering);
|
|
|
|
// Check setSyncScopeID().
|
|
Ctx.save();
|
|
auto OrigSSID = CmpXchg->getSyncScopeID();
|
|
auto NewSSID = SyncScope::SingleThread;
|
|
EXPECT_NE(NewSSID, OrigSSID);
|
|
CmpXchg->setSyncScopeID(NewSSID);
|
|
EXPECT_EQ(CmpXchg->getSyncScopeID(), NewSSID);
|
|
Ctx.revert();
|
|
EXPECT_EQ(CmpXchg->getSyncScopeID(), OrigSSID);
|
|
}
|
|
|
|
TEST_F(TrackerTest, AllocaInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i8 %arg) {
|
|
%alloca = alloca i32, align 64
|
|
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();
|
|
auto *Alloca = cast<sandboxir::AllocaInst>(&*It++);
|
|
|
|
// Check setAllocatedType().
|
|
Ctx.save();
|
|
auto *OrigTy = Alloca->getAllocatedType();
|
|
auto *NewTy = sandboxir::Type::getInt64Ty(Ctx);
|
|
EXPECT_NE(NewTy, OrigTy);
|
|
Alloca->setAllocatedType(NewTy);
|
|
EXPECT_EQ(Alloca->getAllocatedType(), NewTy);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Alloca->getAllocatedType(), OrigTy);
|
|
|
|
// Check setAlignment().
|
|
Ctx.save();
|
|
auto OrigAlign = Alloca->getAlign();
|
|
Align NewAlign(128);
|
|
EXPECT_NE(NewAlign, OrigAlign);
|
|
Alloca->setAlignment(NewAlign);
|
|
EXPECT_EQ(Alloca->getAlign(), NewAlign);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Alloca->getAlign(), OrigAlign);
|
|
|
|
// Check setUsedWithInAlloca().
|
|
Ctx.save();
|
|
auto OrigWIA = Alloca->isUsedWithInAlloca();
|
|
bool NewWIA = true;
|
|
EXPECT_NE(NewWIA, OrigWIA);
|
|
Alloca->setUsedWithInAlloca(NewWIA);
|
|
EXPECT_EQ(Alloca->isUsedWithInAlloca(), NewWIA);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Alloca->isUsedWithInAlloca(), OrigWIA);
|
|
}
|
|
|
|
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, FuncletPadInstSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo() {
|
|
dispatch:
|
|
%cs = catchswitch within none [label %handler0] unwind to caller
|
|
handler0:
|
|
%catchpad = catchpad within %cs [ptr @foo]
|
|
ret void
|
|
handler1:
|
|
%cleanuppad = cleanuppad within %cs [ptr @foo]
|
|
ret void
|
|
bb:
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
|
|
auto *Dispatch = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "dispatch")));
|
|
auto *Handler0 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "handler0")));
|
|
auto *Handler1 = cast<sandboxir::BasicBlock>(
|
|
Ctx.getValue(getBasicBlockByName(LLVMF, "handler1")));
|
|
auto *CP = cast<sandboxir::CatchPadInst>(&*Handler0->begin());
|
|
auto *CLP = cast<sandboxir::CleanupPadInst>(&*Handler1->begin());
|
|
|
|
for (auto *FPI : {static_cast<sandboxir::FuncletPadInst *>(CP),
|
|
static_cast<sandboxir::FuncletPadInst *>(CLP)}) {
|
|
// Check setParentPad().
|
|
auto *OrigParentPad = FPI->getParentPad();
|
|
auto *NewParentPad = Dispatch;
|
|
EXPECT_NE(NewParentPad, OrigParentPad);
|
|
Ctx.save();
|
|
FPI->setParentPad(NewParentPad);
|
|
EXPECT_EQ(FPI->getParentPad(), NewParentPad);
|
|
Ctx.revert();
|
|
EXPECT_EQ(FPI->getParentPad(), OrigParentPad);
|
|
|
|
// Check setArgOperand().
|
|
auto *OrigArgOperand = FPI->getArgOperand(0);
|
|
auto *NewArgOperand = Dispatch;
|
|
EXPECT_NE(NewArgOperand, OrigArgOperand);
|
|
Ctx.save();
|
|
FPI->setArgOperand(0, NewArgOperand);
|
|
EXPECT_EQ(FPI->getArgOperand(0), NewArgOperand);
|
|
Ctx.revert();
|
|
EXPECT_EQ(FPI->getArgOperand(0), OrigArgOperand);
|
|
}
|
|
}
|
|
|
|
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 removeIncomingValueIf(FromBB1).
|
|
Ctx.save();
|
|
PHI->removeIncomingValueIf(
|
|
[&](unsigned Idx) { return PHI->getIncomingBlock(Idx) == BB1; });
|
|
EXPECT_EQ(PHI->getNumIncomingValues(), 1u);
|
|
Ctx.revert();
|
|
EXPECT_EQ(PHI->getNumIncomingValues(), 2u);
|
|
EXPECT_EQ(PHI->getIncomingBlock(0), BB0);
|
|
EXPECT_EQ(PHI->getIncomingBlock(1), BB1);
|
|
// 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);
|
|
}
|
|
|
|
void checkCmpInst(sandboxir::Context &Ctx, sandboxir::CmpInst *Cmp) {
|
|
Ctx.save();
|
|
auto OrigP = Cmp->getPredicate();
|
|
auto NewP = Cmp->getSwappedPredicate();
|
|
Cmp->setPredicate(NewP);
|
|
EXPECT_EQ(Cmp->getPredicate(), NewP);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Cmp->getPredicate(), OrigP);
|
|
|
|
Ctx.save();
|
|
auto OrigOp0 = Cmp->getOperand(0);
|
|
auto OrigOp1 = Cmp->getOperand(1);
|
|
Cmp->swapOperands();
|
|
EXPECT_EQ(Cmp->getPredicate(), NewP);
|
|
EXPECT_EQ(Cmp->getOperand(0), OrigOp1);
|
|
EXPECT_EQ(Cmp->getOperand(1), OrigOp0);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Cmp->getPredicate(), OrigP);
|
|
EXPECT_EQ(Cmp->getOperand(0), OrigOp0);
|
|
EXPECT_EQ(Cmp->getOperand(1), OrigOp1);
|
|
}
|
|
|
|
TEST_F(TrackerTest, CmpInst) {
|
|
SCOPED_TRACE("TrackerTest sandboxir::CmpInst tests");
|
|
parseIR(C, R"IR(
|
|
define void @foo(i64 %i0, i64 %i1, float %f0, float %f1) {
|
|
%foeq = fcmp ogt float %f0, %f1
|
|
%ioeq = icmp uge i64 %i0, %i1
|
|
|
|
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();
|
|
auto *FCmp = cast<sandboxir::CmpInst>(&*It++);
|
|
checkCmpInst(Ctx, FCmp);
|
|
auto *ICmp = cast<sandboxir::CmpInst>(&*It++);
|
|
checkCmpInst(Ctx, ICmp);
|
|
}
|
|
|
|
TEST_F(TrackerTest, GlobalValueSetters) {
|
|
parseIR(C, R"IR(
|
|
define void @foo() {
|
|
call void @foo()
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
|
|
auto &F = *Ctx.createFunction(&LLVMF);
|
|
auto *BB = &*F.begin();
|
|
auto *Call = cast<sandboxir::CallInst>(&*BB->begin());
|
|
|
|
auto *GV = cast<sandboxir::GlobalValue>(Call->getCalledOperand());
|
|
// Check setUnnamedAddr().
|
|
auto OrigUnnamedAddr = GV->getUnnamedAddr();
|
|
auto NewUnnamedAddr = sandboxir::GlobalValue::UnnamedAddr::Global;
|
|
EXPECT_NE(NewUnnamedAddr, OrigUnnamedAddr);
|
|
Ctx.save();
|
|
GV->setUnnamedAddr(NewUnnamedAddr);
|
|
EXPECT_EQ(GV->getUnnamedAddr(), NewUnnamedAddr);
|
|
Ctx.revert();
|
|
EXPECT_EQ(GV->getUnnamedAddr(), OrigUnnamedAddr);
|
|
|
|
// Check setVisibility().
|
|
auto OrigVisibility = GV->getVisibility();
|
|
auto NewVisibility =
|
|
sandboxir::GlobalValue::VisibilityTypes::ProtectedVisibility;
|
|
EXPECT_NE(NewVisibility, OrigVisibility);
|
|
Ctx.save();
|
|
GV->setVisibility(NewVisibility);
|
|
EXPECT_EQ(GV->getVisibility(), NewVisibility);
|
|
Ctx.revert();
|
|
EXPECT_EQ(GV->getVisibility(), OrigVisibility);
|
|
}
|
|
|
|
TEST_F(TrackerTest, GlobalIFuncSetters) {
|
|
parseIR(C, R"IR(
|
|
declare external void @bar()
|
|
@ifunc = ifunc void(), ptr @foo
|
|
define void @foo() {
|
|
call void @ifunc()
|
|
call void @bar()
|
|
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();
|
|
auto *Call0 = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Call1 = cast<sandboxir::CallInst>(&*It++);
|
|
// Check classof(), creation.
|
|
auto *IFunc = cast<sandboxir::GlobalIFunc>(Call0->getCalledOperand());
|
|
auto *Bar = cast<sandboxir::Function>(Call1->getCalledOperand());
|
|
// Check setResolver().
|
|
auto *OrigResolver = IFunc->getResolver();
|
|
auto *NewResolver = Bar;
|
|
EXPECT_NE(NewResolver, OrigResolver);
|
|
Ctx.save();
|
|
IFunc->setResolver(NewResolver);
|
|
EXPECT_EQ(IFunc->getResolver(), NewResolver);
|
|
Ctx.revert();
|
|
EXPECT_EQ(IFunc->getResolver(), OrigResolver);
|
|
}
|
|
|
|
TEST_F(TrackerTest, GlobalVariableSetters) {
|
|
parseIR(C, R"IR(
|
|
@glob0 = global i32 42
|
|
@glob1 = global i32 43
|
|
define void @foo() {
|
|
%ld0 = load i32, ptr @glob0
|
|
%ld1 = load i32, ptr @glob1
|
|
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();
|
|
auto *Ld0 = cast<sandboxir::LoadInst>(&*It++);
|
|
auto *Ld1 = cast<sandboxir::LoadInst>(&*It++);
|
|
// Check classof(), creation.
|
|
auto *GV0 = cast<sandboxir::GlobalVariable>(Ld0->getPointerOperand());
|
|
auto *GV1 = cast<sandboxir::GlobalVariable>(Ld1->getPointerOperand());
|
|
// Check setInitializer().
|
|
auto *OrigInitializer = GV0->getInitializer();
|
|
auto *NewInitializer = GV1->getInitializer();
|
|
EXPECT_NE(NewInitializer, OrigInitializer);
|
|
Ctx.save();
|
|
GV0->setInitializer(NewInitializer);
|
|
EXPECT_EQ(GV0->getInitializer(), NewInitializer);
|
|
Ctx.revert();
|
|
EXPECT_EQ(GV0->getInitializer(), OrigInitializer);
|
|
// Check setConstant().
|
|
bool OrigIsConstant = GV0->isConstant();
|
|
bool NewIsConstant = !OrigIsConstant;
|
|
Ctx.save();
|
|
GV0->setConstant(NewIsConstant);
|
|
EXPECT_EQ(GV0->isConstant(), NewIsConstant);
|
|
Ctx.revert();
|
|
EXPECT_EQ(GV0->isConstant(), OrigIsConstant);
|
|
// Check setExternallyInitialized().
|
|
bool OrigIsExtInit = GV0->isExternallyInitialized();
|
|
bool NewIsExtInit = !OrigIsExtInit;
|
|
Ctx.save();
|
|
GV0->setExternallyInitialized(NewIsExtInit);
|
|
EXPECT_EQ(GV0->isExternallyInitialized(), NewIsExtInit);
|
|
Ctx.revert();
|
|
EXPECT_EQ(GV0->isExternallyInitialized(), OrigIsExtInit);
|
|
}
|
|
|
|
TEST_F(TrackerTest, GlobalAliasSetters) {
|
|
parseIR(C, R"IR(
|
|
@alias = dso_local alias void(), ptr @foo
|
|
declare void @bar();
|
|
define void @foo() {
|
|
call void @alias()
|
|
call void @bar()
|
|
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();
|
|
auto *Call0 = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Call1 = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Callee1 = cast<sandboxir::Constant>(Call1->getCalledOperand());
|
|
auto *Alias = cast<sandboxir::GlobalAlias>(Call0->getCalledOperand());
|
|
// Check setAliasee().
|
|
auto *OrigAliasee = Alias->getAliasee();
|
|
auto *NewAliasee = Callee1;
|
|
EXPECT_NE(NewAliasee, OrigAliasee);
|
|
Ctx.save();
|
|
Alias->setAliasee(NewAliasee);
|
|
EXPECT_EQ(Alias->getAliasee(), NewAliasee);
|
|
Ctx.revert();
|
|
EXPECT_EQ(Alias->getAliasee(), OrigAliasee);
|
|
}
|
|
|
|
TEST_F(TrackerTest, SetVolatile) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %arg0, i8 %val) {
|
|
%ld = load i8, ptr %arg0, align 64
|
|
store i8 %val, ptr %arg0, align 64
|
|
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();
|
|
auto *Load = cast<sandboxir::LoadInst>(&*It++);
|
|
auto *Store = cast<sandboxir::StoreInst>(&*It++);
|
|
|
|
EXPECT_FALSE(Load->isVolatile());
|
|
Ctx.save();
|
|
Load->setVolatile(true);
|
|
EXPECT_TRUE(Load->isVolatile());
|
|
Ctx.revert();
|
|
EXPECT_FALSE(Load->isVolatile());
|
|
|
|
EXPECT_FALSE(Store->isVolatile());
|
|
Ctx.save();
|
|
Store->setVolatile(true);
|
|
EXPECT_TRUE(Store->isVolatile());
|
|
Ctx.revert();
|
|
EXPECT_FALSE(Store->isVolatile());
|
|
}
|
|
|
|
TEST_F(TrackerTest, Flags) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i32 %arg, float %farg) {
|
|
%add = add i32 %arg, %arg
|
|
%fadd = fadd float %farg, %farg
|
|
%udiv = udiv i32 %arg, %arg
|
|
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();
|
|
auto *Add = &*It++;
|
|
auto *FAdd = &*It++;
|
|
auto *UDiv = &*It++;
|
|
|
|
#define CHECK_FLAG(I, GETTER, SETTER) \
|
|
{ \
|
|
Ctx.save(); \
|
|
bool OrigFlag = I->GETTER(); \
|
|
bool NewFlag = !OrigFlag; \
|
|
I->SETTER(NewFlag); \
|
|
EXPECT_EQ(I->GETTER(), NewFlag); \
|
|
Ctx.revert(); \
|
|
EXPECT_EQ(I->GETTER(), OrigFlag); \
|
|
}
|
|
|
|
CHECK_FLAG(Add, hasNoUnsignedWrap, setHasNoUnsignedWrap);
|
|
CHECK_FLAG(Add, hasNoSignedWrap, setHasNoSignedWrap);
|
|
CHECK_FLAG(FAdd, isFast, setFast);
|
|
CHECK_FLAG(FAdd, hasAllowReassoc, setHasAllowReassoc);
|
|
CHECK_FLAG(UDiv, isExact, setIsExact);
|
|
CHECK_FLAG(FAdd, hasNoNaNs, setHasNoNaNs);
|
|
CHECK_FLAG(FAdd, hasNoInfs, setHasNoInfs);
|
|
CHECK_FLAG(FAdd, hasNoSignedZeros, setHasNoSignedZeros);
|
|
CHECK_FLAG(FAdd, hasAllowReciprocal, setHasAllowReciprocal);
|
|
CHECK_FLAG(FAdd, hasAllowContract, setHasAllowContract);
|
|
CHECK_FLAG(FAdd, hasApproxFunc, setHasApproxFunc);
|
|
|
|
// Check setFastMathFlags().
|
|
FastMathFlags OrigFMF = FAdd->getFastMathFlags();
|
|
FastMathFlags NewFMF;
|
|
NewFMF.setAllowReassoc(true);
|
|
EXPECT_TRUE(NewFMF != OrigFMF);
|
|
|
|
Ctx.save();
|
|
FAdd->setFastMathFlags(NewFMF);
|
|
EXPECT_FALSE(FAdd->getFastMathFlags() != NewFMF);
|
|
Ctx.revert();
|
|
EXPECT_FALSE(FAdd->getFastMathFlags() != OrigFMF);
|
|
|
|
// Check copyFastMathFlags().
|
|
Ctx.save();
|
|
FAdd->copyFastMathFlags(NewFMF);
|
|
EXPECT_FALSE(FAdd->getFastMathFlags() != NewFMF);
|
|
Ctx.revert();
|
|
EXPECT_FALSE(FAdd->getFastMathFlags() != OrigFMF);
|
|
}
|
|
|
|
// IRSnapshotChecker is only defined in debug mode.
|
|
#ifndef NDEBUG
|
|
|
|
TEST_F(TrackerTest, IRSnapshotCheckerNoChanges) {
|
|
parseIR(C, R"IR(
|
|
define i32 @foo(i32 %arg) {
|
|
%add0 = add i32 %arg, %arg
|
|
ret i32 %add0
|
|
}
|
|
)IR");
|
|
Function &LLVMF = *M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
|
|
[[maybe_unused]] auto *F = Ctx.createFunction(&LLVMF);
|
|
sandboxir::IRSnapshotChecker Checker(Ctx);
|
|
Checker.save();
|
|
Checker.expectNoDiff();
|
|
}
|
|
|
|
TEST_F(TrackerTest, IRSnapshotCheckerDiesWithUnexpectedChanges) {
|
|
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::IRSnapshotChecker Checker(Ctx);
|
|
Checker.save();
|
|
Add1->setOperand(1, Add0);
|
|
EXPECT_DEATH(Checker.expectNoDiff(), "Found IR difference");
|
|
}
|
|
|
|
TEST_F(TrackerTest, IRSnapshotCheckerSaveMultipleTimes) {
|
|
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::IRSnapshotChecker Checker(Ctx);
|
|
Checker.save();
|
|
Add1->setOperand(1, Add0);
|
|
// Now IR differs from the last snapshot. Let's take a new snapshot.
|
|
Checker.save();
|
|
// The new snapshot should have replaced the old one, so this should succeed.
|
|
Checker.expectNoDiff();
|
|
}
|
|
|
|
#endif // NDEBUG
|