forked from OSchip/llvm-project
281 lines
11 KiB
C++
281 lines
11 KiB
C++
//===--------- VectorBuilderTest.cpp - VectorBuilder unit tests -----------===//
|
|
//
|
|
// 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/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;
|
|
}
|
|
};
|
|
|
|
/// Check that creating binary arithmetic VP intrinsics works.
|
|
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;
|
|
|
|
// Structure check.
|
|
if (!ConstMask->isAllOnesValue())
|
|
return false;
|
|
|
|
// Type check.
|
|
auto *MaskVecTy = cast<FixedVectorType>(ConstMask->getType());
|
|
if (MaskVecTy->getNumElements() != NumElements)
|
|
return false;
|
|
|
|
return MaskVecTy->getElementType()->isIntegerTy(1);
|
|
}
|
|
|
|
/// Check that creating binary arithmetic VP intrinsics works.
|
|
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;
|
|
|
|
// Value check.
|
|
if (ConstEVL->getZExtValue() != ExpectedEVL)
|
|
return false;
|
|
|
|
// Type check.
|
|
return ConstEVL->getType()->isIntegerTy(32);
|
|
}
|
|
|
|
/// Check that creating binary arithmetic VP intrinsics works.
|
|
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"
|
|
}
|
|
|
|
/// Check that creating binary arithmetic VP intrinsics works.
|
|
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"
|
|
}
|
|
/// Check that creating vp.load/vp.store works.
|
|
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);
|
|
|
|
// vp.load
|
|
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);
|
|
|
|
// vp.store
|
|
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);
|
|
}
|
|
|
|
/// Check that the SilentlyReturnNone error handling mode works.
|
|
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);
|
|
}
|
|
|
|
/// Check that the ReportAndFail error handling mode aborts as advertised.
|
|
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");
|
|
}
|
|
|
|
} // end anonymous namespace
|