vporpo 467cf7caed
[SandboxIR] Implement UncondBrInst and CondBrInst (#187196)
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.
2026-03-19 10:28:32 -07:00

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