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>(args: "TestModule" , args&: Context); |
32 | auto *Int32Ty = Type::getInt32Ty(C&: Context); |
33 | auto *Mask8Ty = |
34 | FixedVectorType::get(ElementType: Type::getInt1Ty(C&: Context), NumElts: VectorNumElements); |
35 | auto *VoidFuncTy = |
36 | FunctionType::get(Result: Type::getVoidTy(C&: Context), Params: {Mask8Ty, Int32Ty}, isVarArg: false); |
37 | Func = |
38 | Function::Create(Ty: VoidFuncTy, Linkage: GlobalValue::ExternalLinkage, N: "bla" , M&: *Mod); |
39 | Mask = Func->getArg(i: 0); |
40 | EVL = Func->getArg(i: 1); |
41 | BB = BasicBlock::Create(Context, Name: "entry" , Parent: 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(Func&: 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(ElementType: Type::getFloatTy(C&: Context), NumElts: VectorNumElements); |
60 | auto *IntVecTy = |
61 | FixedVectorType::get(ElementType: Type::getInt32Ty(C&: Context), NumElts: 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>(Val: ConstMask->getType()); |
91 | if (MaskVecTy->getNumElements() != NumElements) |
92 | return false; |
93 | |
94 | return MaskVecTy->getElementType()->isIntegerTy(Bitwidth: 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(Func&: 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(ElementType: Type::getFloatTy(C&: Context), NumElts: VectorNumElements); |
110 | auto *IntVecTy = |
111 | FixedVectorType::get(ElementType: Type::getInt32Ty(C&: Context), NumElts: 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(Bitwidth: 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(Func&: 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(ElementType: Type::getFloatTy(C&: Context), NumElts: VectorNumElements); |
156 | auto *IntVecTy = |
157 | FixedVectorType::get(ElementType: Type::getInt32Ty(C&: Context), NumElts: 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(Func&: F, BB, Mask, EVL); |
184 | |
185 | IRBuilder<> Builder(BB); |
186 | VectorBuilder VBuild(Builder); |
187 | VBuild.setStaticVL(VectorNumElements); |
188 | |
189 | auto *FloatVecTy = |
190 | FixedVectorType::get(ElementType: Type::getFloatTy(C&: Context), NumElts: VectorNumElements); |
191 | auto *IntVecTy = |
192 | FixedVectorType::get(ElementType: Type::getInt32Ty(C&: Context), NumElts: 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(Func&: 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(ElementType: Type::getFloatTy(C&: Context), NumElts: VectorNumElements); |
224 | |
225 | Value *FloatVecPtr = UndefValue::get(T: Builder.getPtrTy(AddrSpace: 0)); |
226 | Value *FloatVec = UndefValue::get(T: FloatVecTy); |
227 | |
228 | // vp.load |
229 | auto LoadVPID = VPIntrinsic::getForOpcode(OC: Instruction::Load); |
230 | auto *LoadIntrin = VBuild.createVectorInstruction(Opcode: Instruction::Load, |
231 | ReturnTy: FloatVecTy, VecOpArray: {FloatVecPtr}); |
232 | ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin)); |
233 | auto *VPLoad = cast<VPIntrinsic>(Val: 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(OC: Instruction::Store); |
240 | auto *StoreIntrin = VBuild.createVectorInstruction(Opcode: Instruction::Store, ReturnTy: VoidTy, |
241 | VecOpArray: {FloatVec, FloatVecPtr}); |
242 | ASSERT_TRUE(isa<VPIntrinsic>(LoadIntrin)); |
243 | auto *VPStore = cast<VPIntrinsic>(Val: 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. |
250 | TEST_F(VectorBuilderTest, TestFail_SilentlyReturnNone) { |
251 | Function *F; |
252 | BasicBlock *BB; |
253 | Value *Mask, *EVL; |
254 | auto Mod = createBuilderModule(Func&: 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(Opcode: Instruction::Br, ReturnTy: VoidTy, VecOpArray: {}); |
261 | ASSERT_EQ(Val, nullptr); |
262 | } |
263 | |
264 | /// Check that the ReportAndFail error handling mode aborts as advertised. |
265 | TEST_F(VectorBuilderTest, TestFail_ReportAndAbort) { |
266 | Function *F; |
267 | BasicBlock *BB; |
268 | Value *Mask, *EVL; |
269 | auto Mod = createBuilderModule(Func&: 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 | |