llvm-project/llvm/unittests/SandboxIR/SandboxIRTest.cpp
vporpo 1987f93d03
[SandboxIR] OpaqueValue (#127699)
This patch implements a new subclass of the Value class used for Sandbox
IR Values that we don't support, like metadata or inline asm. The goal
is to never have null sandboxir::Value objects, because this is not the
expected behavior.
2025-02-19 11:54:44 -08:00

6192 lines
224 KiB
C++

//===- SandboxIRTest.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/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Module.h"
#include "llvm/SandboxIR/BasicBlock.h"
#include "llvm/SandboxIR/Constant.h"
#include "llvm/SandboxIR/Function.h"
#include "llvm/SandboxIR/Instruction.h"
#include "llvm/SandboxIR/Module.h"
#include "llvm/SandboxIR/Utils.h"
#include "llvm/SandboxIR/Value.h"
#include "llvm/Support/SourceMgr.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace llvm;
struct SandboxIRTest : 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("SandboxIRTest", 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(SandboxIRTest, ClassID) {
parseIR(C, R"IR(
define void @foo(i32 %v1) {
%add = add i32 %v1, 42
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
llvm::BasicBlock *LLVMBB = &*LLVMF->begin();
llvm::Instruction *LLVMAdd = &*LLVMBB->begin();
auto *LLVMC = cast<llvm::Constant>(LLVMAdd->getOperand(1));
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
sandboxir::Argument *Arg0 = F->getArg(0);
sandboxir::BasicBlock *BB = &*F->begin();
sandboxir::Instruction *AddI = &*BB->begin();
sandboxir::Constant *Const0 = cast<sandboxir::Constant>(Ctx.getValue(LLVMC));
EXPECT_TRUE(isa<sandboxir::Function>(F));
EXPECT_FALSE(isa<sandboxir::Function>(Arg0));
EXPECT_FALSE(isa<sandboxir::Function>(BB));
EXPECT_FALSE(isa<sandboxir::Function>(AddI));
EXPECT_FALSE(isa<sandboxir::Function>(Const0));
EXPECT_FALSE(isa<sandboxir::Argument>(F));
EXPECT_TRUE(isa<sandboxir::Argument>(Arg0));
EXPECT_FALSE(isa<sandboxir::Argument>(BB));
EXPECT_FALSE(isa<sandboxir::Argument>(AddI));
EXPECT_FALSE(isa<sandboxir::Argument>(Const0));
EXPECT_TRUE(isa<sandboxir::Constant>(F));
EXPECT_FALSE(isa<sandboxir::Constant>(Arg0));
EXPECT_FALSE(isa<sandboxir::Constant>(BB));
EXPECT_FALSE(isa<sandboxir::Constant>(AddI));
EXPECT_TRUE(isa<sandboxir::Constant>(Const0));
EXPECT_FALSE(isa<sandboxir::OpaqueInst>(F));
EXPECT_FALSE(isa<sandboxir::OpaqueInst>(Arg0));
EXPECT_FALSE(isa<sandboxir::OpaqueInst>(BB));
EXPECT_FALSE(isa<sandboxir::OpaqueInst>(AddI));
EXPECT_FALSE(isa<sandboxir::OpaqueInst>(Const0));
EXPECT_FALSE(isa<sandboxir::Instruction>(F));
EXPECT_FALSE(isa<sandboxir::Instruction>(Arg0));
EXPECT_FALSE(isa<sandboxir::Instruction>(BB));
EXPECT_TRUE(isa<sandboxir::Instruction>(AddI));
EXPECT_FALSE(isa<sandboxir::Instruction>(Const0));
EXPECT_TRUE(isa<sandboxir::User>(F));
EXPECT_FALSE(isa<sandboxir::User>(Arg0));
EXPECT_FALSE(isa<sandboxir::User>(BB));
EXPECT_TRUE(isa<sandboxir::User>(AddI));
EXPECT_TRUE(isa<sandboxir::User>(Const0));
#ifndef NDEBUG
std::string Buff;
raw_string_ostream BS(Buff);
F->dumpOS(BS);
Arg0->dumpOS(BS);
BB->dumpOS(BS);
AddI->dumpOS(BS);
Const0->dumpOS(BS);
#endif
}
TEST_F(SandboxIRTest, ConstantInt) {
parseIR(C, R"IR(
define void @foo(i32 %v0) {
%add0 = add i32 %v0, 42
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB = &*LLVMF.begin();
auto *LLVMAdd0 = &*LLVMBB->begin();
auto *LLVMFortyTwo = cast<llvm::ConstantInt>(LLVMAdd0->getOperand(1));
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto &BB = *F.begin();
auto It = BB.begin();
auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
auto *FortyTwo = cast<sandboxir::ConstantInt>(Add0->getOperand(1));
// Check that creating an identical constant gives us the same object.
auto *NewCI =
sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42);
EXPECT_EQ(NewCI, FortyTwo);
{
// Check getTrue(Ctx).
auto *True = sandboxir::ConstantInt::getTrue(Ctx);
EXPECT_EQ(True, Ctx.getValue(llvm::ConstantInt::getTrue(C)));
// Check getFalse(Ctx).
auto *False = sandboxir::ConstantInt::getFalse(Ctx);
EXPECT_EQ(False, Ctx.getValue(llvm::ConstantInt::getFalse(C)));
// Check getBool(Ctx).
auto *Bool = sandboxir::ConstantInt::getBool(Ctx, true);
EXPECT_EQ(Bool, Ctx.getValue(llvm::ConstantInt::getBool(C, true)));
}
{
auto *Int1Ty = sandboxir::Type::getInt1Ty(Ctx);
auto *LLVMInt1Ty = llvm::Type::getInt1Ty(C);
// Check getTrue(Ty).
auto *True = sandboxir::ConstantInt::getTrue(Int1Ty);
EXPECT_EQ(True, Ctx.getValue(llvm::ConstantInt::getTrue(LLVMInt1Ty)));
// Check getFalse(Ty).
auto *False = sandboxir::ConstantInt::getFalse(Int1Ty);
EXPECT_EQ(False, Ctx.getValue(llvm::ConstantInt::getFalse(LLVMInt1Ty)));
// Check getBool(Ty).
auto *Bool = sandboxir::ConstantInt::getBool(Int1Ty, true);
EXPECT_EQ(Bool, Ctx.getValue(llvm::ConstantInt::getBool(LLVMInt1Ty, true)));
}
auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx);
auto *LLVMInt32Ty = llvm::Type::getInt32Ty(C);
{
// Check get(Type, V).
auto *FortyThree = sandboxir::ConstantInt::get(Int32Ty, 43);
auto *LLVMFortyThree = llvm::ConstantInt::get(LLVMInt32Ty, 43);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check get(Type, V, IsSigned).
auto *FortyThree =
sandboxir::ConstantInt::get(Int32Ty, 43, /*IsSigned=*/true);
auto *LLVMFortyThree =
llvm::ConstantInt::get(LLVMInt32Ty, 43, /*IsSigned=*/true);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check get(IntegerType, V).
auto *FortyThree =
sandboxir::ConstantInt::get(sandboxir::IntegerType::get(Ctx, 32), 43);
auto *LLVMFortyThree =
llvm::ConstantInt::get(llvm::IntegerType::get(C, 32), 43);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check get(IntegerType, V, IsSigned).
auto *FortyThree = sandboxir::ConstantInt::get(
sandboxir::IntegerType::get(Ctx, 32), 43, /*IsSigned=*/true);
auto *LLVMFortyThree = llvm::ConstantInt::get(llvm::IntegerType::get(C, 32),
43, /*IsSigned=*/true);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check getSigned(IntegerType, V).
auto *FortyThree = sandboxir::ConstantInt::getSigned(
sandboxir::IntegerType::get(Ctx, 32), 43);
auto *LLVMFortyThree =
llvm::ConstantInt::getSigned(llvm::IntegerType::get(C, 32), 43);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check getSigned(Type, V).
auto *FortyThree = sandboxir::ConstantInt::getSigned(Int32Ty, 43);
auto *LLVMFortyThree = llvm::ConstantInt::getSigned(LLVMInt32Ty, 43);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check get(Ctx, APInt).
APInt APInt43(32, 43);
auto *FortyThree = sandboxir::ConstantInt::get(Ctx, APInt43);
auto *LLVMFortyThree = llvm::ConstantInt::get(C, APInt43);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check get(Ty, Str, Radix).
StringRef Str("43");
uint8_t Radix(10);
auto *FortyThree = sandboxir::ConstantInt::get(
sandboxir::IntegerType::get(Ctx, 32), Str, Radix);
auto *LLVMFortyThree =
llvm::ConstantInt::get(llvm::IntegerType::get(C, 32), Str, Radix);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
{
// Check get(Ty, APInt).
APInt APInt43(32, 43);
auto *FortyThree = sandboxir::ConstantInt::get(Int32Ty, APInt43);
auto *LLVMFortyThree = llvm::ConstantInt::get(LLVMInt32Ty, APInt43);
EXPECT_NE(FortyThree, FortyTwo);
EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree));
}
// Check getValue().
EXPECT_EQ(FortyTwo->getValue(), LLVMFortyTwo->getValue());
// Check getBitWidth().
EXPECT_EQ(FortyTwo->getBitWidth(), LLVMFortyTwo->getBitWidth());
// Check getZExtValue().
EXPECT_EQ(FortyTwo->getZExtValue(), LLVMFortyTwo->getZExtValue());
// Check getSExtValue().
EXPECT_EQ(FortyTwo->getSExtValue(), LLVMFortyTwo->getSExtValue());
// Check getMaybeAlignValue().
auto *SixtyFour =
cast<sandboxir::ConstantInt>(sandboxir::ConstantInt::get(Int32Ty, 64));
auto *LLVMSixtyFour =
cast<llvm::ConstantInt>(llvm::ConstantInt::get(LLVMInt32Ty, 64));
EXPECT_EQ(SixtyFour->getMaybeAlignValue(),
LLVMSixtyFour->getMaybeAlignValue());
// Check getAlignValue().
EXPECT_EQ(SixtyFour->getAlignValue(), LLVMSixtyFour->getAlignValue());
// Check equalsInt().
EXPECT_TRUE(FortyTwo->equalsInt(42));
EXPECT_FALSE(FortyTwo->equalsInt(43));
// Check getIntegerType().
EXPECT_EQ(FortyTwo->getIntegerType(), sandboxir::IntegerType::get(Ctx, 32));
// Check isValueValidForType().
EXPECT_TRUE(
sandboxir::ConstantInt::isValueValidForType(Int32Ty, (uint64_t)42));
EXPECT_TRUE(
sandboxir::ConstantInt::isValueValidForType(Int32Ty, (int64_t)42));
// Check isNegative().
EXPECT_FALSE(FortyTwo->isNegative());
EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, -42));
// Check isZero().
EXPECT_FALSE(FortyTwo->isZero());
EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, 0)->isZero());
// Check isOne().
EXPECT_FALSE(FortyTwo->isOne());
EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, 1)->isOne());
// Check isMinusOne().
EXPECT_FALSE(FortyTwo->isMinusOne());
EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, -1)->isMinusOne());
// Check isMaxValue().
EXPECT_FALSE(FortyTwo->isMaxValue(/*Signed=*/true));
EXPECT_TRUE(
sandboxir::ConstantInt::get(Int32Ty, std::numeric_limits<int32_t>::max())
->isMaxValue(/*Signed=*/true));
// Check isMinValue().
EXPECT_FALSE(FortyTwo->isMinValue(/*Signed=*/true));
EXPECT_TRUE(
sandboxir::ConstantInt::get(Int32Ty, std::numeric_limits<int32_t>::min())
->isMinValue(/*Signed=*/true));
// Check uge().
EXPECT_TRUE(FortyTwo->uge(41));
EXPECT_FALSE(FortyTwo->uge(43));
// Check getLimitedValue().
EXPECT_EQ(FortyTwo->getLimitedValue(40u), 40u);
EXPECT_EQ(FortyTwo->getLimitedValue(50u), 42u);
}
TEST_F(SandboxIRTest, ConstantFP) {
parseIR(C, R"IR(
define void @foo(float %v0, double %v1) {
%fadd0 = fadd float %v0, 42.0
%fadd1 = fadd double %v1, 43.0
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 *FAdd0 = cast<sandboxir::BinaryOperator>(&*It++);
auto *FAdd1 = cast<sandboxir::BinaryOperator>(&*It++);
auto *FortyTwo = cast<sandboxir::ConstantFP>(FAdd0->getOperand(1));
[[maybe_unused]] auto *FortyThree =
cast<sandboxir::ConstantFP>(FAdd1->getOperand(1));
auto *FloatTy = sandboxir::Type::getFloatTy(Ctx);
auto *DoubleTy = sandboxir::Type::getDoubleTy(Ctx);
auto *LLVMFloatTy = Type::getFloatTy(C);
auto *LLVMDoubleTy = Type::getDoubleTy(C);
// Check that creating an identical constant gives us the same object.
auto *NewFortyTwo = sandboxir::ConstantFP::get(FloatTy, 42.0);
EXPECT_EQ(NewFortyTwo, FortyTwo);
// Check get(Type, double).
auto *FortyFour =
cast<sandboxir::ConstantFP>(sandboxir::ConstantFP::get(FloatTy, 44.0));
auto *LLVMFortyFour =
cast<llvm::ConstantFP>(llvm::ConstantFP::get(LLVMFloatTy, 44.0));
EXPECT_NE(FortyFour, FortyTwo);
EXPECT_EQ(FortyFour, Ctx.getValue(LLVMFortyFour));
// Check get(Type, APFloat).
auto *FortyFive = cast<sandboxir::ConstantFP>(
sandboxir::ConstantFP::get(DoubleTy, APFloat(45.0)));
auto *LLVMFortyFive = cast<llvm::ConstantFP>(
llvm::ConstantFP::get(LLVMDoubleTy, APFloat(45.0)));
EXPECT_EQ(FortyFive, Ctx.getValue(LLVMFortyFive));
// Check get(Type, StringRef).
auto *FortySix = sandboxir::ConstantFP::get(FloatTy, "46.0");
EXPECT_EQ(FortySix, Ctx.getValue(llvm::ConstantFP::get(LLVMFloatTy, "46.0")));
// Check get(APFloat).
auto *FortySeven = sandboxir::ConstantFP::get(APFloat(47.0), Ctx);
EXPECT_EQ(FortySeven, Ctx.getValue(llvm::ConstantFP::get(C, APFloat(47.0))));
// Check getNaN().
{
auto *NaN = sandboxir::ConstantFP::getNaN(FloatTy);
EXPECT_EQ(NaN, Ctx.getValue(llvm::ConstantFP::getNaN(LLVMFloatTy)));
}
{
auto *NaN = sandboxir::ConstantFP::getNaN(FloatTy, /*Negative=*/true);
EXPECT_EQ(NaN, Ctx.getValue(llvm::ConstantFP::getNaN(LLVMFloatTy,
/*Negative=*/true)));
}
{
auto *NaN = sandboxir::ConstantFP::getNaN(FloatTy, /*Negative=*/true,
/*Payload=*/1);
EXPECT_EQ(NaN, Ctx.getValue(llvm::ConstantFP::getNaN(
LLVMFloatTy, /*Negative=*/true, /*Payload=*/1)));
}
// Check getQNaN().
{
auto *QNaN = sandboxir::ConstantFP::getQNaN(FloatTy);
EXPECT_EQ(QNaN, Ctx.getValue(llvm::ConstantFP::getQNaN(LLVMFloatTy)));
}
{
auto *QNaN = sandboxir::ConstantFP::getQNaN(FloatTy, /*Negative=*/true);
EXPECT_EQ(QNaN, Ctx.getValue(llvm::ConstantFP::getQNaN(LLVMFloatTy,
/*Negative=*/true)));
}
{
APInt Payload(1, 1);
auto *QNaN =
sandboxir::ConstantFP::getQNaN(FloatTy, /*Negative=*/true, &Payload);
EXPECT_EQ(QNaN, Ctx.getValue(llvm::ConstantFP::getQNaN(
LLVMFloatTy, /*Negative=*/true, &Payload)));
}
// Check getSNaN().
{
auto *SNaN = sandboxir::ConstantFP::getSNaN(FloatTy);
EXPECT_EQ(SNaN, Ctx.getValue(llvm::ConstantFP::getSNaN(LLVMFloatTy)));
}
{
auto *SNaN = sandboxir::ConstantFP::getSNaN(FloatTy, /*Negative=*/true);
EXPECT_EQ(SNaN, Ctx.getValue(llvm::ConstantFP::getSNaN(LLVMFloatTy,
/*Negative=*/true)));
}
{
APInt Payload(1, 1);
auto *SNaN =
sandboxir::ConstantFP::getSNaN(FloatTy, /*Negative=*/true, &Payload);
EXPECT_EQ(SNaN, Ctx.getValue(llvm::ConstantFP::getSNaN(
LLVMFloatTy, /*Negative=*/true, &Payload)));
}
// Check getZero().
{
auto *Zero = sandboxir::ConstantFP::getZero(FloatTy);
EXPECT_EQ(Zero, Ctx.getValue(llvm::ConstantFP::getZero(LLVMFloatTy)));
}
{
auto *Zero = sandboxir::ConstantFP::getZero(FloatTy, /*Negative=*/true);
EXPECT_EQ(Zero, Ctx.getValue(llvm::ConstantFP::getZero(LLVMFloatTy,
/*Negative=*/true)));
}
// Check getNegativeZero().
auto *NegZero = cast<sandboxir::ConstantFP>(
sandboxir::ConstantFP::getNegativeZero(FloatTy));
EXPECT_EQ(NegZero,
Ctx.getValue(llvm::ConstantFP::getNegativeZero(LLVMFloatTy)));
// Check getInfinity().
{
auto *Inf = sandboxir::ConstantFP::getInfinity(FloatTy);
EXPECT_EQ(Inf, Ctx.getValue(llvm::ConstantFP::getInfinity(LLVMFloatTy)));
}
{
auto *Inf = sandboxir::ConstantFP::getInfinity(FloatTy, /*Negative=*/true);
EXPECT_EQ(Inf, Ctx.getValue(llvm::ConstantFP::getInfinity(
LLVMFloatTy, /*Negative=*/true)));
}
// Check isValueValidForType().
APFloat V(1.1);
EXPECT_EQ(sandboxir::ConstantFP::isValueValidForType(FloatTy, V),
llvm::ConstantFP::isValueValidForType(LLVMFloatTy, V));
// Check getValueAPF().
EXPECT_EQ(FortyFour->getValueAPF(), LLVMFortyFour->getValueAPF());
// Check getValue().
EXPECT_EQ(FortyFour->getValue(), LLVMFortyFour->getValue());
// Check isZero().
EXPECT_EQ(FortyFour->isZero(), LLVMFortyFour->isZero());
EXPECT_TRUE(sandboxir::ConstantFP::getZero(FloatTy));
EXPECT_TRUE(sandboxir::ConstantFP::getZero(FloatTy, /*Negative=*/true));
// Check isNegative().
EXPECT_TRUE(cast<sandboxir::ConstantFP>(
sandboxir::ConstantFP::getZero(FloatTy, /*Negative=*/true))
->isNegative());
// Check isInfinity().
EXPECT_TRUE(
cast<sandboxir::ConstantFP>(sandboxir::ConstantFP::getInfinity(FloatTy))
->isInfinity());
// Check isNaN().
EXPECT_TRUE(
cast<sandboxir::ConstantFP>(sandboxir::ConstantFP::getNaN(FloatTy))
->isNaN());
// Check isExactlyValue(APFloat).
EXPECT_TRUE(NegZero->isExactlyValue(NegZero->getValueAPF()));
// Check isExactlyValue(double).
EXPECT_TRUE(NegZero->isExactlyValue(-0.0));
}
// Tests ConstantArray, ConstantStruct and ConstantVector.
TEST_F(SandboxIRTest, ConstantAggregate) {
// Note: we are using i42 to avoid the creation of ConstantDataVector or
// ConstantDataArray.
parseIR(C, R"IR(
define void @foo() {
%array = extractvalue [2 x i42] [i42 0, i42 1], 0
%struct = extractvalue {i42, i42} {i42 0, i42 1}, 0
%vector = extractelement <2 x i42> <i42 0, i42 1>, i32 0
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 *I0 = &*It++;
auto *I1 = &*It++;
auto *I2 = &*It++;
// Check classof() and creation.
auto *Array = cast<sandboxir::ConstantArray>(I0->getOperand(0));
EXPECT_TRUE(isa<sandboxir::ConstantAggregate>(Array));
auto *Struct = cast<sandboxir::ConstantStruct>(I1->getOperand(0));
EXPECT_TRUE(isa<sandboxir::ConstantAggregate>(Struct));
auto *Vector = cast<sandboxir::ConstantVector>(I2->getOperand(0));
EXPECT_TRUE(isa<sandboxir::ConstantAggregate>(Vector));
auto *ZeroI42 = cast<sandboxir::ConstantInt>(Array->getOperand(0));
auto *OneI42 = cast<sandboxir::ConstantInt>(Array->getOperand(1));
// Check ConstantArray::get(), getType().
auto *NewCA =
sandboxir::ConstantArray::get(Array->getType(), {ZeroI42, OneI42});
EXPECT_EQ(NewCA, Array);
// Check ConstantStruct::get(), getType().
auto *NewCS =
sandboxir::ConstantStruct::get(Struct->getType(), {ZeroI42, OneI42});
EXPECT_EQ(NewCS, Struct);
// Check ConstantStruct::get(...).
auto *NewCS2 =
sandboxir::ConstantStruct::get(Struct->getType(), ZeroI42, OneI42);
EXPECT_EQ(NewCS2, Struct);
// Check ConstantStruct::getAnon(ArayRef).
auto *AnonCS = sandboxir::ConstantStruct::getAnon({ZeroI42, OneI42});
EXPECT_FALSE(cast<sandboxir::StructType>(AnonCS->getType())->isPacked());
auto *AnonCSPacked =
sandboxir::ConstantStruct::getAnon({ZeroI42, OneI42}, /*Packed=*/true);
EXPECT_TRUE(cast<sandboxir::StructType>(AnonCSPacked->getType())->isPacked());
// Check ConstantStruct::getAnon(Ctx, ArrayRef).
auto *AnonCS2 = sandboxir::ConstantStruct::getAnon(Ctx, {ZeroI42, OneI42});
EXPECT_EQ(AnonCS2, AnonCS);
auto *AnonCS2Packed = sandboxir::ConstantStruct::getAnon(
Ctx, {ZeroI42, OneI42}, /*Packed=*/true);
EXPECT_EQ(AnonCS2Packed, AnonCSPacked);
// Check ConstantStruct::getTypeForElements(Ctx, ArrayRef).
auto *StructTy =
sandboxir::ConstantStruct::getTypeForElements(Ctx, {ZeroI42, OneI42});
EXPECT_EQ(StructTy, Struct->getType());
EXPECT_FALSE(StructTy->isPacked());
// Check ConstantStruct::getTypeForElements(Ctx, ArrayRef, Packed).
auto *StructTyPacked = sandboxir::ConstantStruct::getTypeForElements(
Ctx, {ZeroI42, OneI42}, /*Packed=*/true);
EXPECT_TRUE(StructTyPacked->isPacked());
// Check ConstantStruct::getTypeForElements(ArrayRef).
auto *StructTy2 =
sandboxir::ConstantStruct::getTypeForElements(Ctx, {ZeroI42, OneI42});
EXPECT_EQ(StructTy2, Struct->getType());
// Check ConstantStruct::getTypeForElements(ArrayRef, Packed).
auto *StructTy2Packed = sandboxir::ConstantStruct::getTypeForElements(
Ctx, {ZeroI42, OneI42}, /*Packed=*/true);
EXPECT_EQ(StructTy2Packed, StructTyPacked);
}
TEST_F(SandboxIRTest, ConstantAggregateZero) {
parseIR(C, R"IR(
define void @foo(ptr %ptr, {i32, i8} %v1, <2 x i8> %v2) {
%extr0 = extractvalue [2 x i8] zeroinitializer, 0
%extr1 = extractvalue {i32, i8} zeroinitializer, 0
%extr2 = extractelement <2 x i8> zeroinitializer, i32 0
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 *Extr0 = &*It++;
auto *Extr1 = &*It++;
auto *Extr2 = &*It++;
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
auto *Zero32 =
sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0);
auto *Zero8 = sandboxir::ConstantInt::get(sandboxir::Type::getInt8Ty(Ctx), 0);
auto *Int8Ty = sandboxir::Type::getInt8Ty(Ctx);
auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx);
auto *ArrayTy = sandboxir::ArrayType::get(Int8Ty, 2u);
auto *StructTy = sandboxir::StructType::get(Ctx, {Int32Ty, Int8Ty});
auto *VectorTy =
sandboxir::VectorType::get(Int8Ty, ElementCount::getFixed(2u));
// Check creation and classof().
auto *ArrayCAZ = cast<sandboxir::ConstantAggregateZero>(Extr0->getOperand(0));
EXPECT_EQ(ArrayCAZ->getType(), ArrayTy);
auto *StructCAZ =
cast<sandboxir::ConstantAggregateZero>(Extr1->getOperand(0));
EXPECT_EQ(StructCAZ->getType(), StructTy);
auto *VectorCAZ =
cast<sandboxir::ConstantAggregateZero>(Extr2->getOperand(0));
EXPECT_EQ(VectorCAZ->getType(), VectorTy);
// Check get().
auto *SameVectorCAZ =
sandboxir::ConstantAggregateZero::get(sandboxir::VectorType::get(
sandboxir::Type::getInt8Ty(Ctx), ElementCount::getFixed(2)));
EXPECT_EQ(SameVectorCAZ, VectorCAZ); // Should be uniqued.
auto *NewVectorCAZ =
sandboxir::ConstantAggregateZero::get(sandboxir::VectorType::get(
sandboxir::Type::getInt8Ty(Ctx), ElementCount::getFixed(4)));
EXPECT_NE(NewVectorCAZ, VectorCAZ);
// Check getSequentialElement().
auto *SeqElm = VectorCAZ->getSequentialElement();
EXPECT_EQ(SeqElm,
sandboxir::ConstantInt::get(sandboxir::Type::getInt8Ty(Ctx), 0));
// Check getStructElement().
auto *StructElm0 = StructCAZ->getStructElement(0);
auto *StructElm1 = StructCAZ->getStructElement(1);
EXPECT_EQ(StructElm0, Zero32);
EXPECT_EQ(StructElm1, Zero8);
// Check getElementValue(Constant).
EXPECT_EQ(ArrayCAZ->getElementValue(Zero32), Zero8);
EXPECT_EQ(StructCAZ->getElementValue(Zero32), Zero32);
EXPECT_EQ(VectorCAZ->getElementValue(Zero32), Zero8);
// Check getElementValue(unsigned).
EXPECT_EQ(ArrayCAZ->getElementValue(0u), Zero8);
EXPECT_EQ(StructCAZ->getElementValue(0u), Zero32);
EXPECT_EQ(VectorCAZ->getElementValue(0u), Zero8);
// Check getElementCount().
EXPECT_EQ(ArrayCAZ->getElementCount(), ElementCount::getFixed(2));
EXPECT_EQ(NewVectorCAZ->getElementCount(), ElementCount::getFixed(4));
}
TEST_F(SandboxIRTest, ConstantPointerNull) {
parseIR(C, R"IR(
define ptr @foo() {
ret ptr null
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto &BB = *F.begin();
auto It = BB.begin();
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check classof() and creation.
auto *CPNull = cast<sandboxir::ConstantPointerNull>(Ret->getReturnValue());
// Check get().
auto *NewCPNull =
sandboxir::ConstantPointerNull::get(sandboxir::PointerType::get(Ctx, 0u));
EXPECT_EQ(NewCPNull, CPNull);
auto *NewCPNull2 =
sandboxir::ConstantPointerNull::get(sandboxir::PointerType::get(Ctx, 1u));
EXPECT_NE(NewCPNull2, CPNull);
// Check getType().
EXPECT_EQ(CPNull->getType(), sandboxir::PointerType::get(Ctx, 0u));
EXPECT_EQ(NewCPNull2->getType(), sandboxir::PointerType::get(Ctx, 1u));
}
TEST_F(SandboxIRTest, PoisonValue) {
parseIR(C, R"IR(
define void @foo() {
%i0 = add i32 poison, poison
%i1 = add <2 x i32> poison, poison
%i2 = extractvalue {i32, i8} poison, 0
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 *I0 = &*It++;
auto *I1 = &*It++;
auto *I2 = &*It++;
auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx);
auto *Int8Ty = sandboxir::Type::getInt8Ty(Ctx);
auto *Zero32 = sandboxir::ConstantInt::get(Int32Ty, 0u);
auto *One32 = sandboxir::ConstantInt::get(Int32Ty, 1u);
// Check classof() and creation.
auto *Poison = cast<sandboxir::PoisonValue>(I0->getOperand(0));
EXPECT_EQ(Poison->getType(), Int32Ty);
EXPECT_TRUE(isa<sandboxir::UndefValue>(Poison)); // Poison is Undef
// Check get().
auto *NewPoison = sandboxir::PoisonValue::get(Int32Ty);
EXPECT_EQ(NewPoison, Poison);
auto *NewPoison2 =
sandboxir::PoisonValue::get(sandboxir::PointerType::get(Ctx, 0u));
EXPECT_NE(NewPoison2, Poison);
// Check getSequentialElement().
auto *PoisonVector = cast<sandboxir::PoisonValue>(I1->getOperand(0));
auto *SeqElm = PoisonVector->getSequentialElement();
EXPECT_EQ(SeqElm->getType(), Int32Ty);
// Check getStructElement().
auto *PoisonStruct = cast<sandboxir::PoisonValue>(I2->getOperand(0));
auto *StrElm0 = PoisonStruct->getStructElement(0);
auto *StrElm1 = PoisonStruct->getStructElement(1);
EXPECT_EQ(StrElm0->getType(), Int32Ty);
EXPECT_EQ(StrElm1->getType(), Int8Ty);
// Check getElementValue(Constant)
EXPECT_EQ(PoisonStruct->getElementValue(Zero32),
sandboxir::PoisonValue::get(Int32Ty));
EXPECT_EQ(PoisonStruct->getElementValue(One32),
sandboxir::PoisonValue::get(Int8Ty));
// Check getElementValue(unsigned)
EXPECT_EQ(PoisonStruct->getElementValue(0u),
sandboxir::PoisonValue::get(Int32Ty));
EXPECT_EQ(PoisonStruct->getElementValue(1u),
sandboxir::PoisonValue::get(Int8Ty));
}
TEST_F(SandboxIRTest, UndefValue) {
parseIR(C, R"IR(
define void @foo() {
%i0 = add i32 undef, undef
%i1 = add <2 x i32> undef, undef
%i2 = extractvalue {i32, i8} undef, 0
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 *I0 = &*It++;
auto *I1 = &*It++;
auto *I2 = &*It++;
auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx);
auto *Int8Ty = sandboxir::Type::getInt8Ty(Ctx);
auto *Zero32 = sandboxir::ConstantInt::get(Int32Ty, 0u);
auto *One32 = sandboxir::ConstantInt::get(Int32Ty, 1u);
// Check classof() and creation.
auto *Undef = cast<sandboxir::UndefValue>(I0->getOperand(0));
EXPECT_EQ(Undef->getType(), Int32Ty);
EXPECT_FALSE(isa<sandboxir::PoisonValue>(Undef)); // Undef is not Poison
// Check get().
auto *NewUndef = sandboxir::UndefValue::get(Int32Ty);
EXPECT_EQ(NewUndef, Undef);
auto *NewUndef2 =
sandboxir::UndefValue::get(sandboxir::PointerType::get(Ctx, 0u));
EXPECT_NE(NewUndef2, Undef);
// Check getSequentialElement().
auto *UndefVector = cast<sandboxir::UndefValue>(I1->getOperand(0));
auto *SeqElm = UndefVector->getSequentialElement();
EXPECT_EQ(SeqElm->getType(), Int32Ty);
// Check getStructElement().
auto *UndefStruct = cast<sandboxir::UndefValue>(I2->getOperand(0));
auto *StrElm0 = UndefStruct->getStructElement(0);
auto *StrElm1 = UndefStruct->getStructElement(1);
EXPECT_EQ(StrElm0->getType(), Int32Ty);
EXPECT_EQ(StrElm1->getType(), Int8Ty);
// Check getElementValue(Constant)
EXPECT_EQ(UndefStruct->getElementValue(Zero32),
sandboxir::UndefValue::get(Int32Ty));
EXPECT_EQ(UndefStruct->getElementValue(One32),
sandboxir::UndefValue::get(Int8Ty));
// Check getElementValue(unsigned)
EXPECT_EQ(UndefStruct->getElementValue(0u),
sandboxir::UndefValue::get(Int32Ty));
EXPECT_EQ(UndefStruct->getElementValue(1u),
sandboxir::UndefValue::get(Int8Ty));
// Check getNumElements().
EXPECT_EQ(UndefVector->getNumElements(), 2u);
EXPECT_EQ(UndefStruct->getNumElements(), 2u);
}
TEST_F(SandboxIRTest, GlobalValue) {
parseIR(C, R"IR(
declare external void @bar()
define void @foo() {
call void @bar()
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMCall = cast<llvm::CallInst>(&*LLVMIt++);
auto *LLVMGV = cast<llvm::GlobalValue>(LLVMCall->getCalledOperand());
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *Call = cast<sandboxir::CallInst>(&*It++);
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check classof(), creation, getFunction(), getBasicBlock().
auto *GV = cast<sandboxir::GlobalValue>(Call->getCalledOperand());
// Check getAddressSpace().
EXPECT_EQ(GV->getAddressSpace(), LLVMGV->getAddressSpace());
// Check hasGlobalUnnamedAddr().
EXPECT_EQ(GV->hasGlobalUnnamedAddr(), LLVMGV->hasGlobalUnnamedAddr());
// Check hasAtLeastLocalUnnamedAddr().
EXPECT_EQ(GV->hasAtLeastLocalUnnamedAddr(),
LLVMGV->hasAtLeastLocalUnnamedAddr());
// Check getUnnamedAddr().
EXPECT_EQ(GV->getUnnamedAddr(), LLVMGV->getUnnamedAddr());
// Check setUnnamedAddr().
auto OrigUnnamedAddr = GV->getUnnamedAddr();
auto NewUnnamedAddr = sandboxir::GlobalValue::UnnamedAddr::Global;
EXPECT_NE(NewUnnamedAddr, OrigUnnamedAddr);
GV->setUnnamedAddr(NewUnnamedAddr);
EXPECT_EQ(GV->getUnnamedAddr(), NewUnnamedAddr);
GV->setUnnamedAddr(OrigUnnamedAddr);
EXPECT_EQ(GV->getUnnamedAddr(), OrigUnnamedAddr);
// Check getMinUnnamedAddr().
EXPECT_EQ(
sandboxir::GlobalValue::getMinUnnamedAddr(OrigUnnamedAddr,
NewUnnamedAddr),
llvm::GlobalValue::getMinUnnamedAddr(OrigUnnamedAddr, NewUnnamedAddr));
// Check hasComdat().
EXPECT_EQ(GV->hasComdat(), LLVMGV->hasComdat());
// Check getVisibility().
EXPECT_EQ(GV->getVisibility(), LLVMGV->getVisibility());
// Check hasDefaultVisibility().
EXPECT_EQ(GV->hasDefaultVisibility(), LLVMGV->hasDefaultVisibility());
// Check hasHiddenVisibility().
EXPECT_EQ(GV->hasHiddenVisibility(), LLVMGV->hasHiddenVisibility());
// Check hasProtectedVisibility().
EXPECT_EQ(GV->hasProtectedVisibility(), LLVMGV->hasProtectedVisibility());
// Check setVisibility().
auto OrigVisibility = GV->getVisibility();
auto NewVisibility =
sandboxir::GlobalValue::VisibilityTypes::ProtectedVisibility;
EXPECT_NE(NewVisibility, OrigVisibility);
GV->setVisibility(NewVisibility);
EXPECT_EQ(GV->getVisibility(), NewVisibility);
GV->setVisibility(OrigVisibility);
EXPECT_EQ(GV->getVisibility(), OrigVisibility);
}
TEST_F(SandboxIRTest, GlobalObject) {
parseIR(C, R"IR(
declare external void @bar()
define void @foo() {
call void @bar()
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMCall = cast<llvm::CallInst>(&*LLVMIt++);
auto *LLVMGO = cast<llvm::GlobalObject>(LLVMCall->getCalledOperand());
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *Call = cast<sandboxir::CallInst>(&*It++);
// Check classof(), creation.
auto *GO = cast<sandboxir::GlobalObject>(Call->getCalledOperand());
// Check getAlignment().
EXPECT_EQ(GO->getAlignment(), LLVMGO->getAlignment());
// Check getAlign().
EXPECT_EQ(GO->getAlign(), LLVMGO->getAlign());
// Check setAlignment().
auto OrigMaybeAlign = GO->getAlign();
auto NewMaybeAlign = MaybeAlign(128);
EXPECT_NE(NewMaybeAlign, OrigMaybeAlign);
GO->setAlignment(NewMaybeAlign);
EXPECT_EQ(GO->getAlign(), NewMaybeAlign);
GO->setAlignment(OrigMaybeAlign);
EXPECT_EQ(GO->getAlign(), OrigMaybeAlign);
// Check getGlobalObjectSubClassData().
EXPECT_EQ(GO->getGlobalObjectSubClassData(),
LLVMGO->getGlobalObjectSubClassData());
// Check setGlobalObjectSubClassData().
auto OrigGOSCD = GO->getGlobalObjectSubClassData();
auto NewGOSCD = 1u;
EXPECT_NE(NewGOSCD, OrigGOSCD);
GO->setGlobalObjectSubClassData(NewGOSCD);
EXPECT_EQ(GO->getGlobalObjectSubClassData(), NewGOSCD);
GO->setGlobalObjectSubClassData(OrigGOSCD);
EXPECT_EQ(GO->getGlobalObjectSubClassData(), OrigGOSCD);
// Check hasSection().
EXPECT_EQ(GO->hasSection(), LLVMGO->hasSection());
// Check getSection().
EXPECT_EQ(GO->getSection(), LLVMGO->getSection());
// Check setSection().
auto OrigSection = GO->getSection();
auto NewSection = ".some_section";
EXPECT_NE(NewSection, OrigSection);
GO->setSection(NewSection);
EXPECT_EQ(GO->getSection(), NewSection);
GO->setSection(OrigSection);
EXPECT_EQ(GO->getSection(), OrigSection);
// Check hasComdat().
EXPECT_EQ(GO->hasComdat(), LLVMGO->hasComdat());
// Check getVCallVisibility().
EXPECT_EQ(GO->getVCallVisibility(), LLVMGO->getVCallVisibility());
// Check canIncreaseAlignment().
EXPECT_EQ(GO->canIncreaseAlignment(), LLVMGO->canIncreaseAlignment());
}
TEST_F(SandboxIRTest, GlobalIFunc) {
parseIR(C, R"IR(
declare external void @bar()
@ifunc0 = ifunc void(), ptr @foo
@ifunc1 = ifunc void(), ptr @foo
define void @foo() {
call void @ifunc0()
call void @ifunc1()
call void @bar()
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMCall0 = cast<llvm::CallInst>(&*LLVMIt++);
auto *LLVMIFunc0 = cast<llvm::GlobalIFunc>(LLVMCall0->getCalledOperand());
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 *CallBar = cast<sandboxir::CallInst>(&*It++);
// Check classof(), creation.
auto *IFunc0 = cast<sandboxir::GlobalIFunc>(Call0->getCalledOperand());
auto *IFunc1 = cast<sandboxir::GlobalIFunc>(Call1->getCalledOperand());
auto *Bar = cast<sandboxir::Function>(CallBar->getCalledOperand());
// Check getIterator().
{
auto It0 = IFunc0->getIterator();
auto It1 = IFunc1->getIterator();
EXPECT_EQ(&*It0, IFunc0);
EXPECT_EQ(&*It1, IFunc1);
EXPECT_EQ(std::next(It0), It1);
EXPECT_EQ(std::prev(It1), It0);
EXPECT_EQ(&*std::next(It0), IFunc1);
EXPECT_EQ(&*std::prev(It1), IFunc0);
}
// Check getReverseIterator().
{
auto RevIt0 = IFunc0->getReverseIterator();
auto RevIt1 = IFunc1->getReverseIterator();
EXPECT_EQ(&*RevIt0, IFunc0);
EXPECT_EQ(&*RevIt1, IFunc1);
EXPECT_EQ(std::prev(RevIt0), RevIt1);
EXPECT_EQ(std::next(RevIt1), RevIt0);
EXPECT_EQ(&*std::prev(RevIt0), IFunc1);
EXPECT_EQ(&*std::next(RevIt1), IFunc0);
}
// Check setResolver(), getResolver().
EXPECT_EQ(IFunc0->getResolver(), Ctx.getValue(LLVMIFunc0->getResolver()));
auto *OrigResolver = IFunc0->getResolver();
auto *NewResolver = Bar;
EXPECT_NE(NewResolver, OrigResolver);
IFunc0->setResolver(NewResolver);
EXPECT_EQ(IFunc0->getResolver(), NewResolver);
IFunc0->setResolver(OrigResolver);
EXPECT_EQ(IFunc0->getResolver(), OrigResolver);
// Check getResolverFunction().
EXPECT_EQ(IFunc0->getResolverFunction(),
Ctx.getValue(LLVMIFunc0->getResolverFunction()));
// Check isValidLinkage().
for (auto L :
{GlobalValue::ExternalLinkage, GlobalValue::AvailableExternallyLinkage,
GlobalValue::LinkOnceAnyLinkage, GlobalValue::LinkOnceODRLinkage,
GlobalValue::WeakAnyLinkage, GlobalValue::WeakODRLinkage,
GlobalValue::AppendingLinkage, GlobalValue::InternalLinkage,
GlobalValue::PrivateLinkage, GlobalValue::ExternalWeakLinkage,
GlobalValue::CommonLinkage}) {
EXPECT_EQ(IFunc0->isValidLinkage(L), LLVMIFunc0->isValidLinkage(L));
}
}
TEST_F(SandboxIRTest, GlobalVariable) {
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");
auto *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMLd0 = cast<llvm::LoadInst>(&*LLVMIt++);
auto *LLVMGV0 = cast<llvm::GlobalVariable>(LLVMLd0->getPointerOperand());
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 getIterator().
{
auto It0 = GV0->getIterator();
auto It1 = GV1->getIterator();
EXPECT_EQ(&*It0, GV0);
EXPECT_EQ(&*It1, GV1);
EXPECT_EQ(std::next(It0), It1);
EXPECT_EQ(std::prev(It1), It0);
EXPECT_EQ(&*std::next(It0), GV1);
EXPECT_EQ(&*std::prev(It1), GV0);
}
// Check getReverseIterator().
{
auto RevIt0 = GV0->getReverseIterator();
auto RevIt1 = GV1->getReverseIterator();
EXPECT_EQ(&*RevIt0, GV0);
EXPECT_EQ(&*RevIt1, GV1);
EXPECT_EQ(std::prev(RevIt0), RevIt1);
EXPECT_EQ(std::next(RevIt1), RevIt0);
EXPECT_EQ(&*std::prev(RevIt0), GV1);
EXPECT_EQ(&*std::next(RevIt1), GV0);
}
// Check hasInitializer().
EXPECT_EQ(GV0->hasInitializer(), LLVMGV0->hasInitializer());
// Check hasDefinitiveInitializer().
EXPECT_EQ(GV0->hasDefinitiveInitializer(),
LLVMGV0->hasDefinitiveInitializer());
// Check hasUniqueInitializer().
EXPECT_EQ(GV0->hasUniqueInitializer(), LLVMGV0->hasUniqueInitializer());
// Check getInitializer().
EXPECT_EQ(GV0->getInitializer(), Ctx.getValue(LLVMGV0->getInitializer()));
// Check setInitializer().
auto *OrigInitializer = GV0->getInitializer();
auto *NewInitializer = GV1->getInitializer();
EXPECT_NE(NewInitializer, OrigInitializer);
GV0->setInitializer(NewInitializer);
EXPECT_EQ(GV0->getInitializer(), NewInitializer);
GV0->setInitializer(OrigInitializer);
EXPECT_EQ(GV0->getInitializer(), OrigInitializer);
// Check isConstant().
EXPECT_EQ(GV0->isConstant(), LLVMGV0->isConstant());
// Check setConstant().
bool OrigIsConstant = GV0->isConstant();
bool NewIsConstant = !OrigIsConstant;
GV0->setConstant(NewIsConstant);
EXPECT_EQ(GV0->isConstant(), NewIsConstant);
GV0->setConstant(OrigIsConstant);
EXPECT_EQ(GV0->isConstant(), OrigIsConstant);
// Check isExternallyInitialized().
EXPECT_EQ(GV0->isExternallyInitialized(), LLVMGV0->isExternallyInitialized());
// Check setExternallyInitialized().
bool OrigIsExtInit = GV0->isExternallyInitialized();
bool NewIsExtInit = !OrigIsExtInit;
GV0->setExternallyInitialized(NewIsExtInit);
EXPECT_EQ(GV0->isExternallyInitialized(), NewIsExtInit);
GV0->setExternallyInitialized(OrigIsExtInit);
EXPECT_EQ(GV0->isExternallyInitialized(), OrigIsExtInit);
for (auto KindIdx : seq<int>(0, Attribute::AttrKind::EndAttrKinds)) {
// Check hasAttribute(AttrKind).
auto Kind = static_cast<Attribute::AttrKind>(KindIdx);
EXPECT_EQ(GV0->hasAttribute(Kind), LLVMGV0->hasAttribute(Kind));
// Check hasAttribute(StringRef).
StringRef KindStr = Attribute::getNameFromAttrKind(Kind);
EXPECT_EQ(GV0->hasAttribute(KindStr), LLVMGV0->hasAttribute(KindStr));
}
// Check hasAttributes().
EXPECT_EQ(GV0->hasAttributes(), LLVMGV0->hasAttributes());
for (auto KindIdx : seq<int>(0, Attribute::AttrKind::EndAttrKinds)) {
// Check getAttribute(AttrKind).
auto Kind = static_cast<Attribute::AttrKind>(KindIdx);
EXPECT_EQ(GV0->getAttribute(Kind), LLVMGV0->getAttribute(Kind));
// Check getAttribute(StringRef).
StringRef KindStr = Attribute::getNameFromAttrKind(Kind);
EXPECT_EQ(GV0->getAttribute(KindStr), LLVMGV0->getAttribute(KindStr));
}
// Check getAttributes().
EXPECT_EQ(GV0->getAttributes(), LLVMGV0->getAttributes());
// Check getAttributesAsList().
EXPECT_THAT(GV0->getAttributesAsList(0u),
testing::ContainerEq(LLVMGV0->getAttributesAsList(0u)));
// Check hasImplicitSection().
EXPECT_EQ(GV0->hasImplicitSection(), LLVMGV0->hasImplicitSection());
// Check getCodeModelRaw().
EXPECT_EQ(GV0->getCodeModelRaw(), LLVMGV0->getCodeModelRaw());
// Check getCodeModel().
EXPECT_EQ(GV0->getCodeModel(), LLVMGV0->getCodeModel());
}
TEST_F(SandboxIRTest, GlobalAlias) {
parseIR(C, R"IR(
@alias0 = dso_local alias void(), ptr @foo
@alias1 = dso_local alias void(), ptr @foo
declare void @bar();
define void @foo() {
call void @alias0()
call void @alias1()
call void @bar()
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMCall0 = cast<llvm::CallInst>(&*LLVMIt++);
auto *LLVMAlias0 = cast<llvm::GlobalAlias>(LLVMCall0->getCalledOperand());
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 *CallBar = cast<sandboxir::CallInst>(&*It++);
auto *CalleeBar = cast<sandboxir::Constant>(CallBar->getCalledOperand());
// Check classof(), creation.
auto *Alias0 = cast<sandboxir::GlobalAlias>(Call0->getCalledOperand());
auto *Alias1 = cast<sandboxir::GlobalAlias>(Call1->getCalledOperand());
// Check getIterator().
{
auto It0 = Alias0->getIterator();
auto It1 = Alias1->getIterator();
EXPECT_EQ(&*It0, Alias0);
EXPECT_EQ(&*It1, Alias1);
EXPECT_EQ(std::next(It0), It1);
EXPECT_EQ(std::prev(It1), It0);
EXPECT_EQ(&*std::next(It0), Alias1);
EXPECT_EQ(&*std::prev(It1), Alias0);
}
// Check getReverseIterator().
{
auto RevIt0 = Alias0->getReverseIterator();
auto RevIt1 = Alias1->getReverseIterator();
EXPECT_EQ(&*RevIt0, Alias0);
EXPECT_EQ(&*RevIt1, Alias1);
EXPECT_EQ(std::prev(RevIt0), RevIt1);
EXPECT_EQ(std::next(RevIt1), RevIt0);
EXPECT_EQ(&*std::prev(RevIt0), Alias1);
EXPECT_EQ(&*std::next(RevIt1), Alias0);
}
// Check getAliasee().
EXPECT_EQ(Alias0->getAliasee(), Ctx.getValue(LLVMAlias0->getAliasee()));
// Check setAliasee().
auto *OrigAliasee = Alias0->getAliasee();
auto *NewAliasee = CalleeBar;
EXPECT_NE(NewAliasee, OrigAliasee);
Alias0->setAliasee(NewAliasee);
EXPECT_EQ(Alias0->getAliasee(), NewAliasee);
Alias0->setAliasee(OrigAliasee);
EXPECT_EQ(Alias0->getAliasee(), OrigAliasee);
// Check getAliaseeObject().
EXPECT_EQ(Alias0->getAliaseeObject(),
Ctx.getValue(LLVMAlias0->getAliaseeObject()));
}
TEST_F(SandboxIRTest, NoCFIValue) {
parseIR(C, R"IR(
define void @foo() {
call void no_cfi @foo()
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 *Call = cast<sandboxir::CallInst>(&*It++);
// Check classof(), creation.
auto *NoCFI = cast<sandboxir::NoCFIValue>(Call->getCalledOperand());
// Check get().
auto *NewNoCFI = sandboxir::NoCFIValue::get(&F);
EXPECT_EQ(NewNoCFI, NoCFI);
// Check getGlobalValue().
EXPECT_EQ(NoCFI->getGlobalValue(), &F);
// Check getType().
EXPECT_EQ(NoCFI->getType(), F.getType());
}
TEST_F(SandboxIRTest, ConstantPtrAuth) {
parseIR(C, R"IR(
define ptr @foo() {
ret ptr ptrauth (ptr @foo, i32 2, i64 1234)
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB = &*LLVMF.begin();
auto *LLVMRet = cast<llvm::ReturnInst>(&*LLVMBB->begin());
auto *LLVMPtrAuth = cast<llvm::ConstantPtrAuth>(LLVMRet->getReturnValue());
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check classof(), creation.
auto *PtrAuth = cast<sandboxir::ConstantPtrAuth>(Ret->getReturnValue());
// Check get(), getKey(), getDiscriminator(), getAddrDiscriminator().
auto *NewPtrAuth = sandboxir::ConstantPtrAuth::get(
&F, PtrAuth->getKey(), PtrAuth->getDiscriminator(),
PtrAuth->getAddrDiscriminator());
EXPECT_EQ(NewPtrAuth, PtrAuth);
// Check hasAddressDiscriminator().
EXPECT_EQ(PtrAuth->hasAddressDiscriminator(),
LLVMPtrAuth->hasAddressDiscriminator());
// Check hasSpecialAddressDiscriminator().
EXPECT_EQ(PtrAuth->hasSpecialAddressDiscriminator(0u),
LLVMPtrAuth->hasSpecialAddressDiscriminator(0u));
// Check isKnownCompatibleWith().
const DataLayout &DL = M->getDataLayout();
EXPECT_TRUE(PtrAuth->isKnownCompatibleWith(PtrAuth->getKey(),
PtrAuth->getDiscriminator(), DL));
// Check getWithSameSchema().
EXPECT_EQ(PtrAuth->getWithSameSchema(&F), PtrAuth);
}
TEST_F(SandboxIRTest, ConstantExpr) {
parseIR(C, R"IR(
define i32 @foo() {
ret i32 ptrtoint (ptr @foo to i32)
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check classof(), creation.
[[maybe_unused]] auto *ConstExpr =
cast<sandboxir::ConstantExpr>(Ret->getReturnValue());
}
TEST_F(SandboxIRTest, BlockAddress) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
bb0:
store ptr blockaddress(@foo, %bb0), ptr %ptr
ret void
bb1:
ret void
bb2:
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
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 *BB2 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb2")));
auto It = BB0->begin();
auto *SI = cast<sandboxir::StoreInst>(&*It++);
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check classof(), creation, getFunction(), getBasicBlock().
auto *BB0Addr = cast<sandboxir::BlockAddress>(SI->getValueOperand());
EXPECT_EQ(BB0Addr->getBasicBlock(), BB0);
EXPECT_EQ(BB0Addr->getFunction(), &F);
// Check get(F, BB).
auto *NewBB0Addr = sandboxir::BlockAddress::get(&F, BB0);
EXPECT_EQ(NewBB0Addr, BB0Addr);
// Check get(BB).
auto *NewBB0Addr2 = sandboxir::BlockAddress::get(BB0);
EXPECT_EQ(NewBB0Addr2, BB0Addr);
auto *BB1Addr = sandboxir::BlockAddress::get(BB1);
EXPECT_EQ(BB1Addr->getBasicBlock(), BB1);
EXPECT_NE(BB1Addr, BB0Addr);
// Check lookup().
auto *LookupBB0Addr = sandboxir::BlockAddress::lookup(BB0);
EXPECT_EQ(LookupBB0Addr, BB0Addr);
auto *LookupBB1Addr = sandboxir::BlockAddress::lookup(BB1);
EXPECT_EQ(LookupBB1Addr, BB1Addr);
auto *LookupBB2Addr = sandboxir::BlockAddress::lookup(BB2);
EXPECT_EQ(LookupBB2Addr, nullptr);
}
TEST_F(SandboxIRTest, DSOLocalEquivalent) {
parseIR(C, R"IR(
declare void @bar()
define void @foo() {
call void dso_local_equivalent @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 *CI = cast<sandboxir::CallInst>(&*It++);
// Check classof().
auto *DSOLE = cast<sandboxir::DSOLocalEquivalent>(CI->getCalledOperand());
// Check getGlobalValue().
auto *GV = DSOLE->getGlobalValue();
// Check get().
auto *NewDSOLE = sandboxir::DSOLocalEquivalent::get(GV);
EXPECT_EQ(NewDSOLE, DSOLE);
}
TEST_F(SandboxIRTest, ConstantTokenNone) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
bb0:
%cs = catchswitch within none [label %handler] unwind to caller
handler:
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 *CS = cast<sandboxir::CatchSwitchInst>(&*BB0->begin());
// Check classof(), creation, getFunction(), getBasicBlock().
auto *CTN = cast<sandboxir::ConstantTokenNone>(CS->getParentPad());
// Check get().
auto *NewCTN = sandboxir::ConstantTokenNone::get(Ctx);
EXPECT_EQ(NewCTN, CTN);
}
TEST_F(SandboxIRTest, Use) {
parseIR(C, R"IR(
define i32 @foo(i32 %v0, i32 %v1) {
%add0 = add i32 %v0, %v1
ret i32 %add0
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
BasicBlock *LLVMBB = &*LLVMF.begin();
auto LLVMBBIt = LLVMBB->begin();
Instruction *LLVMI0 = &*LLVMBBIt++;
Instruction *LLVMRet = &*LLVMBBIt++;
Argument *LLVMArg0 = LLVMF.getArg(0);
Argument *LLVMArg1 = LLVMF.getArg(1);
auto &F = *Ctx.createFunction(&LLVMF);
auto &BB = *F.begin();
auto *Arg0 = F.getArg(0);
auto *Arg1 = F.getArg(1);
auto It = BB.begin();
auto *I0 = &*It++;
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
SmallVector<sandboxir::Argument *> Args{Arg0, Arg1};
unsigned OpIdx = 0;
for (sandboxir::Use Use : I0->operands()) {
// Check Use.getOperandNo().
EXPECT_EQ(Use.getOperandNo(), OpIdx);
// Check Use.getUser().
EXPECT_EQ(Use.getUser(), I0);
// Check Use.getContext().
EXPECT_EQ(Use.getContext(), &Ctx);
// Check Use.get().
sandboxir::Value *Op = Use.get();
EXPECT_EQ(Op, Ctx.getValue(LLVMI0->getOperand(OpIdx)));
// Check Use.getUser().
EXPECT_EQ(Use.getUser(), I0);
// Check implicit cast to Value.
sandboxir::Value *Cast = Use;
EXPECT_EQ(Cast, Op);
// Check that Use points to the correct operand.
EXPECT_EQ(Op, Args[OpIdx]);
// Check getOperand().
EXPECT_EQ(Op, I0->getOperand(OpIdx));
// Check getOperandUse().
EXPECT_EQ(Use, I0->getOperandUse(OpIdx));
++OpIdx;
}
EXPECT_EQ(OpIdx, 2u);
// Check Use.operator==() and Use.operator!=().
sandboxir::Use UseA = I0->getOperandUse(0);
sandboxir::Use UseB = I0->getOperandUse(0);
EXPECT_TRUE(UseA == UseB);
EXPECT_FALSE(UseA != UseB);
// Check getNumOperands().
EXPECT_EQ(I0->getNumOperands(), 2u);
EXPECT_EQ(Ret->getNumOperands(), 1u);
EXPECT_EQ(Ret->getOperand(0), I0);
#ifndef NDEBUG
// Check Use.dump(()
std::string Buff;
raw_string_ostream BS(Buff);
BS << "\n";
I0->getOperandUse(0).dumpOS(BS);
EXPECT_EQ(Buff, R"IR(
Def: i32 %v0 ; SB2. (Argument)
User: %add0 = add i32 %v0, %v1 ; SB5. (BinaryOperator)
OperandNo: 0
)IR");
#endif // NDEBUG
// Check Value.user_begin().
sandboxir::Value::user_iterator UIt = I0->user_begin();
sandboxir::User *U = *UIt;
EXPECT_EQ(U, Ret);
// Check Value.uses().
EXPECT_EQ(range_size(I0->uses()), 1u);
EXPECT_EQ((*I0->uses().begin()).getUser(), Ret);
// Check Value.users().
EXPECT_EQ(range_size(I0->users()), 1u);
EXPECT_EQ(*I0->users().begin(), Ret);
// Check Value.getNumUses().
EXPECT_EQ(I0->getNumUses(), 1u);
// Check Value.hasNUsesOrMore().
EXPECT_TRUE(I0->hasNUsesOrMore(0u));
EXPECT_TRUE(I0->hasNUsesOrMore(1u));
EXPECT_FALSE(I0->hasNUsesOrMore(2u));
// Check Value.hasNUses().
EXPECT_FALSE(I0->hasNUses(0u));
EXPECT_TRUE(I0->hasNUses(1u));
EXPECT_FALSE(I0->hasNUses(2u));
// Check Value.getExpectedType
// Check User.setOperand().
Ret->setOperand(0, Arg0);
EXPECT_EQ(Ret->getOperand(0), Arg0);
EXPECT_EQ(Ret->getOperandUse(0).get(), Arg0);
EXPECT_EQ(LLVMRet->getOperand(0), LLVMArg0);
Ret->setOperand(0, Arg1);
EXPECT_EQ(Ret->getOperand(0), Arg1);
EXPECT_EQ(Ret->getOperandUse(0).get(), Arg1);
EXPECT_EQ(LLVMRet->getOperand(0), LLVMArg1);
}
TEST_F(SandboxIRTest, RUOW) {
parseIR(C, R"IR(
declare void @bar0()
declare void @bar1()
@glob0 = global ptr @bar0
@glob1 = global ptr @bar1
define i32 @foo(i32 %arg0, i32 %arg1) {
%add0 = add i32 %arg0, %arg1
%gep1 = getelementptr i8, ptr @glob0, i32 1
%gep2 = getelementptr i8, ptr @glob1, i32 1
ret i32 %add0
}
)IR");
llvm::Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto &BB = *F.begin();
auto *Arg0 = F.getArg(0);
auto *Arg1 = F.getArg(1);
auto It = BB.begin();
auto *I0 = &*It++;
auto *I1 = &*It++;
auto *I2 = &*It++;
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
bool Replaced;
// Try to replace an operand that doesn't match.
Replaced = I0->replaceUsesOfWith(Ret, Arg1);
EXPECT_FALSE(Replaced);
EXPECT_EQ(I0->getOperand(0), Arg0);
EXPECT_EQ(I0->getOperand(1), Arg1);
// Replace I0 operands when operands differ.
Replaced = I0->replaceUsesOfWith(Arg0, Arg1);
EXPECT_TRUE(Replaced);
EXPECT_EQ(I0->getOperand(0), Arg1);
EXPECT_EQ(I0->getOperand(1), Arg1);
// Replace I0 operands when operands are the same.
Replaced = I0->replaceUsesOfWith(Arg1, Arg0);
EXPECT_TRUE(Replaced);
EXPECT_EQ(I0->getOperand(0), Arg0);
EXPECT_EQ(I0->getOperand(1), Arg0);
// Replace Ret operand.
Replaced = Ret->replaceUsesOfWith(I0, Arg0);
EXPECT_TRUE(Replaced);
EXPECT_EQ(Ret->getOperand(0), Arg0);
// Check RAUW on constant.
auto *Glob0 = cast<sandboxir::Constant>(I1->getOperand(0));
auto *Glob1 = cast<sandboxir::Constant>(I2->getOperand(0));
auto *Glob0Op = Glob0->getOperand(0);
Glob0->replaceUsesOfWith(Glob0Op, Glob1);
EXPECT_EQ(Glob0->getOperand(0), Glob1);
}
TEST_F(SandboxIRTest, RAUW_RUWIf) {
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++;
// 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);
St0->setOperand(0, Ld0);
St1->setOperand(0, Ld0);
// Check RUWIf user == St0.
Ld0->replaceUsesWithIf(
Ld1, [St0](const sandboxir::Use &Use) { return Use.getUser() == St0; });
EXPECT_EQ(St0->getOperand(0), Ld1);
EXPECT_EQ(St1->getOperand(0), Ld0);
St0->setOperand(0, Ld0);
// Check RUWIf user == St1.
Ld0->replaceUsesWithIf(
Ld1, [St1](const sandboxir::Use &Use) { return Use.getUser() == St1; });
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld1);
St1->setOperand(0, Ld0);
// Check RAUW.
Ld1->replaceAllUsesWith(Ld0);
EXPECT_EQ(St0->getOperand(0), Ld0);
EXPECT_EQ(St1->getOperand(0), Ld0);
}
// Check that the operands/users are counted correctly.
// I1
// / \
// \ /
// I2
TEST_F(SandboxIRTest, DuplicateUses) {
parseIR(C, R"IR(
define void @foo(i8 %v) {
%I1 = add i8 %v, %v
%I2 = add i8 %I1, %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 *I1 = &*It++;
auto *I2 = &*It++;
EXPECT_EQ(range_size(I1->users()), 2u);
EXPECT_EQ(range_size(I2->operands()), 2u);
}
TEST_F(SandboxIRTest, Function) {
parseIR(C, R"IR(
define void @foo0(i32 %arg0, i32 %arg1) {
bb0:
br label %bb1
bb1:
ret void
}
define void @foo1() {
ret void
}
)IR");
llvm::Function *LLVMF0 = &*M->getFunction("foo0");
llvm::Function *LLVMF1 = &*M->getFunction("foo1");
llvm::Argument *LLVMArg0 = LLVMF0->getArg(0);
llvm::Argument *LLVMArg1 = LLVMF0->getArg(1);
sandboxir::Context Ctx(C);
sandboxir::Function *F0 = Ctx.createFunction(LLVMF0);
sandboxir::Function *F1 = Ctx.createFunction(LLVMF1);
// Check getIterator().
{
auto It0 = F0->getIterator();
auto It1 = F1->getIterator();
EXPECT_EQ(&*It0, F0);
EXPECT_EQ(&*It1, F1);
EXPECT_EQ(std::next(It0), It1);
EXPECT_EQ(std::prev(It1), It0);
EXPECT_EQ(&*std::next(It0), F1);
EXPECT_EQ(&*std::prev(It1), F0);
}
// Check getReverseIterator().
{
auto RevIt0 = F0->getReverseIterator();
auto RevIt1 = F1->getReverseIterator();
EXPECT_EQ(&*RevIt0, F0);
EXPECT_EQ(&*RevIt1, F1);
EXPECT_EQ(std::prev(RevIt0), RevIt1);
EXPECT_EQ(std::next(RevIt1), RevIt0);
EXPECT_EQ(&*std::prev(RevIt0), F1);
EXPECT_EQ(&*std::next(RevIt1), F0);
}
// Check F arguments
EXPECT_EQ(F0->arg_size(), 2u);
EXPECT_FALSE(F0->arg_empty());
EXPECT_EQ(F0->getArg(0), Ctx.getValue(LLVMArg0));
EXPECT_EQ(F0->getArg(1), Ctx.getValue(LLVMArg1));
// Check F.begin(), F.end(), Function::iterator
llvm::BasicBlock *LLVMBB = &*LLVMF0->begin();
for (sandboxir::BasicBlock &BB : *F0) {
EXPECT_EQ(&BB, Ctx.getValue(LLVMBB));
LLVMBB = LLVMBB->getNextNode();
}
#ifndef NDEBUG
{
// Check F.dumpNameAndArgs()
std::string Buff;
raw_string_ostream BS(Buff);
F0->dumpNameAndArgs(BS);
EXPECT_EQ(Buff, "void @foo0(i32 %arg0, i32 %arg1)");
}
{
// Check F.dump()
std::string Buff;
raw_string_ostream BS(Buff);
BS << "\n";
F0->dumpOS(BS);
EXPECT_EQ(Buff, R"IR(
void @foo0(i32 %arg0, i32 %arg1) {
bb0:
br label %bb1 ; SB4. (Br)
bb1:
ret void ; SB6. (Ret)
}
)IR");
}
#endif // NDEBUG
}
TEST_F(SandboxIRTest, Module) {
parseIR(C, R"IR(
@glob0 = global i32 42
@glob1 = global i32 43
@internal0 = internal global i32 42
@const0 = constant i32 42
@alias0 = dso_local alias void(), ptr @foo
@ifunc = ifunc void(), ptr @foo
define void @foo() {
ret void
}
define void @bar() {
ret void
}
)IR");
llvm::Module *LLVMM = &*M;
llvm::Function *LLVMFFoo = &*M->getFunction("foo");
llvm::Function *LLVMFBar = &*M->getFunction("bar");
sandboxir::Context Ctx(C);
auto *M = Ctx.createModule(LLVMM);
// Check getContext().
EXPECT_EQ(&M->getContext(), &Ctx);
// Check getFunction().
auto *FFoo = M->getFunction("foo");
auto *FBar = M->getFunction("bar");
EXPECT_EQ(FFoo, Ctx.getValue(LLVMFFoo));
EXPECT_EQ(FBar, Ctx.getValue(LLVMFBar));
// Check getDataLayout().
EXPECT_EQ(&M->getDataLayout(), &LLVMM->getDataLayout());
// Check getSourceFileName().
EXPECT_EQ(M->getSourceFileName(), LLVMM->getSourceFileName());
// Check getGlobalVariable().
for (const char *Name : {"global0", "global1", "internal0"})
EXPECT_EQ(M->getGlobalVariable(Name),
Ctx.getValue(LLVMM->getGlobalVariable(Name)));
// Check getGlobalVariable(AllowInternal).
{
auto *Internal0 = M->getGlobalVariable("internal0", /*AllowInternal=*/true);
EXPECT_TRUE(Internal0 != nullptr);
EXPECT_EQ(Internal0, Ctx.getValue(LLVMM->getNamedGlobal("internal0")));
}
// Check getNamedGlobal().
{
auto *Internal = M->getNamedGlobal("internal0");
EXPECT_TRUE(Internal != nullptr);
EXPECT_EQ(Internal, Ctx.getValue(LLVMM->getNamedGlobal("internal0")));
}
// Check getNamedAlias().
auto *Alias0 = M->getNamedAlias("alias0");
EXPECT_EQ(Alias0, Ctx.getValue(LLVMM->getNamedAlias("alias0")));
EXPECT_EQ(M->getNamedAlias("aliasFOO"), nullptr);
// Check getNamedIFunc().
auto *IFunc0 = M->getNamedIFunc("ifunc0");
EXPECT_EQ(IFunc0, Ctx.getValue(LLVMM->getNamedAlias("ifunc0")));
EXPECT_EQ(M->getNamedIFunc("ifuncFOO"), nullptr);
}
TEST_F(SandboxIRTest, BasicBlock) {
parseIR(C, R"IR(
define void @foo(i32 %v1) {
bb0:
br label %bb1
bb1:
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
llvm::BasicBlock *LLVMBB0 = getBasicBlockByName(*LLVMF, "bb0");
llvm::BasicBlock *LLVMBB1 = getBasicBlockByName(*LLVMF, "bb1");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto &BB0 = cast<sandboxir::BasicBlock>(*Ctx.getValue(LLVMBB0));
auto &BB1 = cast<sandboxir::BasicBlock>(*Ctx.getValue(LLVMBB1));
// Check BB::classof()
EXPECT_TRUE(isa<sandboxir::Value>(BB0));
EXPECT_FALSE(isa<sandboxir::User>(BB0));
EXPECT_FALSE(isa<sandboxir::Instruction>(BB0));
EXPECT_FALSE(isa<sandboxir::Constant>(BB0));
EXPECT_FALSE(isa<sandboxir::Argument>(BB0));
// Check BB.getParent()
EXPECT_EQ(BB0.getParent(), F);
EXPECT_EQ(BB1.getParent(), F);
// Check BBIterator, BB.begin(), BB.end().
llvm::Instruction *LLVMI = &*LLVMBB0->begin();
for (sandboxir::Instruction &I : BB0) {
EXPECT_EQ(&I, Ctx.getValue(LLVMI));
LLVMI = LLVMI->getNextNode();
// Check getNodeParent().
EXPECT_EQ(I.getIterator().getNodeParent(), &BB0);
}
LLVMI = &*LLVMBB1->begin();
for (sandboxir::Instruction &I : BB1) {
EXPECT_EQ(&I, Ctx.getValue(LLVMI));
LLVMI = LLVMI->getNextNode();
}
// Check NodeParent() for BB::end().
EXPECT_EQ(BB0.end().getNodeParent(), &BB0);
// Check BB.getTerminator()
EXPECT_EQ(BB0.getTerminator(), Ctx.getValue(LLVMBB0->getTerminator()));
EXPECT_EQ(BB1.getTerminator(), Ctx.getValue(LLVMBB1->getTerminator()));
// Check BB.rbegin(), BB.rend()
EXPECT_EQ(&*BB0.rbegin(), BB0.getTerminator());
EXPECT_EQ(&*std::prev(BB0.rend()), &*BB0.begin());
#ifndef NDEBUG
{
// Check BB.dump()
std::string Buff;
raw_string_ostream BS(Buff);
BS << "\n";
BB0.dumpOS(BS);
EXPECT_EQ(Buff, R"IR(
bb0:
br label %bb1 ; SB3. (Br)
)IR");
}
#endif // NDEBUG
}
TEST_F(SandboxIRTest, Instruction) {
parseIR(C, R"IR(
define void @foo(i8 %v1, ptr %ptr) {
bb0:
%add0 = add i8 %v1, %v1
%sub1 = sub i8 %add0, %v1
ret void
bb1:
%add1 = add i8 %v1, %v1
%sub2 = sub i8 %add1, %v1
%ld0 = load i8, ptr %ptr
store i8 %ld0, ptr %ptr
store volatile i8 %ld0, ptr %ptr
%atomicrmw = atomicrmw add ptr %ptr, i8 %v1 acquire
%udiv = udiv i8 %ld0, %v1
%urem = urem i8 %ld0, %v1
call void @foo(), !dbg !1
ret void, !tbaa !2
}
!1 = !{}
!2 = !{}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
llvm::BasicBlock *LLVMBB1 = getBasicBlockByName(*LLVMF, "bb1");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *Arg = F->getArg(0);
auto *BB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(*LLVMF, "bb0")));
auto It = BB->begin();
auto *I0 = &*It++;
auto *I1 = &*It++;
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check getPrevNode().
EXPECT_EQ(Ret->getPrevNode(), I1);
EXPECT_EQ(I1->getPrevNode(), I0);
EXPECT_EQ(I0->getPrevNode(), nullptr);
// Check getNextNode().
EXPECT_EQ(I0->getNextNode(), I1);
EXPECT_EQ(I1->getNextNode(), Ret);
EXPECT_EQ(Ret->getNextNode(), nullptr);
// Check getIterator().
EXPECT_EQ(I0->getIterator(), std::next(BB->begin(), 0));
EXPECT_EQ(I1->getIterator(), std::next(BB->begin(), 1));
EXPECT_EQ(Ret->getIterator(), std::next(BB->begin(), 2));
// Check getOpcode().
EXPECT_EQ(I0->getOpcode(), sandboxir::Instruction::Opcode::Add);
EXPECT_EQ(I1->getOpcode(), sandboxir::Instruction::Opcode::Sub);
EXPECT_EQ(Ret->getOpcode(), sandboxir::Instruction::Opcode::Ret);
// Check getOpcodeName().
EXPECT_STREQ(I0->getOpcodeName(), "Add");
EXPECT_STREQ(I1->getOpcodeName(), "Sub");
EXPECT_STREQ(Ret->getOpcodeName(), "Ret");
EXPECT_STREQ(sandboxir::Instruction::getOpcodeName(
sandboxir::Instruction::Opcode::Alloca),
"Alloca");
// Check moveBefore(I).
I1->moveBefore(I0);
EXPECT_EQ(I0->getPrevNode(), I1);
EXPECT_EQ(I1->getNextNode(), I0);
// Check moveAfter(I).
I1->moveAfter(I0);
EXPECT_EQ(I0->getNextNode(), I1);
EXPECT_EQ(I1->getPrevNode(), I0);
// Check comesBefore(I).
EXPECT_TRUE(I0->comesBefore(I1));
EXPECT_FALSE(I1->comesBefore(I0));
// Check moveBefore(BB, It).
I1->moveBefore(*BB, BB->begin());
EXPECT_EQ(I1->getPrevNode(), nullptr);
EXPECT_EQ(I1->getNextNode(), I0);
I1->moveBefore(*BB, BB->end());
EXPECT_EQ(I1->getNextNode(), nullptr);
EXPECT_EQ(Ret->getNextNode(), I1);
I1->moveBefore(*BB, std::next(BB->begin()));
EXPECT_EQ(I0->getNextNode(), I1);
EXPECT_EQ(I1->getNextNode(), Ret);
// Check removeFromParent().
I0->removeFromParent();
#ifndef NDEBUG
EXPECT_DEATH(I0->getPrevNode(), ".*Detached.*");
EXPECT_DEATH(I0->getNextNode(), ".*Detached.*");
#endif // NDEBUG
EXPECT_EQ(I0->getParent(), nullptr);
EXPECT_EQ(I1->getPrevNode(), nullptr);
EXPECT_EQ(I0->getOperand(0), Arg);
// Check insertBefore().
I0->insertBefore(I1);
EXPECT_EQ(I1->getPrevNode(), I0);
// Check insertInto().
I0->removeFromParent();
I0->insertInto(BB, BB->end());
EXPECT_EQ(Ret->getNextNode(), I0);
I0->moveBefore(I1);
EXPECT_EQ(I0->getNextNode(), I1);
// Check eraseFromParent().
#ifndef NDEBUG
EXPECT_DEATH(I0->eraseFromParent(), "Still connected to users.*");
#endif
I1->eraseFromParent();
EXPECT_EQ(I0->getNumUses(), 0u);
EXPECT_EQ(I0->getNextNode(), Ret);
for (auto &LLVMI : *LLVMBB1) {
auto &I = cast<sandboxir::Instruction>(*Ctx.getValue(&LLVMI));
// Check isTerminator().
EXPECT_EQ(LLVMI.isTerminator(), I.isTerminator());
// Check isUnaryOp().
EXPECT_EQ(LLVMI.isUnaryOp(), I.isUnaryOp());
// Check isBinaryOp().
EXPECT_EQ(LLVMI.isBinaryOp(), I.isBinaryOp());
// Check isIntDivRem().
EXPECT_EQ(LLVMI.isIntDivRem(), I.isIntDivRem());
// Check isShift().
EXPECT_EQ(LLVMI.isShift(), I.isShift());
// Check isCast().
EXPECT_EQ(LLVMI.isCast(), I.isCast());
// Check isFuncletPad().
EXPECT_EQ(LLVMI.isFuncletPad(), I.isFuncletPad());
// Check isSpecialTerminator().
EXPECT_EQ(LLVMI.isSpecialTerminator(), I.isSpecialTerminator());
// Check isOnlyUserOfAnyOperand().
EXPECT_EQ(LLVMI.isOnlyUserOfAnyOperand(), I.isOnlyUserOfAnyOperand());
// Check isLogicalShift().
EXPECT_EQ(LLVMI.isLogicalShift(), I.isLogicalShift());
// Check hasMetadata().
EXPECT_EQ(LLVMI.hasMetadata(), I.hasMetadata());
// Check hasMetadataOtherThanDebugLoc().
EXPECT_EQ(LLVMI.hasMetadataOtherThanDebugLoc(),
I.hasMetadataOtherThanDebugLoc());
// Check isAssociative().
EXPECT_EQ(LLVMI.isAssociative(), I.isAssociative());
// Check isCommutative().
EXPECT_EQ(LLVMI.isCommutative(), I.isCommutative());
// Check isIdempotent().
EXPECT_EQ(LLVMI.isIdempotent(), I.isIdempotent());
// Check isNilpotent().
EXPECT_EQ(LLVMI.isNilpotent(), I.isNilpotent());
// Check mayWriteToMemory().
EXPECT_EQ(LLVMI.mayWriteToMemory(), I.mayWriteToMemory());
// Check mayReadFromMemory().
EXPECT_EQ(LLVMI.mayReadFromMemory(), I.mayReadFromMemory());
// Check mayReadOrWriteMemory().
EXPECT_EQ(LLVMI.mayReadOrWriteMemory(), I.mayReadOrWriteMemory());
// Check isAtomic().
EXPECT_EQ(LLVMI.isAtomic(), I.isAtomic());
if (I.isAtomic()) {
// Check hasAtomicLoad().
EXPECT_EQ(LLVMI.hasAtomicLoad(), I.hasAtomicLoad());
// Check hasAtomicStore().
EXPECT_EQ(LLVMI.hasAtomicStore(), I.hasAtomicStore());
}
// Check isVolatile().
EXPECT_EQ(LLVMI.isVolatile(), I.isVolatile());
// Check getAccessType().
EXPECT_EQ(Ctx.getType(LLVMI.getAccessType()), I.getAccessType());
// Check mayThrow().
EXPECT_EQ(LLVMI.mayThrow(), I.mayThrow());
// Check isFenceLike().
EXPECT_EQ(LLVMI.isFenceLike(), I.isFenceLike());
// Check mayHaveSideEffects().
EXPECT_EQ(LLVMI.mayHaveSideEffects(), I.mayHaveSideEffects());
}
}
TEST_F(SandboxIRTest, VAArgInst) {
parseIR(C, R"IR(
define void @foo(ptr %va) {
%va_arg = va_arg ptr %va, i32
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *Arg = F->getArg(0);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *VA = cast<sandboxir::VAArgInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check getPointerOperand().
EXPECT_EQ(VA->getPointerOperand(), Arg);
// Check getPOinterOperandIndex().
EXPECT_EQ(sandboxir::VAArgInst::getPointerOperandIndex(),
llvm::VAArgInst::getPointerOperandIndex());
// Check create().
auto *NewVATy = sandboxir::Type::getInt8Ty(Ctx);
auto *NewVA = sandboxir::VAArgInst::create(Arg, NewVATy, Ret->getIterator(),
Ctx, "NewVA");
EXPECT_EQ(NewVA->getNextNode(), Ret);
EXPECT_EQ(NewVA->getType(), NewVATy);
#ifndef NDEBUG
EXPECT_EQ(NewVA->getName(), "NewVA");
#endif // NDEBUG
}
TEST_F(SandboxIRTest, FreezeInst) {
parseIR(C, R"IR(
define void @foo(i8 %arg) {
freeze i8 %arg
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *Arg = F->getArg(0);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Freeze = cast<sandboxir::FreezeInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(Freeze));
EXPECT_EQ(Freeze->getOperand(0), Arg);
// Check create().
auto *NewFreeze =
sandboxir::FreezeInst::create(Arg, Ret->getIterator(), Ctx, "NewFreeze");
EXPECT_EQ(NewFreeze->getNextNode(), Ret);
#ifndef NDEBUG
EXPECT_EQ(NewFreeze->getName(), "NewFreeze");
#endif // NDEBUG
}
TEST_F(SandboxIRTest, FenceInst) {
parseIR(C, R"IR(
define void @foo() {
fence syncscope("singlethread") seq_cst
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
llvm::BasicBlock *LLVMBB = &*LLVMF->begin();
auto *LLVMFence = cast<llvm::FenceInst>(&*LLVMBB->begin());
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Fence = cast<sandboxir::FenceInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check getOrdering().
EXPECT_EQ(Fence->getOrdering(), LLVMFence->getOrdering());
// Check setOrdering().
auto OrigOrdering = Fence->getOrdering();
auto NewOrdering = AtomicOrdering::Release;
EXPECT_NE(NewOrdering, OrigOrdering);
Fence->setOrdering(NewOrdering);
EXPECT_EQ(Fence->getOrdering(), NewOrdering);
Fence->setOrdering(OrigOrdering);
EXPECT_EQ(Fence->getOrdering(), OrigOrdering);
// Check getSyncScopeID().
EXPECT_EQ(Fence->getSyncScopeID(), LLVMFence->getSyncScopeID());
// Check setSyncScopeID().
auto OrigSSID = Fence->getSyncScopeID();
auto NewSSID = SyncScope::System;
EXPECT_NE(NewSSID, OrigSSID);
Fence->setSyncScopeID(NewSSID);
EXPECT_EQ(Fence->getSyncScopeID(), NewSSID);
Fence->setSyncScopeID(OrigSSID);
EXPECT_EQ(Fence->getSyncScopeID(), OrigSSID);
// Check create().
auto *NewFence =
sandboxir::FenceInst::create(AtomicOrdering::Release, Ret->getIterator(),
Ctx, SyncScope::SingleThread);
EXPECT_EQ(NewFence->getNextNode(), Ret);
EXPECT_EQ(NewFence->getOrdering(), AtomicOrdering::Release);
EXPECT_EQ(NewFence->getSyncScopeID(), SyncScope::SingleThread);
}
TEST_F(SandboxIRTest, SelectInst) {
parseIR(C, R"IR(
define void @foo(i1 %c0, i8 %v0, i8 %v1, i1 %c1) {
%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 *Cond0 = F->getArg(0);
auto *V0 = F->getArg(1);
auto *V1 = F->getArg(2);
auto *Cond1 = F->getArg(3);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Select = cast<sandboxir::SelectInst>(&*It++);
const auto *ConstSelect = Select; // To test the const getters.
auto *Ret = &*It++;
// Check getCondition().
EXPECT_EQ(Select->getCondition(), Cond0);
EXPECT_EQ(ConstSelect->getCondition(), Cond0);
// Check getTrueValue().
EXPECT_EQ(Select->getTrueValue(), V0);
EXPECT_EQ(ConstSelect->getTrueValue(), V0);
// Check getFalseValue().
EXPECT_EQ(Select->getFalseValue(), V1);
EXPECT_EQ(ConstSelect->getFalseValue(), V1);
// Check setCondition().
Select->setCondition(Cond1);
EXPECT_EQ(Select->getCondition(), Cond1);
// Check setTrueValue().
Select->setTrueValue(V1);
EXPECT_EQ(Select->getTrueValue(), V1);
// Check setFalseValue().
Select->setFalseValue(V0);
EXPECT_EQ(Select->getFalseValue(), V0);
// Check swapValues().
Select->swapValues();
EXPECT_EQ(Select->getTrueValue(), V0);
EXPECT_EQ(Select->getFalseValue(), V1);
// Check areInvalidOperands.
EXPECT_EQ(sandboxir::SelectInst::areInvalidOperands(Cond0, V0, V1), nullptr);
EXPECT_NE(sandboxir::SelectInst::areInvalidOperands(V0, V1, Cond0), nullptr);
{
// Check SelectInst::create() InsertBefore.
auto *NewSel = cast<sandboxir::SelectInst>(sandboxir::SelectInst::create(
Cond0, V0, V1, /*InsertBefore=*/Ret->getIterator(), Ctx));
EXPECT_EQ(NewSel->getCondition(), Cond0);
EXPECT_EQ(NewSel->getTrueValue(), V0);
EXPECT_EQ(NewSel->getFalseValue(), V1);
EXPECT_EQ(NewSel->getNextNode(), Ret);
}
{
// Check SelectInst::create() InsertAtEnd.
auto *NewSel = cast<sandboxir::SelectInst>(
sandboxir::SelectInst::create(Cond0, V0, V1, /*InsertAtEnd=*/BB, Ctx));
EXPECT_EQ(NewSel->getCondition(), Cond0);
EXPECT_EQ(NewSel->getTrueValue(), V0);
EXPECT_EQ(NewSel->getFalseValue(), V1);
EXPECT_EQ(NewSel->getPrevNode(), Ret);
}
{
// Check SelectInst::create() Folded.
auto *False = sandboxir::ConstantInt::get(sandboxir::Type::getInt1Ty(Ctx),
0, /*IsSigned=*/false);
auto *FortyTwo =
sandboxir::ConstantInt::get(sandboxir::Type::getInt1Ty(Ctx), 42,
/*IsSigned=*/false);
auto *NewSel = sandboxir::SelectInst::create(False, FortyTwo, FortyTwo,
Ret->getIterator(), Ctx);
EXPECT_TRUE(isa<sandboxir::Constant>(NewSel));
EXPECT_EQ(NewSel, FortyTwo);
}
}
TEST_F(SandboxIRTest, ExtractElementInst) {
parseIR(C, R"IR(
define void @foo(<2 x i8> %vec, i32 %idx) {
%ins0 = extractelement <2 x i8> %vec, i32 %idx
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *ArgVec = F.getArg(0);
auto *ArgIdx = F.getArg(1);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *EI = cast<sandboxir::ExtractElementInst>(&*It++);
auto *Ret = &*It++;
EXPECT_EQ(EI->getOpcode(), sandboxir::Instruction::Opcode::ExtractElement);
EXPECT_EQ(EI->getOperand(0), ArgVec);
EXPECT_EQ(EI->getOperand(1), ArgIdx);
EXPECT_EQ(EI->getVectorOperand(), ArgVec);
EXPECT_EQ(EI->getIndexOperand(), ArgIdx);
EXPECT_EQ(EI->getVectorOperandType(), ArgVec->getType());
auto *NewI1 =
cast<sandboxir::ExtractElementInst>(sandboxir::ExtractElementInst::create(
ArgVec, ArgIdx, Ret->getIterator(), Ctx, "NewExtrBeforeRet"));
EXPECT_EQ(NewI1->getOperand(0), ArgVec);
EXPECT_EQ(NewI1->getOperand(1), ArgIdx);
EXPECT_EQ(NewI1->getNextNode(), Ret);
auto *NewI2 =
cast<sandboxir::ExtractElementInst>(sandboxir::ExtractElementInst::create(
ArgVec, ArgIdx, BB, Ctx, "NewExtrAtEndOfBB"));
EXPECT_EQ(NewI2->getPrevNode(), Ret);
auto *LLVMArgVec = LLVMF.getArg(0);
auto *LLVMArgIdx = LLVMF.getArg(1);
EXPECT_EQ(sandboxir::ExtractElementInst::isValidOperands(ArgVec, ArgIdx),
llvm::ExtractElementInst::isValidOperands(LLVMArgVec, LLVMArgIdx));
EXPECT_EQ(sandboxir::ExtractElementInst::isValidOperands(ArgIdx, ArgVec),
llvm::ExtractElementInst::isValidOperands(LLVMArgIdx, LLVMArgVec));
}
TEST_F(SandboxIRTest, InsertElementInst) {
parseIR(C, R"IR(
define void @foo(i8 %v0, i8 %v1, <2 x i8> %vec) {
%ins0 = insertelement <2 x i8> poison, i8 %v0, i32 0
%ins1 = insertelement <2 x i8> %ins0, i8 %v1, i32 1
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *Arg0 = F.getArg(0);
auto *Arg1 = F.getArg(1);
auto *ArgVec = F.getArg(2);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *Ins0 = cast<sandboxir::InsertElementInst>(&*It++);
auto *Ins1 = cast<sandboxir::InsertElementInst>(&*It++);
auto *Ret = &*It++;
EXPECT_EQ(Ins0->getOpcode(), sandboxir::Instruction::Opcode::InsertElement);
EXPECT_EQ(Ins0->getOperand(1), Arg0);
EXPECT_EQ(Ins1->getOperand(1), Arg1);
EXPECT_EQ(Ins1->getOperand(0), Ins0);
auto *Poison = Ins0->getOperand(0);
auto *Idx = Ins0->getOperand(2);
auto *NewI1 =
cast<sandboxir::InsertElementInst>(sandboxir::InsertElementInst::create(
Poison, Arg0, Idx, Ret->getIterator(), Ctx, "NewIns1"));
EXPECT_EQ(NewI1->getOperand(0), Poison);
EXPECT_EQ(NewI1->getNextNode(), Ret);
auto *NewI2 =
cast<sandboxir::InsertElementInst>(sandboxir::InsertElementInst::create(
Poison, Arg0, Idx, BB, Ctx, "NewIns2"));
EXPECT_EQ(NewI2->getPrevNode(), Ret);
auto *LLVMArg0 = LLVMF.getArg(0);
auto *LLVMArgVec = LLVMF.getArg(2);
auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt8Ty(Ctx), 0);
auto *LLVMZero = llvm::ConstantInt::get(Type::getInt8Ty(C), 0);
EXPECT_EQ(
sandboxir::InsertElementInst::isValidOperands(ArgVec, Arg0, Zero),
llvm::InsertElementInst::isValidOperands(LLVMArgVec, LLVMArg0, LLVMZero));
EXPECT_EQ(
sandboxir::InsertElementInst::isValidOperands(Arg0, ArgVec, Zero),
llvm::InsertElementInst::isValidOperands(LLVMArg0, LLVMArgVec, LLVMZero));
}
TEST_F(SandboxIRTest, 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 0, i32 2>
%extr = extractelement <2 x i8> <i8 0, i8 1>, i32 0
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *ArgV1 = F.getArg(0);
auto *ArgV2 = F.getArg(1);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *SVI = cast<sandboxir::ShuffleVectorInst>(&*It++);
auto *EEI = cast<sandboxir::ExtractElementInst>(&*It++);
auto *Ret = &*It++;
EXPECT_EQ(SVI->getOpcode(), sandboxir::Instruction::Opcode::ShuffleVector);
EXPECT_EQ(SVI->getOperand(0), ArgV1);
EXPECT_EQ(SVI->getOperand(1), ArgV2);
// In order to test all the methods we need masks of different lengths, so we
// can't simply reuse one of the instructions created above. This helper
// creates a new `shufflevector %v1, %2, <mask>` with the given mask indices.
auto CreateShuffleWithMask = [&](auto &&...Indices) {
SmallVector<int, 4> Mask = {Indices...};
return cast<sandboxir::ShuffleVectorInst>(
sandboxir::ShuffleVectorInst::create(ArgV1, ArgV2, Mask,
Ret->getIterator(), Ctx));
};
// create (InsertBefore)
auto *NewI1 =
cast<sandboxir::ShuffleVectorInst>(sandboxir::ShuffleVectorInst::create(
ArgV1, ArgV2, ArrayRef<int>({0, 2, 1, 3}), Ret->getIterator(), Ctx,
"NewShuffleBeforeRet"));
EXPECT_EQ(NewI1->getOperand(0), ArgV1);
EXPECT_EQ(NewI1->getOperand(1), ArgV2);
EXPECT_EQ(NewI1->getNextNode(), Ret);
#ifndef NDEBUG
EXPECT_EQ(NewI1->getName(), "NewShuffleBeforeRet");
#endif
// create (InsertAtEnd)
auto *NewI2 =
cast<sandboxir::ShuffleVectorInst>(sandboxir::ShuffleVectorInst::create(
ArgV1, ArgV2, ArrayRef<int>({0, 1}), BB, Ctx, "NewShuffleAtEndOfBB"));
EXPECT_EQ(NewI2->getPrevNode(), Ret);
// Test the path that creates a folded constant. We're currently using an
// extractelement instruction with a constant operand in the textual IR above
// to obtain a constant vector to work with.
// TODO: Refactor this once sandboxir::ConstantVector lands.
auto *ShouldBeConstant = sandboxir::ShuffleVectorInst::create(
EEI->getOperand(0), EEI->getOperand(0), ArrayRef<int>({0, 3}), BB, Ctx);
EXPECT_TRUE(isa<sandboxir::Constant>(ShouldBeConstant));
// isValidOperands
auto *LLVMArgV1 = LLVMF.getArg(0);
auto *LLVMArgV2 = LLVMF.getArg(1);
SmallVector<int, 2> Mask({1, 2});
EXPECT_EQ(
sandboxir::ShuffleVectorInst::isValidOperands(ArgV1, ArgV2, Mask),
llvm::ShuffleVectorInst::isValidOperands(LLVMArgV1, LLVMArgV2, Mask));
EXPECT_EQ(sandboxir::ShuffleVectorInst::isValidOperands(ArgV1, ArgV1, ArgV1),
llvm::ShuffleVectorInst::isValidOperands(LLVMArgV1, LLVMArgV1,
LLVMArgV1));
// commute
{
auto *I = CreateShuffleWithMask(0, 2);
I->commute();
EXPECT_EQ(I->getOperand(0), ArgV2);
EXPECT_EQ(I->getOperand(1), ArgV1);
EXPECT_THAT(I->getShuffleMask(), testing::ElementsAre(2, 0));
}
// getType
EXPECT_EQ(SVI->getType(), ArgV1->getType());
// getMaskValue
EXPECT_EQ(SVI->getMaskValue(0), 0);
EXPECT_EQ(SVI->getMaskValue(1), 2);
// getShuffleMask / getShuffleMaskForBitcode
{
EXPECT_THAT(SVI->getShuffleMask(), testing::ElementsAre(0, 2));
SmallVector<int, 2> Result;
SVI->getShuffleMask(Result);
EXPECT_THAT(Result, testing::ElementsAre(0, 2));
Result.clear();
sandboxir::ShuffleVectorInst::getShuffleMask(
SVI->getShuffleMaskForBitcode(), Result);
EXPECT_THAT(Result, testing::ElementsAre(0, 2));
}
// convertShuffleMaskForBitcode
{
auto *C = sandboxir::ShuffleVectorInst::convertShuffleMaskForBitcode(
ArrayRef<int>({2, 3}), ArgV1->getType());
SmallVector<int, 2> Result;
sandboxir::ShuffleVectorInst::getShuffleMask(C, Result);
EXPECT_THAT(Result, testing::ElementsAre(2, 3));
}
// setShuffleMask
{
auto *I = CreateShuffleWithMask(0, 1);
I->setShuffleMask(ArrayRef<int>({2, 3}));
EXPECT_THAT(I->getShuffleMask(), testing::ElementsAre(2, 3));
}
// The following functions check different mask properties. Note that most
// of these come in three different flavors: a method that checks the mask
// in the current instructions and two static member functions that check
// a mask given as an ArrayRef<int> or Constant*, so there's quite a bit of
// repetition in order to check all of them.
// changesLength / increasesLength
{
auto *I = CreateShuffleWithMask(1);
EXPECT_TRUE(I->changesLength());
EXPECT_FALSE(I->increasesLength());
}
{
auto *I = CreateShuffleWithMask(1, 1);
EXPECT_FALSE(I->changesLength());
EXPECT_FALSE(I->increasesLength());
}
{
auto *I = CreateShuffleWithMask(1, 1, 1);
EXPECT_TRUE(I->changesLength());
EXPECT_TRUE(I->increasesLength());
}
// isSingleSource / isSingleSourceMask
{
auto *I = CreateShuffleWithMask(0, 1);
EXPECT_TRUE(I->isSingleSource());
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSingleSourceMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSingleSourceMask(
I->getShuffleMask(), 2));
}
{
auto *I = CreateShuffleWithMask(0, 2);
EXPECT_FALSE(I->isSingleSource());
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSingleSourceMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSingleSourceMask(
I->getShuffleMask(), 2));
}
// isIdentity / isIdentityMask
{
auto *I = CreateShuffleWithMask(0, 1);
EXPECT_TRUE(I->isIdentity());
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isIdentityMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_TRUE(
sandboxir::ShuffleVectorInst::isIdentityMask(I->getShuffleMask(), 2));
}
{
auto *I = CreateShuffleWithMask(1, 0);
EXPECT_FALSE(I->isIdentity());
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isIdentityMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_FALSE(
sandboxir::ShuffleVectorInst::isIdentityMask(I->getShuffleMask(), 2));
}
// isIdentityWithPadding
EXPECT_TRUE(CreateShuffleWithMask(0, 1, -1, -1)->isIdentityWithPadding());
EXPECT_FALSE(CreateShuffleWithMask(0, 1)->isIdentityWithPadding());
// isIdentityWithExtract
EXPECT_TRUE(CreateShuffleWithMask(0)->isIdentityWithExtract());
EXPECT_FALSE(CreateShuffleWithMask(0, 1)->isIdentityWithExtract());
EXPECT_FALSE(CreateShuffleWithMask(0, 1, 2)->isIdentityWithExtract());
EXPECT_FALSE(CreateShuffleWithMask(1)->isIdentityWithExtract());
// isConcat
EXPECT_TRUE(CreateShuffleWithMask(0, 1, 2, 3)->isConcat());
EXPECT_FALSE(CreateShuffleWithMask(0, 3)->isConcat());
// isSelect / isSelectMask
{
auto *I = CreateShuffleWithMask(0, 3);
EXPECT_TRUE(I->isSelect());
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSelectMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_TRUE(
sandboxir::ShuffleVectorInst::isSelectMask(I->getShuffleMask(), 2));
}
{
auto *I = CreateShuffleWithMask(0, 2);
EXPECT_FALSE(I->isSelect());
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSelectMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_FALSE(
sandboxir::ShuffleVectorInst::isSelectMask(I->getShuffleMask(), 2));
}
// isReverse / isReverseMask
{
auto *I = CreateShuffleWithMask(1, 0);
EXPECT_TRUE(I->isReverse());
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isReverseMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_TRUE(
sandboxir::ShuffleVectorInst::isReverseMask(I->getShuffleMask(), 2));
}
{
auto *I = CreateShuffleWithMask(1, 2);
EXPECT_FALSE(I->isReverse());
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isReverseMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_FALSE(
sandboxir::ShuffleVectorInst::isReverseMask(I->getShuffleMask(), 2));
}
// isZeroEltSplat / isZeroEltSplatMask
{
auto *I = CreateShuffleWithMask(0, 0);
EXPECT_TRUE(I->isZeroEltSplat());
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask(
I->getShuffleMask(), 2));
}
{
auto *I = CreateShuffleWithMask(1, 1);
EXPECT_FALSE(I->isZeroEltSplat());
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask(
I->getShuffleMask(), 2));
}
// isTranspose / isTransposeMask
{
auto *I = CreateShuffleWithMask(0, 2);
EXPECT_TRUE(I->isTranspose());
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isTransposeMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_TRUE(
sandboxir::ShuffleVectorInst::isTransposeMask(I->getShuffleMask(), 2));
}
{
auto *I = CreateShuffleWithMask(1, 1);
EXPECT_FALSE(I->isTranspose());
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isTransposeMask(
I->getShuffleMaskForBitcode(), 2));
EXPECT_FALSE(
sandboxir::ShuffleVectorInst::isTransposeMask(I->getShuffleMask(), 2));
}
// isSplice / isSpliceMask
{
auto *I = CreateShuffleWithMask(1, 2);
int Index;
EXPECT_TRUE(I->isSplice(Index));
EXPECT_EQ(Index, 1);
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSpliceMask(
I->getShuffleMaskForBitcode(), 2, Index));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSpliceMask(I->getShuffleMask(),
2, Index));
}
{
auto *I = CreateShuffleWithMask(2, 1);
int Index;
EXPECT_FALSE(I->isSplice(Index));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSpliceMask(
I->getShuffleMaskForBitcode(), 2, Index));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSpliceMask(I->getShuffleMask(),
2, Index));
}
// isExtractSubvectorMask
{
auto *I = CreateShuffleWithMask(1);
int Index;
EXPECT_TRUE(I->isExtractSubvectorMask(Index));
EXPECT_EQ(Index, 1);
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask(
I->getShuffleMaskForBitcode(), 2, Index));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask(
I->getShuffleMask(), 2, Index));
}
{
auto *I = CreateShuffleWithMask(1, 2);
int Index;
EXPECT_FALSE(I->isExtractSubvectorMask(Index));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask(
I->getShuffleMaskForBitcode(), 2, Index));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask(
I->getShuffleMask(), 2, Index));
}
// isInsertSubvectorMask
{
auto *I = CreateShuffleWithMask(0, 2);
int NumSubElts, Index;
EXPECT_TRUE(I->isInsertSubvectorMask(NumSubElts, Index));
EXPECT_EQ(Index, 1);
EXPECT_EQ(NumSubElts, 1);
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask(
I->getShuffleMaskForBitcode(), 2, NumSubElts, Index));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask(
I->getShuffleMask(), 2, NumSubElts, Index));
}
{
auto *I = CreateShuffleWithMask(0, 1);
int NumSubElts, Index;
EXPECT_FALSE(I->isInsertSubvectorMask(NumSubElts, Index));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask(
I->getShuffleMaskForBitcode(), 2, NumSubElts, Index));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask(
I->getShuffleMask(), 2, NumSubElts, Index));
}
// isReplicationMask
{
auto *I = CreateShuffleWithMask(0, 0, 0, 1, 1, 1);
int ReplicationFactor, VF;
EXPECT_TRUE(I->isReplicationMask(ReplicationFactor, VF));
EXPECT_EQ(ReplicationFactor, 3);
EXPECT_EQ(VF, 2);
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isReplicationMask(
I->getShuffleMaskForBitcode(), ReplicationFactor, VF));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isReplicationMask(
I->getShuffleMask(), ReplicationFactor, VF));
}
{
auto *I = CreateShuffleWithMask(1, 2);
int ReplicationFactor, VF;
EXPECT_FALSE(I->isReplicationMask(ReplicationFactor, VF));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isReplicationMask(
I->getShuffleMaskForBitcode(), ReplicationFactor, VF));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isReplicationMask(
I->getShuffleMask(), ReplicationFactor, VF));
}
// isOneUseSingleSourceMask
{
auto *I = CreateShuffleWithMask(0, 1, 1, 0);
EXPECT_TRUE(I->isOneUseSingleSourceMask(2));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isOneUseSingleSourceMask(
I->getShuffleMask(), 2));
}
{
auto *I = CreateShuffleWithMask(0, 1, 0, 0);
EXPECT_FALSE(I->isOneUseSingleSourceMask(2));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isOneUseSingleSourceMask(
I->getShuffleMask(), 2));
}
// commuteShuffleMask
{
SmallVector<int, 4> M = {0, 2, 1, 3};
ShuffleVectorInst::commuteShuffleMask(M, 2);
EXPECT_THAT(M, testing::ElementsAre(2, 0, 3, 1));
}
// isInterleave / isInterleaveMask
{
auto *I = CreateShuffleWithMask(0, 2, 1, 3);
EXPECT_TRUE(I->isInterleave(2));
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInterleaveMask(
I->getShuffleMask(), 2, 4));
SmallVector<unsigned, 4> StartIndexes;
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInterleaveMask(
I->getShuffleMask(), 2, 4, StartIndexes));
EXPECT_THAT(StartIndexes, testing::ElementsAre(0, 2));
}
{
auto *I = CreateShuffleWithMask(0, 3, 1, 2);
EXPECT_FALSE(I->isInterleave(2));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isInterleaveMask(
I->getShuffleMask(), 2, 4));
}
// isDeInterleaveMaskOfFactor
{
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isDeInterleaveMaskOfFactor(
ArrayRef<int>({0, 2}), 2));
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isDeInterleaveMaskOfFactor(
ArrayRef<int>({0, 1}), 2));
unsigned Index;
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isDeInterleaveMaskOfFactor(
ArrayRef<int>({1, 3}), 2, Index));
EXPECT_EQ(Index, 1u);
}
// isBitRotateMask
{
unsigned NumSubElts, RotateAmt;
EXPECT_TRUE(sandboxir::ShuffleVectorInst::isBitRotateMask(
ArrayRef<int>({1, 0, 3, 2, 5, 4, 7, 6}), 8, 2, 2, NumSubElts,
RotateAmt));
EXPECT_EQ(NumSubElts, 2u);
EXPECT_EQ(RotateAmt, 8u);
EXPECT_FALSE(sandboxir::ShuffleVectorInst::isBitRotateMask(
ArrayRef<int>({0, 7, 1, 6, 2, 5, 3, 4}), 8, 2, 2, NumSubElts,
RotateAmt));
}
}
TEST_F(SandboxIRTest, ExtractValueInst) {
parseIR(C, R"IR(
define void @foo({i32, float} %agg) {
%ext_simple = extractvalue {i32, float} %agg, 0
%ext_nested = extractvalue {float, {i32}} undef, 1, 0
%const1 = extractvalue {i32, float} {i32 0, float 99.0}, 0
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
[[maybe_unused]] auto *LLVMExtSimple =
cast<llvm::ExtractValueInst>(&*LLVMIt++);
auto *LLVMExtNested = cast<llvm::ExtractValueInst>(&*LLVMIt++);
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *ArgAgg = F.getArg(0);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *ExtSimple = cast<sandboxir::ExtractValueInst>(&*It++);
auto *ExtNested = cast<sandboxir::ExtractValueInst>(&*It++);
auto *Const1 = cast<sandboxir::ExtractValueInst>(&*It++);
auto *Ret = &*It++;
EXPECT_EQ(ExtSimple->getOperand(0), ArgAgg);
// create before instruction
auto *NewExtBeforeRet =
cast<sandboxir::ExtractValueInst>(sandboxir::ExtractValueInst::create(
ArgAgg, ArrayRef<unsigned>({0}), Ret->getIterator(), Ctx,
"NewExtBeforeRet"));
EXPECT_EQ(NewExtBeforeRet->getNextNode(), Ret);
#ifndef NDEBUG
EXPECT_EQ(NewExtBeforeRet->getName(), "NewExtBeforeRet");
#endif // NDEBUG
// create at end of BB
auto *NewExtAtEnd =
cast<sandboxir::ExtractValueInst>(sandboxir::ExtractValueInst::create(
ArgAgg, ArrayRef<unsigned>({0}), BB->end(), Ctx, "NewExtAtEnd"));
EXPECT_EQ(NewExtAtEnd->getPrevNode(), Ret);
#ifndef NDEBUG
EXPECT_EQ(NewExtAtEnd->getName(), "NewExtAtEnd");
#endif // NDEBUG
// Test the path that creates a folded constant.
auto *ShouldBeConstant = sandboxir::ExtractValueInst::create(
Const1->getOperand(0), ArrayRef<unsigned>({0}), BB->end(), Ctx);
EXPECT_TRUE(isa<sandboxir::Constant>(ShouldBeConstant));
auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0);
EXPECT_EQ(ShouldBeConstant, Zero);
// getIndexedType
sandboxir::Type *AggType = ExtNested->getAggregateOperand()->getType();
llvm::Type *LLVMAggType = LLVMExtNested->getAggregateOperand()->getType();
EXPECT_EQ(sandboxir::ExtractValueInst::getIndexedType(
AggType, ArrayRef<unsigned>({1, 0})),
Ctx.getType(llvm::ExtractValueInst::getIndexedType(
LLVMAggType, ArrayRef<unsigned>({1, 0}))));
EXPECT_EQ(sandboxir::ExtractValueInst::getIndexedType(
AggType, ArrayRef<unsigned>({2})),
nullptr);
// idx_begin / idx_end
{
SmallVector<int, 2> IndicesSimple(ExtSimple->idx_begin(),
ExtSimple->idx_end());
EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u));
SmallVector<int, 2> IndicesNested(ExtNested->idx_begin(),
ExtNested->idx_end());
EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u));
}
// indices
{
SmallVector<int, 2> IndicesSimple(ExtSimple->indices());
EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u));
SmallVector<int, 2> IndicesNested(ExtNested->indices());
EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u));
}
// getAggregateOperand
EXPECT_EQ(ExtSimple->getAggregateOperand(), ArgAgg);
const auto *ConstExtSimple = ExtSimple;
EXPECT_EQ(ConstExtSimple->getAggregateOperand(), ArgAgg);
// getAggregateOperandIndex
EXPECT_EQ(sandboxir::ExtractValueInst::getAggregateOperandIndex(),
llvm::ExtractValueInst::getAggregateOperandIndex());
// getIndices
EXPECT_EQ(ExtSimple->getIndices().size(), 1u);
EXPECT_EQ(ExtSimple->getIndices()[0], 0u);
// getNumIndices
EXPECT_EQ(ExtSimple->getNumIndices(), 1u);
// hasIndices
EXPECT_EQ(ExtSimple->hasIndices(), true);
}
TEST_F(SandboxIRTest, InsertValueInst) {
parseIR(C, R"IR(
define void @foo({i32, float} %agg, i32 %i) {
%ins_simple = insertvalue {i32, float} %agg, i32 %i, 0
%ins_nested = insertvalue {float, {i32}} undef, i32 %i, 1, 0
%const1 = insertvalue {i32, float} {i32 99, float 99.0}, i32 %i, 0
%const2 = insertvalue {i32, float} {i32 0, float 99.0}, i32 %i, 0
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *ArgAgg = F.getArg(0);
auto *ArgInt = F.getArg(1);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *InsSimple = cast<sandboxir::InsertValueInst>(&*It++);
auto *InsNested = cast<sandboxir::InsertValueInst>(&*It++);
// These "const" instructions are helpers to create constant struct operands.
// TODO: Remove them once sandboxir::ConstantStruct gets added.
auto *Const1 = cast<sandboxir::InsertValueInst>(&*It++);
auto *Const2 = cast<sandboxir::InsertValueInst>(&*It++);
auto *Ret = &*It++;
EXPECT_EQ(InsSimple->getOperand(0), ArgAgg);
EXPECT_EQ(InsSimple->getOperand(1), ArgInt);
// create before instruction
auto *NewInsBeforeRet =
cast<sandboxir::InsertValueInst>(sandboxir::InsertValueInst::create(
ArgAgg, ArgInt, ArrayRef<unsigned>({0}), Ret->getIterator(), Ctx,
"NewInsBeforeRet"));
EXPECT_EQ(NewInsBeforeRet->getNextNode(), Ret);
#ifndef NDEBUG
EXPECT_EQ(NewInsBeforeRet->getName(), "NewInsBeforeRet");
#endif // NDEBUG
// create at end of BB
auto *NewInsAtEnd =
cast<sandboxir::InsertValueInst>(sandboxir::InsertValueInst::create(
ArgAgg, ArgInt, ArrayRef<unsigned>({0}), BB, Ctx, "NewInsAtEnd"));
EXPECT_EQ(NewInsAtEnd->getPrevNode(), Ret);
#ifndef NDEBUG
EXPECT_EQ(NewInsAtEnd->getName(), "NewInsAtEnd");
#endif // NDEBUG
// Test the path that creates a folded constant.
auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0);
auto *ShouldBeConstant = sandboxir::InsertValueInst::create(
Const1->getOperand(0), Zero, ArrayRef<unsigned>({0}), BB, Ctx);
auto *ExpectedConstant = Const2->getOperand(0);
EXPECT_TRUE(isa<sandboxir::Constant>(ShouldBeConstant));
EXPECT_EQ(ShouldBeConstant, ExpectedConstant);
// idx_begin / idx_end
{
SmallVector<int, 2> IndicesSimple(InsSimple->idx_begin(),
InsSimple->idx_end());
EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u));
SmallVector<int, 2> IndicesNested(InsNested->idx_begin(),
InsNested->idx_end());
EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u));
}
// indices
{
SmallVector<int, 2> IndicesSimple(InsSimple->indices());
EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u));
SmallVector<int, 2> IndicesNested(InsNested->indices());
EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u));
}
// getAggregateOperand
EXPECT_EQ(InsSimple->getAggregateOperand(), ArgAgg);
const auto *ConstInsSimple = InsSimple;
EXPECT_EQ(ConstInsSimple->getAggregateOperand(), ArgAgg);
// getAggregateOperandIndex
EXPECT_EQ(sandboxir::InsertValueInst::getAggregateOperandIndex(),
llvm::InsertValueInst::getAggregateOperandIndex());
// getInsertedValueOperand
EXPECT_EQ(InsSimple->getInsertedValueOperand(), ArgInt);
EXPECT_EQ(ConstInsSimple->getInsertedValueOperand(), ArgInt);
// getInsertedValueOperandIndex
EXPECT_EQ(sandboxir::InsertValueInst::getInsertedValueOperandIndex(),
llvm::InsertValueInst::getInsertedValueOperandIndex());
// getIndices
EXPECT_EQ(InsSimple->getIndices().size(), 1u);
EXPECT_EQ(InsSimple->getIndices()[0], 0u);
// getNumIndices
EXPECT_EQ(InsSimple->getNumIndices(), 1u);
// hasIndices
EXPECT_EQ(InsSimple->hasIndices(), true);
}
TEST_F(SandboxIRTest, BranchInst) {
parseIR(C, R"IR(
define void @foo(i1 %cond0, i1 %cond2) {
bb0:
br i1 %cond0, label %bb1, label %bb2
bb1:
ret void
bb2:
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *Cond0 = F->getArg(0);
auto *Cond1 = F->getArg(1);
auto *BB0 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(*LLVMF, "bb0")));
auto *BB1 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(*LLVMF, "bb1")));
auto *Ret1 = BB1->getTerminator();
auto *BB2 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(*LLVMF, "bb2")));
auto *Ret2 = BB2->getTerminator();
auto It = BB0->begin();
auto *Br0 = cast<sandboxir::BranchInst>(&*It++);
// Check isUnconditional().
EXPECT_FALSE(Br0->isUnconditional());
// Check isConditional().
EXPECT_TRUE(Br0->isConditional());
// Check getCondition().
EXPECT_EQ(Br0->getCondition(), Cond0);
// Check setCondition().
Br0->setCondition(Cond1);
EXPECT_EQ(Br0->getCondition(), Cond1);
// Check getNumSuccessors().
EXPECT_EQ(Br0->getNumSuccessors(), 2u);
// Check getSuccessor().
EXPECT_EQ(Br0->getSuccessor(0), BB1);
EXPECT_EQ(Br0->getSuccessor(1), BB2);
// Check swapSuccessors().
Br0->swapSuccessors();
EXPECT_EQ(Br0->getSuccessor(0), BB2);
EXPECT_EQ(Br0->getSuccessor(1), BB1);
// Check successors().
EXPECT_EQ(range_size(Br0->successors()), 2u);
unsigned SuccIdx = 0;
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB1, BB2});
for (sandboxir::BasicBlock *Succ : Br0->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
{
// Check unconditional BranchInst::create() InsertBefore.
auto *Br = sandboxir::BranchInst::create(BB1, Ret1->getIterator(), Ctx);
EXPECT_FALSE(Br->isConditional());
EXPECT_TRUE(Br->isUnconditional());
#ifndef NDEBUG
EXPECT_DEATH(Br->getCondition(), ".*condition.*");
#endif // NDEBUG
unsigned SuccIdx = 0;
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB1});
for (sandboxir::BasicBlock *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
EXPECT_EQ(Br->getNextNode(), Ret1);
}
{
// Check unconditional BranchInst::create() InsertAtEnd.
auto *Br = sandboxir::BranchInst::create(BB1, /*InsertAtEnd=*/BB1, Ctx);
EXPECT_FALSE(Br->isConditional());
EXPECT_TRUE(Br->isUnconditional());
#ifndef NDEBUG
EXPECT_DEATH(Br->getCondition(), ".*condition.*");
#endif // NDEBUG
unsigned SuccIdx = 0;
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB1});
for (sandboxir::BasicBlock *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
EXPECT_EQ(Br->getPrevNode(), Ret1);
}
{
// Check conditional BranchInst::create() InsertBefore.
auto *Br = sandboxir::BranchInst::create(BB1, BB2, Cond0,
Ret1->getIterator(), Ctx);
EXPECT_TRUE(Br->isConditional());
EXPECT_EQ(Br->getCondition(), Cond0);
unsigned SuccIdx = 0;
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB2, BB1});
for (sandboxir::BasicBlock *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
EXPECT_EQ(Br->getNextNode(), Ret1);
}
{
// Check conditional BranchInst::create() InsertAtEnd.
auto *Br = sandboxir::BranchInst::create(BB1, BB2, Cond0,
/*InsertAtEnd=*/BB2, Ctx);
EXPECT_TRUE(Br->isConditional());
EXPECT_EQ(Br->getCondition(), Cond0);
unsigned SuccIdx = 0;
SmallVector<sandboxir::BasicBlock *> ExpectedSuccs({BB2, BB1});
for (sandboxir::BasicBlock *Succ : Br->successors())
EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]);
EXPECT_EQ(Br->getPrevNode(), Ret2);
}
}
TEST_F(SandboxIRTest, LoadInst) {
parseIR(C, R"IR(
define void @foo(ptr %arg0, ptr %arg1) {
%ld = load i8, ptr %arg0, align 64
%vld = load volatile i8, ptr %arg0, align 64
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *Arg0 = F->getArg(0);
auto *Arg1 = F->getArg(1);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Ld = cast<sandboxir::LoadInst>(&*It++);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(Ld));
auto *VLd = cast<sandboxir::LoadInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
bool OrigVolatileValue;
// Check isVolatile()
EXPECT_FALSE(Ld->isVolatile());
// Check isVolatile()
EXPECT_TRUE(VLd->isVolatile());
// Check getPointerOperand()
EXPECT_EQ(Ld->getPointerOperand(), Arg0);
// Check getAlign()
EXPECT_EQ(Ld->getAlign(), 64);
// Check create(InsertBefore)
sandboxir::LoadInst *NewLd = sandboxir::LoadInst::create(
Ld->getType(), Arg1, Align(8), Ret->getIterator(), Ctx, "NewLd");
EXPECT_FALSE(NewLd->isVolatile());
OrigVolatileValue = NewLd->isVolatile();
NewLd->setVolatile(true);
EXPECT_TRUE(NewLd->isVolatile());
NewLd->setVolatile(OrigVolatileValue);
EXPECT_FALSE(NewLd->isVolatile());
EXPECT_EQ(NewLd->getType(), Ld->getType());
EXPECT_EQ(NewLd->getPointerOperand(), Arg1);
EXPECT_EQ(NewLd->getAlign(), 8);
EXPECT_EQ(NewLd->getName(), "NewLd");
// Check create(InsertBefore, IsVolatile=true)
sandboxir::LoadInst *NewVLd = sandboxir::LoadInst::create(
VLd->getType(), Arg1, Align(8), Ret->getIterator(),
/*IsVolatile=*/true, Ctx, "NewVLd");
EXPECT_TRUE(NewVLd->isVolatile());
OrigVolatileValue = NewVLd->isVolatile();
NewVLd->setVolatile(false);
EXPECT_FALSE(NewVLd->isVolatile());
NewVLd->setVolatile(OrigVolatileValue);
EXPECT_TRUE(NewVLd->isVolatile());
EXPECT_EQ(NewVLd->getName(), "NewVLd");
// Check create(InsertAtEnd)
sandboxir::LoadInst *NewLdEnd =
sandboxir::LoadInst::create(Ld->getType(), Arg1, Align(8),
/*InsertAtEnd=*/BB, Ctx, "NewLdEnd");
EXPECT_FALSE(NewLdEnd->isVolatile());
EXPECT_EQ(NewLdEnd->getName(), "NewLdEnd");
EXPECT_EQ(NewLdEnd->getType(), Ld->getType());
EXPECT_EQ(NewLdEnd->getPointerOperand(), Arg1);
EXPECT_EQ(NewLdEnd->getAlign(), 8);
EXPECT_EQ(NewLdEnd->getParent(), BB);
EXPECT_EQ(NewLdEnd->getNextNode(), nullptr);
// Check create(InsertAtEnd, IsVolatile=true)
sandboxir::LoadInst *NewVLdEnd =
sandboxir::LoadInst::create(VLd->getType(), Arg1, Align(8),
/*InsertAtEnd=*/BB,
/*IsVolatile=*/true, Ctx, "NewVLdEnd");
EXPECT_TRUE(NewVLdEnd->isVolatile());
EXPECT_EQ(NewVLdEnd->getName(), "NewVLdEnd");
EXPECT_EQ(NewVLdEnd->getType(), VLd->getType());
EXPECT_EQ(NewVLdEnd->getPointerOperand(), Arg1);
EXPECT_EQ(NewVLdEnd->getAlign(), 8);
EXPECT_EQ(NewVLdEnd->getParent(), BB);
EXPECT_EQ(NewVLdEnd->getNextNode(), nullptr);
}
TEST_F(SandboxIRTest, StoreInst) {
parseIR(C, R"IR(
define void @foo(i8 %val, ptr %ptr) {
store i8 %val, ptr %ptr, align 64
store volatile i8 %val, ptr %ptr, align 64
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *Val = F->getArg(0);
auto *Ptr = F->getArg(1);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *St = cast<sandboxir::StoreInst>(&*It++);
auto *VSt = cast<sandboxir::StoreInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
bool OrigVolatileValue;
// Check that the StoreInst has been created correctly.
EXPECT_FALSE(St->isVolatile());
EXPECT_TRUE(VSt->isVolatile());
// Check getPointerOperand()
EXPECT_EQ(St->getValueOperand(), Val);
EXPECT_EQ(St->getPointerOperand(), Ptr);
// Check getAlign()
EXPECT_EQ(St->getAlign(), 64);
// Check create(InsertBefore)
sandboxir::StoreInst *NewSt =
sandboxir::StoreInst::create(Val, Ptr, Align(8), Ret->getIterator(), Ctx);
EXPECT_FALSE(NewSt->isVolatile());
OrigVolatileValue = NewSt->isVolatile();
NewSt->setVolatile(true);
EXPECT_TRUE(NewSt->isVolatile());
NewSt->setVolatile(OrigVolatileValue);
EXPECT_FALSE(NewSt->isVolatile());
EXPECT_EQ(NewSt->getType(), St->getType());
EXPECT_EQ(NewSt->getValueOperand(), Val);
EXPECT_EQ(NewSt->getPointerOperand(), Ptr);
EXPECT_EQ(NewSt->getAlign(), 8);
EXPECT_EQ(NewSt->getNextNode(), Ret);
// Check create(InsertBefore, IsVolatile=true)
sandboxir::StoreInst *NewVSt =
sandboxir::StoreInst::create(Val, Ptr, Align(8), Ret->getIterator(),
/*IsVolatile=*/true, Ctx);
EXPECT_TRUE(NewVSt->isVolatile());
OrigVolatileValue = NewVSt->isVolatile();
NewVSt->setVolatile(false);
EXPECT_FALSE(NewVSt->isVolatile());
NewVSt->setVolatile(OrigVolatileValue);
EXPECT_TRUE(NewVSt->isVolatile());
EXPECT_EQ(NewVSt->getType(), VSt->getType());
EXPECT_EQ(NewVSt->getValueOperand(), Val);
EXPECT_EQ(NewVSt->getPointerOperand(), Ptr);
EXPECT_EQ(NewVSt->getAlign(), 8);
EXPECT_EQ(NewVSt->getNextNode(), Ret);
// Check create(InsertAtEnd)
sandboxir::StoreInst *NewStEnd =
sandboxir::StoreInst::create(Val, Ptr, Align(8),
/*InsertAtEnd=*/BB, Ctx);
EXPECT_FALSE(NewStEnd->isVolatile());
EXPECT_EQ(NewStEnd->getType(), St->getType());
EXPECT_EQ(NewStEnd->getValueOperand(), Val);
EXPECT_EQ(NewStEnd->getPointerOperand(), Ptr);
EXPECT_EQ(NewStEnd->getAlign(), 8);
EXPECT_EQ(NewStEnd->getParent(), BB);
EXPECT_EQ(NewStEnd->getNextNode(), nullptr);
// Check create(InsertAtEnd, IsVolatile=true)
sandboxir::StoreInst *NewVStEnd =
sandboxir::StoreInst::create(Val, Ptr, Align(8),
/*InsertAtEnd=*/BB,
/*IsVolatile=*/true, Ctx);
EXPECT_TRUE(NewVStEnd->isVolatile());
EXPECT_EQ(NewVStEnd->getType(), VSt->getType());
EXPECT_EQ(NewVStEnd->getValueOperand(), Val);
EXPECT_EQ(NewVStEnd->getPointerOperand(), Ptr);
EXPECT_EQ(NewVStEnd->getAlign(), 8);
EXPECT_EQ(NewVStEnd->getParent(), BB);
EXPECT_EQ(NewVStEnd->getNextNode(), nullptr);
}
TEST_F(SandboxIRTest, ReturnInst) {
parseIR(C, R"IR(
define i8 @foo(i8 %val) {
%add = add i8 %val, 42
ret i8 %val
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(LLVMF);
auto *Val = F->getArg(0);
auto *BB = &*F->begin();
auto It = BB->begin();
It++;
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check that the ReturnInst has been created correctly.
// Check getReturnValue().
EXPECT_EQ(Ret->getReturnValue(), Val);
// Check create(InsertBefore) a void ReturnInst.
auto *NewRet1 = cast<sandboxir::ReturnInst>(
sandboxir::ReturnInst::create(nullptr, Ret->getIterator(), Ctx));
EXPECT_EQ(NewRet1->getReturnValue(), nullptr);
// Check create(InsertBefore) a non-void ReturnInst.
auto *NewRet2 = cast<sandboxir::ReturnInst>(
sandboxir::ReturnInst::create(Val, Ret->getIterator(), Ctx));
EXPECT_EQ(NewRet2->getReturnValue(), Val);
// Check create(InsertAtEnd) a void ReturnInst.
auto *NewRet3 = cast<sandboxir::ReturnInst>(
sandboxir::ReturnInst::create(nullptr, /*InsertAtEnd=*/BB, Ctx));
EXPECT_EQ(NewRet3->getReturnValue(), nullptr);
// Check create(InsertAtEnd) a non-void ReturnInst.
auto *NewRet4 = cast<sandboxir::ReturnInst>(
sandboxir::ReturnInst::create(Val, /*InsertAtEnd=*/BB, Ctx));
EXPECT_EQ(NewRet4->getReturnValue(), Val);
}
TEST_F(SandboxIRTest, CallBase) {
parseIR(C, R"IR(
declare void @bar1(i8)
declare void @bar2()
declare void @bar3()
declare void @variadic(ptr, ...)
define i8 @foo(i8 %arg0, i32 %arg1, ptr %indirectFoo) {
%call = call i8 @foo(i8 %arg0, i32 %arg1)
call void @bar1(i8 %arg0)
call void @bar2()
call void %indirectFoo()
call void @bar2() noreturn
tail call fastcc void @bar2()
call void (ptr, ...) @variadic(ptr %indirectFoo, i32 1)
ret i8 %call
}
)IR");
llvm::Function &LLVMF = *M->getFunction("foo");
unsigned ArgIdx = 0;
llvm::Argument *LLVMArg0 = LLVMF.getArg(ArgIdx++);
llvm::Argument *LLVMArg1 = LLVMF.getArg(ArgIdx++);
llvm::BasicBlock *LLVMBB = &*LLVMF.begin();
SmallVector<llvm::CallBase *, 8> LLVMCalls;
auto LLVMIt = LLVMBB->begin();
while (isa<llvm::CallBase>(&*LLVMIt))
LLVMCalls.push_back(cast<llvm::CallBase>(&*LLVMIt++));
sandboxir::Context Ctx(C);
sandboxir::Function &F = *Ctx.createFunction(&LLVMF);
for (llvm::CallBase *LLVMCall : LLVMCalls) {
// Check classof(Instruction *).
auto *Call = cast<sandboxir::CallBase>(Ctx.getValue(LLVMCall));
// Check classof(Value *).
EXPECT_TRUE(isa<sandboxir::CallBase>((sandboxir::Value *)Call));
// Check getFunctionType().
EXPECT_EQ(Call->getFunctionType(),
Ctx.getType(LLVMCall->getFunctionType()));
// Check data_ops().
EXPECT_EQ(range_size(Call->data_ops()), range_size(LLVMCall->data_ops()));
auto DataOpIt = Call->data_operands_begin();
for (llvm::Use &LLVMUse : LLVMCall->data_ops()) {
Value *LLVMOp = LLVMUse.get();
sandboxir::Use Use = *DataOpIt++;
EXPECT_EQ(Ctx.getValue(LLVMOp), Use.get());
// Check isDataOperand().
EXPECT_EQ(Call->isDataOperand(Use), LLVMCall->isDataOperand(&LLVMUse));
// Check getDataOperandNo().
EXPECT_EQ(Call->getDataOperandNo(Use),
LLVMCall->getDataOperandNo(&LLVMUse));
// Check isArgOperand().
EXPECT_EQ(Call->isArgOperand(Use), LLVMCall->isArgOperand(&LLVMUse));
// Check isCallee().
EXPECT_EQ(Call->isCallee(Use), LLVMCall->isCallee(&LLVMUse));
}
// Check data_operands_empty().
EXPECT_EQ(Call->data_operands_empty(), LLVMCall->data_operands_empty());
// Check data_operands_size().
EXPECT_EQ(Call->data_operands_size(), LLVMCall->data_operands_size());
// Check getNumTotalBundleOperands().
EXPECT_EQ(Call->getNumTotalBundleOperands(),
LLVMCall->getNumTotalBundleOperands());
// Check args().
EXPECT_EQ(range_size(Call->args()), range_size(LLVMCall->args()));
auto ArgIt = Call->arg_begin();
for (llvm::Use &LLVMUse : LLVMCall->args()) {
Value *LLVMArg = LLVMUse.get();
sandboxir::Use Use = *ArgIt++;
EXPECT_EQ(Ctx.getValue(LLVMArg), Use.get());
}
// Check arg_empty().
EXPECT_EQ(Call->arg_empty(), LLVMCall->arg_empty());
// Check arg_size().
EXPECT_EQ(Call->arg_size(), LLVMCall->arg_size());
for (unsigned ArgIdx = 0, E = Call->arg_size(); ArgIdx != E; ++ArgIdx) {
// Check getArgOperand().
EXPECT_EQ(Call->getArgOperand(ArgIdx),
Ctx.getValue(LLVMCall->getArgOperand(ArgIdx)));
// Check getArgOperandUse().
sandboxir::Use Use = Call->getArgOperandUse(ArgIdx);
llvm::Use &LLVMUse = LLVMCall->getArgOperandUse(ArgIdx);
EXPECT_EQ(Use.get(), Ctx.getValue(LLVMUse.get()));
// Check getArgOperandNo().
EXPECT_EQ(Call->getArgOperandNo(Use),
LLVMCall->getArgOperandNo(&LLVMUse));
}
// Check hasArgument().
SmallVector<llvm::Value *> TestArgs(
{LLVMArg0, LLVMArg1, &LLVMF, LLVMBB, LLVMCall});
for (llvm::Value *LLVMV : TestArgs) {
sandboxir::Value *V = Ctx.getValue(LLVMV);
EXPECT_EQ(Call->hasArgument(V), LLVMCall->hasArgument(LLVMV));
}
// Check getCalledOperand().
EXPECT_EQ(Call->getCalledOperand(),
Ctx.getValue(LLVMCall->getCalledOperand()));
// Check getCalledOperandUse().
EXPECT_EQ(Call->getCalledOperandUse().get(),
Ctx.getValue(LLVMCall->getCalledOperandUse()));
// Check getCalledFunction().
if (LLVMCall->getCalledFunction() == nullptr)
EXPECT_EQ(Call->getCalledFunction(), nullptr);
else {
auto *LLVMCF = cast<llvm::Function>(LLVMCall->getCalledFunction());
(void)LLVMCF;
EXPECT_EQ(Call->getCalledFunction(),
cast<sandboxir::Function>(
Ctx.getValue(LLVMCall->getCalledFunction())));
}
// Check isIndirectCall().
EXPECT_EQ(Call->isIndirectCall(), LLVMCall->isIndirectCall());
// Check getCaller().
EXPECT_EQ(Call->getCaller(), Ctx.getValue(LLVMCall->getCaller()));
// Check isMustTailCall().
EXPECT_EQ(Call->isMustTailCall(), LLVMCall->isMustTailCall());
// Check isTailCall().
EXPECT_EQ(Call->isTailCall(), LLVMCall->isTailCall());
// Check getIntrinsicID().
EXPECT_EQ(Call->getIntrinsicID(), LLVMCall->getIntrinsicID());
// Check getCallingConv().
EXPECT_EQ(Call->getCallingConv(), LLVMCall->getCallingConv());
// Check isInlineAsm().
EXPECT_EQ(Call->isInlineAsm(), LLVMCall->isInlineAsm());
}
auto *Arg0 = F.getArg(0);
auto *Arg1 = F.getArg(1);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *Call0 = cast<sandboxir::CallBase>(&*It++);
[[maybe_unused]] auto *Call1 = cast<sandboxir::CallBase>(&*It++);
auto *Call2 = cast<sandboxir::CallBase>(&*It++);
// Check setArgOperand
Call0->setArgOperand(0, Arg1);
EXPECT_EQ(Call0->getArgOperand(0), Arg1);
Call0->setArgOperand(0, Arg0);
EXPECT_EQ(Call0->getArgOperand(0), Arg0);
auto *Bar3F = Ctx.createFunction(M->getFunction("bar3"));
// Check setCalledOperand
auto *SvOp = Call0->getCalledOperand();
Call0->setCalledOperand(Bar3F);
EXPECT_EQ(Call0->getCalledOperand(), Bar3F);
Call0->setCalledOperand(SvOp);
// Check setCalledFunction
Call2->setCalledFunction(Bar3F);
EXPECT_EQ(Call2->getCalledFunction(), Bar3F);
}
TEST_F(SandboxIRTest, CallInst) {
parseIR(C, R"IR(
define i8 @foo(i8 %arg) {
%call = call i8 @foo(i8 %arg)
ret i8 %call
}
)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 It = BB->begin();
auto *Call = cast<sandboxir::CallInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
EXPECT_EQ(Call->getNumOperands(), 2u);
EXPECT_EQ(Ret->getOpcode(), sandboxir::Instruction::Opcode::Ret);
sandboxir::FunctionType *FTy = F.getFunctionType();
SmallVector<sandboxir::Value *, 1> Args;
Args.push_back(Arg0);
{
// Check create() WhereIt.
auto *Call = cast<sandboxir::CallInst>(sandboxir::CallInst::create(
FTy, &F, Args, /*WhereIt=*/Ret->getIterator(), Ctx));
EXPECT_EQ(Call->getNextNode(), Ret);
EXPECT_EQ(Call->getCalledFunction(), &F);
EXPECT_EQ(range_size(Call->args()), 1u);
EXPECT_EQ(Call->getArgOperand(0), Arg0);
}
{
// Check create() InsertBefore.
auto *Call = cast<sandboxir::CallInst>(
sandboxir::CallInst::create(FTy, &F, Args, Ret->getIterator(), Ctx));
EXPECT_EQ(Call->getNextNode(), Ret);
EXPECT_EQ(Call->getCalledFunction(), &F);
EXPECT_EQ(range_size(Call->args()), 1u);
EXPECT_EQ(Call->getArgOperand(0), Arg0);
}
{
// Check create() InsertAtEnd.
auto *Call = cast<sandboxir::CallInst>(
sandboxir::CallInst::create(FTy, &F, Args, /*InsertAtEnd=*/BB, Ctx));
EXPECT_EQ(Call->getPrevNode(), Ret);
EXPECT_EQ(Call->getCalledFunction(), &F);
EXPECT_EQ(range_size(Call->args()), 1u);
EXPECT_EQ(Call->getArgOperand(0), Arg0);
}
}
TEST_F(SandboxIRTest, InvokeInst) {
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:
%lpad = landingpad { ptr, i32}
cleanup
ret void
other_bb:
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *Arg = F.getArg(0);
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 *LandingPad = &*ExceptionBB->begin();
auto *OtherBB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "other_bb")));
auto It = BB0->begin();
// Check classof(Instruction *).
auto *Invoke = cast<sandboxir::InvokeInst>(&*It++);
// Check getNormalDest().
EXPECT_EQ(Invoke->getNormalDest(), NormalBB);
// Check getUnwindDest().
EXPECT_EQ(Invoke->getUnwindDest(), ExceptionBB);
// Check getSuccessor().
EXPECT_EQ(Invoke->getSuccessor(0), NormalBB);
EXPECT_EQ(Invoke->getSuccessor(1), ExceptionBB);
// Check setNormalDest().
Invoke->setNormalDest(OtherBB);
EXPECT_EQ(Invoke->getNormalDest(), OtherBB);
EXPECT_EQ(Invoke->getUnwindDest(), ExceptionBB);
// Check setUnwindDest().
Invoke->setUnwindDest(OtherBB);
EXPECT_EQ(Invoke->getNormalDest(), OtherBB);
EXPECT_EQ(Invoke->getUnwindDest(), OtherBB);
// Check setSuccessor().
Invoke->setSuccessor(0, NormalBB);
EXPECT_EQ(Invoke->getNormalDest(), NormalBB);
Invoke->setSuccessor(1, ExceptionBB);
EXPECT_EQ(Invoke->getUnwindDest(), ExceptionBB);
// Check getLandingPadInst().
EXPECT_EQ(Invoke->getLandingPadInst(), LandingPad);
{
// Check create() WhereIt, WhereBB.
SmallVector<sandboxir::Value *> Args({Arg});
auto *InsertBefore = &*BB0->begin();
auto *NewInvoke = cast<sandboxir::InvokeInst>(sandboxir::InvokeInst::create(
F.getFunctionType(), &F, NormalBB, ExceptionBB, Args,
InsertBefore->getIterator(), Ctx));
EXPECT_EQ(NewInvoke->getNormalDest(), NormalBB);
EXPECT_EQ(NewInvoke->getUnwindDest(), ExceptionBB);
EXPECT_EQ(NewInvoke->getNextNode(), InsertBefore);
}
{
// Check create() InsertBefore.
SmallVector<sandboxir::Value *> Args({Arg});
auto *InsertBefore = &*BB0->begin();
auto *NewInvoke = cast<sandboxir::InvokeInst>(sandboxir::InvokeInst::create(
F.getFunctionType(), &F, NormalBB, ExceptionBB, Args,
InsertBefore->getIterator(), Ctx));
EXPECT_EQ(NewInvoke->getNormalDest(), NormalBB);
EXPECT_EQ(NewInvoke->getUnwindDest(), ExceptionBB);
EXPECT_EQ(NewInvoke->getNextNode(), InsertBefore);
}
{
// Check create() InsertAtEnd.
SmallVector<sandboxir::Value *> Args({Arg});
auto *NewInvoke = cast<sandboxir::InvokeInst>(sandboxir::InvokeInst::create(
F.getFunctionType(), &F, NormalBB, ExceptionBB, Args, BB0, Ctx));
EXPECT_EQ(NewInvoke->getNormalDest(), NormalBB);
EXPECT_EQ(NewInvoke->getUnwindDest(), ExceptionBB);
EXPECT_EQ(NewInvoke->getParent(), BB0);
EXPECT_EQ(NewInvoke->getNextNode(), nullptr);
}
}
TEST_F(SandboxIRTest, CallBrInst) {
parseIR(C, R"IR(
define void @foo(i8 %arg) {
bb0:
callbr void asm "", ""()
to label %bb1 [label %bb2]
bb1:
ret void
bb2:
ret void
other_bb:
ret void
bb3:
callbr void @foo(i8 %arg)
to label %bb1 [label %bb2]
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB0 = getBasicBlockByName(LLVMF, "bb0");
auto *LLVMCallBr = cast<llvm::CallBrInst>(&*LLVMBB0->begin());
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *Arg = F.getArg(0);
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 *BB3 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb3")));
auto *OtherBB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "other_bb")));
auto It = BB0->begin();
// Check classof(Instruction *).
auto *CallBr0 = cast<sandboxir::CallBrInst>(&*It++);
It = BB3->begin();
auto *CallBr1 = cast<sandboxir::CallBrInst>(&*It++);
for (sandboxir::CallBrInst *CallBr : {CallBr0, CallBr1}) {
// Check getNumIndirectDests().
EXPECT_EQ(CallBr->getNumIndirectDests(), 1u);
// Check getIndirectDestLabel().
EXPECT_EQ(CallBr->getIndirectDestLabel(0),
Ctx.getValue(LLVMCallBr->getIndirectDestLabel(0)));
// Check getIndirectDestLabelUse().
EXPECT_EQ(CallBr->getIndirectDestLabelUse(0),
Ctx.getValue(LLVMCallBr->getIndirectDestLabelUse(0)));
// Check getDefaultDest().
EXPECT_EQ(CallBr->getDefaultDest(),
Ctx.getValue(LLVMCallBr->getDefaultDest()));
// Check getIndirectDest().
EXPECT_EQ(CallBr->getIndirectDest(0),
Ctx.getValue(LLVMCallBr->getIndirectDest(0)));
// Check getIndirectDests().
auto Dests = CallBr->getIndirectDests();
EXPECT_EQ(Dests.size(), LLVMCallBr->getIndirectDests().size());
EXPECT_EQ(Dests[0], Ctx.getValue(LLVMCallBr->getIndirectDests()[0]));
// Check getNumSuccessors().
EXPECT_EQ(CallBr->getNumSuccessors(), LLVMCallBr->getNumSuccessors());
// Check getSuccessor().
for (unsigned SuccIdx = 0, E = CallBr->getNumSuccessors(); SuccIdx != E;
++SuccIdx)
EXPECT_EQ(CallBr->getSuccessor(SuccIdx),
Ctx.getValue(LLVMCallBr->getSuccessor(SuccIdx)));
// Check setDefaultDest().
auto *SvDefaultDest = CallBr->getDefaultDest();
CallBr->setDefaultDest(OtherBB);
EXPECT_EQ(CallBr->getDefaultDest(), OtherBB);
CallBr->setDefaultDest(SvDefaultDest);
// Check setIndirectDest().
auto *SvIndirectDest = CallBr->getIndirectDest(0);
CallBr->setIndirectDest(0, OtherBB);
EXPECT_EQ(CallBr->getIndirectDest(0), OtherBB);
CallBr->setIndirectDest(0, SvIndirectDest);
}
{
// Check create() WhereIt, WhereBB.
SmallVector<sandboxir::Value *> Args({Arg});
auto *NewCallBr = cast<sandboxir::CallBrInst>(sandboxir::CallBrInst::create(
F.getFunctionType(), &F, BB1, {BB2}, Args, BB0->end(), Ctx));
EXPECT_EQ(NewCallBr->getDefaultDest(), BB1);
EXPECT_EQ(NewCallBr->getIndirectDests().size(), 1u);
EXPECT_EQ(NewCallBr->getIndirectDests()[0], BB2);
EXPECT_EQ(NewCallBr->getNextNode(), nullptr);
EXPECT_EQ(NewCallBr->getParent(), BB0);
}
{
// Check create() InsertBefore
SmallVector<sandboxir::Value *> Args({Arg});
auto *InsertBefore = &*BB0->rbegin();
auto *NewCallBr = cast<sandboxir::CallBrInst>(
sandboxir::CallBrInst::create(F.getFunctionType(), &F, BB1, {BB2}, Args,
InsertBefore->getIterator(), Ctx));
EXPECT_EQ(NewCallBr->getDefaultDest(), BB1);
EXPECT_EQ(NewCallBr->getIndirectDests().size(), 1u);
EXPECT_EQ(NewCallBr->getIndirectDests()[0], BB2);
EXPECT_EQ(NewCallBr->getNextNode(), InsertBefore);
}
{
// Check create() InsertAtEnd.
SmallVector<sandboxir::Value *> Args({Arg});
auto *NewCallBr = cast<sandboxir::CallBrInst>(sandboxir::CallBrInst::create(
F.getFunctionType(), &F, BB1, {BB2}, Args, BB0, Ctx));
EXPECT_EQ(NewCallBr->getDefaultDest(), BB1);
EXPECT_EQ(NewCallBr->getIndirectDests().size(), 1u);
EXPECT_EQ(NewCallBr->getIndirectDests()[0], BB2);
EXPECT_EQ(NewCallBr->getNextNode(), nullptr);
EXPECT_EQ(NewCallBr->getParent(), BB0);
}
}
TEST_F(SandboxIRTest, LandingPadInst) {
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");
auto *LLVMLPad = cast<llvm::LandingPadInst>(&*LLVMUnwind->begin());
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *Unwind = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMUnwind));
auto *BB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb")));
auto It = Unwind->begin();
auto *LPad = cast<sandboxir::LandingPadInst>(&*It++);
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check isCleanup().
EXPECT_EQ(LPad->isCleanup(), LLVMLPad->isCleanup());
// Check setCleanup().
auto OrigIsCleanup = LPad->isCleanup();
auto NewIsCleanup = true;
EXPECT_NE(NewIsCleanup, OrigIsCleanup);
LPad->setCleanup(NewIsCleanup);
EXPECT_EQ(LPad->isCleanup(), NewIsCleanup);
LPad->setCleanup(OrigIsCleanup);
EXPECT_EQ(LPad->isCleanup(), OrigIsCleanup);
// Check getNumClauses().
EXPECT_EQ(LPad->getNumClauses(), LLVMLPad->getNumClauses());
// Check getClause().
for (auto Idx : seq<unsigned>(0, LPad->getNumClauses()))
EXPECT_EQ(LPad->getClause(Idx), Ctx.getValue(LLVMLPad->getClause(Idx)));
// Check isCatch().
for (auto Idx : seq<unsigned>(0, LPad->getNumClauses()))
EXPECT_EQ(LPad->isCatch(Idx), LLVMLPad->isCatch(Idx));
// Check isFilter().
for (auto Idx : seq<unsigned>(0, LPad->getNumClauses()))
EXPECT_EQ(LPad->isFilter(Idx), LLVMLPad->isFilter(Idx));
// Check create().
auto *BBRet = &*BB->begin();
auto *NewLPad = cast<sandboxir::LandingPadInst>(
sandboxir::LandingPadInst::create(sandboxir::Type::getInt8Ty(Ctx), 0,
BBRet->getIterator(), Ctx, "NewLPad"));
EXPECT_EQ(NewLPad->getNextNode(), BBRet);
EXPECT_FALSE(NewLPad->isCleanup());
#ifndef NDEBUG
EXPECT_EQ(NewLPad->getName(), "NewLPad");
#endif // NDEBUG
}
TEST_F(SandboxIRTest, FuncletPadInst_CatchPadInst_CleanupPadInst) {
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");
BasicBlock *LLVMDispatch = getBasicBlockByName(LLVMF, "dispatch");
BasicBlock *LLVMHandler0 = getBasicBlockByName(LLVMF, "handler0");
BasicBlock *LLVMHandler1 = getBasicBlockByName(LLVMF, "handler1");
auto *LLVMCP = cast<llvm::CatchPadInst>(&*LLVMHandler0->begin());
auto *LLVMCLP = cast<llvm::CleanupPadInst>(&*LLVMHandler1->begin());
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *Dispatch = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMDispatch));
auto *Handler0 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMHandler0));
auto *Handler1 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMHandler1));
auto *BB = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb")));
auto *BBRet = cast<sandboxir::ReturnInst>(&*BB->begin());
auto *CS = cast<sandboxir::CatchSwitchInst>(&*Dispatch->begin());
[[maybe_unused]] auto *CP =
cast<sandboxir::CatchPadInst>(&*Handler0->begin());
[[maybe_unused]] auto *CLP =
cast<sandboxir::CleanupPadInst>(&*Handler1->begin());
// Check getCatchSwitch().
EXPECT_EQ(CP->getCatchSwitch(), CS);
EXPECT_EQ(CP->getCatchSwitch(), Ctx.getValue(LLVMCP->getCatchSwitch()));
for (llvm::FuncletPadInst *LLVMFPI :
{static_cast<llvm::FuncletPadInst *>(LLVMCP),
static_cast<llvm::FuncletPadInst *>(LLVMCLP)}) {
auto *FPI = cast<sandboxir::FuncletPadInst>(Ctx.getValue(LLVMFPI));
// Check arg_size().
EXPECT_EQ(FPI->arg_size(), LLVMFPI->arg_size());
// Check getParentPad().
EXPECT_EQ(FPI->getParentPad(), Ctx.getValue(LLVMFPI->getParentPad()));
// Check setParentPad().
auto *OrigParentPad = FPI->getParentPad();
auto *NewParentPad = Dispatch;
EXPECT_NE(NewParentPad, OrigParentPad);
FPI->setParentPad(NewParentPad);
EXPECT_EQ(FPI->getParentPad(), NewParentPad);
FPI->setParentPad(OrigParentPad);
EXPECT_EQ(FPI->getParentPad(), OrigParentPad);
// Check getArgOperand().
for (auto Idx : seq<unsigned>(0, FPI->arg_size()))
EXPECT_EQ(FPI->getArgOperand(Idx),
Ctx.getValue(LLVMFPI->getArgOperand(Idx)));
// Check setArgOperand().
auto *OrigArgOperand = FPI->getArgOperand(0);
auto *NewArgOperand = Dispatch;
EXPECT_NE(NewArgOperand, OrigArgOperand);
FPI->setArgOperand(0, NewArgOperand);
EXPECT_EQ(FPI->getArgOperand(0), NewArgOperand);
FPI->setArgOperand(0, OrigArgOperand);
EXPECT_EQ(FPI->getArgOperand(0), OrigArgOperand);
}
// Check CatchPadInst::create().
auto *NewCPI = cast<sandboxir::CatchPadInst>(sandboxir::CatchPadInst::create(
CS, {}, BBRet->getIterator(), Ctx, "NewCPI"));
EXPECT_EQ(NewCPI->getCatchSwitch(), CS);
EXPECT_EQ(NewCPI->arg_size(), 0u);
EXPECT_EQ(NewCPI->getNextNode(), BBRet);
#ifndef NDEBUG
EXPECT_EQ(NewCPI->getName(), "NewCPI");
#endif // NDEBUG
// Check CleanupPadInst::create().
auto *NewCLPI =
cast<sandboxir::CleanupPadInst>(sandboxir::CleanupPadInst::create(
CS, {}, BBRet->getIterator(), Ctx, "NewCLPI"));
EXPECT_EQ(NewCLPI->getParentPad(), CS);
EXPECT_EQ(NewCLPI->arg_size(), 0u);
EXPECT_EQ(NewCLPI->getNextNode(), BBRet);
#ifndef NDEBUG
EXPECT_EQ(NewCLPI->getName(), "NewCLPI");
#endif // NDEBUG
}
TEST_F(SandboxIRTest, CatchReturnInst) {
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++);
auto *LLVMCR = cast<llvm::CatchReturnInst>(&*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 getCatchPad().
EXPECT_EQ(CR->getCatchPad(), Ctx.getValue(LLVMCR->getCatchPad()));
// Check setCatchPad().
auto *OrigCP = CR->getCatchPad();
auto *NewCP = CP2;
EXPECT_NE(NewCP, OrigCP);
CR->setCatchPad(NewCP);
EXPECT_EQ(CR->getCatchPad(), NewCP);
CR->setCatchPad(OrigCP);
EXPECT_EQ(CR->getCatchPad(), OrigCP);
// Check getSuccessor().
EXPECT_EQ(CR->getSuccessor(), Ctx.getValue(LLVMCR->getSuccessor()));
// Check setSuccessor().
auto *OrigSucc = CR->getSuccessor();
auto *NewSucc = Catch;
EXPECT_NE(NewSucc, OrigSucc);
CR->setSuccessor(NewSucc);
EXPECT_EQ(CR->getSuccessor(), NewSucc);
CR->setSuccessor(OrigSucc);
EXPECT_EQ(CR->getSuccessor(), OrigSucc);
// Check getNumSuccessors().
EXPECT_EQ(CR->getNumSuccessors(), LLVMCR->getNumSuccessors());
// Check getCatchSwitchParentPad().
EXPECT_EQ(CR->getCatchSwitchParentPad(),
Ctx.getValue(LLVMCR->getCatchSwitchParentPad()));
// Check create().
auto *CRI = cast<sandboxir::CatchReturnInst>(
sandboxir::CatchReturnInst::create(CP, Catch, CP->getIterator(), Ctx));
EXPECT_EQ(CRI->getNextNode(), CP);
EXPECT_EQ(CRI->getCatchPad(), CP);
EXPECT_EQ(CRI->getSuccessor(), Catch);
}
TEST_F(SandboxIRTest, CleanupReturnInst) {
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");
auto LLVMIt = LLVMCleanup->begin();
[[maybe_unused]] auto *LLVMCP = cast<llvm::CleanupPadInst>(&*LLVMIt++);
auto *LLVMCRI = cast<llvm::CleanupReturnInst>(&*LLVMIt++);
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++);
It = Cleanup2->begin();
auto *CP2 = cast<sandboxir::CleanupPadInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check hasUnwindDest().
EXPECT_EQ(CRI->hasUnwindDest(), LLVMCRI->hasUnwindDest());
// Check unwindsToCaller().
EXPECT_EQ(CRI->unwindsToCaller(), LLVMCRI->unwindsToCaller());
// Check getCleanupPad().
EXPECT_EQ(CRI->getCleanupPad(), Ctx.getValue(LLVMCRI->getCleanupPad()));
// Check setCleanupPad().
auto *OrigCleanupPad = CRI->getCleanupPad();
auto *NewCleanupPad = CP2;
EXPECT_NE(NewCleanupPad, OrigCleanupPad);
CRI->setCleanupPad(NewCleanupPad);
EXPECT_EQ(CRI->getCleanupPad(), NewCleanupPad);
CRI->setCleanupPad(OrigCleanupPad);
EXPECT_EQ(CRI->getCleanupPad(), OrigCleanupPad);
// Check setNumSuccessors().
EXPECT_EQ(CRI->getNumSuccessors(), LLVMCRI->getNumSuccessors());
// Check getUnwindDest().
EXPECT_EQ(CRI->getUnwindDest(), Ctx.getValue(LLVMCRI->getUnwindDest()));
// Check setUnwindDest().
auto *OrigUnwindDest = CRI->getUnwindDest();
auto *NewUnwindDest = Throw;
EXPECT_NE(NewUnwindDest, OrigUnwindDest);
CRI->setUnwindDest(NewUnwindDest);
EXPECT_EQ(CRI->getUnwindDest(), NewUnwindDest);
CRI->setUnwindDest(OrigUnwindDest);
EXPECT_EQ(CRI->getUnwindDest(), OrigUnwindDest);
// Check create().
auto *UnwindBB = Cleanup;
auto *NewCRI = sandboxir::CleanupReturnInst::create(CP2, UnwindBB,
Ret->getIterator(), Ctx);
EXPECT_EQ(NewCRI->getCleanupPad(), CP2);
EXPECT_EQ(NewCRI->getUnwindDest(), UnwindBB);
EXPECT_EQ(NewCRI->getNextNode(), Ret);
}
TEST_F(SandboxIRTest, GetElementPtrInstruction) {
parseIR(C, R"IR(
define void @foo(ptr %ptr, <2 x ptr> %ptrs) {
%gep0 = getelementptr i8, ptr %ptr, i32 0
%gep1 = getelementptr nusw i8, ptr %ptr, i32 0
%gep2 = getelementptr nuw i8, ptr %ptr, i32 0
%gep3 = getelementptr inbounds {i32, {i32, i8}}, ptr %ptr, i32 1, i32 0
%gep4 = getelementptr inbounds {i8, i8, {i32, i16}}, <2 x ptr> %ptrs, i32 2, <2 x i32> <i32 0, i32 0>
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
BasicBlock *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
SmallVector<llvm::GetElementPtrInst *, 4> LLVMGEPs;
while (isa<llvm::GetElementPtrInst>(&*LLVMIt))
LLVMGEPs.push_back(cast<llvm::GetElementPtrInst>(&*LLVMIt++));
auto *LLVMRet = cast<llvm::ReturnInst>(&*LLVMIt++);
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
for (llvm::GetElementPtrInst *LLVMGEP : LLVMGEPs) {
// Check classof().
auto *GEP = cast<sandboxir::GetElementPtrInst>(Ctx.getValue(LLVMGEP));
// Check getSourceElementType().
EXPECT_EQ(GEP->getSourceElementType(),
Ctx.getType(LLVMGEP->getSourceElementType()));
// Check getResultElementType().
EXPECT_EQ(GEP->getResultElementType(),
Ctx.getType(LLVMGEP->getResultElementType()));
// Check getAddressSpace().
EXPECT_EQ(GEP->getAddressSpace(), LLVMGEP->getAddressSpace());
// Check indices().
EXPECT_EQ(range_size(GEP->indices()), range_size(LLVMGEP->indices()));
auto IdxIt = GEP->idx_begin();
for (llvm::Value *LLVMIdxV : LLVMGEP->indices()) {
sandboxir::Value *IdxV = *IdxIt++;
EXPECT_EQ(IdxV, Ctx.getValue(LLVMIdxV));
}
// Check getPointerOperand().
EXPECT_EQ(GEP->getPointerOperand(),
Ctx.getValue(LLVMGEP->getPointerOperand()));
// Check getPointerOperandIndex().
EXPECT_EQ(GEP->getPointerOperandIndex(), LLVMGEP->getPointerOperandIndex());
// Check getPointerOperandType().
EXPECT_EQ(GEP->getPointerOperandType(),
Ctx.getType(LLVMGEP->getPointerOperandType()));
// Check getPointerAddressSpace().
EXPECT_EQ(GEP->getPointerAddressSpace(), LLVMGEP->getPointerAddressSpace());
// Check getNumIndices().
EXPECT_EQ(GEP->getNumIndices(), LLVMGEP->getNumIndices());
// Check hasIndices().
EXPECT_EQ(GEP->hasIndices(), LLVMGEP->hasIndices());
// Check hasAllConstantIndices().
EXPECT_EQ(GEP->hasAllConstantIndices(), LLVMGEP->hasAllConstantIndices());
// Check getNoWrapFlags().
EXPECT_EQ(GEP->getNoWrapFlags(), LLVMGEP->getNoWrapFlags());
// Check isInBounds().
EXPECT_EQ(GEP->isInBounds(), LLVMGEP->isInBounds());
// Check hasNoUnsignedWrap().
EXPECT_EQ(GEP->hasNoUnsignedWrap(), LLVMGEP->hasNoUnsignedWrap());
// Check accumulateConstantOffset().
const DataLayout &DL = M->getDataLayout();
APInt Offset1 =
APInt::getZero(DL.getIndexSizeInBits(GEP->getPointerAddressSpace()));
APInt Offset2 =
APInt::getZero(DL.getIndexSizeInBits(GEP->getPointerAddressSpace()));
EXPECT_EQ(GEP->accumulateConstantOffset(DL, Offset1),
LLVMGEP->accumulateConstantOffset(DL, Offset2));
EXPECT_EQ(Offset1, Offset2);
}
auto *BB = &*F.begin();
auto *GEP0 = cast<sandboxir::GetElementPtrInst>(&*BB->begin());
auto *Ret = cast<sandboxir::ReturnInst>(Ctx.getValue(LLVMRet));
SmallVector<sandboxir::Value *> Indices(GEP0->indices());
// Check create() WhereIt, WhereBB.
auto *NewGEP0 =
cast<sandboxir::GetElementPtrInst>(sandboxir::GetElementPtrInst::create(
GEP0->getType(), GEP0->getPointerOperand(), Indices,
Ret->getIterator(), Ctx, "NewGEP0"));
EXPECT_EQ(NewGEP0->getName(), "NewGEP0");
EXPECT_EQ(NewGEP0->getType(), GEP0->getType());
EXPECT_EQ(NewGEP0->getPointerOperand(), GEP0->getPointerOperand());
EXPECT_EQ(range_size(NewGEP0->indices()), range_size(GEP0->indices()));
for (auto NewIt = NewGEP0->idx_begin(), NewItE = NewGEP0->idx_end(),
OldIt = GEP0->idx_begin();
NewIt != NewItE; ++NewIt) {
sandboxir::Value *NewIdxV = *NewIt;
sandboxir::Value *OldIdxV = *OldIt;
EXPECT_EQ(NewIdxV, OldIdxV);
}
EXPECT_EQ(NewGEP0->getNextNode(), Ret);
// Check create() InsertBefore.
auto *NewGEP1 =
cast<sandboxir::GetElementPtrInst>(sandboxir::GetElementPtrInst::create(
GEP0->getType(), GEP0->getPointerOperand(), Indices,
Ret->getIterator(), Ctx, "NewGEP1"));
EXPECT_EQ(NewGEP1->getName(), "NewGEP1");
EXPECT_EQ(NewGEP1->getType(), GEP0->getType());
EXPECT_EQ(NewGEP1->getPointerOperand(), GEP0->getPointerOperand());
EXPECT_EQ(range_size(NewGEP1->indices()), range_size(GEP0->indices()));
for (auto NewIt = NewGEP0->idx_begin(), NewItE = NewGEP0->idx_end(),
OldIt = GEP0->idx_begin();
NewIt != NewItE; ++NewIt) {
sandboxir::Value *NewIdxV = *NewIt;
sandboxir::Value *OldIdxV = *OldIt;
EXPECT_EQ(NewIdxV, OldIdxV);
}
EXPECT_EQ(NewGEP1->getNextNode(), Ret);
// Check create() InsertAtEnd.
auto *NewGEP2 =
cast<sandboxir::GetElementPtrInst>(sandboxir::GetElementPtrInst::create(
GEP0->getType(), GEP0->getPointerOperand(), Indices, BB, Ctx,
"NewGEP2"));
EXPECT_EQ(NewGEP2->getName(), "NewGEP2");
EXPECT_EQ(NewGEP2->getType(), GEP0->getType());
EXPECT_EQ(NewGEP2->getPointerOperand(), GEP0->getPointerOperand());
EXPECT_EQ(range_size(NewGEP2->indices()), range_size(GEP0->indices()));
for (auto NewIt = NewGEP0->idx_begin(), NewItE = NewGEP0->idx_end(),
OldIt = GEP0->idx_begin();
NewIt != NewItE; ++NewIt) {
sandboxir::Value *NewIdxV = *NewIt;
sandboxir::Value *OldIdxV = *OldIt;
EXPECT_EQ(NewIdxV, OldIdxV);
}
EXPECT_EQ(NewGEP2->getPrevNode(), Ret);
EXPECT_EQ(NewGEP2->getNextNode(), nullptr);
}
TEST_F(SandboxIRTest, 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");
BasicBlock *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMAdd = &*LLVMIt++;
auto *LLVMFAdd = &*LLVMIt++;
auto *LLVMUDiv = &*LLVMIt++;
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, LLVMI, GETTER, SETTER) \
{ \
EXPECT_EQ(I->GETTER(), LLVMI->GETTER()); \
bool NewFlagVal = !I->GETTER(); \
I->SETTER(NewFlagVal); \
EXPECT_EQ(I->GETTER(), NewFlagVal); \
EXPECT_EQ(I->GETTER(), LLVMI->GETTER()); \
}
CHECK_FLAG(Add, LLVMAdd, hasNoUnsignedWrap, setHasNoUnsignedWrap);
CHECK_FLAG(Add, LLVMAdd, hasNoSignedWrap, setHasNoSignedWrap);
CHECK_FLAG(FAdd, LLVMFAdd, isFast, setFast);
CHECK_FLAG(FAdd, LLVMFAdd, hasAllowReassoc, setHasAllowReassoc);
CHECK_FLAG(UDiv, LLVMUDiv, isExact, setIsExact);
CHECK_FLAG(FAdd, LLVMFAdd, hasNoNaNs, setHasNoNaNs);
CHECK_FLAG(FAdd, LLVMFAdd, hasNoInfs, setHasNoInfs);
CHECK_FLAG(FAdd, LLVMFAdd, hasNoSignedZeros, setHasNoSignedZeros);
CHECK_FLAG(FAdd, LLVMFAdd, hasAllowReciprocal, setHasAllowReciprocal);
CHECK_FLAG(FAdd, LLVMFAdd, hasAllowContract, setHasAllowContract);
CHECK_FLAG(FAdd, LLVMFAdd, hasApproxFunc, setHasApproxFunc);
// Check getFastMathFlags(), copyFastMathFlags().
FAdd->setFastMathFlags(FastMathFlags::getFast());
EXPECT_FALSE(FAdd->getFastMathFlags() != LLVMFAdd->getFastMathFlags());
FastMathFlags OrigFMF = FAdd->getFastMathFlags();
FastMathFlags NewFMF;
NewFMF.setAllowReassoc(true);
EXPECT_TRUE(NewFMF != OrigFMF);
FAdd->setFastMathFlags(NewFMF);
EXPECT_FALSE(FAdd->getFastMathFlags() != OrigFMF);
FAdd->copyFastMathFlags(NewFMF);
EXPECT_FALSE(FAdd->getFastMathFlags() != NewFMF);
EXPECT_FALSE(FAdd->getFastMathFlags() != LLVMFAdd->getFastMathFlags());
}
TEST_F(SandboxIRTest, 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");
auto *LLVMBB0 = getBasicBlockByName(LLVMF, "bb0");
auto *LLVMBB1 = getBasicBlockByName(LLVMF, "bb1");
auto *LLVMHandler0 = getBasicBlockByName(LLVMF, "handler0");
auto *LLVMHandler1 = getBasicBlockByName(LLVMF, "handler1");
auto *LLVMCleanup = getBasicBlockByName(LLVMF, "cleanup");
auto *LLVMCS0 = cast<llvm::CatchSwitchInst>(&*LLVMBB0->begin());
auto *LLVMCS1 = cast<llvm::CatchSwitchInst>(&*LLVMBB1->begin());
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *BB0 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB0));
auto *BB1 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB1));
auto *Handler0 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMHandler0));
auto *Handler1 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMHandler1));
auto *Cleanup = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMCleanup));
auto *CS0 = cast<sandboxir::CatchSwitchInst>(&*BB0->begin());
auto *CS1 = cast<sandboxir::CatchSwitchInst>(&*BB1->begin());
// Check getParentPad().
EXPECT_EQ(CS0->getParentPad(), Ctx.getValue(LLVMCS0->getParentPad()));
EXPECT_EQ(CS1->getParentPad(), Ctx.getValue(LLVMCS1->getParentPad()));
// Check setParentPad().
auto *OrigPad = CS0->getParentPad();
auto *NewPad = CS1;
EXPECT_NE(NewPad, OrigPad);
CS0->setParentPad(NewPad);
EXPECT_EQ(CS0->getParentPad(), NewPad);
CS0->setParentPad(OrigPad);
EXPECT_EQ(CS0->getParentPad(), OrigPad);
// Check hasUnwindDest().
EXPECT_EQ(CS0->hasUnwindDest(), LLVMCS0->hasUnwindDest());
EXPECT_EQ(CS1->hasUnwindDest(), LLVMCS1->hasUnwindDest());
// Check unwindsToCaller().
EXPECT_EQ(CS0->unwindsToCaller(), LLVMCS0->unwindsToCaller());
EXPECT_EQ(CS1->unwindsToCaller(), LLVMCS1->unwindsToCaller());
// Check getUnwindDest().
EXPECT_EQ(CS0->getUnwindDest(), Ctx.getValue(LLVMCS0->getUnwindDest()));
EXPECT_EQ(CS1->getUnwindDest(), Ctx.getValue(LLVMCS1->getUnwindDest()));
// Check setUnwindDest().
auto *OrigUnwindDest = CS1->getUnwindDest();
auto *NewUnwindDest = BB0;
EXPECT_NE(NewUnwindDest, OrigUnwindDest);
CS1->setUnwindDest(NewUnwindDest);
EXPECT_EQ(CS1->getUnwindDest(), NewUnwindDest);
CS1->setUnwindDest(OrigUnwindDest);
EXPECT_EQ(CS1->getUnwindDest(), OrigUnwindDest);
// Check getNumHandlers().
EXPECT_EQ(CS0->getNumHandlers(), LLVMCS0->getNumHandlers());
EXPECT_EQ(CS1->getNumHandlers(), LLVMCS1->getNumHandlers());
// Check handler_begin(), handler_end().
auto It = CS0->handler_begin();
EXPECT_EQ(*It++, Handler0);
EXPECT_EQ(*It++, Handler1);
EXPECT_EQ(It, CS0->handler_end());
// Check handlers().
SmallVector<sandboxir::BasicBlock *, 2> Handlers;
for (sandboxir::BasicBlock *Handler : CS0->handlers())
Handlers.push_back(Handler);
EXPECT_EQ(Handlers.size(), 2u);
EXPECT_EQ(Handlers[0], Handler0);
EXPECT_EQ(Handlers[1], Handler1);
// Check addHandler().
CS0->addHandler(BB0);
EXPECT_EQ(CS0->getNumHandlers(), 3u);
EXPECT_EQ(*std::next(CS0->handler_begin(), 2), BB0);
// Check getNumSuccessors().
EXPECT_EQ(CS0->getNumSuccessors(), LLVMCS0->getNumSuccessors());
EXPECT_EQ(CS1->getNumSuccessors(), LLVMCS1->getNumSuccessors());
// Check getSuccessor().
for (auto SuccIdx : seq<unsigned>(0, CS0->getNumSuccessors()))
EXPECT_EQ(CS0->getSuccessor(SuccIdx),
Ctx.getValue(LLVMCS0->getSuccessor(SuccIdx)));
// Check setSuccessor().
auto *OrigSuccessor = CS0->getSuccessor(0);
auto *NewSuccessor = BB0;
EXPECT_NE(NewSuccessor, OrigSuccessor);
CS0->setSuccessor(0, NewSuccessor);
EXPECT_EQ(CS0->getSuccessor(0), NewSuccessor);
CS0->setSuccessor(0, OrigSuccessor);
EXPECT_EQ(CS0->getSuccessor(0), OrigSuccessor);
// Check create().
CS1->eraseFromParent();
auto *NewCSI = sandboxir::CatchSwitchInst::create(
CS0, Cleanup, 2, BB1->begin(), Ctx, "NewCSI");
EXPECT_TRUE(isa<sandboxir::CatchSwitchInst>(NewCSI));
EXPECT_EQ(NewCSI->getParentPad(), CS0);
}
TEST_F(SandboxIRTest, ResumeInst) {
parseIR(C, R"IR(
define void @foo() {
entry:
invoke void @foo()
to label %bb unwind label %unwind
bb:
ret void
unwind:
%lpad = landingpad { ptr, i32 }
cleanup
resume { ptr, i32 } %lpad
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMUnwindBB = getBasicBlockByName(LLVMF, "unwind");
auto LLVMIt = LLVMUnwindBB->begin();
[[maybe_unused]] auto *LLVMLPad = cast<llvm::LandingPadInst>(&*LLVMIt++);
auto *LLVMResume = cast<llvm::ResumeInst>(&*LLVMIt++);
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *UnwindBB = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMUnwindBB));
auto It = UnwindBB->begin();
auto *LPad = cast<sandboxir::LandingPadInst>(&*It++);
auto *Resume = cast<sandboxir::ResumeInst>(&*It++);
// Check getValue().
EXPECT_EQ(Resume->getValue(), LPad);
EXPECT_EQ(Resume->getValue(), Ctx.getValue(LLVMResume->getValue()));
// Check getNumSuccessors().
EXPECT_EQ(Resume->getNumSuccessors(), LLVMResume->getNumSuccessors());
// Check create().
auto *NewResume = sandboxir::ResumeInst::create(LPad, UnwindBB->end(), Ctx);
EXPECT_EQ(NewResume->getValue(), LPad);
EXPECT_EQ(NewResume->getParent(), UnwindBB);
EXPECT_EQ(NewResume->getNextNode(), nullptr);
}
TEST_F(SandboxIRTest, SwitchInst) {
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");
auto *LLVMSwitch = cast<llvm::SwitchInst>(&*LLVMEntry->begin());
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *Cond1 = F.getArg(1);
auto *Entry = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMEntry));
auto *Switch = cast<sandboxir::SwitchInst>(&*Entry->begin());
auto *BB0 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
auto *BB1 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb1")));
auto *Default = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "default")));
// Check getCondition().
EXPECT_EQ(Switch->getCondition(), Ctx.getValue(LLVMSwitch->getCondition()));
// Check setCondition().
auto *OrigCond = Switch->getCondition();
auto *NewCond = Cond1;
EXPECT_NE(NewCond, OrigCond);
Switch->setCondition(NewCond);
EXPECT_EQ(Switch->getCondition(), NewCond);
Switch->setCondition(OrigCond);
EXPECT_EQ(Switch->getCondition(), OrigCond);
// Check getDefaultDest().
EXPECT_EQ(Switch->getDefaultDest(),
Ctx.getValue(LLVMSwitch->getDefaultDest()));
EXPECT_EQ(Switch->getDefaultDest(), Default);
// Check defaultDestUndefined().
EXPECT_EQ(Switch->defaultDestUndefined(), LLVMSwitch->defaultDestUndefined());
// Check setDefaultDest().
auto *OrigDefaultDest = Switch->getDefaultDest();
auto *NewDefaultDest = Entry;
EXPECT_NE(NewDefaultDest, OrigDefaultDest);
Switch->setDefaultDest(NewDefaultDest);
EXPECT_EQ(Switch->getDefaultDest(), NewDefaultDest);
Switch->setDefaultDest(OrigDefaultDest);
EXPECT_EQ(Switch->getDefaultDest(), OrigDefaultDest);
// Check getNumCases().
EXPECT_EQ(Switch->getNumCases(), LLVMSwitch->getNumCases());
// Check getNumSuccessors().
EXPECT_EQ(Switch->getNumSuccessors(), LLVMSwitch->getNumSuccessors());
// Check getSuccessor().
for (auto SuccIdx : seq<unsigned>(0, Switch->getNumSuccessors()))
EXPECT_EQ(Switch->getSuccessor(SuccIdx),
Ctx.getValue(LLVMSwitch->getSuccessor(SuccIdx)));
// Check setSuccessor().
auto *OrigSucc = Switch->getSuccessor(0);
auto *NewSucc = Entry;
EXPECT_NE(NewSucc, OrigSucc);
Switch->setSuccessor(0, NewSucc);
EXPECT_EQ(Switch->getSuccessor(0), NewSucc);
Switch->setSuccessor(0, OrigSucc);
EXPECT_EQ(Switch->getSuccessor(0), OrigSucc);
// Check case_begin(), case_end(), CaseIt.
auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0);
auto *One = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 1);
auto CaseIt = Switch->case_begin();
{
sandboxir::SwitchInst::CaseHandle Case = *CaseIt++;
EXPECT_EQ(Case.getCaseValue(), Zero);
EXPECT_EQ(Case.getCaseSuccessor(), BB0);
EXPECT_EQ(Case.getCaseIndex(), 0u);
EXPECT_EQ(Case.getSuccessorIndex(), 1u);
}
{
sandboxir::SwitchInst::CaseHandle Case = *CaseIt++;
EXPECT_EQ(Case.getCaseValue(), One);
EXPECT_EQ(Case.getCaseSuccessor(), BB1);
EXPECT_EQ(Case.getCaseIndex(), 1u);
EXPECT_EQ(Case.getSuccessorIndex(), 2u);
}
EXPECT_EQ(CaseIt, Switch->case_end());
// Check cases().
unsigned CntCase = 0;
for (auto &Case : Switch->cases()) {
EXPECT_EQ(Case.getCaseIndex(), CntCase);
++CntCase;
}
EXPECT_EQ(CntCase, 2u);
// Check case_default().
auto CaseDefault = *Switch->case_default();
EXPECT_EQ(CaseDefault.getCaseSuccessor(), Default);
EXPECT_EQ(CaseDefault.getCaseIndex(),
sandboxir::SwitchInst::DefaultPseudoIndex);
// Check findCaseValue().
EXPECT_EQ(Switch->findCaseValue(Zero)->getCaseIndex(), 0u);
EXPECT_EQ(Switch->findCaseValue(One)->getCaseIndex(), 1u);
// Check findCaseDest().
EXPECT_EQ(Switch->findCaseDest(BB0), Zero);
EXPECT_EQ(Switch->findCaseDest(BB1), One);
EXPECT_EQ(Switch->findCaseDest(Entry), nullptr);
// Check addCase().
auto *Two = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 2);
Switch->addCase(Two, Entry);
auto CaseTwoIt = Switch->findCaseValue(Two);
auto CaseTwo = *CaseTwoIt;
EXPECT_EQ(CaseTwo.getCaseValue(), Two);
EXPECT_EQ(CaseTwo.getCaseSuccessor(), Entry);
EXPECT_EQ(Switch->getNumCases(), 3u);
// Check removeCase().
auto RemovedIt = Switch->removeCase(CaseTwoIt);
EXPECT_EQ(RemovedIt, Switch->case_end());
EXPECT_EQ(Switch->getNumCases(), 2u);
// Check create().
auto NewSwitch = sandboxir::SwitchInst::create(
Cond1, Default, 1, Default->begin(), Ctx, "NewSwitch");
EXPECT_TRUE(isa<sandboxir::SwitchInst>(NewSwitch));
EXPECT_EQ(NewSwitch->getCondition(), Cond1);
EXPECT_EQ(NewSwitch->getDefaultDest(), Default);
}
TEST_F(SandboxIRTest, UnaryOperator) {
parseIR(C, R"IR(
define void @foo(float %arg0) {
%fneg = fneg float %arg0
%copyfrom = fadd reassoc float %arg0, 42.0
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *Arg0 = F.getArg(0);
auto *BB = &*F.begin();
auto It = BB->begin();
auto *I = cast<sandboxir::UnaryOperator>(&*It++);
auto *CopyFrom = cast<sandboxir::BinaryOperator>(&*It++);
auto *Ret = &*It++;
EXPECT_EQ(I->getOpcode(), sandboxir::Instruction::Opcode::FNeg);
EXPECT_EQ(I->getOperand(0), Arg0);
{
// Check create() WhereIt, WhereBB.
auto *NewI =
cast<sandboxir::UnaryOperator>(sandboxir::UnaryOperator::create(
sandboxir::Instruction::Opcode::FNeg, Arg0, Ret->getIterator(), Ctx,
"New1"));
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg);
EXPECT_EQ(NewI->getOperand(0), Arg0);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "New1");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertBefore.
auto *NewI =
cast<sandboxir::UnaryOperator>(sandboxir::UnaryOperator::create(
sandboxir::Instruction::Opcode::FNeg, Arg0, Ret->getIterator(), Ctx,
"New2"));
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg);
EXPECT_EQ(NewI->getOperand(0), Arg0);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "New2");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertAtEnd.
auto *NewI =
cast<sandboxir::UnaryOperator>(sandboxir::UnaryOperator::create(
sandboxir::Instruction::Opcode::FNeg, Arg0, BB, Ctx, "New3"));
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg);
EXPECT_EQ(NewI->getOperand(0), Arg0);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "New3");
#endif // NDEBUG
EXPECT_EQ(NewI->getParent(), BB);
EXPECT_EQ(NewI->getNextNode(), nullptr);
}
{
// Check create() when it gets folded.
auto *FortyTwo = CopyFrom->getOperand(1);
auto *NewV = sandboxir::UnaryOperator::create(
sandboxir::Instruction::Opcode::FNeg, FortyTwo, Ret->getIterator(), Ctx,
"Folded");
EXPECT_TRUE(isa<sandboxir::Constant>(NewV));
}
{
// Check createWithCopiedFlags() WhereIt, WhereBB.
auto *NewI = cast<sandboxir::UnaryOperator>(
sandboxir::UnaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::FNeg, Arg0, CopyFrom,
Ret->getIterator(), Ctx, "NewCopyFrom1"));
EXPECT_EQ(NewI->hasAllowReassoc(), CopyFrom->hasAllowReassoc());
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg);
EXPECT_EQ(NewI->getOperand(0), Arg0);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "NewCopyFrom1");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check createWithCopiedFlags() InsertBefore,
auto *NewI = cast<sandboxir::UnaryOperator>(
sandboxir::UnaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::FNeg, Arg0, CopyFrom,
Ret->getIterator(), Ctx, "NewCopyFrom2"));
EXPECT_EQ(NewI->hasAllowReassoc(), CopyFrom->hasAllowReassoc());
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg);
EXPECT_EQ(NewI->getOperand(0), Arg0);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "NewCopyFrom2");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check createWithCopiedFlags() InsertAtEnd,
auto *NewI = cast<sandboxir::UnaryOperator>(
sandboxir::UnaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::FNeg, Arg0, CopyFrom, BB, Ctx,
"NewCopyFrom3"));
EXPECT_EQ(NewI->hasAllowReassoc(), CopyFrom->hasAllowReassoc());
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg);
EXPECT_EQ(NewI->getOperand(0), Arg0);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "NewCopyFrom3");
#endif // NDEBUG
EXPECT_EQ(NewI->getParent(), BB);
EXPECT_EQ(NewI->getNextNode(), nullptr);
}
{
// Check createWithCopiedFlags() when it gets folded.
auto *FortyTwo = CopyFrom->getOperand(1);
auto *NewV = sandboxir::UnaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::FNeg, FortyTwo, CopyFrom, BB, Ctx,
"Folded");
EXPECT_TRUE(isa<sandboxir::Constant>(NewV));
}
}
TEST_F(SandboxIRTest, BinaryOperator) {
parseIR(C, R"IR(
define void @foo(i8 %arg0, i8 %arg1, float %farg0, float %farg1) {
%add = add i8 %arg0, %arg1
%fadd = fadd float %farg0, %farg1
%sub = sub i8 %arg0, %arg1
%fsub = fsub float %farg0, %farg1
%mul = mul i8 %arg0, %arg1
%fmul = fmul float %farg0, %farg1
%udiv = udiv i8 %arg0, %arg1
%sdiv = sdiv i8 %arg0, %arg1
%fdiv = fdiv float %farg0, %farg1
%urem = urem i8 %arg0, %arg1
%srem = srem i8 %arg0, %arg1
%frem = frem float %farg0, %farg1
%shl = shl i8 %arg0, %arg1
%lshr = lshr i8 %arg0, %arg1
%ashr = ashr i8 %arg0, %arg1
%and = and i8 %arg0, %arg1
%or = or i8 %arg0, %arg1
%xor = xor i8 %arg0, %arg1
%copyfrom = add nsw i8 %arg0, %arg1
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto *Arg0 = F.getArg(0);
auto *Arg1 = F.getArg(1);
auto *FArg0 = F.getArg(2);
auto *FArg1 = F.getArg(3);
auto *BB = &*F.begin();
auto It = BB->begin();
#define CHECK_IBINOP(OPCODE) \
{ \
auto *I = cast<sandboxir::BinaryOperator>(&*It++); \
EXPECT_EQ(I->getOpcode(), OPCODE); \
EXPECT_EQ(I->getOperand(0), Arg0); \
EXPECT_EQ(I->getOperand(1), Arg1); \
}
#define CHECK_FBINOP(OPCODE) \
{ \
auto *I = cast<sandboxir::BinaryOperator>(&*It++); \
EXPECT_EQ(I->getOpcode(), OPCODE); \
EXPECT_EQ(I->getOperand(0), FArg0); \
EXPECT_EQ(I->getOperand(1), FArg1); \
}
CHECK_IBINOP(sandboxir::Instruction::Opcode::Add);
CHECK_FBINOP(sandboxir::Instruction::Opcode::FAdd);
CHECK_IBINOP(sandboxir::Instruction::Opcode::Sub);
CHECK_FBINOP(sandboxir::Instruction::Opcode::FSub);
CHECK_IBINOP(sandboxir::Instruction::Opcode::Mul);
CHECK_FBINOP(sandboxir::Instruction::Opcode::FMul);
CHECK_IBINOP(sandboxir::Instruction::Opcode::UDiv);
CHECK_IBINOP(sandboxir::Instruction::Opcode::SDiv);
CHECK_FBINOP(sandboxir::Instruction::Opcode::FDiv);
CHECK_IBINOP(sandboxir::Instruction::Opcode::URem);
CHECK_IBINOP(sandboxir::Instruction::Opcode::SRem);
CHECK_FBINOP(sandboxir::Instruction::Opcode::FRem);
CHECK_IBINOP(sandboxir::Instruction::Opcode::Shl);
CHECK_IBINOP(sandboxir::Instruction::Opcode::LShr);
CHECK_IBINOP(sandboxir::Instruction::Opcode::AShr);
CHECK_IBINOP(sandboxir::Instruction::Opcode::And);
CHECK_IBINOP(sandboxir::Instruction::Opcode::Or);
CHECK_IBINOP(sandboxir::Instruction::Opcode::Xor);
auto *CopyFrom = cast<sandboxir::BinaryOperator>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
{
// Check create() WhereIt, WhereBB.
auto *NewI =
cast<sandboxir::BinaryOperator>(sandboxir::BinaryOperator::create(
sandboxir::Instruction::Opcode::Add, Arg0, Arg1, Ret->getIterator(),
Ctx, "New1"));
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add);
EXPECT_EQ(NewI->getOperand(0), Arg0);
EXPECT_EQ(NewI->getOperand(1), Arg1);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "New1");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertBefore.
auto *NewI =
cast<sandboxir::BinaryOperator>(sandboxir::BinaryOperator::create(
sandboxir::Instruction::Opcode::Add, Arg0, Arg1, Ret->getIterator(),
Ctx, "New2"));
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add);
EXPECT_EQ(NewI->getOperand(0), Arg0);
EXPECT_EQ(NewI->getOperand(1), Arg1);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "New2");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertAtEnd.
auto *NewI =
cast<sandboxir::BinaryOperator>(sandboxir::BinaryOperator::create(
sandboxir::Instruction::Opcode::Add, Arg0, Arg1,
/*InsertAtEnd=*/BB, Ctx, "New3"));
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add);
EXPECT_EQ(NewI->getOperand(0), Arg0);
EXPECT_EQ(NewI->getOperand(1), Arg1);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "New3");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), nullptr);
EXPECT_EQ(NewI->getParent(), BB);
}
{
// Check create() when it gets folded.
auto *FortyTwo =
sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42);
auto *NewV = sandboxir::BinaryOperator::create(
sandboxir::Instruction::Opcode::Add, FortyTwo, FortyTwo,
Ret->getIterator(), Ctx, "Folded");
EXPECT_TRUE(isa<sandboxir::Constant>(NewV));
}
{
// Check createWithCopiedFlags() WhereIt, WhereBB.
auto *NewI = cast<sandboxir::BinaryOperator>(
sandboxir::BinaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::Add, Arg0, Arg1, CopyFrom,
Ret->getIterator(), Ctx, "NewNSW1"));
EXPECT_EQ(NewI->hasNoSignedWrap(), CopyFrom->hasNoSignedWrap());
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add);
EXPECT_EQ(NewI->getOperand(0), Arg0);
EXPECT_EQ(NewI->getOperand(1), Arg1);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "NewNSW1");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check createWithCopiedFlags() InsertBefore.
auto *NewI = cast<sandboxir::BinaryOperator>(
sandboxir::BinaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::Add, Arg0, Arg1, CopyFrom,
Ret->getIterator(), Ctx, "NewNSW2"));
EXPECT_EQ(NewI->hasNoSignedWrap(), CopyFrom->hasNoSignedWrap());
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add);
EXPECT_EQ(NewI->getOperand(0), Arg0);
EXPECT_EQ(NewI->getOperand(1), Arg1);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "NewNSW2");
#endif // NDEBUG
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check createWithCopiedFlags() InsertAtEnd.
auto *NewI = cast<sandboxir::BinaryOperator>(
sandboxir::BinaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::Add, Arg0, Arg1, CopyFrom, BB, Ctx,
"NewNSW3"));
EXPECT_EQ(NewI->hasNoSignedWrap(), CopyFrom->hasNoSignedWrap());
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add);
EXPECT_EQ(NewI->getOperand(0), Arg0);
EXPECT_EQ(NewI->getOperand(1), Arg1);
#ifndef NDEBUG
EXPECT_EQ(NewI->getName(), "NewNSW3");
#endif // NDEBUG
EXPECT_EQ(NewI->getParent(), BB);
EXPECT_EQ(NewI->getNextNode(), nullptr);
}
{
// Check createWithCopiedFlags() when it gets folded.
auto *FortyTwo =
sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42);
auto *NewV = sandboxir::BinaryOperator::createWithCopiedFlags(
sandboxir::Instruction::Opcode::Add, FortyTwo, FortyTwo, CopyFrom,
Ret->getIterator(), Ctx, "Folded");
EXPECT_TRUE(isa<sandboxir::Constant>(NewV));
}
}
TEST_F(SandboxIRTest, PossiblyDisjointInst) {
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(), isDisjoint().
auto OrigIsDisjoint = PDI->isDisjoint();
auto NewIsDisjoint = true;
EXPECT_NE(NewIsDisjoint, OrigIsDisjoint);
PDI->setIsDisjoint(NewIsDisjoint);
EXPECT_EQ(PDI->isDisjoint(), NewIsDisjoint);
PDI->setIsDisjoint(OrigIsDisjoint);
EXPECT_EQ(PDI->isDisjoint(), OrigIsDisjoint);
}
TEST_F(SandboxIRTest, AtomicRMWInst) {
parseIR(C, R"IR(
define void @foo(ptr %ptr, i8 %arg) {
%atomicrmw = atomicrmw add ptr %ptr, i8 %arg acquire, align 128
ret void
}
)IR");
llvm::Function &LLVMF = *M->getFunction("foo");
llvm::BasicBlock *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMRMW = cast<llvm::AtomicRMWInst>(&*LLVMIt++);
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
auto *Ptr = F->getArg(0);
auto *Arg = F->getArg(1);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *RMW = cast<sandboxir::AtomicRMWInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check getOperationName().
EXPECT_EQ(
sandboxir::AtomicRMWInst::getOperationName(
sandboxir::AtomicRMWInst::BinOp::Add),
llvm::AtomicRMWInst::getOperationName(llvm::AtomicRMWInst::BinOp::Add));
// Check isFPOperation().
EXPECT_EQ(
sandboxir::AtomicRMWInst::isFPOperation(
sandboxir::AtomicRMWInst::BinOp::Add),
llvm::AtomicRMWInst::isFPOperation(llvm::AtomicRMWInst::BinOp::Add));
EXPECT_FALSE(sandboxir::AtomicRMWInst::isFPOperation(
sandboxir::AtomicRMWInst::BinOp::Add));
EXPECT_TRUE(sandboxir::AtomicRMWInst::isFPOperation(
sandboxir::AtomicRMWInst::BinOp::FAdd));
// Check setOperation(), getOperation().
EXPECT_EQ(RMW->getOperation(), LLVMRMW->getOperation());
RMW->setOperation(sandboxir::AtomicRMWInst::BinOp::Sub);
EXPECT_EQ(RMW->getOperation(), sandboxir::AtomicRMWInst::BinOp::Sub);
RMW->setOperation(sandboxir::AtomicRMWInst::BinOp::Add);
// Check getAlign().
EXPECT_EQ(RMW->getAlign(), LLVMRMW->getAlign());
auto OrigAlign = RMW->getAlign();
Align NewAlign(256);
EXPECT_NE(NewAlign, OrigAlign);
RMW->setAlignment(NewAlign);
EXPECT_EQ(RMW->getAlign(), NewAlign);
RMW->setAlignment(OrigAlign);
EXPECT_EQ(RMW->getAlign(), OrigAlign);
// Check isVolatile(), setVolatile().
EXPECT_EQ(RMW->isVolatile(), LLVMRMW->isVolatile());
bool OrigV = RMW->isVolatile();
bool NewV = true;
EXPECT_NE(NewV, OrigV);
RMW->setVolatile(NewV);
EXPECT_EQ(RMW->isVolatile(), NewV);
RMW->setVolatile(OrigV);
EXPECT_EQ(RMW->isVolatile(), OrigV);
// Check getOrdering(), setOrdering().
EXPECT_EQ(RMW->getOrdering(), LLVMRMW->getOrdering());
auto OldOrdering = RMW->getOrdering();
auto NewOrdering = AtomicOrdering::Monotonic;
EXPECT_NE(NewOrdering, OldOrdering);
RMW->setOrdering(NewOrdering);
EXPECT_EQ(RMW->getOrdering(), NewOrdering);
RMW->setOrdering(OldOrdering);
EXPECT_EQ(RMW->getOrdering(), OldOrdering);
// Check getSyncScopeID(), setSyncScopeID().
EXPECT_EQ(RMW->getSyncScopeID(), LLVMRMW->getSyncScopeID());
auto OrigSSID = RMW->getSyncScopeID();
SyncScope::ID NewSSID = SyncScope::SingleThread;
EXPECT_NE(NewSSID, OrigSSID);
RMW->setSyncScopeID(NewSSID);
EXPECT_EQ(RMW->getSyncScopeID(), NewSSID);
RMW->setSyncScopeID(OrigSSID);
EXPECT_EQ(RMW->getSyncScopeID(), OrigSSID);
// Check getPointerOperand().
EXPECT_EQ(RMW->getPointerOperand(),
Ctx.getValue(LLVMRMW->getPointerOperand()));
// Check getValOperand().
EXPECT_EQ(RMW->getValOperand(), Ctx.getValue(LLVMRMW->getValOperand()));
// Check getPointerAddressSpace().
EXPECT_EQ(RMW->getPointerAddressSpace(), LLVMRMW->getPointerAddressSpace());
// Check isFloatingPointOperation().
EXPECT_EQ(RMW->isFloatingPointOperation(),
LLVMRMW->isFloatingPointOperation());
Align Align(1024);
auto Ordering = AtomicOrdering::Acquire;
auto SSID = SyncScope::System;
{
// Check create() WhereIt, WhereBB.
auto *NewI =
cast<sandboxir::AtomicRMWInst>(sandboxir::AtomicRMWInst::create(
sandboxir::AtomicRMWInst::BinOp::Sub, Ptr, Arg, Align, Ordering,
Ret->getIterator(), Ctx, SSID, "NewAtomicRMW1"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicRMW);
// Check getAlign().
EXPECT_EQ(NewI->getAlign(), Align);
// Check getSuccessOrdering().
EXPECT_EQ(NewI->getOrdering(), Ordering);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
// Check getPointerOperand().
EXPECT_EQ(NewI->getPointerOperand(), Ptr);
// Check getValOperand().
EXPECT_EQ(NewI->getValOperand(), Arg);
#ifndef NDEBUG
// Check getName().
EXPECT_EQ(NewI->getName(), "NewAtomicRMW1");
#endif // NDEBUG
}
{
// Check create() InsertBefore.
auto *NewI =
cast<sandboxir::AtomicRMWInst>(sandboxir::AtomicRMWInst::create(
sandboxir::AtomicRMWInst::BinOp::Sub, Ptr, Arg, Align, Ordering,
Ret->getIterator(), Ctx, SSID, "NewAtomicRMW2"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicRMW);
// Check getAlign().
EXPECT_EQ(NewI->getAlign(), Align);
// Check getSuccessOrdering().
EXPECT_EQ(NewI->getOrdering(), Ordering);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
// Check getPointerOperand().
EXPECT_EQ(NewI->getPointerOperand(), Ptr);
// Check getValOperand().
EXPECT_EQ(NewI->getValOperand(), Arg);
#ifndef NDEBUG
// Check getName().
EXPECT_EQ(NewI->getName(), "NewAtomicRMW2");
#endif // NDEBUG
}
{
// Check create() InsertAtEnd.
auto *NewI =
cast<sandboxir::AtomicRMWInst>(sandboxir::AtomicRMWInst::create(
sandboxir::AtomicRMWInst::BinOp::Sub, Ptr, Arg, Align, Ordering, BB,
Ctx, SSID, "NewAtomicRMW3"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicRMW);
// Check getAlign().
EXPECT_EQ(NewI->getAlign(), Align);
// Check getSuccessOrdering().
EXPECT_EQ(NewI->getOrdering(), Ordering);
// Check instr position.
EXPECT_EQ(NewI->getParent(), BB);
EXPECT_EQ(NewI->getNextNode(), nullptr);
// Check getPointerOperand().
EXPECT_EQ(NewI->getPointerOperand(), Ptr);
// Check getValOperand().
EXPECT_EQ(NewI->getValOperand(), Arg);
#ifndef NDEBUG
// Check getName().
EXPECT_EQ(NewI->getName(), "NewAtomicRMW3");
#endif // NDEBUG
}
}
TEST_F(SandboxIRTest, AtomicCmpXchgInst) {
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");
llvm::Function &LLVMF = *M->getFunction("foo");
llvm::BasicBlock *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMCmpXchg = cast<llvm::AtomicCmpXchgInst>(&*LLVMIt++);
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
auto *Ptr = F->getArg(0);
auto *Cmp = F->getArg(1);
auto *New = F->getArg(2);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *CmpXchg = cast<sandboxir::AtomicCmpXchgInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check getAlign(), setAlignment().
EXPECT_EQ(CmpXchg->getAlign(), LLVMCmpXchg->getAlign());
auto OrigAlign = CmpXchg->getAlign();
Align NewAlign(256);
EXPECT_NE(NewAlign, OrigAlign);
CmpXchg->setAlignment(NewAlign);
EXPECT_EQ(CmpXchg->getAlign(), NewAlign);
CmpXchg->setAlignment(OrigAlign);
EXPECT_EQ(CmpXchg->getAlign(), OrigAlign);
// Check isVolatile(), setVolatile().
EXPECT_EQ(CmpXchg->isVolatile(), LLVMCmpXchg->isVolatile());
bool OrigV = CmpXchg->isVolatile();
bool NewV = true;
EXPECT_NE(NewV, OrigV);
CmpXchg->setVolatile(NewV);
EXPECT_EQ(CmpXchg->isVolatile(), NewV);
CmpXchg->setVolatile(OrigV);
EXPECT_EQ(CmpXchg->isVolatile(), OrigV);
// Check isWeak(), setWeak().
EXPECT_EQ(CmpXchg->isWeak(), LLVMCmpXchg->isWeak());
bool OrigWeak = CmpXchg->isWeak();
bool NewWeak = true;
EXPECT_NE(NewWeak, OrigWeak);
CmpXchg->setWeak(NewWeak);
EXPECT_EQ(CmpXchg->isWeak(), NewWeak);
CmpXchg->setWeak(OrigWeak);
EXPECT_EQ(CmpXchg->isWeak(), OrigWeak);
// Check isValidSuccessOrdering(), isValidFailureOrdering().
SmallVector<AtomicOrdering> AllOrderings(
{AtomicOrdering::NotAtomic, AtomicOrdering::Unordered,
AtomicOrdering::Monotonic, AtomicOrdering::Acquire,
AtomicOrdering::Release, AtomicOrdering::AcquireRelease,
AtomicOrdering::SequentiallyConsistent});
for (auto Ordering : AllOrderings) {
EXPECT_EQ(sandboxir::AtomicCmpXchgInst::isValidSuccessOrdering(Ordering),
llvm::AtomicCmpXchgInst::isValidSuccessOrdering(Ordering));
EXPECT_EQ(sandboxir::AtomicCmpXchgInst::isValidFailureOrdering(Ordering),
llvm::AtomicCmpXchgInst::isValidFailureOrdering(Ordering));
}
// Check getSuccessOrdering(), setSuccessOrdering().
EXPECT_EQ(CmpXchg->getSuccessOrdering(), LLVMCmpXchg->getSuccessOrdering());
auto OldSuccOrdering = CmpXchg->getSuccessOrdering();
auto NewSuccOrdering = AtomicOrdering::Acquire;
EXPECT_NE(NewSuccOrdering, OldSuccOrdering);
CmpXchg->setSuccessOrdering(NewSuccOrdering);
EXPECT_EQ(CmpXchg->getSuccessOrdering(), NewSuccOrdering);
CmpXchg->setSuccessOrdering(OldSuccOrdering);
EXPECT_EQ(CmpXchg->getSuccessOrdering(), OldSuccOrdering);
// Check getFailureOrdering(), setFailureOrdering().
EXPECT_EQ(CmpXchg->getFailureOrdering(), LLVMCmpXchg->getFailureOrdering());
auto OldFailOrdering = CmpXchg->getFailureOrdering();
auto NewFailOrdering = AtomicOrdering::Acquire;
EXPECT_NE(NewFailOrdering, OldFailOrdering);
CmpXchg->setFailureOrdering(NewFailOrdering);
EXPECT_EQ(CmpXchg->getFailureOrdering(), NewFailOrdering);
CmpXchg->setFailureOrdering(OldFailOrdering);
EXPECT_EQ(CmpXchg->getFailureOrdering(), OldFailOrdering);
// Check getMergedOrdering().
EXPECT_EQ(CmpXchg->getMergedOrdering(), LLVMCmpXchg->getMergedOrdering());
// Check getSyncScopeID(), setSyncScopeID().
EXPECT_EQ(CmpXchg->getSyncScopeID(), LLVMCmpXchg->getSyncScopeID());
auto OrigSSID = CmpXchg->getSyncScopeID();
SyncScope::ID NewSSID = SyncScope::SingleThread;
EXPECT_NE(NewSSID, OrigSSID);
CmpXchg->setSyncScopeID(NewSSID);
EXPECT_EQ(CmpXchg->getSyncScopeID(), NewSSID);
CmpXchg->setSyncScopeID(OrigSSID);
EXPECT_EQ(CmpXchg->getSyncScopeID(), OrigSSID);
// Check getPointerOperand().
EXPECT_EQ(CmpXchg->getPointerOperand(),
Ctx.getValue(LLVMCmpXchg->getPointerOperand()));
// Check getCompareOperand().
EXPECT_EQ(CmpXchg->getCompareOperand(),
Ctx.getValue(LLVMCmpXchg->getCompareOperand()));
// Check getNewValOperand().
EXPECT_EQ(CmpXchg->getNewValOperand(),
Ctx.getValue(LLVMCmpXchg->getNewValOperand()));
// Check getPointerAddressSpace().
EXPECT_EQ(CmpXchg->getPointerAddressSpace(),
LLVMCmpXchg->getPointerAddressSpace());
Align Align(1024);
auto SuccOrdering = AtomicOrdering::Acquire;
auto FailOrdering = AtomicOrdering::Monotonic;
auto SSID = SyncScope::System;
{
// Check create() WhereIt, WhereBB.
auto *NewI =
cast<sandboxir::AtomicCmpXchgInst>(sandboxir::AtomicCmpXchgInst::create(
Ptr, Cmp, New, Align, SuccOrdering, FailOrdering,
Ret->getIterator(), Ctx, SSID, "NewAtomicCmpXchg1"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicCmpXchg);
// Check getAlign().
EXPECT_EQ(NewI->getAlign(), Align);
// Check getSuccessOrdering().
EXPECT_EQ(NewI->getSuccessOrdering(), SuccOrdering);
// Check getFailureOrdering().
EXPECT_EQ(NewI->getFailureOrdering(), FailOrdering);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
// Check getPointerOperand().
EXPECT_EQ(NewI->getPointerOperand(), Ptr);
// Check getCompareOperand().
EXPECT_EQ(NewI->getCompareOperand(), Cmp);
// Check getNewValOperand().
EXPECT_EQ(NewI->getNewValOperand(), New);
#ifndef NDEBUG
// Check getName().
EXPECT_EQ(NewI->getName(), "NewAtomicCmpXchg1");
#endif // NDEBUG
}
{
// Check create() InsertBefore.
auto *NewI =
cast<sandboxir::AtomicCmpXchgInst>(sandboxir::AtomicCmpXchgInst::create(
Ptr, Cmp, New, Align, SuccOrdering, FailOrdering,
Ret->getIterator(), Ctx, SSID, "NewAtomicCmpXchg2"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicCmpXchg);
// Check getAlign().
EXPECT_EQ(NewI->getAlign(), Align);
// Check getSuccessOrdering().
EXPECT_EQ(NewI->getSuccessOrdering(), SuccOrdering);
// Check getFailureOrdering().
EXPECT_EQ(NewI->getFailureOrdering(), FailOrdering);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
// Check getPointerOperand().
EXPECT_EQ(NewI->getPointerOperand(), Ptr);
// Check getCompareOperand().
EXPECT_EQ(NewI->getCompareOperand(), Cmp);
// Check getNewValOperand().
EXPECT_EQ(NewI->getNewValOperand(), New);
#ifndef NDEBUG
// Check getName().
EXPECT_EQ(NewI->getName(), "NewAtomicCmpXchg2");
#endif // NDEBUG
}
{
// Check create() InsertAtEnd.
auto *NewI =
cast<sandboxir::AtomicCmpXchgInst>(sandboxir::AtomicCmpXchgInst::create(
Ptr, Cmp, New, Align, SuccOrdering, FailOrdering, BB, Ctx, SSID,
"NewAtomicCmpXchg3"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicCmpXchg);
// Check getAlign().
EXPECT_EQ(NewI->getAlign(), Align);
// Check getSuccessOrdering().
EXPECT_EQ(NewI->getSuccessOrdering(), SuccOrdering);
// Check getFailureOrdering().
EXPECT_EQ(NewI->getFailureOrdering(), FailOrdering);
// Check instr position.
EXPECT_EQ(NewI->getParent(), BB);
EXPECT_EQ(NewI->getNextNode(), nullptr);
// Check getPointerOperand().
EXPECT_EQ(NewI->getPointerOperand(), Ptr);
// Check getCompareOperand().
EXPECT_EQ(NewI->getCompareOperand(), Cmp);
// Check getNewValOperand().
EXPECT_EQ(NewI->getNewValOperand(), New);
#ifndef NDEBUG
// Check getName().
EXPECT_EQ(NewI->getName(), "NewAtomicCmpXchg3");
#endif // NDEBUG
}
}
TEST_F(SandboxIRTest, AllocaInst) {
parseIR(C, R"IR(
define void @foo() {
%allocaScalar = alloca i32, align 1024
%allocaArray = alloca i32, i32 42
ret void
}
)IR");
const DataLayout &DL = M->getDataLayout();
llvm::Function &LLVMF = *M->getFunction("foo");
llvm::BasicBlock *LLVMBB = &*LLVMF.begin();
auto LLVMIt = LLVMBB->begin();
auto *LLVMAllocaScalar = cast<llvm::AllocaInst>(&*LLVMIt++);
auto *LLVMAllocaArray = cast<llvm::AllocaInst>(&*LLVMIt++);
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *AllocaScalar = cast<sandboxir::AllocaInst>(&*It++);
auto *AllocaArray = cast<sandboxir::AllocaInst>(&*It++);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
// Check isArrayAllocation().
EXPECT_EQ(AllocaScalar->isArrayAllocation(),
LLVMAllocaScalar->isArrayAllocation());
EXPECT_EQ(AllocaArray->isArrayAllocation(),
LLVMAllocaArray->isArrayAllocation());
// Check getArraySize().
EXPECT_EQ(AllocaScalar->getArraySize(),
Ctx.getValue(LLVMAllocaScalar->getArraySize()));
EXPECT_EQ(AllocaArray->getArraySize(),
Ctx.getValue(LLVMAllocaArray->getArraySize()));
// Check getType().
EXPECT_EQ(AllocaScalar->getType(), Ctx.getType(LLVMAllocaScalar->getType()));
EXPECT_EQ(AllocaArray->getType(), Ctx.getType(LLVMAllocaArray->getType()));
// Check getAddressSpace().
EXPECT_EQ(AllocaScalar->getAddressSpace(),
LLVMAllocaScalar->getAddressSpace());
EXPECT_EQ(AllocaArray->getAddressSpace(), LLVMAllocaArray->getAddressSpace());
// Check getAllocationSize().
EXPECT_EQ(AllocaScalar->getAllocationSize(DL),
LLVMAllocaScalar->getAllocationSize(DL));
EXPECT_EQ(AllocaArray->getAllocationSize(DL),
LLVMAllocaArray->getAllocationSize(DL));
// Check getAllocationSizeInBits().
EXPECT_EQ(AllocaScalar->getAllocationSizeInBits(DL),
LLVMAllocaScalar->getAllocationSizeInBits(DL));
EXPECT_EQ(AllocaArray->getAllocationSizeInBits(DL),
LLVMAllocaArray->getAllocationSizeInBits(DL));
// Check getAllocatedType().
EXPECT_EQ(AllocaScalar->getAllocatedType(),
Ctx.getType(LLVMAllocaScalar->getAllocatedType()));
EXPECT_EQ(AllocaArray->getAllocatedType(),
Ctx.getType(LLVMAllocaArray->getAllocatedType()));
// Check setAllocatedType().
auto *OrigType = AllocaScalar->getAllocatedType();
auto *NewType = sandboxir::PointerType::get(Ctx, 0);
EXPECT_NE(NewType, OrigType);
AllocaScalar->setAllocatedType(NewType);
EXPECT_EQ(AllocaScalar->getAllocatedType(), NewType);
AllocaScalar->setAllocatedType(OrigType);
EXPECT_EQ(AllocaScalar->getAllocatedType(), OrigType);
// Check getAlign().
EXPECT_EQ(AllocaScalar->getAlign(), LLVMAllocaScalar->getAlign());
EXPECT_EQ(AllocaArray->getAlign(), LLVMAllocaArray->getAlign());
// Check setAlignment().
Align OrigAlign = AllocaScalar->getAlign();
Align NewAlign(16);
EXPECT_NE(NewAlign, OrigAlign);
AllocaScalar->setAlignment(NewAlign);
EXPECT_EQ(AllocaScalar->getAlign(), NewAlign);
AllocaScalar->setAlignment(OrigAlign);
EXPECT_EQ(AllocaScalar->getAlign(), OrigAlign);
// Check isStaticAlloca().
EXPECT_EQ(AllocaScalar->isStaticAlloca(), LLVMAllocaScalar->isStaticAlloca());
EXPECT_EQ(AllocaArray->isStaticAlloca(), LLVMAllocaArray->isStaticAlloca());
// Check isUsedWithInAlloca(), setUsedWithInAlloca().
EXPECT_EQ(AllocaScalar->isUsedWithInAlloca(),
LLVMAllocaScalar->isUsedWithInAlloca());
bool OrigUsedWithInAlloca = AllocaScalar->isUsedWithInAlloca();
bool NewUsedWithInAlloca = true;
EXPECT_NE(NewUsedWithInAlloca, OrigUsedWithInAlloca);
AllocaScalar->setUsedWithInAlloca(NewUsedWithInAlloca);
EXPECT_EQ(AllocaScalar->isUsedWithInAlloca(), NewUsedWithInAlloca);
AllocaScalar->setUsedWithInAlloca(OrigUsedWithInAlloca);
EXPECT_EQ(AllocaScalar->isUsedWithInAlloca(), OrigUsedWithInAlloca);
auto *Ty = sandboxir::Type::getInt32Ty(Ctx);
unsigned AddrSpace = 42;
auto *PtrTy = sandboxir::PointerType::get(Ctx, AddrSpace);
auto *ArraySize = sandboxir::ConstantInt::get(Ty, 43);
{
// Check create() WhereIt, WhereBB.
auto *NewI = cast<sandboxir::AllocaInst>(sandboxir::AllocaInst::create(
Ty, AddrSpace, Ret->getIterator(), Ctx, ArraySize, "NewAlloca1"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Alloca);
// Check getType().
EXPECT_EQ(NewI->getType(), PtrTy);
// Check getArraySize().
EXPECT_EQ(NewI->getArraySize(), ArraySize);
// Check getAddrSpace().
EXPECT_EQ(NewI->getAddressSpace(), AddrSpace);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertBefore.
auto *NewI = cast<sandboxir::AllocaInst>(sandboxir::AllocaInst::create(
Ty, AddrSpace, Ret->getIterator(), Ctx, ArraySize, "NewAlloca2"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Alloca);
// Check getType().
EXPECT_EQ(NewI->getType(), PtrTy);
// Check getArraySize().
EXPECT_EQ(NewI->getArraySize(), ArraySize);
// Check getAddrSpace().
EXPECT_EQ(NewI->getAddressSpace(), AddrSpace);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertAtEnd.
auto *NewI = cast<sandboxir::AllocaInst>(sandboxir::AllocaInst::create(
Ty, AddrSpace, BB, Ctx, ArraySize, "NewAlloca3"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Alloca);
// Check getType().
EXPECT_EQ(NewI->getType(), PtrTy);
// Check getArraySize().
EXPECT_EQ(NewI->getArraySize(), ArraySize);
// Check getAddrSpace().
EXPECT_EQ(NewI->getAddressSpace(), AddrSpace);
// Check instr position.
EXPECT_EQ(NewI->getParent(), BB);
EXPECT_EQ(NewI->getNextNode(), nullptr);
}
}
TEST_F(SandboxIRTest, CastInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg, float %farg, double %darg, ptr %ptr) {
%zext = zext i32 %arg to i64
%sext = sext i32 %arg to i64
%fptoui = fptoui float %farg to i32
%fptosi = fptosi float %farg to i32
%fpext = fpext float %farg to double
%ptrtoint = ptrtoint ptr %ptr to i32
%inttoptr = inttoptr i32 %arg to ptr
%sitofp = sitofp i32 %arg to float
%uitofp = uitofp i32 %arg to float
%trunc = trunc i32 %arg to i16
%fptrunc = fptrunc double %darg to float
%bitcast = bitcast i32 %arg to float
%addrspacecast = addrspacecast ptr %ptr to ptr addrspace(1)
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
unsigned ArgIdx = 0;
auto *Arg = F->getArg(ArgIdx++);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Ti64 = sandboxir::Type::getInt64Ty(Ctx);
auto *Ti32 = sandboxir::Type::getInt32Ty(Ctx);
auto *Ti16 = sandboxir::Type::getInt16Ty(Ctx);
auto *Tdouble = sandboxir::Type::getDoubleTy(Ctx);
auto *Tfloat = sandboxir::Type::getFloatTy(Ctx);
auto *Tptr = sandboxir::PointerType::get(Ctx, 0);
auto *Tptr1 = sandboxir::PointerType::get(Ctx, 1);
// Check classof(), getOpcode(), getSrcTy(), getDstTy()
auto *ZExt = cast<sandboxir::CastInst>(&*It++);
auto *ZExtI = cast<sandboxir::ZExtInst>(ZExt);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(ZExtI));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(ZExtI));
EXPECT_EQ(ZExt->getOpcode(), sandboxir::Instruction::Opcode::ZExt);
EXPECT_EQ(ZExt->getSrcTy(), Ti32);
EXPECT_EQ(ZExt->getDestTy(), Ti64);
auto *SExt = cast<sandboxir::CastInst>(&*It++);
auto *SExtI = cast<sandboxir::SExtInst>(SExt);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(SExt));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(SExtI));
EXPECT_EQ(SExt->getOpcode(), sandboxir::Instruction::Opcode::SExt);
EXPECT_EQ(SExt->getSrcTy(), Ti32);
EXPECT_EQ(SExt->getDestTy(), Ti64);
auto *FPToUI = cast<sandboxir::CastInst>(&*It++);
auto *FPToUII = cast<sandboxir::FPToUIInst>(FPToUI);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPToUI));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPToUII));
EXPECT_EQ(FPToUI->getOpcode(), sandboxir::Instruction::Opcode::FPToUI);
EXPECT_EQ(FPToUI->getSrcTy(), Tfloat);
EXPECT_EQ(FPToUI->getDestTy(), Ti32);
auto *FPToSI = cast<sandboxir::CastInst>(&*It++);
auto *FPToSII = cast<sandboxir::FPToSIInst>(FPToSI);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPToSI));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPToSII));
EXPECT_EQ(FPToSI->getOpcode(), sandboxir::Instruction::Opcode::FPToSI);
EXPECT_EQ(FPToSI->getSrcTy(), Tfloat);
EXPECT_EQ(FPToSI->getDestTy(), Ti32);
auto *FPExt = cast<sandboxir::CastInst>(&*It++);
auto *FPExtI = cast<sandboxir::FPExtInst>(FPExt);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPExt));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPExtI));
EXPECT_EQ(FPExt->getOpcode(), sandboxir::Instruction::Opcode::FPExt);
EXPECT_EQ(FPExt->getSrcTy(), Tfloat);
EXPECT_EQ(FPExt->getDestTy(), Tdouble);
auto *PtrToInt = cast<sandboxir::CastInst>(&*It++);
auto *PtrToIntI = cast<sandboxir::PtrToIntInst>(PtrToInt);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(PtrToInt));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(PtrToIntI));
EXPECT_EQ(PtrToInt->getOpcode(), sandboxir::Instruction::Opcode::PtrToInt);
EXPECT_EQ(PtrToInt->getSrcTy(), Tptr);
EXPECT_EQ(PtrToInt->getDestTy(), Ti32);
auto *IntToPtr = cast<sandboxir::CastInst>(&*It++);
auto *IntToPtrI = cast<sandboxir::IntToPtrInst>(IntToPtr);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(IntToPtr));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(IntToPtrI));
EXPECT_EQ(IntToPtr->getOpcode(), sandboxir::Instruction::Opcode::IntToPtr);
EXPECT_EQ(IntToPtr->getSrcTy(), Ti32);
EXPECT_EQ(IntToPtr->getDestTy(), Tptr);
auto *SIToFP = cast<sandboxir::CastInst>(&*It++);
auto *SIToFPI = cast<sandboxir::SIToFPInst>(SIToFP);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(SIToFP));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(SIToFPI));
EXPECT_EQ(SIToFP->getOpcode(), sandboxir::Instruction::Opcode::SIToFP);
EXPECT_EQ(SIToFP->getSrcTy(), Ti32);
EXPECT_EQ(SIToFP->getDestTy(), Tfloat);
auto *UIToFP = cast<sandboxir::CastInst>(&*It++);
auto *UIToFPI = cast<sandboxir::UIToFPInst>(UIToFP);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(UIToFP));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(UIToFPI));
EXPECT_EQ(UIToFP->getOpcode(), sandboxir::Instruction::Opcode::UIToFP);
EXPECT_EQ(UIToFP->getSrcTy(), Ti32);
EXPECT_EQ(UIToFP->getDestTy(), Tfloat);
auto *Trunc = cast<sandboxir::CastInst>(&*It++);
auto *TruncI = cast<sandboxir::TruncInst>(Trunc);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(Trunc));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(TruncI));
EXPECT_EQ(Trunc->getOpcode(), sandboxir::Instruction::Opcode::Trunc);
EXPECT_EQ(Trunc->getSrcTy(), Ti32);
EXPECT_EQ(Trunc->getDestTy(), Ti16);
auto *FPTrunc = cast<sandboxir::CastInst>(&*It++);
auto *FPTruncI = cast<sandboxir::FPTruncInst>(FPTrunc);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPTrunc));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(FPTruncI));
EXPECT_EQ(FPTrunc->getOpcode(), sandboxir::Instruction::Opcode::FPTrunc);
EXPECT_EQ(FPTrunc->getSrcTy(), Tdouble);
EXPECT_EQ(FPTrunc->getDestTy(), Tfloat);
auto *BitCast = cast<sandboxir::CastInst>(&*It++);
auto *BitCastI = cast<sandboxir::BitCastInst>(BitCast);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(BitCast));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(BitCastI));
EXPECT_EQ(BitCast->getOpcode(), sandboxir::Instruction::Opcode::BitCast);
EXPECT_EQ(BitCast->getSrcTy(), Ti32);
EXPECT_EQ(BitCast->getDestTy(), Tfloat);
auto *AddrSpaceCast = cast<sandboxir::CastInst>(&*It++);
auto *AddrSpaceCastI = cast<sandboxir::AddrSpaceCastInst>(AddrSpaceCast);
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(AddrSpaceCast));
EXPECT_TRUE(isa<sandboxir::UnaryInstruction>(AddrSpaceCastI));
EXPECT_EQ(AddrSpaceCast->getOpcode(),
sandboxir::Instruction::Opcode::AddrSpaceCast);
EXPECT_EQ(AddrSpaceCast->getSrcTy(), Tptr);
EXPECT_EQ(AddrSpaceCast->getDestTy(), Tptr1);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
{
// Check create() WhereIt, WhereBB
auto *NewI = cast<sandboxir::CastInst>(
sandboxir::CastInst::create(Ti64, sandboxir::Instruction::Opcode::SExt,
Arg, BB->end(), Ctx, "SExt"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::SExt);
// Check getSrcTy().
EXPECT_EQ(NewI->getSrcTy(), Arg->getType());
// Check getDestTy().
EXPECT_EQ(NewI->getDestTy(), Ti64);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), nullptr);
EXPECT_EQ(NewI->getPrevNode(), Ret);
}
{
// Check create() InsertBefore.
auto *NewI = cast<sandboxir::CastInst>(
sandboxir::CastInst::create(Ti64, sandboxir::Instruction::Opcode::ZExt,
Arg, Ret->getIterator(), Ctx, "ZExt"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::ZExt);
// Check getSrcTy().
EXPECT_EQ(NewI->getSrcTy(), Arg->getType());
// Check getDestTy().
EXPECT_EQ(NewI->getDestTy(), Ti64);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertAtEnd.
auto *NewI = cast<sandboxir::CastInst>(sandboxir::CastInst::create(
Ti64, sandboxir::Instruction::Opcode::ZExt, Arg, BB, Ctx, "ZExt"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::ZExt);
// Check getSrcTy().
EXPECT_EQ(NewI->getSrcTy(), Arg->getType());
// Check getDestTy().
EXPECT_EQ(NewI->getDestTy(), Ti64);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), nullptr);
EXPECT_EQ(NewI->getParent(), BB);
}
{
#ifndef NDEBUG
// Check that passing a non-cast opcode crashes.
EXPECT_DEATH(
sandboxir::CastInst::create(Ti64, sandboxir::Instruction::Opcode::Store,
Arg, Ret->getIterator(), Ctx, "Bad"),
".*Opcode.*");
#endif // NDEBUG
}
}
TEST_F(SandboxIRTest, PossiblyNonNegInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg, float %farg, double %darg, ptr %ptr) {
%zext = zext i32 %arg to i64
%uitofp = uitofp i32 %arg to float
%sext = sext i32 %arg to i64
%fptoui = fptoui float %farg to i32
%fptosi = fptosi float %farg to i32
%fpext = fpext float %farg to double
%ptrtoint = ptrtoint ptr %ptr to i32
%inttoptr = inttoptr i32 %arg to ptr
%sitofp = sitofp i32 %arg to float
%trunc = trunc i32 %arg to i16
%fptrunc = fptrunc double %darg to float
%bitcast = bitcast i32 %arg to float
%addrspacecast = addrspacecast ptr %ptr to ptr addrspace(1)
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *PNNI0 = cast<sandboxir::PossiblyNonNegInst>(&*It++);
auto *PNNI1 = cast<sandboxir::PossiblyNonNegInst>(&*It++);
for (auto ItE = BB->end(); It != ItE; ++It)
EXPECT_FALSE(isa<sandboxir::PossiblyNonNegInst>(&*It++));
for (auto *PNNI : {PNNI0, PNNI1}) {
// Check setNonNeg(), hasNonNeg().
auto OrigNonNeg = PNNI->hasNonNeg();
auto NewNonNeg = true;
EXPECT_NE(NewNonNeg, OrigNonNeg);
PNNI->setNonNeg(NewNonNeg);
EXPECT_EQ(PNNI->hasNonNeg(), NewNonNeg);
PNNI->setNonNeg(OrigNonNeg);
EXPECT_EQ(PNNI->hasNonNeg(), OrigNonNeg);
}
}
/// CastInst's subclasses are very similar so we can use a common test function
/// for them.
template <typename SubclassT, sandboxir::Instruction::Opcode OpcodeT>
void testCastInst(llvm::Module &M, llvm::Type *LLVMSrcTy,
llvm::Type *LLVMDstTy) {
Function &LLVMF = *M.getFunction("foo");
sandboxir::Context Ctx(M.getContext());
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
sandboxir::Type *SrcTy = Ctx.getType(LLVMSrcTy);
sandboxir::Type *DstTy = Ctx.getType(LLVMDstTy);
unsigned ArgIdx = 0;
auto *Arg = F->getArg(ArgIdx++);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *CI = cast<SubclassT>(&*It++);
EXPECT_EQ(CI->getOpcode(), OpcodeT);
EXPECT_EQ(CI->getSrcTy(), SrcTy);
EXPECT_EQ(CI->getDestTy(), DstTy);
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
{
// Check create() WhereIt, WhereBB
auto *NewI =
cast<SubclassT>(SubclassT::create(Arg, DstTy, BB->end(), Ctx, "NewCI"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), OpcodeT);
// Check getSrcTy().
EXPECT_EQ(NewI->getSrcTy(), Arg->getType());
// Check getDestTy().
EXPECT_EQ(NewI->getDestTy(), DstTy);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), nullptr);
EXPECT_EQ(NewI->getPrevNode(), Ret);
// Check instr name.
EXPECT_EQ(NewI->getName(), "NewCI");
}
{
// Check create() InsertBefore.
auto *NewI = cast<SubclassT>(
SubclassT::create(Arg, DstTy, Ret->getIterator(), Ctx, "NewCI"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), OpcodeT);
// Check getSrcTy().
EXPECT_EQ(NewI->getSrcTy(), Arg->getType());
// Check getDestTy().
EXPECT_EQ(NewI->getDestTy(), DstTy);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), Ret);
}
{
// Check create() InsertAtEnd.
auto *NewI =
cast<SubclassT>(SubclassT::create(Arg, DstTy,
/*InsertAtEnd=*/BB, Ctx, "NewCI"));
// Check getOpcode().
EXPECT_EQ(NewI->getOpcode(), OpcodeT);
// Check getSrcTy().
EXPECT_EQ(NewI->getSrcTy(), Arg->getType());
// Check getDestTy().
EXPECT_EQ(NewI->getDestTy(), DstTy);
// Check instr position.
EXPECT_EQ(NewI->getNextNode(), nullptr);
EXPECT_EQ(NewI->getParent(), BB);
}
}
TEST_F(SandboxIRTest, TruncInst) {
parseIR(C, R"IR(
define void @foo(i64 %arg) {
%trunc = trunc i64 %arg to i32
ret void
}
)IR");
testCastInst<sandboxir::TruncInst, sandboxir::Instruction::Opcode::Trunc>(
*M,
/*SrcTy=*/Type::getInt64Ty(C), /*DstTy=*/Type::getInt32Ty(C));
}
TEST_F(SandboxIRTest, ZExtInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg) {
%zext = zext i32 %arg to i64
ret void
}
)IR");
testCastInst<sandboxir::ZExtInst, sandboxir::Instruction::Opcode::ZExt>(
*M,
/*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getInt64Ty(C));
}
TEST_F(SandboxIRTest, SExtInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg) {
%sext = sext i32 %arg to i64
ret void
}
)IR");
testCastInst<sandboxir::SExtInst, sandboxir::Instruction::Opcode::SExt>(
*M,
/*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getInt64Ty(C));
}
TEST_F(SandboxIRTest, FPTruncInst) {
parseIR(C, R"IR(
define void @foo(double %arg) {
%fptrunc = fptrunc double %arg to float
ret void
}
)IR");
testCastInst<sandboxir::FPTruncInst, sandboxir::Instruction::Opcode::FPTrunc>(
*M,
/*SrcTy=*/Type::getDoubleTy(C), /*DstTy=*/Type::getFloatTy(C));
}
TEST_F(SandboxIRTest, FPExtInst) {
parseIR(C, R"IR(
define void @foo(float %arg) {
%fpext = fpext float %arg to double
ret void
}
)IR");
testCastInst<sandboxir::FPExtInst, sandboxir::Instruction::Opcode::FPExt>(
*M,
/*SrcTy=*/Type::getFloatTy(C), /*DstTy=*/Type::getDoubleTy(C));
}
TEST_F(SandboxIRTest, UIToFPInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg) {
%uitofp = uitofp i32 %arg to float
ret void
}
)IR");
testCastInst<sandboxir::UIToFPInst, sandboxir::Instruction::Opcode::UIToFP>(
*M,
/*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getFloatTy(C));
}
TEST_F(SandboxIRTest, SIToFPInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg) {
%sitofp = sitofp i32 %arg to float
ret void
}
)IR");
testCastInst<sandboxir::SIToFPInst, sandboxir::Instruction::Opcode::SIToFP>(
*M,
/*SrcTy=*/Type::getInt32Ty(C),
/*DstTy=*/Type::getFloatTy(C));
}
TEST_F(SandboxIRTest, FPToUIInst) {
parseIR(C, R"IR(
define void @foo(float %arg) {
%fptoui = fptoui float %arg to i32
ret void
}
)IR");
testCastInst<sandboxir::FPToUIInst, sandboxir::Instruction::Opcode::FPToUI>(
*M, /*SrcTy=*/Type::getFloatTy(C), /*DstTy=*/Type::getInt32Ty(C));
}
TEST_F(SandboxIRTest, FPToSIInst) {
parseIR(C, R"IR(
define void @foo(float %arg) {
%fptosi = fptosi float %arg to i32
ret void
}
)IR");
testCastInst<sandboxir::FPToSIInst, sandboxir::Instruction::Opcode::FPToSI>(
*M, /*SrcTy=*/Type::getFloatTy(C), /*DstTy=*/Type::getInt32Ty(C));
}
TEST_F(SandboxIRTest, IntToPtrInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg) {
%inttoptr = inttoptr i32 %arg to ptr
ret void
}
)IR");
testCastInst<sandboxir::IntToPtrInst,
sandboxir::Instruction::Opcode::IntToPtr>(
*M,
/*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/PointerType::get(C, 0));
}
TEST_F(SandboxIRTest, PtrToIntInst) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
%ptrtoint = ptrtoint ptr %ptr to i32
ret void
}
)IR");
testCastInst<sandboxir::PtrToIntInst,
sandboxir::Instruction::Opcode::PtrToInt>(
*M, /*SrcTy=*/PointerType::get(C, 0), /*DstTy=*/Type::getInt32Ty(C));
}
TEST_F(SandboxIRTest, BitCastInst) {
parseIR(C, R"IR(
define void @foo(i32 %arg) {
%bitcast = bitcast i32 %arg to float
ret void
}
)IR");
testCastInst<sandboxir::BitCastInst, sandboxir::Instruction::Opcode::BitCast>(
*M,
/*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getFloatTy(C));
}
TEST_F(SandboxIRTest, AddrSpaceCastInst) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
%addrspacecast = addrspacecast ptr %ptr to ptr addrspace(1)
ret void
}
)IR");
Type *Tptr0 = PointerType::get(C, 0);
Type *Tptr1 = PointerType::get(C, 1);
testCastInst<sandboxir::AddrSpaceCastInst,
sandboxir::Instruction::Opcode::AddrSpaceCast>(*M,
/*SrcTy=*/Tptr0,
/*DstTy=*/Tptr1);
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
unsigned ArgIdx = 0;
auto *Arg = F->getArg(ArgIdx++);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *AddrSpaceCast = cast<sandboxir::AddrSpaceCastInst>(&*It++);
EXPECT_EQ(AddrSpaceCast->getOpcode(),
sandboxir::Instruction::Opcode::AddrSpaceCast);
EXPECT_EQ(AddrSpaceCast->getPointerOperand(), Arg);
EXPECT_EQ(sandboxir::AddrSpaceCastInst::getPointerOperandIndex(), 0u);
EXPECT_EQ(AddrSpaceCast->getSrcAddressSpace(),
cast<PointerType>(Tptr0)->getPointerAddressSpace());
EXPECT_EQ(AddrSpaceCast->getDestAddressSpace(),
cast<PointerType>(Tptr1)->getPointerAddressSpace());
}
TEST_F(SandboxIRTest, PHINode) {
parseIR(C, R"IR(
define void @foo(i32 %arg) {
bb1:
br label %bb2
bb2:
%phi = phi i32 [ %arg, %bb1 ], [ 0, %bb2 ], [ 1, %bb3 ], [ 2, %bb4 ], [ 3, %bb5 ]
br label %bb2
bb3:
br label %bb2
bb4:
br label %bb2
bb5:
br label %bb2
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
auto *LLVMBB1 = getBasicBlockByName(LLVMF, "bb1");
auto *LLVMBB2 = getBasicBlockByName(LLVMF, "bb2");
auto *LLVMBB3 = getBasicBlockByName(LLVMF, "bb3");
auto LLVMIt = LLVMBB2->begin();
auto *LLVMPHI = cast<llvm::PHINode>(&*LLVMIt++);
sandboxir::Context Ctx(C);
sandboxir::Function *F = Ctx.createFunction(&LLVMF);
auto *Arg = F->getArg(0);
auto *BB1 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB1));
auto *BB2 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB2));
auto *BB3 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB3));
auto It = BB2->begin();
// Check classof().
auto *PHI = cast<sandboxir::PHINode>(&*It++);
auto *Br = cast<sandboxir::BranchInst>(&*It++);
// Check blocks().
EXPECT_EQ(range_size(PHI->blocks()), range_size(LLVMPHI->blocks()));
auto BlockIt = PHI->block_begin();
for (llvm::BasicBlock *LLVMBB : LLVMPHI->blocks()) {
sandboxir::BasicBlock *BB = *BlockIt++;
EXPECT_EQ(BB, Ctx.getValue(LLVMBB));
}
// Check incoming_values().
EXPECT_EQ(range_size(PHI->incoming_values()),
range_size(LLVMPHI->incoming_values()));
auto IncIt = PHI->incoming_values().begin();
for (llvm::Value *LLVMV : LLVMPHI->incoming_values()) {
sandboxir::Value *IncV = *IncIt++;
EXPECT_EQ(IncV, Ctx.getValue(LLVMV));
}
// Check getNumIncomingValues().
EXPECT_EQ(PHI->getNumIncomingValues(), LLVMPHI->getNumIncomingValues());
// Check getIncomingValue().
EXPECT_EQ(PHI->getIncomingValue(0),
Ctx.getValue(LLVMPHI->getIncomingValue(0)));
EXPECT_EQ(PHI->getIncomingValue(1),
Ctx.getValue(LLVMPHI->getIncomingValue(1)));
// Check setIncomingValue().
auto *OrigV = PHI->getIncomingValue(0);
PHI->setIncomingValue(0, PHI);
EXPECT_EQ(PHI->getIncomingValue(0), PHI);
PHI->setIncomingValue(0, OrigV);
// Check getOperandNumForIncomingValue().
EXPECT_EQ(sandboxir::PHINode::getOperandNumForIncomingValue(0),
llvm::PHINode::getOperandNumForIncomingValue(0));
// Check getIncomingValueNumForOperand().
EXPECT_EQ(sandboxir::PHINode::getIncomingValueNumForOperand(0),
llvm::PHINode::getIncomingValueNumForOperand(0));
// Check getIncomingBlock(unsigned).
EXPECT_EQ(PHI->getIncomingBlock(0),
Ctx.getValue(LLVMPHI->getIncomingBlock(0)));
// Check getIncomingBlock(Use).
llvm::Use &LLVMUse = LLVMPHI->getOperandUse(0);
sandboxir::Use Use = PHI->getOperandUse(0);
EXPECT_EQ(PHI->getIncomingBlock(Use),
Ctx.getValue(LLVMPHI->getIncomingBlock(LLVMUse)));
// Check setIncomingBlock().
sandboxir::BasicBlock *OrigBB = PHI->getIncomingBlock(0);
EXPECT_NE(OrigBB, BB2);
PHI->setIncomingBlock(0, BB2);
EXPECT_EQ(PHI->getIncomingBlock(0), BB2);
PHI->setIncomingBlock(0, OrigBB);
EXPECT_EQ(PHI->getIncomingBlock(0), OrigBB);
// Check addIncoming().
unsigned OrigNumIncoming = PHI->getNumIncomingValues();
PHI->addIncoming(Arg, BB3);
EXPECT_EQ(PHI->getNumIncomingValues(), LLVMPHI->getNumIncomingValues());
EXPECT_EQ(PHI->getNumIncomingValues(), OrigNumIncoming + 1);
EXPECT_EQ(PHI->getIncomingValue(OrigNumIncoming), Arg);
EXPECT_EQ(PHI->getIncomingBlock(OrigNumIncoming), BB3);
// Check removeIncomingValue(unsigned).
PHI->removeIncomingValue(OrigNumIncoming);
EXPECT_EQ(PHI->getNumIncomingValues(), OrigNumIncoming);
// Check removeIncomingValue(BasicBlock *).
PHI->addIncoming(Arg, BB3);
PHI->removeIncomingValue(BB3);
EXPECT_EQ(PHI->getNumIncomingValues(), OrigNumIncoming);
// Check getBasicBlockIndex().
EXPECT_EQ(PHI->getBasicBlockIndex(BB1), LLVMPHI->getBasicBlockIndex(LLVMBB1));
// Check getIncomingValueForBlock().
EXPECT_EQ(PHI->getIncomingValueForBlock(BB1),
Ctx.getValue(LLVMPHI->getIncomingValueForBlock(LLVMBB1)));
// Check hasConstantValue().
llvm::Value *ConstV = LLVMPHI->hasConstantValue();
EXPECT_EQ(PHI->hasConstantValue(),
ConstV != nullptr ? Ctx.getValue(ConstV) : nullptr);
// Check hasConstantOrUndefValue().
EXPECT_EQ(PHI->hasConstantOrUndefValue(), LLVMPHI->hasConstantOrUndefValue());
// Check isComplete().
EXPECT_EQ(PHI->isComplete(), LLVMPHI->isComplete());
// Check replaceIncomingValueIf
EXPECT_EQ(PHI->getNumIncomingValues(), 5u);
auto *RemainBB0 = PHI->getIncomingBlock(0);
auto *RemoveBB0 = PHI->getIncomingBlock(1);
auto *RemainBB1 = PHI->getIncomingBlock(2);
auto *RemoveBB1 = PHI->getIncomingBlock(3);
auto *RemainBB2 = PHI->getIncomingBlock(4);
PHI->removeIncomingValueIf([&](unsigned Idx) {
return PHI->getIncomingBlock(Idx) == RemoveBB0 ||
PHI->getIncomingBlock(Idx) == RemoveBB1;
});
EXPECT_EQ(PHI->getNumIncomingValues(), 3u);
EXPECT_EQ(PHI->getIncomingBlock(0), RemainBB0);
EXPECT_EQ(PHI->getIncomingBlock(1), RemainBB1);
EXPECT_EQ(PHI->getIncomingBlock(2), RemainBB2);
// Check replaceIncomingBlockWith
OrigBB = RemainBB0;
auto *NewBB = RemainBB1;
EXPECT_NE(NewBB, OrigBB);
PHI->replaceIncomingBlockWith(OrigBB, NewBB);
EXPECT_EQ(PHI->getIncomingBlock(0), NewBB);
EXPECT_EQ(PHI->getIncomingBlock(1), RemainBB1);
EXPECT_EQ(PHI->getIncomingBlock(2), RemainBB2);
// Check create().
auto *NewPHI = cast<sandboxir::PHINode>(sandboxir::PHINode::create(
PHI->getType(), 0, Br->getIterator(), Ctx, "NewPHI"));
EXPECT_EQ(NewPHI->getType(), PHI->getType());
EXPECT_EQ(NewPHI->getNextNode(), Br);
EXPECT_EQ(NewPHI->getName(), "NewPHI");
EXPECT_EQ(NewPHI->getNumIncomingValues(), 0u);
for (auto [Idx, V] : enumerate(PHI->incoming_values())) {
sandboxir::BasicBlock *IncBB = PHI->getIncomingBlock(Idx);
NewPHI->addIncoming(V, IncBB);
}
EXPECT_EQ(NewPHI->getNumIncomingValues(), PHI->getNumIncomingValues());
}
static void checkSwapOperands(sandboxir::Context &Ctx,
llvm::sandboxir::CmpInst *Cmp,
llvm::CmpInst *LLVMCmp) {
auto OrigOp0 = Cmp->getOperand(0);
auto OrigOp1 = Cmp->getOperand(1);
EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(0)), OrigOp0);
EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(1)), OrigOp1);
// This checks the dispatch mechanism in CmpInst, as well as
// the specific implementations.
Cmp->swapOperands();
EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(1)), OrigOp0);
EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(0)), OrigOp1);
EXPECT_EQ(Cmp->getOperand(0), OrigOp1);
EXPECT_EQ(Cmp->getOperand(1), OrigOp0);
// Undo it to keep the rest of the test consistent
Cmp->swapOperands();
}
static void checkCommonPredicates(sandboxir::CmpInst *Cmp,
llvm::CmpInst *LLVMCmp) {
// Check proper creation
auto Pred = Cmp->getPredicate();
auto LLVMPred = LLVMCmp->getPredicate();
EXPECT_EQ(Pred, LLVMPred);
// Check setPredicate
Cmp->setPredicate(llvm::CmpInst::FCMP_FALSE);
EXPECT_EQ(Cmp->getPredicate(), llvm::CmpInst::FCMP_FALSE);
EXPECT_EQ(LLVMCmp->getPredicate(), llvm::CmpInst::FCMP_FALSE);
Cmp->setPredicate(Pred);
EXPECT_EQ(LLVMCmp->getPredicate(), Pred);
// Ensure the accessors properly forward to the underlying implementation
EXPECT_STREQ(sandboxir::CmpInst::getPredicateName(Pred).data(),
llvm::CmpInst::getPredicateName(LLVMPred).data());
EXPECT_EQ(Cmp->isFPPredicate(), LLVMCmp->isFPPredicate());
EXPECT_EQ(Cmp->isIntPredicate(), LLVMCmp->isIntPredicate());
EXPECT_EQ(Cmp->getInversePredicate(), LLVMCmp->getInversePredicate());
EXPECT_EQ(Cmp->getOrderedPredicate(), LLVMCmp->getOrderedPredicate());
EXPECT_EQ(Cmp->getUnorderedPredicate(), LLVMCmp->getUnorderedPredicate());
EXPECT_EQ(Cmp->getSwappedPredicate(), LLVMCmp->getSwappedPredicate());
EXPECT_EQ(Cmp->isStrictPredicate(), LLVMCmp->isStrictPredicate());
EXPECT_EQ(Cmp->isNonStrictPredicate(), LLVMCmp->isNonStrictPredicate());
EXPECT_EQ(Cmp->isRelational(), LLVMCmp->isRelational());
if (Cmp->isRelational()) {
EXPECT_EQ(Cmp->getFlippedStrictnessPredicate(),
LLVMCmp->getFlippedStrictnessPredicate());
}
EXPECT_EQ(Cmp->isCommutative(), LLVMCmp->isCommutative());
EXPECT_EQ(Cmp->isTrueWhenEqual(), LLVMCmp->isTrueWhenEqual());
EXPECT_EQ(Cmp->isFalseWhenEqual(), LLVMCmp->isFalseWhenEqual());
EXPECT_EQ(sandboxir::CmpInst::isOrdered(Pred),
llvm::CmpInst::isOrdered(LLVMPred));
EXPECT_EQ(sandboxir::CmpInst::isUnordered(Pred),
llvm::CmpInst::isUnordered(LLVMPred));
}
TEST_F(SandboxIRTest, ICmpInst) {
SCOPED_TRACE("SandboxIRTest sandboxir::ICmpInst tests");
parseIR(C, R"IR(
define void @foo(i32 %i0, i32 %i1) {
bb:
%ine = icmp ne i32 %i0, %i1
%iugt = icmp ugt i32 %i0, %i1
%iuge = icmp uge i32 %i0, %i1
%iult = icmp ult i32 %i0, %i1
%iule = icmp ule i32 %i0, %i1
%isgt = icmp sgt i32 %i0, %i1
%isle = icmp sle i32 %i0, %i1
%ieg = icmp eq i32 %i0, %i1
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *LLVMBB = getBasicBlockByName(LLVMF, "bb");
auto LLVMIt = LLVMBB->begin();
auto *BB = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB));
auto It = BB->begin();
// Check classof()
while (auto *ICmp = dyn_cast<sandboxir::ICmpInst>(&*It++)) {
auto *LLVMICmp = cast<llvm::ICmpInst>(&*LLVMIt++);
checkSwapOperands(Ctx, ICmp, LLVMICmp);
checkCommonPredicates(ICmp, LLVMICmp);
EXPECT_EQ(ICmp->isSigned(), LLVMICmp->isSigned());
EXPECT_EQ(ICmp->isUnsigned(), LLVMICmp->isUnsigned());
EXPECT_EQ(ICmp->getSignedPredicate(), LLVMICmp->getSignedPredicate());
EXPECT_EQ(ICmp->getUnsignedPredicate(), LLVMICmp->getUnsignedPredicate());
}
auto *NewCmp = cast<sandboxir::CmpInst>(
sandboxir::CmpInst::create(llvm::CmpInst::ICMP_ULE, F.getArg(0),
F.getArg(1), BB->begin(), Ctx, "NewCmp"));
EXPECT_EQ(NewCmp, &*BB->begin());
EXPECT_EQ(NewCmp->getPredicate(), llvm::CmpInst::ICMP_ULE);
EXPECT_EQ(NewCmp->getOperand(0), F.getArg(0));
EXPECT_EQ(NewCmp->getOperand(1), F.getArg(1));
#ifndef NDEBUG
EXPECT_EQ(NewCmp->getName(), "NewCmp");
#endif // NDEBUG
// TODO: Improve this test when sandboxir::VectorType is more completely
// implemented.
sandboxir::Type *RT =
sandboxir::CmpInst::makeCmpResultType(F.getArg(0)->getType());
EXPECT_TRUE(RT->isIntegerTy(1)); // Only one bit in a single comparison
{
// Check create() when operands are constant.
auto *Const42 =
sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42);
auto *NewConstCmp =
sandboxir::CmpInst::create(llvm::CmpInst::ICMP_ULE, Const42, Const42,
BB->begin(), Ctx, "NewConstCmp");
EXPECT_TRUE(isa<sandboxir::Constant>(NewConstCmp));
}
}
TEST_F(SandboxIRTest, FCmpInst) {
SCOPED_TRACE("SandboxIRTest sandboxir::FCmpInst tests");
parseIR(C, R"IR(
define void @foo(float %f0, float %f1) {
bb:
%ffalse = fcmp false float %f0, %f1
%foeq = fcmp oeq float %f0, %f1
%fogt = fcmp ogt float %f0, %f1
%folt = fcmp olt float %f0, %f1
%fole = fcmp ole float %f0, %f1
%fone = fcmp one float %f0, %f1
%ford = fcmp ord float %f0, %f1
%funo = fcmp uno float %f0, %f1
%fueq = fcmp ueq float %f0, %f1
%fugt = fcmp ugt float %f0, %f1
%fuge = fcmp uge float %f0, %f1
%fult = fcmp ult float %f0, %f1
%fule = fcmp ule float %f0, %f1
%fune = fcmp une float %f0, %f1
%ftrue = fcmp true float %f0, %f1
ret void
bb1:
%copyfrom = fadd reassoc float %f0, 42.0
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
[[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF);
auto *LLVMBB = getBasicBlockByName(LLVMF, "bb");
auto LLVMIt = LLVMBB->begin();
auto *BB = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB));
auto It = BB->begin();
// Check classof()
while (auto *FCmp = dyn_cast<sandboxir::ICmpInst>(&*It++)) {
auto *LLVMFCmp = cast<llvm::ICmpInst>(&*LLVMIt++);
checkSwapOperands(Ctx, FCmp, LLVMFCmp);
checkCommonPredicates(FCmp, LLVMFCmp);
}
auto *LLVMBB1 = getBasicBlockByName(LLVMF, "bb1");
auto *BB1 = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB1));
auto It1 = BB1->begin();
auto *CopyFrom = &*It1++;
CopyFrom->setFastMathFlags(FastMathFlags::getFast());
// create with default flags
auto *NewFCmp = cast<sandboxir::CmpInst>(sandboxir::CmpInst::create(
llvm::CmpInst::FCMP_ONE, F.getArg(0), F.getArg(1), It1, Ctx, "NewFCmp"));
EXPECT_EQ(NewFCmp->getPredicate(), llvm::CmpInst::FCMP_ONE);
EXPECT_EQ(NewFCmp->getOperand(0), F.getArg(0));
EXPECT_EQ(NewFCmp->getOperand(1), F.getArg(1));
#ifndef NDEBUG
EXPECT_EQ(NewFCmp->getName(), "NewFCmp");
#endif // NDEBUG
FastMathFlags DefaultFMF = NewFCmp->getFastMathFlags();
EXPECT_TRUE(CopyFrom->getFastMathFlags() != DefaultFMF);
// create with copied flags
auto *NewFCmpFlags =
cast<sandboxir::CmpInst>(sandboxir::CmpInst::createWithCopiedFlags(
llvm::CmpInst::FCMP_ONE, F.getArg(0), F.getArg(1), CopyFrom, It1, Ctx,
"NewFCmpFlags"));
EXPECT_FALSE(NewFCmpFlags->getFastMathFlags() !=
CopyFrom->getFastMathFlags());
EXPECT_EQ(NewFCmpFlags->getPredicate(), llvm::CmpInst::FCMP_ONE);
EXPECT_EQ(NewFCmpFlags->getOperand(0), F.getArg(0));
EXPECT_EQ(NewFCmpFlags->getOperand(1), F.getArg(1));
#ifndef NDEBUG
EXPECT_EQ(NewFCmpFlags->getName(), "NewFCmpFlags");
#endif // NDEBUG
{
// Check create() when operands are constant.
auto *Const42 =
sandboxir::ConstantFP::get(sandboxir::Type::getFloatTy(Ctx), 42.0);
auto *NewConstCmp =
sandboxir::CmpInst::create(llvm::CmpInst::FCMP_ULE, Const42, Const42,
BB->begin(), Ctx, "NewConstCmp");
EXPECT_TRUE(isa<sandboxir::Constant>(NewConstCmp));
}
}
TEST_F(SandboxIRTest, UnreachableInst) {
parseIR(C, R"IR(
define void @foo() {
unreachable
}
)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 *UI = cast<sandboxir::UnreachableInst>(&*It++);
EXPECT_EQ(UI->getNumSuccessors(), 0u);
EXPECT_EQ(UI->getNumOfIRInstrs(), 1u);
// Check create(InsertBefore)
sandboxir::UnreachableInst *NewUI =
sandboxir::UnreachableInst::create(UI->getIterator(), Ctx);
EXPECT_EQ(NewUI->getNextNode(), UI);
// Check create(InsertAtEnd)
sandboxir::UnreachableInst *NewUIEnd =
sandboxir::UnreachableInst::create(/*InsertAtEnd=*/BB, Ctx);
EXPECT_EQ(NewUIEnd->getParent(), BB);
EXPECT_EQ(NewUIEnd->getNextNode(), nullptr);
}
/// Makes sure that all Instruction sub-classes have a classof().
TEST_F(SandboxIRTest, CheckClassof) {
#define DEF_INSTR(ID, OPC, CLASS) \
EXPECT_NE(&sandboxir::CLASS::classof, &sandboxir::Instruction::classof);
#include "llvm/SandboxIR/Values.def"
}
TEST_F(SandboxIRTest, InstructionCallbacks) {
parseIR(C, R"IR(
define void @foo(ptr %ptr, i8 %val) {
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto &F = *Ctx.createFunction(&LLVMF);
auto &BB = *F.begin();
sandboxir::Argument *Ptr = F.getArg(0);
sandboxir::Argument *Val = F.getArg(1);
sandboxir::Instruction *Ret = &BB.front();
SmallVector<sandboxir::Instruction *> Inserted;
auto InsertCbId = Ctx.registerCreateInstrCallback(
[&Inserted](sandboxir::Instruction *I) { Inserted.push_back(I); });
SmallVector<sandboxir::Instruction *> Removed;
auto RemoveCbId = Ctx.registerEraseInstrCallback(
[&Removed](sandboxir::Instruction *I) { Removed.push_back(I); });
// Keep the moved instruction and the instruction pointed by the Where
// iterator so we can check both callback arguments work as expected.
SmallVector<std::pair<sandboxir::Instruction *, sandboxir::Instruction *>>
Moved;
auto MoveCbId = Ctx.registerMoveInstrCallback(
[&Moved](sandboxir::Instruction *I, const sandboxir::BBIterator &Where) {
// Use a nullptr to signal "move to end" to keep it single. We only
// have a basic block in this test case anyway.
if (Where == Where.getNodeParent()->end())
Moved.push_back(std::make_pair(I, nullptr));
else
Moved.push_back(std::make_pair(I, &*Where));
});
// Two more insertion callbacks, to check that they're called in registration
// order.
SmallVector<int> Order;
auto CheckOrderInsertCbId1 = Ctx.registerCreateInstrCallback(
[&Order](sandboxir::Instruction *I) { Order.push_back(1); });
auto CheckOrderInsertCbId2 = Ctx.registerCreateInstrCallback(
[&Order](sandboxir::Instruction *I) { Order.push_back(2); });
Ctx.save();
auto *NewI = sandboxir::StoreInst::create(Val, Ptr, /*Align=*/std::nullopt,
Ret->getIterator(), Ctx);
EXPECT_THAT(Inserted, testing::ElementsAre(NewI));
EXPECT_THAT(Removed, testing::IsEmpty());
EXPECT_THAT(Moved, testing::IsEmpty());
EXPECT_THAT(Order, testing::ElementsAre(1, 2));
Ret->moveBefore(NewI);
EXPECT_THAT(Inserted, testing::ElementsAre(NewI));
EXPECT_THAT(Removed, testing::IsEmpty());
EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI)));
Ret->eraseFromParent();
EXPECT_THAT(Inserted, testing::ElementsAre(NewI));
EXPECT_THAT(Removed, testing::ElementsAre(Ret));
EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI)));
NewI->eraseFromParent();
EXPECT_THAT(Inserted, testing::ElementsAre(NewI));
EXPECT_THAT(Removed, testing::ElementsAre(Ret, NewI));
EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI)));
// Check that after revert the callbacks have been called for the inverse
// operations of the changes made so far.
Ctx.revert();
EXPECT_THAT(Inserted, testing::ElementsAre(NewI, NewI, Ret));
EXPECT_THAT(Removed, testing::ElementsAre(Ret, NewI, NewI));
EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI),
std::make_pair(Ret, nullptr)));
EXPECT_THAT(Order, testing::ElementsAre(1, 2, 1, 2, 1, 2));
// Check that deregistration works. Do an operation of each type after
// deregistering callbacks and check.
Inserted.clear();
Removed.clear();
Moved.clear();
Ctx.unregisterCreateInstrCallback(InsertCbId);
Ctx.unregisterEraseInstrCallback(RemoveCbId);
Ctx.unregisterMoveInstrCallback(MoveCbId);
Ctx.unregisterCreateInstrCallback(CheckOrderInsertCbId1);
Ctx.unregisterCreateInstrCallback(CheckOrderInsertCbId2);
auto *NewI2 = sandboxir::StoreInst::create(Val, Ptr, /*Align=*/std::nullopt,
Ret->getIterator(), Ctx);
Ret->moveBefore(NewI2);
Ret->eraseFromParent();
EXPECT_THAT(Inserted, testing::IsEmpty());
EXPECT_THAT(Removed, testing::IsEmpty());
EXPECT_THAT(Moved, testing::IsEmpty());
}
// Check callbacks when we set a Use.
TEST_F(SandboxIRTest, SetUseCallbacks) {
parseIR(C, R"IR(
define void @foo(i8 %v0, i8 %v1) {
%add0 = add i8 %v0, %v1
%add1 = add i8 %add0, %v1
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(LLVMF);
auto *Arg0 = F->getArg(0);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
auto *Add1 = cast<sandboxir::BinaryOperator>(&*It++);
SmallVector<std::pair<sandboxir::Use, sandboxir::Value *>> UsesSet;
auto Id = Ctx.registerSetUseCallback(
[&UsesSet](sandboxir::Use U, sandboxir::Value *NewSrc) {
UsesSet.push_back({U, NewSrc});
});
// Now change %add1 operand to not use %add0.
Add1->setOperand(0, Arg0);
EXPECT_EQ(UsesSet.size(), 1u);
EXPECT_EQ(UsesSet[0].first.get(), Add1->getOperandUse(0).get());
EXPECT_EQ(UsesSet[0].second, Arg0);
// Restore to previous state.
Add1->setOperand(0, Add0);
UsesSet.clear();
// RAUW
Add0->replaceAllUsesWith(Arg0);
EXPECT_EQ(UsesSet.size(), 1u);
EXPECT_EQ(UsesSet[0].first.get(), Add1->getOperandUse(0).get());
EXPECT_EQ(UsesSet[0].second, Arg0);
// Restore to previous state.
Add1->setOperand(0, Add0);
UsesSet.clear();
// RUWIf
Add0->replaceUsesWithIf(Arg0, [](const auto &U) { return true; });
EXPECT_EQ(UsesSet.size(), 1u);
EXPECT_EQ(UsesSet[0].first.get(), Add1->getOperandUse(0).get());
EXPECT_EQ(UsesSet[0].second, Arg0);
// Restore to previous state.
Add1->setOperand(0, Add0);
UsesSet.clear();
// RUOW
Add1->replaceUsesOfWith(Add0, Arg0);
EXPECT_EQ(UsesSet.size(), 1u);
EXPECT_EQ(UsesSet[0].first.get(), Add1->getOperandUse(0).get());
EXPECT_EQ(UsesSet[0].second, Arg0);
// Restore to previous state.
Add1->setOperand(0, Add0);
UsesSet.clear();
// Check unregister.
Ctx.unregisterSetUseCallback(Id);
Add0->replaceAllUsesWith(Arg0);
EXPECT_TRUE(UsesSet.empty());
}
TEST_F(SandboxIRTest, FunctionObjectAlreadyExists) {
parseIR(C, R"IR(
define void @foo() {
call void @bar()
ret void
}
define void @bar() {
ret void
}
)IR");
Function &LLVMFoo = *M->getFunction("foo");
Function &LLVMBar = *M->getFunction("bar");
sandboxir::Context Ctx(C);
// This will create a Function object for @bar().
Ctx.createFunction(&LLVMFoo);
EXPECT_NE(Ctx.getValue(&LLVMBar), nullptr);
// This should not crash, even though there is already a value for LLVMBar.
Ctx.createFunction(&LLVMBar);
}
TEST_F(SandboxIRTest, OpaqueValue) {
parseIR(C, R"IR(
declare void @bar(metadata)
define void @foo() {
call void @bar(metadata !1)
call void asm "asm", ""()
ret void
}
!1 = !{}
)IR");
Function &LLVMFoo = *M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(&LLVMFoo);
auto *BB = &*F->begin();
auto It = BB->begin();
auto *Call = cast<sandboxir::CallInst>(&*It++);
auto *Op0 = Call->getOperand(0);
EXPECT_TRUE(isa<sandboxir::OpaqueValue>(Op0));
auto *Asm = cast<sandboxir::CallInst>(&*It++);
auto *AsmOp0 = Asm->getOperand(0);
EXPECT_TRUE(isa<sandboxir::OpaqueValue>(AsmOp0));
}