1//===- VPIntrinsicTest.cpp - VPIntrinsic 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/ADT/SmallVector.h"
10#include "llvm/AsmParser/Parser.h"
11#include "llvm/CodeGen/ISDOpcodes.h"
12#include "llvm/IR/Constants.h"
13#include "llvm/IR/IRBuilder.h"
14#include "llvm/IR/IntrinsicInst.h"
15#include "llvm/IR/LLVMContext.h"
16#include "llvm/IR/Module.h"
17#include "llvm/IR/Verifier.h"
18#include "llvm/Support/SourceMgr.h"
19#include "gtest/gtest.h"
20#include <optional>
21#include <sstream>
22
23using namespace llvm;
24
25namespace {
26
27static const char *ReductionIntOpcodes[] = {
28 "add", "mul", "and", "or", "xor", "smin", "smax", "umin", "umax"};
29
30static const char *ReductionFPOpcodes[] = {"fadd", "fmul", "fmin", "fmax"};
31
32class VPIntrinsicTest : public testing::Test {
33protected:
34 LLVMContext Context;
35
36 VPIntrinsicTest() : Context() {}
37
38 LLVMContext C;
39 SMDiagnostic Err;
40
41 std::unique_ptr<Module> createVPDeclarationModule() {
42 const char *BinaryIntOpcodes[] = {"add", "sub", "mul", "sdiv", "srem",
43 "udiv", "urem", "and", "xor", "or",
44 "ashr", "lshr", "shl", "smin", "smax",
45 "umin", "umax"};
46 std::stringstream Str;
47 for (const char *BinaryIntOpcode : BinaryIntOpcodes)
48 Str << " declare <8 x i32> @llvm.vp." << BinaryIntOpcode
49 << ".v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) ";
50
51 const char *BinaryFPOpcodes[] = {"fadd", "fsub", "fmul", "fdiv",
52 "frem", "minnum", "maxnum", "minimum",
53 "maximum", "copysign"};
54 for (const char *BinaryFPOpcode : BinaryFPOpcodes)
55 Str << " declare <8 x float> @llvm.vp." << BinaryFPOpcode
56 << ".v8f32(<8 x float>, <8 x float>, <8 x i1>, i32) ";
57
58 Str << " declare <8 x float> @llvm.vp.floor.v8f32(<8 x float>, <8 x i1>, "
59 "i32)";
60 Str << " declare <8 x float> @llvm.vp.round.v8f32(<8 x float>, <8 x i1>, "
61 "i32)";
62 Str << " declare <8 x float> @llvm.vp.roundeven.v8f32(<8 x float>, <8 x "
63 "i1>, "
64 "i32)";
65 Str << " declare <8 x float> @llvm.vp.roundtozero.v8f32(<8 x float>, <8 x "
66 "i1>, "
67 "i32)";
68 Str << " declare <8 x float> @llvm.vp.rint.v8f32(<8 x float>, <8 x i1>, "
69 "i32)";
70 Str << " declare <8 x float> @llvm.vp.nearbyint.v8f32(<8 x float>, <8 x "
71 "i1>, "
72 "i32)";
73 Str << " declare <8 x float> @llvm.vp.ceil.v8f32(<8 x float>, <8 x i1>, "
74 "i32)";
75 Str << " declare <8 x i32> @llvm.vp.lrint.v8i32.v8f32(<8 x float>, "
76 "<8 x i1>, i32)";
77 Str << " declare <8 x i64> @llvm.vp.llrint.v8i64.v8f32(<8 x float>, "
78 "<8 x i1>, i32)";
79 Str << " declare <8 x float> @llvm.vp.fneg.v8f32(<8 x float>, <8 x i1>, "
80 "i32)";
81 Str << " declare <8 x float> @llvm.vp.fabs.v8f32(<8 x float>, <8 x i1>, "
82 "i32)";
83 Str << " declare <8 x float> @llvm.vp.sqrt.v8f32(<8 x float>, <8 x i1>, "
84 "i32)";
85 Str << " declare <8 x float> @llvm.vp.fma.v8f32(<8 x float>, <8 x float>, "
86 "<8 x float>, <8 x i1>, i32) ";
87 Str << " declare <8 x float> @llvm.vp.fmuladd.v8f32(<8 x float>, "
88 "<8 x float>, <8 x float>, <8 x i1>, i32) ";
89
90 Str << " declare void @llvm.vp.store.v8i32.p0v8i32(<8 x i32>, <8 x i32>*, "
91 "<8 x i1>, i32) ";
92 Str << "declare void "
93 "@llvm.experimental.vp.strided.store.v8i32.i32(<8 x i32>, "
94 "i32*, i32, <8 x i1>, i32) ";
95 Str << "declare void "
96 "@llvm.experimental.vp.strided.store.v8i32.p1i32.i32(<8 x i32>, "
97 "i32 addrspace(1)*, i32, <8 x i1>, i32) ";
98 Str << " declare void @llvm.vp.scatter.v8i32.v8p0i32(<8 x i32>, <8 x "
99 "i32*>, <8 x i1>, i32) ";
100 Str << " declare <8 x i32> @llvm.vp.load.v8i32.p0v8i32(<8 x i32>*, <8 x "
101 "i1>, i32) ";
102 Str << "declare <8 x i32> "
103 "@llvm.experimental.vp.strided.load.v8i32.i32(i32*, i32, <8 "
104 "x i1>, i32) ";
105 Str << "declare <8 x i32> "
106 "@llvm.experimental.vp.strided.load.v8i32.p1i32.i32(i32 "
107 "addrspace(1)*, i32, <8 x i1>, i32) ";
108 Str << " declare <8 x i32> @llvm.vp.gather.v8i32.v8p0i32(<8 x i32*>, <8 x "
109 "i1>, i32) ";
110
111 for (const char *ReductionOpcode : ReductionIntOpcodes)
112 Str << " declare i32 @llvm.vp.reduce." << ReductionOpcode
113 << ".v8i32(i32, <8 x i32>, <8 x i1>, i32) ";
114
115 for (const char *ReductionOpcode : ReductionFPOpcodes)
116 Str << " declare float @llvm.vp.reduce." << ReductionOpcode
117 << ".v8f32(float, <8 x float>, <8 x i1>, i32) ";
118
119 Str << " declare <8 x i32> @llvm.vp.merge.v8i32(<8 x i1>, <8 x i32>, <8 x "
120 "i32>, i32)";
121 Str << " declare <8 x i32> @llvm.vp.select.v8i32(<8 x i1>, <8 x i32>, <8 x "
122 "i32>, i32)";
123 Str << " declare <8 x i1> @llvm.vp.is.fpclass.v8f32(<8 x float>, i32, <8 x "
124 "i1>, i32)";
125 Str << " declare <8 x i32> @llvm.experimental.vp.splice.v8i32(<8 x "
126 "i32>, <8 x i32>, i32, <8 x i1>, i32, i32) ";
127
128 Str << " declare <8 x i32> @llvm.vp.fptoui.v8i32"
129 << ".v8f32(<8 x float>, <8 x i1>, i32) ";
130 Str << " declare <8 x i32> @llvm.vp.fptosi.v8i32"
131 << ".v8f32(<8 x float>, <8 x i1>, i32) ";
132 Str << " declare <8 x float> @llvm.vp.uitofp.v8f32"
133 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
134 Str << " declare <8 x float> @llvm.vp.sitofp.v8f32"
135 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
136 Str << " declare <8 x float> @llvm.vp.fptrunc.v8f32"
137 << ".v8f64(<8 x double>, <8 x i1>, i32) ";
138 Str << " declare <8 x double> @llvm.vp.fpext.v8f64"
139 << ".v8f32(<8 x float>, <8 x i1>, i32) ";
140 Str << " declare <8 x i32> @llvm.vp.trunc.v8i32"
141 << ".v8i64(<8 x i64>, <8 x i1>, i32) ";
142 Str << " declare <8 x i64> @llvm.vp.zext.v8i64"
143 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
144 Str << " declare <8 x i64> @llvm.vp.sext.v8i64"
145 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
146 Str << " declare <8 x i32> @llvm.vp.ptrtoint.v8i32"
147 << ".v8p0i32(<8 x i32*>, <8 x i1>, i32) ";
148 Str << " declare <8 x i32*> @llvm.vp.inttoptr.v8p0i32"
149 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
150
151 Str << " declare <8 x i1> @llvm.vp.fcmp.v8f32"
152 << "(<8 x float>, <8 x float>, metadata, <8 x i1>, i32) ";
153 Str << " declare <8 x i1> @llvm.vp.icmp.v8i16"
154 << "(<8 x i16>, <8 x i16>, metadata, <8 x i1>, i32) ";
155
156 Str << " declare <8 x i32> @llvm.experimental.vp.reverse.v8i32(<8 x i32>, "
157 "<8 x i1>, i32) ";
158 Str << " declare <8 x i16> @llvm.vp.abs.v8i16"
159 << "(<8 x i16>, i1 immarg, <8 x i1>, i32) ";
160 Str << " declare <8 x i16> @llvm.vp.bitreverse.v8i16"
161 << "(<8 x i16>, <8 x i1>, i32) ";
162 Str << " declare <8 x i16> @llvm.vp.bswap.v8i16"
163 << "(<8 x i16>, <8 x i1>, i32) ";
164 Str << " declare <8 x i16> @llvm.vp.ctpop.v8i16"
165 << "(<8 x i16>, <8 x i1>, i32) ";
166 Str << " declare <8 x i16> @llvm.vp.ctlz.v8i16"
167 << "(<8 x i16>, i1 immarg, <8 x i1>, i32) ";
168 Str << " declare <8 x i16> @llvm.vp.cttz.v8i16"
169 << "(<8 x i16>, i1 immarg, <8 x i1>, i32) ";
170 Str << " declare <8 x i16> @llvm.vp.sadd.sat.v8i16"
171 << "(<8 x i16>, <8 x i16>, <8 x i1>, i32) ";
172 Str << " declare <8 x i16> @llvm.vp.uadd.sat.v8i16"
173 << "(<8 x i16>, <8 x i16>, <8 x i1>, i32) ";
174 Str << " declare <8 x i16> @llvm.vp.ssub.sat.v8i16"
175 << "(<8 x i16>, <8 x i16>, <8 x i1>, i32) ";
176 Str << " declare <8 x i16> @llvm.vp.usub.sat.v8i16"
177 << "(<8 x i16>, <8 x i16>, <8 x i1>, i32) ";
178 Str << " declare <8 x i16> @llvm.vp.fshl.v8i16"
179 << "(<8 x i16>, <8 x i16>, <8 x i16>, <8 x i1>, i32) ";
180 Str << " declare <8 x i16> @llvm.vp.fshr.v8i16"
181 << "(<8 x i16>, <8 x i16>, <8 x i16>, <8 x i1>, i32) ";
182
183 return parseAssemblyString(AsmString: Str.str(), Err, Context&: C);
184 }
185};
186
187/// Check that the property scopes include/llvm/IR/VPIntrinsics.def are closed.
188TEST_F(VPIntrinsicTest, VPIntrinsicsDefScopes) {
189 std::optional<Intrinsic::ID> ScopeVPID;
190#define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) \
191 ASSERT_FALSE(ScopeVPID.has_value()); \
192 ScopeVPID = Intrinsic::VPID;
193#define END_REGISTER_VP_INTRINSIC(VPID) \
194 ASSERT_TRUE(ScopeVPID.has_value()); \
195 ASSERT_EQ(*ScopeVPID, Intrinsic::VPID); \
196 ScopeVPID = std::nullopt;
197
198 std::optional<ISD::NodeType> ScopeOPC;
199#define BEGIN_REGISTER_VP_SDNODE(SDOPC, ...) \
200 ASSERT_FALSE(ScopeOPC.has_value()); \
201 ScopeOPC = ISD::SDOPC;
202#define END_REGISTER_VP_SDNODE(SDOPC) \
203 ASSERT_TRUE(ScopeOPC.has_value()); \
204 ASSERT_EQ(*ScopeOPC, ISD::SDOPC); \
205 ScopeOPC = std::nullopt;
206#include "llvm/IR/VPIntrinsics.def"
207
208 ASSERT_FALSE(ScopeVPID.has_value());
209 ASSERT_FALSE(ScopeOPC.has_value());
210}
211
212/// Check that every VP intrinsic in the test module is recognized as a VP
213/// intrinsic.
214TEST_F(VPIntrinsicTest, VPModuleComplete) {
215 std::unique_ptr<Module> M = createVPDeclarationModule();
216 assert(M);
217
218 // Check that all @llvm.vp.* functions in the module are recognized vp
219 // intrinsics.
220 std::set<Intrinsic::ID> SeenIDs;
221 for (const auto &VPDecl : *M) {
222 ASSERT_TRUE(VPDecl.isIntrinsic());
223 ASSERT_TRUE(VPIntrinsic::isVPIntrinsic(VPDecl.getIntrinsicID()));
224 SeenIDs.insert(x: VPDecl.getIntrinsicID());
225 }
226
227 // Check that every registered VP intrinsic has an instance in the test
228 // module.
229#define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) \
230 ASSERT_TRUE(SeenIDs.count(Intrinsic::VPID));
231#include "llvm/IR/VPIntrinsics.def"
232}
233
234/// Check that VPIntrinsic:canIgnoreVectorLengthParam() returns true
235/// if the vector length parameter does not mask off any lanes.
236TEST_F(VPIntrinsicTest, CanIgnoreVectorLength) {
237 LLVMContext C;
238 SMDiagnostic Err;
239
240 std::unique_ptr<Module> M =
241 parseAssemblyString(
242AsmString: "declare <256 x i64> @llvm.vp.mul.v256i64(<256 x i64>, <256 x i64>, <256 x i1>, i32)"
243"declare <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64>, <vscale x 2 x i64>, <vscale x 2 x i1>, i32)"
244"declare <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64>, <vscale x 1 x i64>, <vscale x 1 x i1>, i32)"
245"declare i32 @llvm.vscale.i32()"
246"define void @test_static_vlen( "
247" <256 x i64> %i0, <vscale x 2 x i64> %si0x2, <vscale x 1 x i64> %si0x1,"
248" <256 x i64> %i1, <vscale x 2 x i64> %si1x2, <vscale x 1 x i64> %si1x1,"
249" <256 x i1> %m, <vscale x 2 x i1> %smx2, <vscale x 1 x i1> %smx1, i32 %vl) { "
250" %r0 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 %vl)"
251" %r1 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 256)"
252" %r2 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 0)"
253" %r3 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 7)"
254" %r4 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 123)"
255" %vs = call i32 @llvm.vscale.i32()"
256" %vs.x2 = mul i32 %vs, 2"
257" %r5 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 %vs.x2)"
258" %r6 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 %vs)"
259" %r7 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 99999)"
260" %r8 = call <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64> %si0x1, <vscale x 1 x i64> %si1x1, <vscale x 1 x i1> %smx1, i32 %vs)"
261" %r9 = call <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64> %si0x1, <vscale x 1 x i64> %si1x1, <vscale x 1 x i1> %smx1, i32 1)"
262" %r10 = call <vscale x 1 x i64> @llvm.vp.mul.nxv1i64(<vscale x 1 x i64> %si0x1, <vscale x 1 x i64> %si1x1, <vscale x 1 x i1> %smx1, i32 %vs.x2)"
263" %vs.wat = add i32 %vs, 2"
264" %r11 = call <vscale x 2 x i64> @llvm.vp.mul.nxv2i64(<vscale x 2 x i64> %si0x2, <vscale x 2 x i64> %si1x2, <vscale x 2 x i1> %smx2, i32 %vs.wat)"
265" ret void "
266"}",
267 Err, Context&: C);
268
269 auto *F = M->getFunction(Name: "test_static_vlen");
270 assert(F);
271
272 const bool Expected[] = {false, true, false, false, false, true,
273 false, false, true, false, true, false};
274 const auto *ExpectedIt = std::begin(arr: Expected);
275 for (auto &I : F->getEntryBlock()) {
276 VPIntrinsic *VPI = dyn_cast<VPIntrinsic>(Val: &I);
277 if (!VPI)
278 continue;
279
280 ASSERT_NE(ExpectedIt, std::end(Expected));
281 ASSERT_EQ(*ExpectedIt, VPI->canIgnoreVectorLengthParam());
282 ++ExpectedIt;
283 }
284}
285
286/// Check that the argument returned by
287/// VPIntrinsic::get<X>ParamPos(Intrinsic::ID) has the expected type.
288TEST_F(VPIntrinsicTest, GetParamPos) {
289 std::unique_ptr<Module> M = createVPDeclarationModule();
290 assert(M);
291
292 for (Function &F : *M) {
293 ASSERT_TRUE(F.isIntrinsic());
294 std::optional<unsigned> MaskParamPos =
295 VPIntrinsic::getMaskParamPos(IntrinsicID: F.getIntrinsicID());
296 if (MaskParamPos) {
297 Type *MaskParamType = F.getArg(i: *MaskParamPos)->getType();
298 ASSERT_TRUE(MaskParamType->isVectorTy());
299 ASSERT_TRUE(
300 cast<VectorType>(MaskParamType)->getElementType()->isIntegerTy(1));
301 }
302
303 std::optional<unsigned> VecLenParamPos =
304 VPIntrinsic::getVectorLengthParamPos(IntrinsicID: F.getIntrinsicID());
305 if (VecLenParamPos) {
306 Type *VecLenParamType = F.getArg(i: *VecLenParamPos)->getType();
307 ASSERT_TRUE(VecLenParamType->isIntegerTy(32));
308 }
309 }
310}
311
312/// Check that going from Opcode to VP intrinsic and back results in the same
313/// Opcode.
314TEST_F(VPIntrinsicTest, OpcodeRoundTrip) {
315 std::vector<unsigned> Opcodes;
316 Opcodes.reserve(n: 100);
317
318 {
319#define HANDLE_INST(OCNum, OCName, Class) Opcodes.push_back(OCNum);
320#include "llvm/IR/Instruction.def"
321 }
322
323 unsigned FullTripCounts = 0;
324 for (unsigned OC : Opcodes) {
325 Intrinsic::ID VPID = VPIntrinsic::getForOpcode(OC);
326 // No equivalent VP intrinsic available.
327 if (VPID == Intrinsic::not_intrinsic)
328 continue;
329
330 std::optional<unsigned> RoundTripOC =
331 VPIntrinsic::getFunctionalOpcodeForVP(ID: VPID);
332 // No equivalent Opcode available.
333 if (!RoundTripOC)
334 continue;
335
336 ASSERT_EQ(*RoundTripOC, OC);
337 ++FullTripCounts;
338 }
339 ASSERT_NE(FullTripCounts, 0u);
340}
341
342/// Check that going from VP intrinsic to Opcode and back results in the same
343/// intrinsic id.
344TEST_F(VPIntrinsicTest, IntrinsicIDRoundTrip) {
345 std::unique_ptr<Module> M = createVPDeclarationModule();
346 assert(M);
347
348 unsigned FullTripCounts = 0;
349 for (const auto &VPDecl : *M) {
350 auto VPID = VPDecl.getIntrinsicID();
351 std::optional<unsigned> OC = VPIntrinsic::getFunctionalOpcodeForVP(ID: VPID);
352
353 // no equivalent Opcode available
354 if (!OC)
355 continue;
356
357 Intrinsic::ID RoundTripVPID = VPIntrinsic::getForOpcode(OC: *OC);
358
359 ASSERT_EQ(RoundTripVPID, VPID);
360 ++FullTripCounts;
361 }
362 ASSERT_NE(FullTripCounts, 0u);
363}
364
365/// Check that VPIntrinsic::getDeclarationForParams works.
366TEST_F(VPIntrinsicTest, VPIntrinsicDeclarationForParams) {
367 std::unique_ptr<Module> M = createVPDeclarationModule();
368 assert(M);
369
370 auto OutM = std::make_unique<Module>(args: "", args&: M->getContext());
371
372 for (auto &F : *M) {
373 auto *FuncTy = F.getFunctionType();
374
375 // Declare intrinsic anew with explicit types.
376 std::vector<Value *> Values;
377 for (auto *ParamTy : FuncTy->params())
378 Values.push_back(x: UndefValue::get(T: ParamTy));
379
380 ASSERT_NE(F.getIntrinsicID(), Intrinsic::not_intrinsic);
381 auto *NewDecl = VPIntrinsic::getDeclarationForParams(
382 M: OutM.get(), F.getIntrinsicID(), ReturnType: FuncTy->getReturnType(), Params: Values);
383 ASSERT_TRUE(NewDecl);
384
385 // Check that 'old decl' == 'new decl'.
386 ASSERT_EQ(F.getIntrinsicID(), NewDecl->getIntrinsicID());
387 FunctionType::param_iterator ItNewParams =
388 NewDecl->getFunctionType()->param_begin();
389 FunctionType::param_iterator EndItNewParams =
390 NewDecl->getFunctionType()->param_end();
391 for (auto *ParamTy : FuncTy->params()) {
392 ASSERT_NE(ItNewParams, EndItNewParams);
393 ASSERT_EQ(*ItNewParams, ParamTy);
394 ++ItNewParams;
395 }
396 }
397}
398
399/// Check that the HANDLE_VP_TO_CONSTRAINEDFP maps to an existing intrinsic with
400/// the right amount of constrained-fp metadata args.
401TEST_F(VPIntrinsicTest, HandleToConstrainedFP) {
402#define VP_PROPERTY_CONSTRAINEDFP(HASROUND, HASEXCEPT, CFPID) \
403 { \
404 SmallVector<Intrinsic::IITDescriptor, 5> T; \
405 Intrinsic::getIntrinsicInfoTableEntries(Intrinsic::CFPID, T); \
406 unsigned NumMetadataArgs = 0; \
407 for (auto TD : T) \
408 NumMetadataArgs += (TD.Kind == Intrinsic::IITDescriptor::Metadata); \
409 bool IsCmp = Intrinsic::CFPID == Intrinsic::experimental_constrained_fcmp; \
410 ASSERT_EQ(NumMetadataArgs, (unsigned)(IsCmp + HASROUND + HASEXCEPT)); \
411 }
412#include "llvm/IR/VPIntrinsics.def"
413}
414
415} // end anonymous namespace
416
417/// Check various properties of VPReductionIntrinsics
418TEST_F(VPIntrinsicTest, VPReductions) {
419 LLVMContext C;
420 SMDiagnostic Err;
421
422 std::stringstream Str;
423 Str << "declare <8 x i32> @llvm.vp.mul.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, "
424 "i32)";
425 for (const char *ReductionOpcode : ReductionIntOpcodes)
426 Str << " declare i32 @llvm.vp.reduce." << ReductionOpcode
427 << ".v8i32(i32, <8 x i32>, <8 x i1>, i32) ";
428
429 for (const char *ReductionOpcode : ReductionFPOpcodes)
430 Str << " declare float @llvm.vp.reduce." << ReductionOpcode
431 << ".v8f32(float, <8 x float>, <8 x i1>, i32) ";
432
433 Str << "define void @test_reductions(i32 %start, <8 x i32> %val, float "
434 "%fpstart, <8 x float> %fpval, <8 x i1> %m, i32 %vl) {";
435
436 // Mix in a regular non-reduction intrinsic to check that the
437 // VPReductionIntrinsic subclass works as intended.
438 Str << " %r0 = call <8 x i32> @llvm.vp.mul.v8i32(<8 x i32> %val, <8 x i32> "
439 "%val, <8 x i1> %m, i32 %vl)";
440
441 unsigned Idx = 1;
442 for (const char *ReductionOpcode : ReductionIntOpcodes)
443 Str << " %r" << Idx++ << " = call i32 @llvm.vp.reduce." << ReductionOpcode
444 << ".v8i32(i32 %start, <8 x i32> %val, <8 x i1> %m, i32 %vl)";
445 for (const char *ReductionOpcode : ReductionFPOpcodes)
446 Str << " %r" << Idx++ << " = call float @llvm.vp.reduce."
447 << ReductionOpcode
448 << ".v8f32(float %fpstart, <8 x float> %fpval, <8 x i1> %m, i32 %vl)";
449
450 Str << " ret void"
451 "}";
452
453 std::unique_ptr<Module> M = parseAssemblyString(AsmString: Str.str(), Err, Context&: C);
454 assert(M);
455
456 auto *F = M->getFunction(Name: "test_reductions");
457 assert(F);
458
459 for (const auto &I : F->getEntryBlock()) {
460 const VPIntrinsic *VPI = dyn_cast<VPIntrinsic>(Val: &I);
461 if (!VPI)
462 continue;
463
464 Intrinsic::ID ID = VPI->getIntrinsicID();
465 const auto *VPRedI = dyn_cast<VPReductionIntrinsic>(Val: &I);
466
467 if (!VPReductionIntrinsic::isVPReduction(ID)) {
468 EXPECT_EQ(VPRedI, nullptr);
469 EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID).has_value(), false);
470 EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID).has_value(), false);
471 continue;
472 }
473
474 EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID).has_value(), true);
475 EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID).has_value(), true);
476 ASSERT_NE(VPRedI, nullptr);
477 EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID),
478 VPRedI->getStartParamPos());
479 EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID),
480 VPRedI->getVectorParamPos());
481 EXPECT_EQ(VPRedI->getStartParamPos(), 0u);
482 EXPECT_EQ(VPRedI->getVectorParamPos(), 1u);
483 }
484}
485

source code of llvm/unittests/IR/VPIntrinsicTest.cpp