1 //===- VPIntrinsicTest.cpp - VPIntrinsic unit tests ---------===//
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
7 //===----------------------------------------------------------------------===//
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"
27 static const char *ReductionIntOpcodes
[] = {
28 "add", "mul", "and", "or", "xor", "smin", "smax", "umin", "umax"};
30 static const char *ReductionFPOpcodes
[] = {"fadd", "fmul", "fmin", "fmax"};
32 class VPIntrinsicTest
: public testing::Test
{
36 VPIntrinsicTest() : Context() {}
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",
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) ";
51 const char *BinaryFPOpcodes
[] = {"fadd", "fsub", "fmul", "fdiv",
52 "frem", "minnum", "maxnum", "copysign"};
53 for (const char *BinaryFPOpcode
: BinaryFPOpcodes
)
54 Str
<< " declare <8 x float> @llvm.vp." << BinaryFPOpcode
55 << ".v8f32(<8 x float>, <8 x float>, <8 x i1>, i32) ";
57 Str
<< " declare <8 x float> @llvm.vp.floor.v8f32(<8 x float>, <8 x i1>, "
59 Str
<< " declare <8 x float> @llvm.vp.round.v8f32(<8 x float>, <8 x i1>, "
61 Str
<< " declare <8 x float> @llvm.vp.roundeven.v8f32(<8 x float>, <8 x "
64 Str
<< " declare <8 x float> @llvm.vp.roundtozero.v8f32(<8 x float>, <8 x "
67 Str
<< " declare <8 x float> @llvm.vp.rint.v8f32(<8 x float>, <8 x i1>, "
69 Str
<< " declare <8 x float> @llvm.vp.nearbyint.v8f32(<8 x float>, <8 x "
72 Str
<< " declare <8 x float> @llvm.vp.ceil.v8f32(<8 x float>, <8 x i1>, "
74 Str
<< " declare <8 x float> @llvm.vp.fneg.v8f32(<8 x float>, <8 x i1>, "
76 Str
<< " declare <8 x float> @llvm.vp.fabs.v8f32(<8 x float>, <8 x i1>, "
78 Str
<< " declare <8 x float> @llvm.vp.sqrt.v8f32(<8 x float>, <8 x i1>, "
80 Str
<< " declare <8 x float> @llvm.vp.fma.v8f32(<8 x float>, <8 x float>, "
81 "<8 x float>, <8 x i1>, i32) ";
82 Str
<< " declare <8 x float> @llvm.vp.fmuladd.v8f32(<8 x float>, "
83 "<8 x float>, <8 x float>, <8 x i1>, i32) ";
85 Str
<< " declare void @llvm.vp.store.v8i32.p0v8i32(<8 x i32>, <8 x i32>*, "
87 Str
<< "declare void "
88 "@llvm.experimental.vp.strided.store.v8i32.i32(<8 x i32>, "
89 "i32*, i32, <8 x i1>, i32) ";
90 Str
<< "declare void "
91 "@llvm.experimental.vp.strided.store.v8i32.p1i32.i32(<8 x i32>, "
92 "i32 addrspace(1)*, i32, <8 x i1>, i32) ";
93 Str
<< " declare void @llvm.vp.scatter.v8i32.v8p0i32(<8 x i32>, <8 x "
94 "i32*>, <8 x i1>, i32) ";
95 Str
<< " declare <8 x i32> @llvm.vp.load.v8i32.p0v8i32(<8 x i32>*, <8 x "
97 Str
<< "declare <8 x i32> "
98 "@llvm.experimental.vp.strided.load.v8i32.i32(i32*, i32, <8 "
100 Str
<< "declare <8 x i32> "
101 "@llvm.experimental.vp.strided.load.v8i32.p1i32.i32(i32 "
102 "addrspace(1)*, i32, <8 x i1>, i32) ";
103 Str
<< " declare <8 x i32> @llvm.vp.gather.v8i32.v8p0i32(<8 x i32*>, <8 x "
106 for (const char *ReductionOpcode
: ReductionIntOpcodes
)
107 Str
<< " declare i32 @llvm.vp.reduce." << ReductionOpcode
108 << ".v8i32(i32, <8 x i32>, <8 x i1>, i32) ";
110 for (const char *ReductionOpcode
: ReductionFPOpcodes
)
111 Str
<< " declare float @llvm.vp.reduce." << ReductionOpcode
112 << ".v8f32(float, <8 x float>, <8 x i1>, i32) ";
114 Str
<< " declare <8 x i32> @llvm.vp.merge.v8i32(<8 x i1>, <8 x i32>, <8 x "
116 Str
<< " declare <8 x i32> @llvm.vp.select.v8i32(<8 x i1>, <8 x i32>, <8 x "
118 Str
<< " declare <8 x i1> @llvm.vp.is.fpclass.v8f32(<8 x float>, i32, <8 x "
120 Str
<< " declare <8 x i32> @llvm.experimental.vp.splice.v8i32(<8 x "
121 "i32>, <8 x i32>, i32, <8 x i1>, i32, i32) ";
123 Str
<< " declare <8 x i32> @llvm.vp.fptoui.v8i32"
124 << ".v8f32(<8 x float>, <8 x i1>, i32) ";
125 Str
<< " declare <8 x i32> @llvm.vp.fptosi.v8i32"
126 << ".v8f32(<8 x float>, <8 x i1>, i32) ";
127 Str
<< " declare <8 x float> @llvm.vp.uitofp.v8f32"
128 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
129 Str
<< " declare <8 x float> @llvm.vp.sitofp.v8f32"
130 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
131 Str
<< " declare <8 x float> @llvm.vp.fptrunc.v8f32"
132 << ".v8f64(<8 x double>, <8 x i1>, i32) ";
133 Str
<< " declare <8 x double> @llvm.vp.fpext.v8f64"
134 << ".v8f32(<8 x float>, <8 x i1>, i32) ";
135 Str
<< " declare <8 x i32> @llvm.vp.trunc.v8i32"
136 << ".v8i64(<8 x i64>, <8 x i1>, i32) ";
137 Str
<< " declare <8 x i64> @llvm.vp.zext.v8i64"
138 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
139 Str
<< " declare <8 x i64> @llvm.vp.sext.v8i64"
140 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
141 Str
<< " declare <8 x i32> @llvm.vp.ptrtoint.v8i32"
142 << ".v8p0i32(<8 x i32*>, <8 x i1>, i32) ";
143 Str
<< " declare <8 x i32*> @llvm.vp.inttoptr.v8p0i32"
144 << ".v8i32(<8 x i32>, <8 x i1>, i32) ";
146 Str
<< " declare <8 x i1> @llvm.vp.fcmp.v8f32"
147 << "(<8 x float>, <8 x float>, metadata, <8 x i1>, i32) ";
148 Str
<< " declare <8 x i1> @llvm.vp.icmp.v8i16"
149 << "(<8 x i16>, <8 x i16>, metadata, <8 x i1>, i32) ";
151 Str
<< " declare <8 x i16> @llvm.vp.abs.v8i16"
152 << "(<8 x i16>, i1 immarg, <8 x i1>, i32) ";
153 Str
<< " declare <8 x i16> @llvm.vp.bitreverse.v8i16"
154 << "(<8 x i16>, <8 x i1>, i32) ";
155 Str
<< " declare <8 x i16> @llvm.vp.bswap.v8i16"
156 << "(<8 x i16>, <8 x i1>, i32) ";
157 Str
<< " declare <8 x i16> @llvm.vp.ctpop.v8i16"
158 << "(<8 x i16>, <8 x i1>, i32) ";
159 Str
<< " declare <8 x i16> @llvm.vp.ctlz.v8i16"
160 << "(<8 x i16>, i1 immarg, <8 x i1>, i32) ";
161 Str
<< " declare <8 x i16> @llvm.vp.cttz.v8i16"
162 << "(<8 x i16>, i1 immarg, <8 x i1>, i32) ";
163 Str
<< " declare <8 x i16> @llvm.vp.fshl.v8i16"
164 << "(<8 x i16>, <8 x i16>, <8 x i16>, <8 x i1>, i32) ";
165 Str
<< " declare <8 x i16> @llvm.vp.fshr.v8i16"
166 << "(<8 x i16>, <8 x i16>, <8 x i16>, <8 x i1>, i32) ";
168 return parseAssemblyString(Str
.str(), Err
, C
);
172 /// Check that the property scopes include/llvm/IR/VPIntrinsics.def are closed.
173 TEST_F(VPIntrinsicTest
, VPIntrinsicsDefScopes
) {
174 std::optional
<Intrinsic::ID
> ScopeVPID
;
175 #define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) \
176 ASSERT_FALSE(ScopeVPID.has_value()); \
177 ScopeVPID = Intrinsic::VPID;
178 #define END_REGISTER_VP_INTRINSIC(VPID) \
179 ASSERT_TRUE(ScopeVPID.has_value()); \
180 ASSERT_EQ(*ScopeVPID, Intrinsic::VPID); \
181 ScopeVPID = std::nullopt;
183 std::optional
<ISD::NodeType
> ScopeOPC
;
184 #define BEGIN_REGISTER_VP_SDNODE(SDOPC, ...) \
185 ASSERT_FALSE(ScopeOPC.has_value()); \
186 ScopeOPC = ISD::SDOPC;
187 #define END_REGISTER_VP_SDNODE(SDOPC) \
188 ASSERT_TRUE(ScopeOPC.has_value()); \
189 ASSERT_EQ(*ScopeOPC, ISD::SDOPC); \
190 ScopeOPC = std::nullopt;
191 #include "llvm/IR/VPIntrinsics.def"
193 ASSERT_FALSE(ScopeVPID
.has_value());
194 ASSERT_FALSE(ScopeOPC
.has_value());
197 /// Check that every VP intrinsic in the test module is recognized as a VP
199 TEST_F(VPIntrinsicTest
, VPModuleComplete
) {
200 std::unique_ptr
<Module
> M
= createVPDeclarationModule();
203 // Check that all @llvm.vp.* functions in the module are recognized vp
205 std::set
<Intrinsic::ID
> SeenIDs
;
206 for (const auto &VPDecl
: *M
) {
207 ASSERT_TRUE(VPDecl
.isIntrinsic());
208 ASSERT_TRUE(VPIntrinsic::isVPIntrinsic(VPDecl
.getIntrinsicID()));
209 SeenIDs
.insert(VPDecl
.getIntrinsicID());
212 // Check that every registered VP intrinsic has an instance in the test
214 #define BEGIN_REGISTER_VP_INTRINSIC(VPID, ...) \
215 ASSERT_TRUE(SeenIDs.count(Intrinsic::VPID));
216 #include "llvm/IR/VPIntrinsics.def"
219 /// Check that VPIntrinsic:canIgnoreVectorLengthParam() returns true
220 /// if the vector length parameter does not mask off any lanes.
221 TEST_F(VPIntrinsicTest
, CanIgnoreVectorLength
) {
225 std::unique_ptr
<Module
> M
=
227 "declare <256 x i64> @llvm.vp.mul.v256i64(<256 x i64>, <256 x i64>, <256 x i1>, i32)"
228 "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)"
229 "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)"
230 "declare i32 @llvm.vscale.i32()"
231 "define void @test_static_vlen( "
232 " <256 x i64> %i0, <vscale x 2 x i64> %si0x2, <vscale x 1 x i64> %si0x1,"
233 " <256 x i64> %i1, <vscale x 2 x i64> %si1x2, <vscale x 1 x i64> %si1x1,"
234 " <256 x i1> %m, <vscale x 2 x i1> %smx2, <vscale x 1 x i1> %smx1, i32 %vl) { "
235 " %r0 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 %vl)"
236 " %r1 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 256)"
237 " %r2 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 0)"
238 " %r3 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 7)"
239 " %r4 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 123)"
240 " %vs = call i32 @llvm.vscale.i32()"
241 " %vs.x2 = mul i32 %vs, 2"
242 " %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)"
243 " %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)"
244 " %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)"
245 " %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)"
246 " %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)"
247 " %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)"
248 " %vs.wat = add i32 %vs, 2"
249 " %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)"
254 auto *F
= M
->getFunction("test_static_vlen");
257 const bool Expected
[] = {false, true, false, false, false, true,
258 false, false, true, false, true, false};
259 const auto *ExpectedIt
= std::begin(Expected
);
260 for (auto &I
: F
->getEntryBlock()) {
261 VPIntrinsic
*VPI
= dyn_cast
<VPIntrinsic
>(&I
);
265 ASSERT_NE(ExpectedIt
, std::end(Expected
));
266 ASSERT_EQ(*ExpectedIt
, VPI
->canIgnoreVectorLengthParam());
271 /// Check that the argument returned by
272 /// VPIntrinsic::get<X>ParamPos(Intrinsic::ID) has the expected type.
273 TEST_F(VPIntrinsicTest
, GetParamPos
) {
274 std::unique_ptr
<Module
> M
= createVPDeclarationModule();
277 for (Function
&F
: *M
) {
278 ASSERT_TRUE(F
.isIntrinsic());
279 std::optional
<unsigned> MaskParamPos
=
280 VPIntrinsic::getMaskParamPos(F
.getIntrinsicID());
282 Type
*MaskParamType
= F
.getArg(*MaskParamPos
)->getType();
283 ASSERT_TRUE(MaskParamType
->isVectorTy());
285 cast
<VectorType
>(MaskParamType
)->getElementType()->isIntegerTy(1));
288 std::optional
<unsigned> VecLenParamPos
=
289 VPIntrinsic::getVectorLengthParamPos(F
.getIntrinsicID());
290 if (VecLenParamPos
) {
291 Type
*VecLenParamType
= F
.getArg(*VecLenParamPos
)->getType();
292 ASSERT_TRUE(VecLenParamType
->isIntegerTy(32));
297 /// Check that going from Opcode to VP intrinsic and back results in the same
299 TEST_F(VPIntrinsicTest
, OpcodeRoundTrip
) {
300 std::vector
<unsigned> Opcodes
;
301 Opcodes
.reserve(100);
304 #define HANDLE_INST(OCNum, OCName, Class) Opcodes.push_back(OCNum);
305 #include "llvm/IR/Instruction.def"
308 unsigned FullTripCounts
= 0;
309 for (unsigned OC
: Opcodes
) {
310 Intrinsic::ID VPID
= VPIntrinsic::getForOpcode(OC
);
311 // No equivalent VP intrinsic available.
312 if (VPID
== Intrinsic::not_intrinsic
)
315 std::optional
<unsigned> RoundTripOC
=
316 VPIntrinsic::getFunctionalOpcodeForVP(VPID
);
317 // No equivalent Opcode available.
321 ASSERT_EQ(*RoundTripOC
, OC
);
324 ASSERT_NE(FullTripCounts
, 0u);
327 /// Check that going from VP intrinsic to Opcode and back results in the same
329 TEST_F(VPIntrinsicTest
, IntrinsicIDRoundTrip
) {
330 std::unique_ptr
<Module
> M
= createVPDeclarationModule();
333 unsigned FullTripCounts
= 0;
334 for (const auto &VPDecl
: *M
) {
335 auto VPID
= VPDecl
.getIntrinsicID();
336 std::optional
<unsigned> OC
= VPIntrinsic::getFunctionalOpcodeForVP(VPID
);
338 // no equivalent Opcode available
342 Intrinsic::ID RoundTripVPID
= VPIntrinsic::getForOpcode(*OC
);
344 ASSERT_EQ(RoundTripVPID
, VPID
);
347 ASSERT_NE(FullTripCounts
, 0u);
350 /// Check that VPIntrinsic::getDeclarationForParams works.
351 TEST_F(VPIntrinsicTest
, VPIntrinsicDeclarationForParams
) {
352 std::unique_ptr
<Module
> M
= createVPDeclarationModule();
355 auto OutM
= std::make_unique
<Module
>("", M
->getContext());
358 auto *FuncTy
= F
.getFunctionType();
360 // Declare intrinsic anew with explicit types.
361 std::vector
<Value
*> Values
;
362 for (auto *ParamTy
: FuncTy
->params())
363 Values
.push_back(UndefValue::get(ParamTy
));
365 ASSERT_NE(F
.getIntrinsicID(), Intrinsic::not_intrinsic
);
366 auto *NewDecl
= VPIntrinsic::getDeclarationForParams(
367 OutM
.get(), F
.getIntrinsicID(), FuncTy
->getReturnType(), Values
);
368 ASSERT_TRUE(NewDecl
);
370 // Check that 'old decl' == 'new decl'.
371 ASSERT_EQ(F
.getIntrinsicID(), NewDecl
->getIntrinsicID());
372 FunctionType::param_iterator ItNewParams
=
373 NewDecl
->getFunctionType()->param_begin();
374 FunctionType::param_iterator EndItNewParams
=
375 NewDecl
->getFunctionType()->param_end();
376 for (auto *ParamTy
: FuncTy
->params()) {
377 ASSERT_NE(ItNewParams
, EndItNewParams
);
378 ASSERT_EQ(*ItNewParams
, ParamTy
);
384 /// Check that the HANDLE_VP_TO_CONSTRAINEDFP maps to an existing intrinsic with
385 /// the right amount of constrained-fp metadata args.
386 TEST_F(VPIntrinsicTest
, HandleToConstrainedFP
) {
387 #define VP_PROPERTY_CONSTRAINEDFP(HASROUND, HASEXCEPT, CFPID) \
389 SmallVector<Intrinsic::IITDescriptor, 5> T; \
390 Intrinsic::getIntrinsicInfoTableEntries(Intrinsic::CFPID, T); \
391 unsigned NumMetadataArgs = 0; \
393 NumMetadataArgs += (TD.Kind == Intrinsic::IITDescriptor::Metadata); \
394 bool IsCmp = Intrinsic::CFPID == Intrinsic::experimental_constrained_fcmp; \
395 ASSERT_EQ(NumMetadataArgs, (unsigned)(IsCmp + HASROUND + HASEXCEPT)); \
397 #include "llvm/IR/VPIntrinsics.def"
400 } // end anonymous namespace
402 /// Check various properties of VPReductionIntrinsics
403 TEST_F(VPIntrinsicTest
, VPReductions
) {
407 std::stringstream Str
;
408 Str
<< "declare <8 x i32> @llvm.vp.mul.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, "
410 for (const char *ReductionOpcode
: ReductionIntOpcodes
)
411 Str
<< " declare i32 @llvm.vp.reduce." << ReductionOpcode
412 << ".v8i32(i32, <8 x i32>, <8 x i1>, i32) ";
414 for (const char *ReductionOpcode
: ReductionFPOpcodes
)
415 Str
<< " declare float @llvm.vp.reduce." << ReductionOpcode
416 << ".v8f32(float, <8 x float>, <8 x i1>, i32) ";
418 Str
<< "define void @test_reductions(i32 %start, <8 x i32> %val, float "
419 "%fpstart, <8 x float> %fpval, <8 x i1> %m, i32 %vl) {";
421 // Mix in a regular non-reduction intrinsic to check that the
422 // VPReductionIntrinsic subclass works as intended.
423 Str
<< " %r0 = call <8 x i32> @llvm.vp.mul.v8i32(<8 x i32> %val, <8 x i32> "
424 "%val, <8 x i1> %m, i32 %vl)";
427 for (const char *ReductionOpcode
: ReductionIntOpcodes
)
428 Str
<< " %r" << Idx
++ << " = call i32 @llvm.vp.reduce." << ReductionOpcode
429 << ".v8i32(i32 %start, <8 x i32> %val, <8 x i1> %m, i32 %vl)";
430 for (const char *ReductionOpcode
: ReductionFPOpcodes
)
431 Str
<< " %r" << Idx
++ << " = call float @llvm.vp.reduce."
433 << ".v8f32(float %fpstart, <8 x float> %fpval, <8 x i1> %m, i32 %vl)";
438 std::unique_ptr
<Module
> M
= parseAssemblyString(Str
.str(), Err
, C
);
441 auto *F
= M
->getFunction("test_reductions");
444 for (const auto &I
: F
->getEntryBlock()) {
445 const VPIntrinsic
*VPI
= dyn_cast
<VPIntrinsic
>(&I
);
449 Intrinsic::ID ID
= VPI
->getIntrinsicID();
450 const auto *VPRedI
= dyn_cast
<VPReductionIntrinsic
>(&I
);
452 if (!VPReductionIntrinsic::isVPReduction(ID
)) {
453 EXPECT_EQ(VPRedI
, nullptr);
454 EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID
).has_value(), false);
455 EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID
).has_value(), false);
459 EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID
).has_value(), true);
460 EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID
).has_value(), true);
461 ASSERT_NE(VPRedI
, nullptr);
462 EXPECT_EQ(VPReductionIntrinsic::getStartParamPos(ID
),
463 VPRedI
->getStartParamPos());
464 EXPECT_EQ(VPReductionIntrinsic::getVectorParamPos(ID
),
465 VPRedI
->getVectorParamPos());
466 EXPECT_EQ(VPRedI
->getStartParamPos(), 0u);
467 EXPECT_EQ(VPRedI
->getVectorParamPos(), 1u);