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/IR/Constants.h"
12 #include "llvm/IR/IRBuilder.h"
13 #include "llvm/IR/IntrinsicInst.h"
14 #include "llvm/IR/LLVMContext.h"
15 #include "llvm/IR/Module.h"
16 #include "llvm/IR/Verifier.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "gtest/gtest.h"
19
20 using namespace llvm;
21
22 namespace {
23
24 class VPIntrinsicTest : public testing::Test {
25 protected:
26 LLVMContext Context;
27
VPIntrinsicTest()28 VPIntrinsicTest() : Context() {}
29
30 LLVMContext C;
31 SMDiagnostic Err;
32
CreateVPDeclarationModule()33 std::unique_ptr<Module> CreateVPDeclarationModule() {
34 return parseAssemblyString(
35 " declare <8 x i32> @llvm.vp.add.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
36 " declare <8 x i32> @llvm.vp.sub.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
37 " declare <8 x i32> @llvm.vp.mul.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
38 " declare <8 x i32> @llvm.vp.sdiv.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
39 " declare <8 x i32> @llvm.vp.srem.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
40 " declare <8 x i32> @llvm.vp.udiv.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
41 " declare <8 x i32> @llvm.vp.urem.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
42 " declare <8 x i32> @llvm.vp.and.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
43 " declare <8 x i32> @llvm.vp.xor.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
44 " declare <8 x i32> @llvm.vp.or.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
45 " declare <8 x i32> @llvm.vp.ashr.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
46 " declare <8 x i32> @llvm.vp.lshr.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) "
47 " declare <8 x i32> @llvm.vp.shl.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32) ",
48 Err, C);
49 }
50 };
51
52 /// Check that VPIntrinsic:canIgnoreVectorLengthParam() returns true
53 /// if the vector length parameter does not mask off any lanes.
TEST_F(VPIntrinsicTest,CanIgnoreVectorLength)54 TEST_F(VPIntrinsicTest, CanIgnoreVectorLength) {
55 LLVMContext C;
56 SMDiagnostic Err;
57
58 std::unique_ptr<Module> M =
59 parseAssemblyString(
60 "declare <256 x i64> @llvm.vp.mul.v256i64(<256 x i64>, <256 x i64>, <256 x i1>, i32)"
61 "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)"
62 "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)"
63 "declare i32 @llvm.vscale.i32()"
64 "define void @test_static_vlen( "
65 " <256 x i64> %i0, <vscale x 2 x i64> %si0x2, <vscale x 1 x i64> %si0x1,"
66 " <256 x i64> %i1, <vscale x 2 x i64> %si1x2, <vscale x 1 x i64> %si1x1,"
67 " <256 x i1> %m, <vscale x 2 x i1> %smx2, <vscale x 1 x i1> %smx1, i32 %vl) { "
68 " %r0 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 %vl)"
69 " %r1 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 256)"
70 " %r2 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 0)"
71 " %r3 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 7)"
72 " %r4 = call <256 x i64> @llvm.vp.mul.v256i64(<256 x i64> %i0, <256 x i64> %i1, <256 x i1> %m, i32 123)"
73 " %vs = call i32 @llvm.vscale.i32()"
74 " %vs.x2 = mul i32 %vs, 2"
75 " %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)"
76 " %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)"
77 " %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)"
78 " %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)"
79 " %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)"
80 " %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)"
81 " %vs.wat = add i32 %vs, 2"
82 " %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)"
83 " ret void "
84 "}",
85 Err, C);
86
87 auto *F = M->getFunction("test_static_vlen");
88 assert(F);
89
90 const int NumExpected = 12;
91 const bool Expected[] = {false, true, false, false, false, true, false, false, true, false, true, false};
92 int i = 0;
93 for (auto &I : F->getEntryBlock()) {
94 VPIntrinsic *VPI = dyn_cast<VPIntrinsic>(&I);
95 if (!VPI)
96 continue;
97
98 ASSERT_LT(i, NumExpected);
99 ASSERT_EQ(Expected[i], VPI->canIgnoreVectorLengthParam());
100 ++i;
101 }
102 }
103
104 /// Check that the argument returned by
105 /// VPIntrinsic::Get<X>ParamPos(Intrinsic::ID) has the expected type.
TEST_F(VPIntrinsicTest,GetParamPos)106 TEST_F(VPIntrinsicTest, GetParamPos) {
107 std::unique_ptr<Module> M = CreateVPDeclarationModule();
108 assert(M);
109
110 for (Function &F : *M) {
111 ASSERT_TRUE(F.isIntrinsic());
112 Optional<int> MaskParamPos =
113 VPIntrinsic::GetMaskParamPos(F.getIntrinsicID());
114 if (MaskParamPos.hasValue()) {
115 Type *MaskParamType = F.getArg(MaskParamPos.getValue())->getType();
116 ASSERT_TRUE(MaskParamType->isVectorTy());
117 ASSERT_TRUE(cast<VectorType>(MaskParamType)->getElementType()->isIntegerTy(1));
118 }
119
120 Optional<int> VecLenParamPos =
121 VPIntrinsic::GetVectorLengthParamPos(F.getIntrinsicID());
122 if (VecLenParamPos.hasValue()) {
123 Type *VecLenParamType = F.getArg(VecLenParamPos.getValue())->getType();
124 ASSERT_TRUE(VecLenParamType->isIntegerTy(32));
125 }
126 }
127 }
128
129 /// Check that going from Opcode to VP intrinsic and back results in the same
130 /// Opcode.
TEST_F(VPIntrinsicTest,OpcodeRoundTrip)131 TEST_F(VPIntrinsicTest, OpcodeRoundTrip) {
132 std::vector<unsigned> Opcodes;
133 Opcodes.reserve(100);
134
135 {
136 #define HANDLE_INST(OCNum, OCName, Class) Opcodes.push_back(OCNum);
137 #include "llvm/IR/Instruction.def"
138 }
139
140 unsigned FullTripCounts = 0;
141 for (unsigned OC : Opcodes) {
142 Intrinsic::ID VPID = VPIntrinsic::GetForOpcode(OC);
143 // no equivalent VP intrinsic available
144 if (VPID == Intrinsic::not_intrinsic)
145 continue;
146
147 unsigned RoundTripOC = VPIntrinsic::GetFunctionalOpcodeForVP(VPID);
148 // no equivalent Opcode available
149 if (RoundTripOC == Instruction::Call)
150 continue;
151
152 ASSERT_EQ(RoundTripOC, OC);
153 ++FullTripCounts;
154 }
155 ASSERT_NE(FullTripCounts, 0u);
156 }
157
158 } // end anonymous namespace
159