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
VectorBuilderTest()27 VectorBuilderTest() : Context() {}
28
createBuilderModule(Function * & Func,BasicBlock * & BB,Value * & Mask,Value * & EVL)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.
TEST_F(VectorBuilderTest,TestCreateBinaryInstructions)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 = PoisonValue::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
isAllTrueMask(Value * Val,unsigned NumElements)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.
TEST_F(VectorBuilderTest,TestCreateBinaryInstructions_FixedVector_NoMask)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 = PoisonValue::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
isLegalConstEVL(Value * Val,unsigned ExpectedEVL)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.
TEST_F(VectorBuilderTest,TestCreateBinaryInstructions_FixedVector_NoEVL)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 = PoisonValue::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.
TEST_F(VectorBuilderTest,TestCreateBinaryInstructions_FixedVector_NoMask_NoEVL)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 = PoisonValue::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.
TEST_F(VectorBuilderTest,TestCreateLoadStore)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
225 Value *FloatVecPtr = PoisonValue::get(Builder.getPtrTy(0));
226 Value *FloatVec = PoisonValue::get(FloatVecTy);
227
228 // vp.load
229 auto LoadVPID = VPIntrinsic::getForOpcode(Instruction::Load);
230 auto *LoadIntrin = VBuild.createVectorInstruction(Instruction::Load,
231 FloatVecTy, {FloatVecPtr});
232 ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
233 auto *VPLoad = cast<VPIntrinsic>(LoadIntrin);
234 ASSERT_EQ(VPLoad->getIntrinsicID(), LoadVPID);
235 ASSERT_EQ(VPLoad->getMemoryPointerParam(), FloatVecPtr);
236
237 // vp.store
238 auto *VoidTy = Builder.getVoidTy();
239 auto StoreVPID = VPIntrinsic::getForOpcode(Instruction::Store);
240 auto *StoreIntrin = VBuild.createVectorInstruction(Instruction::Store, VoidTy,
241 {FloatVec, FloatVecPtr});
242 ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin));
243 auto *VPStore = cast<VPIntrinsic>(StoreIntrin);
244 ASSERT_EQ(VPStore->getIntrinsicID(), StoreVPID);
245 ASSERT_EQ(VPStore->getMemoryPointerParam(), FloatVecPtr);
246 ASSERT_EQ(VPStore->getMemoryDataParam(), FloatVec);
247 }
248
249 /// Check that the SilentlyReturnNone error handling mode works.
TEST_F(VectorBuilderTest,TestFail_SilentlyReturnNone)250 TEST_F(VectorBuilderTest, TestFail_SilentlyReturnNone) {
251 Function *F;
252 BasicBlock *BB;
253 Value *Mask, *EVL;
254 auto Mod = createBuilderModule(F, BB, Mask, EVL);
255
256 IRBuilder<> Builder(BB);
257 auto *VoidTy = Builder.getVoidTy();
258 VectorBuilder VBuild(Builder, VectorBuilder::Behavior::SilentlyReturnNone);
259 VBuild.setMask(Mask).setEVL(EVL);
260 auto *Val = VBuild.createVectorInstruction(Instruction::Br, VoidTy, {});
261 ASSERT_EQ(Val, nullptr);
262 }
263
264 /// Check that the ReportAndFail error handling mode aborts as advertised.
TEST_F(VectorBuilderTest,TestFail_ReportAndAbort)265 TEST_F(VectorBuilderTest, TestFail_ReportAndAbort) {
266 Function *F;
267 BasicBlock *BB;
268 Value *Mask, *EVL;
269 auto Mod = createBuilderModule(F, BB, Mask, EVL);
270
271 IRBuilder<> Builder(BB);
272 auto *VoidTy = Builder.getVoidTy();
273 VectorBuilder VBuild(Builder, VectorBuilder::Behavior::ReportAndAbort);
274 VBuild.setMask(Mask).setEVL(EVL);
275 ASSERT_DEATH({ VBuild.createVectorInstruction(Instruction::Br, VoidTy, {}); },
276 "No VPIntrinsic for this opcode");
277 }
278
279 } // end anonymous namespace
280