xref: /llvm-project/llvm/unittests/IR/VectorBuilderTest.cpp (revision 68efc506f58776fdfcfc60707f8fe52584736d8a)
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