1 //===--------- VectorBuilderTest.cpp - VectorBuilder unit tests -----------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/IR/VectorBuilder.h" 10 #include "llvm/IR/Constants.h" 11 #include "llvm/IR/IRBuilder.h" 12 #include "llvm/IR/IntrinsicInst.h" 13 #include "llvm/IR/LLVMContext.h" 14 #include "llvm/IR/Module.h" 15 #include "gtest/gtest.h" 16 17 using namespace llvm; 18 19 namespace { 20 21 static unsigned VectorNumElements = 8; 22 23 class VectorBuilderTest : public testing::Test { 24 protected: 25 LLVMContext Context; 26 27 VectorBuilderTest() : Context() {} 28 29 std::unique_ptr<Module> createBuilderModule(Function *&Func, BasicBlock *&BB, 30 Value *&Mask, Value *&EVL) { 31 auto Mod = std::make_unique<Module>("TestModule", Context); 32 auto *Int32Ty = Type::getInt32Ty(Context); 33 auto *Mask8Ty = 34 FixedVectorType::get(Type::getInt1Ty(Context), VectorNumElements); 35 auto *VoidFuncTy = 36 FunctionType::get(Type::getVoidTy(Context), {Mask8Ty, Int32Ty}, false); 37 Func = 38 Function::Create(VoidFuncTy, GlobalValue::ExternalLinkage, "bla", *Mod); 39 Mask = Func->getArg(0); 40 EVL = Func->getArg(1); 41 BB = BasicBlock::Create(Context, "entry", Func); 42 43 return Mod; 44 } 45 }; 46 47 /// Check that creating binary arithmetic VP intrinsics works. 48 TEST_F(VectorBuilderTest, TestCreateBinaryInstructions) { 49 Function *F; 50 BasicBlock *BB; 51 Value *Mask, *EVL; 52 auto Mod = createBuilderModule(F, BB, Mask, EVL); 53 54 IRBuilder<> Builder(BB); 55 VectorBuilder VBuild(Builder); 56 VBuild.setMask(Mask).setEVL(EVL); 57 58 auto *FloatVecTy = 59 FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements); 60 auto *IntVecTy = 61 FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements); 62 63 #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \ 64 { \ 65 auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \ 66 bool IsFP = (#INSTCLASS)[0] == 'F'; \ 67 auto *ValueTy = IsFP ? FloatVecTy : IntVecTy; \ 68 Value *Op = UndefValue::get(ValueTy); \ 69 auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \ 70 {Op, Op}); \ 71 ASSERT_TRUE(isa<VPIntrinsic>(I)); \ 72 auto *VPIntrin = cast<VPIntrinsic>(I); \ 73 ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ 74 ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \ 75 ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \ 76 } 77 #include "llvm/IR/Instruction.def" 78 } 79 80 static bool isAllTrueMask(Value *Val, unsigned NumElements) { 81 auto *ConstMask = dyn_cast<Constant>(Val); 82 if (!ConstMask) 83 return false; 84 85 // Structure check. 86 if (!ConstMask->isAllOnesValue()) 87 return false; 88 89 // Type check. 90 auto *MaskVecTy = cast<FixedVectorType>(ConstMask->getType()); 91 if (MaskVecTy->getNumElements() != NumElements) 92 return false; 93 94 return MaskVecTy->getElementType()->isIntegerTy(1); 95 } 96 97 /// Check that creating binary arithmetic VP intrinsics works. 98 TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoMask) { 99 Function *F; 100 BasicBlock *BB; 101 Value *Mask, *EVL; 102 auto Mod = createBuilderModule(F, BB, Mask, EVL); 103 104 IRBuilder<> Builder(BB); 105 VectorBuilder VBuild(Builder); 106 VBuild.setEVL(EVL).setStaticVL(VectorNumElements); 107 108 auto *FloatVecTy = 109 FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements); 110 auto *IntVecTy = 111 FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements); 112 113 #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \ 114 { \ 115 auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \ 116 bool IsFP = (#INSTCLASS)[0] == 'F'; \ 117 Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \ 118 Value *Op = UndefValue::get(ValueTy); \ 119 auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \ 120 {Op, Op}); \ 121 ASSERT_TRUE(isa<VPIntrinsic>(I)); \ 122 auto *VPIntrin = cast<VPIntrinsic>(I); \ 123 ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ 124 ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \ 125 ASSERT_EQ(VPIntrin->getVectorLengthParam(), EVL); \ 126 } 127 #include "llvm/IR/Instruction.def" 128 } 129 130 static bool isLegalConstEVL(Value *Val, unsigned ExpectedEVL) { 131 auto *ConstEVL = dyn_cast<ConstantInt>(Val); 132 if (!ConstEVL) 133 return false; 134 135 // Value check. 136 if (ConstEVL->getZExtValue() != ExpectedEVL) 137 return false; 138 139 // Type check. 140 return ConstEVL->getType()->isIntegerTy(32); 141 } 142 143 /// Check that creating binary arithmetic VP intrinsics works. 144 TEST_F(VectorBuilderTest, TestCreateBinaryInstructions_FixedVector_NoEVL) { 145 Function *F; 146 BasicBlock *BB; 147 Value *Mask, *EVL; 148 auto Mod = createBuilderModule(F, BB, Mask, EVL); 149 150 IRBuilder<> Builder(BB); 151 VectorBuilder VBuild(Builder); 152 VBuild.setMask(Mask).setStaticVL(VectorNumElements); 153 154 auto *FloatVecTy = 155 FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements); 156 auto *IntVecTy = 157 FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements); 158 159 #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \ 160 { \ 161 auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \ 162 bool IsFP = (#INSTCLASS)[0] == 'F'; \ 163 Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \ 164 Value *Op = UndefValue::get(ValueTy); \ 165 auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \ 166 {Op, Op}); \ 167 ASSERT_TRUE(isa<VPIntrinsic>(I)); \ 168 auto *VPIntrin = cast<VPIntrinsic>(I); \ 169 ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ 170 ASSERT_EQ(VPIntrin->getMaskParam(), Mask); \ 171 ASSERT_TRUE( \ 172 isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \ 173 } 174 #include "llvm/IR/Instruction.def" 175 } 176 177 /// Check that creating binary arithmetic VP intrinsics works. 178 TEST_F(VectorBuilderTest, 179 TestCreateBinaryInstructions_FixedVector_NoMask_NoEVL) { 180 Function *F; 181 BasicBlock *BB; 182 Value *Mask, *EVL; 183 auto Mod = createBuilderModule(F, BB, Mask, EVL); 184 185 IRBuilder<> Builder(BB); 186 VectorBuilder VBuild(Builder); 187 VBuild.setStaticVL(VectorNumElements); 188 189 auto *FloatVecTy = 190 FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements); 191 auto *IntVecTy = 192 FixedVectorType::get(Type::getInt32Ty(Context), VectorNumElements); 193 194 #define HANDLE_BINARY_INST(NUM, OPCODE, INSTCLASS) \ 195 { \ 196 auto VPID = VPIntrinsic::getForOpcode(Instruction::OPCODE); \ 197 bool IsFP = (#INSTCLASS)[0] == 'F'; \ 198 Type *ValueTy = IsFP ? FloatVecTy : IntVecTy; \ 199 Value *Op = UndefValue::get(ValueTy); \ 200 auto *I = VBuild.createVectorInstruction(Instruction::OPCODE, ValueTy, \ 201 {Op, Op}); \ 202 ASSERT_TRUE(isa<VPIntrinsic>(I)); \ 203 auto *VPIntrin = cast<VPIntrinsic>(I); \ 204 ASSERT_EQ(VPIntrin->getIntrinsicID(), VPID); \ 205 ASSERT_TRUE(isAllTrueMask(VPIntrin->getMaskParam(), VectorNumElements)); \ 206 ASSERT_TRUE( \ 207 isLegalConstEVL(VPIntrin->getVectorLengthParam(), VectorNumElements)); \ 208 } 209 #include "llvm/IR/Instruction.def" 210 } 211 /// Check that creating vp.load/vp.store works. 212 TEST_F(VectorBuilderTest, TestCreateLoadStore) { 213 Function *F; 214 BasicBlock *BB; 215 Value *Mask, *EVL; 216 auto Mod = createBuilderModule(F, BB, Mask, EVL); 217 218 IRBuilder<> Builder(BB); 219 VectorBuilder VBuild(Builder); 220 VBuild.setMask(Mask).setEVL(EVL); 221 222 auto *FloatVecTy = 223 FixedVectorType::get(Type::getFloatTy(Context), VectorNumElements); 224 auto *FloatVecPtrTy = FloatVecTy->getPointerTo(); 225 226 Value *FloatVecPtr = UndefValue::get(FloatVecPtrTy); 227 Value *FloatVec = UndefValue::get(FloatVecTy); 228 229 // vp.load 230 auto LoadVPID = VPIntrinsic::getForOpcode(Instruction::Load); 231 auto *LoadIntrin = VBuild.createVectorInstruction(Instruction::Load, 232 FloatVecTy, {FloatVecPtr}); 233 ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin)); 234 auto *VPLoad = cast<VPIntrinsic>(LoadIntrin); 235 ASSERT_EQ(VPLoad->getIntrinsicID(), LoadVPID); 236 ASSERT_EQ(VPLoad->getMemoryPointerParam(), FloatVecPtr); 237 238 // vp.store 239 auto *VoidTy = Builder.getVoidTy(); 240 auto StoreVPID = VPIntrinsic::getForOpcode(Instruction::Store); 241 auto *StoreIntrin = VBuild.createVectorInstruction(Instruction::Store, VoidTy, 242 {FloatVec, FloatVecPtr}); 243 ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin)); 244 auto *VPStore = cast<VPIntrinsic>(StoreIntrin); 245 ASSERT_EQ(VPStore->getIntrinsicID(), StoreVPID); 246 ASSERT_EQ(VPStore->getMemoryPointerParam(), FloatVecPtr); 247 ASSERT_EQ(VPStore->getMemoryDataParam(), FloatVec); 248 } 249 250 /// Check that the SilentlyReturnNone error handling mode works. 251 TEST_F(VectorBuilderTest, TestFail_SilentlyReturnNone) { 252 Function *F; 253 BasicBlock *BB; 254 Value *Mask, *EVL; 255 auto Mod = createBuilderModule(F, BB, Mask, EVL); 256 257 IRBuilder<> Builder(BB); 258 auto *VoidTy = Builder.getVoidTy(); 259 VectorBuilder VBuild(Builder, VectorBuilder::Behavior::SilentlyReturnNone); 260 VBuild.setMask(Mask).setEVL(EVL); 261 auto *Val = VBuild.createVectorInstruction(Instruction::Br, VoidTy, {}); 262 ASSERT_EQ(Val, nullptr); 263 } 264 265 /// Check that the ReportAndFail error handling mode aborts as advertised. 266 TEST_F(VectorBuilderTest, TestFail_ReportAndAbort) { 267 Function *F; 268 BasicBlock *BB; 269 Value *Mask, *EVL; 270 auto Mod = createBuilderModule(F, BB, Mask, EVL); 271 272 IRBuilder<> Builder(BB); 273 auto *VoidTy = Builder.getVoidTy(); 274 VectorBuilder VBuild(Builder, VectorBuilder::Behavior::ReportAndAbort); 275 VBuild.setMask(Mask).setEVL(EVL); 276 ASSERT_DEATH({ VBuild.createVectorInstruction(Instruction::Br, VoidTy, {}); }, 277 "No VPIntrinsic for this opcode"); 278 } 279 280 } // end anonymous namespace 281