vporpo 69b8cf4f06
[SandboxVec][BottomUpVec] Add cost estimation and tr-accept-or-revert pass (#126325)
The TransactionAcceptOrRevert pass is the final pass in the Sandbox
Vectorizer's default pass pipeline. It's job is to check the cost
before/after vectorization and accept or revert the IR to its original
state.

Since we are now starting the transaction in BottomUpVec, tests that run
a custom pipeline need to accept the transaction. This is done with the
help of the TransactionAlwaysAccept pass (tr-accept).
2025-02-08 08:34:18 -08: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::BranchInst>(&*It++);
unsigned SuccIdx = 0;
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB2, BB1});
for (auto *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
// This calls User::swapOperandsInternal() internally.
Br->swapSuccessors();
SuccIdx = 0;
for (auto *Succ : reverse(Br->successors()))
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
Ctx.getTracker().revert();
SuccIdx = 0;
for (auto *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
}
TEST_F(TrackerTest, RUWIf_RAUW_RUOW) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
%ld0 = load float, ptr %ptr
%ld1 = load float, ptr %ptr
store float %ld0, ptr %ptr
store float %ld0, ptr %ptr
ret void
}
)IR");
llvm::Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
llvm::BasicBlock *LLVMBB = &*LLVMF.begin();
Ctx.createFunction(&LLVMF);
auto *BB = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB));
auto It = BB->begin();
sandboxir::Instruction *Ld0 = &*It++;
sandboxir::Instruction *Ld1 = &*It++;
sandboxir::Instruction *St0 = &*It++;
sandboxir::Instruction *St1 = &*It++;
Ctx.save();
// Check RUWIf when the lambda returns false.
Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return false; });
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUWIf when the lambda returns true.
Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return true; });
EXPECT_EQ(St0->getOperand(0), Ld1);
EXPECT_EQ(St1->getOperand(0), Ld1);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUWIf user == St0.
Ctx.save();
Ld0->replaceUsesWithIf(
Ld1, [St0](const sandboxir::Use &Use) { return Use.getUser() == St0; });
EXPECT_EQ(St0->getOperand(0), Ld1);
EXPECT_EQ(St1->getOperand(0), Ld0);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUWIf user == St1.
Ctx.save();
Ld0->replaceUsesWithIf(
Ld1, [St1](const sandboxir::Use &Use) { return Use.getUser() == St1; });
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld1);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RAUW.
Ctx.save();
Ld1->replaceAllUsesWith(Ld0);
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
// Check RUOW.
Ctx.save();
St0->replaceUsesOfWith(Ld0, Ld1);
EXPECT_EQ(St0->getOperand(0), Ld1);
Ctx.revert();
EXPECT_EQ(St0->getOperand(0), Ld0);
// Check accept().
Ctx.save();
St0->replaceUsesOfWith(Ld0, Ld1);
EXPECT_EQ(St0->getOperand(0), Ld1);
Ctx.accept();
EXPECT_EQ(St0->getOperand(0), Ld1);
}
// TODO: Test multi-instruction patterns.
TEST_F(TrackerTest, EraseFromParent) {
parseIR(C, R"IR(
define void @foo(i32 %v1) {
%add0 = add i32 %v1, %v1
%add1 = add i32 %add0, %v1
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
auto *BB = &*F->begin();
auto It = BB->begin();
sandboxir::Instruction *Add0 = &*It++;
sandboxir::Instruction *Add1 = &*It++;
sandboxir::Instruction *Ret = &*It++;
Ctx.save();
// Check erase.
Add1->eraseFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Add0->getNumUses(), 0u);
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Add1->getOperand(0), Add0);
// Same for the last instruction in the block.
Ctx.save();
Ret->eraseFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
}
// TODO: Test multi-instruction patterns.
TEST_F(TrackerTest, RemoveFromParent) {
parseIR(C, R"IR(
define i32 @foo(i32 %arg) {
%add0 = add i32 %arg, %arg
%add1 = add i32 %add0, %arg
ret i32 %add1
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
auto *Arg = F->getArg(0);
auto *BB = &*F->begin();
auto It = BB->begin();
sandboxir::Instruction *Add0 = &*It++;
sandboxir::Instruction *Add1 = &*It++;
sandboxir::Instruction *Ret = &*It++;
Ctx.save();
// Check removeFromParent().
Add1->removeFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Removed instruction still be connected to operands and users.
EXPECT_EQ(Add1->getOperand(0), Add0);
EXPECT_EQ(Add1->getOperand(1), Arg);
EXPECT_EQ(Add0->getNumUses(), 1u);
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Add1->getOperand(0), Add0);
// Same for the last instruction in the block.
Ctx.save();
Ret->removeFromParent();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
EXPECT_EQ(Ret->getOperand(0), Add1);
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
}
// TODO: Test multi-instruction patterns.
TEST_F(TrackerTest, MoveInstr) {
parseIR(C, R"IR(
define i32 @foo(i32 %arg) {
%add0 = add i32 %arg, %arg
%add1 = add i32 %add0, %arg
ret i32 %add1
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMF);
auto *BB = &*F->begin();
auto It = BB->begin();
sandboxir::Instruction *Add0 = &*It++;
sandboxir::Instruction *Add1 = &*It++;
sandboxir::Instruction *Ret = &*It++;
// Check moveBefore(Instruction *) with tracking enabled.
Ctx.save();
Add1->moveBefore(Add0);
It = BB->begin();
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Same for the last instruction in the block.
Ctx.save();
Ret->moveBefore(Add0);
It = BB->begin();
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check moveBefore(BasicBlock &, BasicBlock::iterator) with tracking enabled.
Ctx.save();
Add1->moveBefore(*BB, Add0->getIterator());
It = BB->begin();
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Same for the last instruction in the block.
Ctx.save();
Ret->moveBefore(*BB, Add0->getIterator());
It = BB->begin();
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check moveAfter(Instruction *) with tracking enabled.
Ctx.save();
Add0->moveAfter(Add1);
It = BB->begin();
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
// Same for the last instruction in the block.
Ctx.save();
Ret->moveAfter(Add0);
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(It, BB->end());
// Check revert().
Ctx.revert();
It = BB->begin();
EXPECT_EQ(&*It++, Add0);
EXPECT_EQ(&*It++, Add1);
EXPECT_EQ(&*It++, Ret);
EXPECT_EQ(It, BB->end());
}
// 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