#include "llvm/IR/VectorBuilder.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
static unsigned VectorNumElements = 8;
class VectorBuilderTest : public testing::Test {
protected:
LLVMContext Context;
VectorBuilderTest() : Context() {}
std::unique_ptr<Module> createBuilderModule(Function *&Func, BasicBlock *&BB,
Value *&Mask, Value *&EVL) {
auto Mod = std::make_unique<Module>("TestModule", Context);
auto *Int32Ty = Type::getInt32Ty(Context);
auto *Mask8Ty =
FixedVectorType::get(Type::getInt1Ty(Context), VectorNumElements);
auto *VoidFuncTy =
FunctionType::get(Type::getVoidTy(Context), {Mask8Ty, Int32Ty}, false);
Func =
Function::Create(VoidFuncTy, GlobalValue::ExternalLinkage, "bla", *Mod);
Mask = Func->getArg(0);
EVL = Func->getArg(1);
BB = BasicBlock::Create(Context, "entry", Func);
return Mod;
}
};
TEST_F(VectorBuilderTest, TestCreateBinaryInstructions) {
Function *F;
BasicBlock *BB;
Value *Mask, *EVL;
auto Mod = createBuilderModule(F, BB, Mask, EVL);
IRBuilder<> Builder(BB);
VectorBuilder VBuild(Builder);
VBuild.setMask(Mask).setEVL(EVL);
auto *FloatVecTy =
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
auto *IntVecTy =
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
{ \
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
bool IsFP = (#INSTCLASS)[0] == 'F'; \
auto *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
Value *Op = UndefValue::get(ValueTy); \
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
{Op, Op}); \
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
auto *VPIntrin = cast<VPIntrinsic>(I); \
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \
ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \
}
#include "llvm/IR/Instruction.def"
}
static bool isAllTrueMask(Value *Val, unsigned NumElements) {
auto *ConstMask = dyn_cast<Constant>(Val);
if (!ConstMask)
return false;
if (!ConstMask->isAllOnesValue())
return false;
auto *MaskVecTy = cast<FixedVectorType>(ConstMask->getType());
if (MaskVecTy->getNumElements() != NumElements)
return false;
return MaskVecTy->getElementType()->isIntegerTy(1);
}
TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoMask) {
Function *F;
BasicBlock *BB;
Value *Mask, *EVL;
auto Mod = createBuilderModule(F, BB, Mask, EVL);
IRBuilder<> Builder(BB);
VectorBuilder VBuild(Builder);
VBuild.setEVL(EVL).setStaticVL(VectorNumElements);
auto *FloatVecTy =
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
auto *IntVecTy =
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
{ \
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
bool IsFP = (#INSTCLASS)[0] == 'F'; \
Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
Value *Op = UndefValue::get(ValueTy); \
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
{Op, Op}); \
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
auto *VPIntrin = cast<VPIntrinsic>(I); \
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \
ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \
}
#include "llvm/IR/Instruction.def"
}
static bool isLegalConstEVL(Value *Val, unsigned ExpectedEVL) {
auto *ConstEVL = dyn_cast<ConstantInt>(Val);
if (!ConstEVL)
return false;
if (ConstEVL->getZExtValue() != ExpectedEVL)
return false;
return ConstEVL->getType()->isIntegerTy(32);
}
TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoEVL) {
Function *F;
BasicBlock *BB;
Value *Mask, *EVL;
auto Mod = createBuilderModule(F, BB, Mask, EVL);
IRBuilder<> Builder(BB);
VectorBuilder VBuild(Builder);
VBuild.setMask(Mask).setStaticVL(VectorNumElements);
auto *FloatVecTy =
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
auto *IntVecTy =
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
{ \
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
bool IsFP = (#INSTCLASS)[0] == 'F'; \
Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
Value *Op = UndefValue::get(ValueTy); \
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
{Op, Op}); \
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
auto *VPIntrin = cast<VPIntrinsic>(I); \
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \
ASSERT_TRUE( \
isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \
}
#include "llvm/IR/Instruction.def"
}
TEST_F(VectorBuilderTest,
TestCreateBinaryInstructions_FixedVector_NoMask_NoEVL) {
Function *F;
BasicBlock *BB;
Value *Mask, *EVL;
auto Mod = createBuilderModule(F, BB, Mask, EVL);
IRBuilder<> Builder(BB);
VectorBuilder VBuild(Builder);
VBuild.setStaticVL(VectorNumElements);
auto *FloatVecTy =
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
auto *IntVecTy =
FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements);
#define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \
{ \
auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \
bool IsFP = (#INSTCLASS)[0] == 'F'; \
Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \
Value *Op = UndefValue::get(ValueTy); \
auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \
{Op, Op}); \
ASSERT_TRUE(isa<VPIntrinsic>(I)); \
auto *VPIntrin = cast<VPIntrinsic>(I); \
ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \
ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \
ASSERT_TRUE( \
isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \
}
#include "llvm/IR/Instruction.def"
}
TEST_F(VectorBuilderTest, TestCreateLoadStore) {
Function *F;
BasicBlock *BB;
Value *Mask, *EVL;
auto Mod = createBuilderModule(F, BB, Mask, EVL);
IRBuilder<> Builder(BB);
VectorBuilder VBuild(Builder);
VBuild.setMask(Mask).setEVL(EVL);
auto *FloatVecTy =
FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements);
auto *FloatVecPtrTy = FloatVecTy->getPointerTo();
Value *FloatVecPtr = UndefValue::get(FloatVecPtrTy);
Value *FloatVec = UndefValue::get(FloatVecTy);
auto LoadVPID = VPIntrinsic::getForOpcode(Instruction::Load);
auto *LoadIntrin = VBuild.createVectorInstruction(Instruction::Load,
FloatVecTy, {FloatVecPtr});
ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
auto *VPLoad = cast<VPIntrinsic>(LoadIntrin);
ASSERT_EQ(VPLoad->getIntrinsicID(), LoadVPID);
ASSERT_EQ(VPLoad->getMemoryPointerParam(), FloatVecPtr);
auto *VoidTy = Builder.getVoidTy();
auto StoreVPID = VPIntrinsic::getForOpcode(Instruction::Store);
auto *StoreIntrin = VBuild.createVectorInstruction(Instruction::Store, VoidTy,
{FloatVec, FloatVecPtr});
ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
auto *VPStore = cast<VPIntrinsic>(StoreIntrin);
ASSERT_EQ(VPStore->getIntrinsicID(), StoreVPID);
ASSERT_EQ(VPStore->getMemoryPointerParam(), FloatVecPtr);
ASSERT_EQ(VPStore->getMemoryDataParam(), FloatVec);
}
TEST_F(VectorBuilderTest, TestFail_SilentlyReturnNone) {
Function *F;
BasicBlock *BB;
Value *Mask, *EVL;
auto Mod = createBuilderModule(F, BB, Mask, EVL);
IRBuilder<> Builder(BB);
auto *VoidTy = Builder.getVoidTy();
VectorBuilder VBuild(Builder, VectorBuilder::Behavior::SilentlyReturnNone);
VBuild.setMask(Mask).setEVL(EVL);
auto *Val = VBuild.createVectorInstruction(Instruction::Br, VoidTy, {});
ASSERT_EQ(Val, nullptr);
}
TEST_F(VectorBuilderTest, TestFail_ReportAndAbort) {
Function *F;
BasicBlock *BB;
Value *Mask, *EVL;
auto Mod = createBuilderModule(F, BB, Mask, EVL);
IRBuilder<> Builder(BB);
auto *VoidTy = Builder.getVoidTy();
VectorBuilder VBuild(Builder, VectorBuilder::Behavior::ReportAndAbort);
VBuild.setMask(Mask).setEVL(EVL);
ASSERT_DEATH({ VBuild.createVectorInstruction(Instruction::Br, VoidTy, {}); },
"No VPIntrinsic for this opcode");
}
}