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