1//===- VectorUtilsTest.cpp - VectorUtils 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/Analysis/VectorUtils.h"
10#include "llvm/Analysis/ValueTracking.h"
11#include "llvm/AsmParser/Parser.h"
12#include "llvm/IR/Function.h"
13#include "llvm/IR/InstIterator.h"
14#include "llvm/IR/IRBuilder.h"
15#include "llvm/IR/LLVMContext.h"
16#include "llvm/IR/Module.h"
17#include "llvm/IR/NoFolder.h"
18#include "llvm/Support/ErrorHandling.h"
19#include "llvm/Support/SourceMgr.h"
20#include "gtest/gtest.h"
21
22using namespace llvm;
23
24namespace {
25
26class VectorUtilsTest : public testing::Test {
27protected:
28 void parseAssembly(const char *Assembly) {
29 SMDiagnostic Error;
30 M = parseAssemblyString(AsmString: Assembly, Err&: Error, Context);
31
32 std::string errMsg;
33 raw_string_ostream os(errMsg);
34 Error.print(ProgName: "", S&: os);
35
36 // A failure here means that the test itself is buggy.
37 if (!M)
38 report_fatal_error(reason: Twine(os.str()));
39
40 Function *F = M->getFunction(Name: "test");
41 if (F == nullptr)
42 report_fatal_error(reason: "Test must have a function named @test");
43
44 A = nullptr;
45 for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
46 if (I->hasName()) {
47 if (I->getName() == "A")
48 A = &*I;
49 }
50 }
51 if (A == nullptr)
52 report_fatal_error(reason: "@test must have an instruction %A");
53 }
54
55 LLVMContext Context;
56 std::unique_ptr<Module> M;
57 Instruction *A;
58};
59
60struct BasicTest : public testing::Test {
61 LLVMContext Ctx;
62 std::unique_ptr<Module> M;
63 Function *F;
64 BasicBlock *BB;
65 IRBuilder<NoFolder> IRB;
66
67 BasicTest()
68 : M(new Module("VectorUtils", Ctx)),
69 F(Function::Create(
70 Ty: FunctionType::get(Result: Type::getVoidTy(C&: Ctx), /* IsVarArg */ isVarArg: false),
71 Linkage: Function::ExternalLinkage, N: "f", M: M.get())),
72 BB(BasicBlock::Create(Context&: Ctx, Name: "entry", Parent: F)), IRB(BB) {}
73};
74
75
76} // namespace
77
78TEST_F(BasicTest, isSplat) {
79 Value *UndefVec = UndefValue::get(T: FixedVectorType::get(ElementType: IRB.getInt8Ty(), NumElts: 4));
80 EXPECT_TRUE(isSplatValue(UndefVec));
81
82 Constant *UndefScalar = UndefValue::get(T: IRB.getInt8Ty());
83 EXPECT_FALSE(isSplatValue(UndefScalar));
84
85 Constant *ScalarC = IRB.getInt8(C: 42);
86 EXPECT_FALSE(isSplatValue(ScalarC));
87
88 Constant *OtherScalarC = IRB.getInt8(C: -42);
89 Constant *NonSplatC = ConstantVector::get(V: {ScalarC, OtherScalarC});
90 EXPECT_FALSE(isSplatValue(NonSplatC));
91
92 Value *SplatC = IRB.CreateVectorSplat(NumElts: 5, V: ScalarC);
93 EXPECT_TRUE(isSplatValue(SplatC));
94
95 Value *SplatC_SVE =
96 IRB.CreateVectorSplat(EC: ElementCount::getScalable(MinVal: 5), V: ScalarC);
97 EXPECT_TRUE(isSplatValue(SplatC_SVE));
98
99 // FIXME: Constant splat analysis does not allow undef elements.
100 Constant *SplatWithUndefC = ConstantVector::get(V: {ScalarC, UndefScalar});
101 EXPECT_FALSE(isSplatValue(SplatWithUndefC));
102}
103
104TEST_F(BasicTest, narrowShuffleMaskElts) {
105 SmallVector<int, 16> ScaledMask;
106 narrowShuffleMaskElts(Scale: 1, Mask: {3,2,0,-2}, ScaledMask);
107 EXPECT_EQ(ArrayRef(ScaledMask), ArrayRef({3, 2, 0, -2}));
108 narrowShuffleMaskElts(Scale: 4, Mask: {3,2,0,-1}, ScaledMask);
109 EXPECT_EQ(ArrayRef(ScaledMask), ArrayRef({12, 13, 14, 15, 8, 9, 10, 11, 0, 1,
110 2, 3, -1, -1, -1, -1}));
111}
112
113TEST_F(BasicTest, widenShuffleMaskElts) {
114 SmallVector<int, 16> WideMask;
115 SmallVector<int, 16> NarrowMask;
116
117 // scale == 1 is a copy
118 EXPECT_TRUE(widenShuffleMaskElts(1, {3,2,0,-1}, WideMask));
119 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({3, 2, 0, -1}));
120
121 // back to original mask
122 narrowShuffleMaskElts(Scale: 1, Mask: ArrayRef(WideMask), ScaledMask&: NarrowMask);
123 EXPECT_EQ(ArrayRef(NarrowMask), ArrayRef({3, 2, 0, -1}));
124
125 // can't widen non-consecutive 3/2
126 EXPECT_FALSE(widenShuffleMaskElts(2, {3,2,0,-1}, WideMask));
127
128 // can't widen if not evenly divisible
129 EXPECT_FALSE(widenShuffleMaskElts(2, {0,1,2}, WideMask));
130
131 // can always widen identity to single element
132 EXPECT_TRUE(widenShuffleMaskElts(3, {0,1,2}, WideMask));
133 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({0}));
134
135 // back to original mask
136 narrowShuffleMaskElts(Scale: 3, Mask: ArrayRef(WideMask), ScaledMask&: NarrowMask);
137 EXPECT_EQ(ArrayRef(NarrowMask), ArrayRef({0, 1, 2}));
138
139 // groups of 4 must be consecutive/undef
140 EXPECT_TRUE(widenShuffleMaskElts(4, {12,13,14,15,8,9,10,11,0,1,2,3,-1,-1,-1,-1}, WideMask));
141 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({3, 2, 0, -1}));
142
143 // back to original mask
144 narrowShuffleMaskElts(Scale: 4, Mask: ArrayRef(WideMask), ScaledMask&: NarrowMask);
145 EXPECT_EQ(ArrayRef(NarrowMask), ArrayRef({12, 13, 14, 15, 8, 9, 10, 11, 0, 1,
146 2, 3, -1, -1, -1, -1}));
147
148 // groups of 2 must be consecutive/undef
149 EXPECT_FALSE(widenShuffleMaskElts(2, {12,12,14,15,8,9,10,11,0,1,2,3,-1,-1,-1,-1}, WideMask));
150
151 // groups of 3 must be consecutive/undef
152 EXPECT_TRUE(widenShuffleMaskElts(3, {6,7,8,0,1,2,-1,-1,-1}, WideMask));
153 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({2, 0, -1}));
154
155 // back to original mask
156 narrowShuffleMaskElts(Scale: 3, Mask: ArrayRef(WideMask), ScaledMask&: NarrowMask);
157 EXPECT_EQ(ArrayRef(NarrowMask), ArrayRef({6, 7, 8, 0, 1, 2, -1, -1, -1}));
158
159 // groups of 3 must be consecutive/undef (partial undefs are not ok)
160 EXPECT_FALSE(widenShuffleMaskElts(3, {-1,7,8,0,-1,2,-1,-1,-1}, WideMask));
161
162 // negative indexes must match across a wide element
163 EXPECT_FALSE(widenShuffleMaskElts(2, {-1,-2,-1,-1}, WideMask));
164
165 // negative indexes must match across a wide element
166 EXPECT_TRUE(widenShuffleMaskElts(2, {-2,-2,-3,-3}, WideMask));
167 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({-2, -3}));
168}
169
170TEST_F(BasicTest, getShuffleMaskWithWidestElts) {
171 SmallVector<int, 16> WideMask;
172
173 // can not widen anything here.
174 getShuffleMaskWithWidestElts(Mask: {3, 2, 0, -1}, ScaledMask&: WideMask);
175 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({3, 2, 0, -1}));
176
177 // can't widen non-consecutive 3/2
178 getShuffleMaskWithWidestElts(Mask: {3, 2, 0, -1}, ScaledMask&: WideMask);
179 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({3, 2, 0, -1}));
180
181 // can always widen identity to single element
182 getShuffleMaskWithWidestElts(Mask: {0, 1, 2}, ScaledMask&: WideMask);
183 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({0}));
184
185 // groups of 4 must be consecutive/undef
186 getShuffleMaskWithWidestElts(
187 Mask: {12, 13, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, -1, -1, -1, -1}, ScaledMask&: WideMask);
188 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({3, 2, 0, -1}));
189
190 // groups of 2 must be consecutive/undef
191 getShuffleMaskWithWidestElts(
192 Mask: {12, 12, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, -1, -1, -1, -1}, ScaledMask&: WideMask);
193 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({12, 12, 14, 15, 8, 9, 10, 11, 0, 1, 2,
194 3, -1, -1, -1, -1}));
195
196 // groups of 3 must be consecutive/undef
197 getShuffleMaskWithWidestElts(Mask: {6, 7, 8, 0, 1, 2, -1, -1, -1}, ScaledMask&: WideMask);
198 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({2, 0, -1}));
199
200 // groups of 3 must be consecutive/undef (partial undefs are not ok)
201 getShuffleMaskWithWidestElts(Mask: {-1, 7, 8, 0, -1, 2, -1, -1, -1}, ScaledMask&: WideMask);
202 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({-1, 7, 8, 0, -1, 2, -1, -1, -1}));
203
204 // negative indexes must match across a wide element
205 getShuffleMaskWithWidestElts(Mask: {-1, -2, -1, -1}, ScaledMask&: WideMask);
206 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({-1, -2, -1, -1}));
207
208 // negative indexes must match across a wide element
209 getShuffleMaskWithWidestElts(Mask: {-2, -2, -3, -3}, ScaledMask&: WideMask);
210 EXPECT_EQ(ArrayRef(WideMask), ArrayRef({-2, -3}));
211}
212
213TEST_F(BasicTest, getShuffleDemandedElts) {
214 APInt LHS, RHS;
215
216 // broadcast zero
217 EXPECT_TRUE(getShuffleDemandedElts(4, {0, 0, 0, 0}, APInt(4,0xf), LHS, RHS));
218 EXPECT_EQ(LHS.getZExtValue(), 0x1U);
219 EXPECT_EQ(RHS.getZExtValue(), 0x0U);
220
221 // broadcast zero (with non-permitted undefs)
222 EXPECT_FALSE(getShuffleDemandedElts(2, {0, -1}, APInt(2, 0x3), LHS, RHS));
223
224 // broadcast zero (with permitted undefs)
225 EXPECT_TRUE(getShuffleDemandedElts(3, {0, 0, -1}, APInt(3, 0x7), LHS, RHS, true));
226 EXPECT_EQ(LHS.getZExtValue(), 0x1U);
227 EXPECT_EQ(RHS.getZExtValue(), 0x0U);
228
229 // broadcast one in demanded
230 EXPECT_TRUE(getShuffleDemandedElts(4, {1, 1, 1, -1}, APInt(4, 0x7), LHS, RHS));
231 EXPECT_EQ(LHS.getZExtValue(), 0x2U);
232 EXPECT_EQ(RHS.getZExtValue(), 0x0U);
233
234 // broadcast 7 in demanded
235 EXPECT_TRUE(getShuffleDemandedElts(4, {7, 0, 7, 7}, APInt(4, 0xd), LHS, RHS));
236 EXPECT_EQ(LHS.getZExtValue(), 0x0U);
237 EXPECT_EQ(RHS.getZExtValue(), 0x8U);
238
239 // general test
240 EXPECT_TRUE(getShuffleDemandedElts(4, {4, 2, 7, 3}, APInt(4, 0xf), LHS, RHS));
241 EXPECT_EQ(LHS.getZExtValue(), 0xcU);
242 EXPECT_EQ(RHS.getZExtValue(), 0x9U);
243}
244
245TEST_F(BasicTest, getSplatIndex) {
246 EXPECT_EQ(getSplatIndex({0,0,0}), 0);
247 EXPECT_EQ(getSplatIndex({1,0,0}), -1); // no splat
248 EXPECT_EQ(getSplatIndex({0,1,1}), -1); // no splat
249 EXPECT_EQ(getSplatIndex({42,42,42}), 42); // array size is independent of splat index
250 EXPECT_EQ(getSplatIndex({42,42,-1}), 42); // ignore negative
251 EXPECT_EQ(getSplatIndex({-1,42,-1}), 42); // ignore negatives
252 EXPECT_EQ(getSplatIndex({-4,42,-42}), 42); // ignore all negatives
253 EXPECT_EQ(getSplatIndex({-4,-1,-42}), -1); // all negative values map to -1
254}
255
256TEST_F(VectorUtilsTest, isSplatValue_00) {
257 parseAssembly(
258 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
259 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> zeroinitializer\n"
260 " ret <2 x i8> %A\n"
261 "}\n");
262 EXPECT_TRUE(isSplatValue(A));
263}
264
265TEST_F(VectorUtilsTest, isSplatValue_00_index0) {
266 parseAssembly(
267 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
268 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> zeroinitializer\n"
269 " ret <2 x i8> %A\n"
270 "}\n");
271 EXPECT_TRUE(isSplatValue(A, 0));
272}
273
274TEST_F(VectorUtilsTest, isSplatValue_00_index1) {
275 parseAssembly(
276 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
277 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> zeroinitializer\n"
278 " ret <2 x i8> %A\n"
279 "}\n");
280 EXPECT_FALSE(isSplatValue(A, 1));
281}
282
283TEST_F(VectorUtilsTest, isSplatValue_11) {
284 parseAssembly(
285 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
286 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
287 " ret <2 x i8> %A\n"
288 "}\n");
289 EXPECT_TRUE(isSplatValue(A));
290}
291
292TEST_F(VectorUtilsTest, isSplatValue_11_index0) {
293 parseAssembly(
294 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
295 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
296 " ret <2 x i8> %A\n"
297 "}\n");
298 EXPECT_FALSE(isSplatValue(A, 0));
299}
300
301TEST_F(VectorUtilsTest, isSplatValue_11_index1) {
302 parseAssembly(
303 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
304 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
305 " ret <2 x i8> %A\n"
306 "}\n");
307 EXPECT_TRUE(isSplatValue(A, 1));
308}
309
310TEST_F(VectorUtilsTest, isSplatValue_01) {
311 parseAssembly(
312 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
313 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"
314 " ret <2 x i8> %A\n"
315 "}\n");
316 EXPECT_FALSE(isSplatValue(A));
317}
318
319TEST_F(VectorUtilsTest, isSplatValue_01_index0) {
320 parseAssembly(
321 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
322 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"
323 " ret <2 x i8> %A\n"
324 "}\n");
325 EXPECT_FALSE(isSplatValue(A, 0));
326}
327
328TEST_F(VectorUtilsTest, isSplatValue_01_index1) {
329 parseAssembly(
330 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
331 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"
332 " ret <2 x i8> %A\n"
333 "}\n");
334 EXPECT_FALSE(isSplatValue(A, 1));
335}
336
337// FIXME: Allow undef matching with Constant (mask) splat analysis.
338
339TEST_F(VectorUtilsTest, isSplatValue_0u) {
340 parseAssembly(
341 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
342 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 undef>\n"
343 " ret <2 x i8> %A\n"
344 "}\n");
345 EXPECT_FALSE(isSplatValue(A));
346}
347
348// FIXME: Allow undef matching with Constant (mask) splat analysis.
349
350TEST_F(VectorUtilsTest, isSplatValue_0u_index0) {
351 parseAssembly(
352 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
353 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 undef>\n"
354 " ret <2 x i8> %A\n"
355 "}\n");
356 EXPECT_FALSE(isSplatValue(A, 0));
357}
358
359TEST_F(VectorUtilsTest, isSplatValue_0u_index1) {
360 parseAssembly(
361 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
362 " %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 undef>\n"
363 " ret <2 x i8> %A\n"
364 "}\n");
365 EXPECT_FALSE(isSplatValue(A, 1));
366}
367
368TEST_F(VectorUtilsTest, isSplatValue_Binop) {
369 parseAssembly(
370 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
371 " %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
372 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
373 " %A = udiv <2 x i8> %v0, %v1\n"
374 " ret <2 x i8> %A\n"
375 "}\n");
376 EXPECT_TRUE(isSplatValue(A));
377}
378
379TEST_F(VectorUtilsTest, isSplatValue_Binop_index0) {
380 parseAssembly(
381 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
382 " %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
383 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
384 " %A = udiv <2 x i8> %v0, %v1\n"
385 " ret <2 x i8> %A\n"
386 "}\n");
387 EXPECT_FALSE(isSplatValue(A, 0));
388}
389
390TEST_F(VectorUtilsTest, isSplatValue_Binop_index1) {
391 parseAssembly(
392 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
393 " %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
394 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
395 " %A = udiv <2 x i8> %v0, %v1\n"
396 " ret <2 x i8> %A\n"
397 "}\n");
398 EXPECT_FALSE(isSplatValue(A, 1));
399}
400
401TEST_F(VectorUtilsTest, isSplatValue_Binop_ConstantOp0) {
402 parseAssembly(
403 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
404 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
405 " %A = ashr <2 x i8> <i8 42, i8 42>, %v1\n"
406 " ret <2 x i8> %A\n"
407 "}\n");
408 EXPECT_TRUE(isSplatValue(A));
409}
410
411TEST_F(VectorUtilsTest, isSplatValue_Binop_ConstantOp0_index0) {
412 parseAssembly(
413 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
414 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
415 " %A = ashr <2 x i8> <i8 42, i8 42>, %v1\n"
416 " ret <2 x i8> %A\n"
417 "}\n");
418 EXPECT_FALSE(isSplatValue(A, 0));
419}
420
421TEST_F(VectorUtilsTest, isSplatValue_Binop_ConstantOp0_index1) {
422 parseAssembly(
423 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
424 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
425 " %A = ashr <2 x i8> <i8 42, i8 42>, %v1\n"
426 " ret <2 x i8> %A\n"
427 "}\n");
428 EXPECT_TRUE(isSplatValue(A, 1));
429}
430
431TEST_F(VectorUtilsTest, isSplatValue_Binop_Not_Op0) {
432 parseAssembly(
433 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
434 " %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 0>\n"
435 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
436 " %A = add <2 x i8> %v0, %v1\n"
437 " ret <2 x i8> %A\n"
438 "}\n");
439 EXPECT_FALSE(isSplatValue(A));
440}
441
442TEST_F(VectorUtilsTest, isSplatValue_Binop_Not_Op1) {
443 parseAssembly(
444 Assembly: "define <2 x i8> @test(<2 x i8> %x) {\n"
445 " %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
446 " %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"
447 " %A = shl <2 x i8> %v0, %v1\n"
448 " ret <2 x i8> %A\n"
449 "}\n");
450 EXPECT_FALSE(isSplatValue(A));
451}
452
453TEST_F(VectorUtilsTest, isSplatValue_Select) {
454 parseAssembly(
455 Assembly: "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
456 " %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
457 " %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
458 " %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
459 " %A = select <2 x i1> %v0, <2 x i8> %v1, <2 x i8> %v2\n"
460 " ret <2 x i8> %A\n"
461 "}\n");
462 EXPECT_TRUE(isSplatValue(A));
463}
464
465TEST_F(VectorUtilsTest, isSplatValue_Select_ConstantOp) {
466 parseAssembly(
467 Assembly: "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
468 " %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
469 " %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
470 " %A = select <2 x i1> %v0, <2 x i8> <i8 42, i8 42>, <2 x i8> %v2\n"
471 " ret <2 x i8> %A\n"
472 "}\n");
473 EXPECT_TRUE(isSplatValue(A));
474}
475
476TEST_F(VectorUtilsTest, isSplatValue_Select_NotCond) {
477 parseAssembly(
478 Assembly: "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
479 " %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
480 " %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
481 " %A = select <2 x i1> %x, <2 x i8> %v1, <2 x i8> %v2\n"
482 " ret <2 x i8> %A\n"
483 "}\n");
484 EXPECT_FALSE(isSplatValue(A));
485}
486
487TEST_F(VectorUtilsTest, isSplatValue_Select_NotOp1) {
488 parseAssembly(
489 Assembly: "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
490 " %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
491 " %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
492 " %A = select <2 x i1> %v0, <2 x i8> %y, <2 x i8> %v2\n"
493 " ret <2 x i8> %A\n"
494 "}\n");
495 EXPECT_FALSE(isSplatValue(A));
496}
497
498TEST_F(VectorUtilsTest, isSplatValue_Select_NotOp2) {
499 parseAssembly(
500 Assembly: "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
501 " %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
502 " %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
503 " %A = select <2 x i1> %v0, <2 x i8> %v1, <2 x i8> %z\n"
504 " ret <2 x i8> %A\n"
505 "}\n");
506 EXPECT_FALSE(isSplatValue(A));
507}
508
509TEST_F(VectorUtilsTest, isSplatValue_SelectBinop) {
510 parseAssembly(
511 Assembly: "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
512 " %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
513 " %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
514 " %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
515 " %bo = xor <2 x i8> %v1, %v2\n"
516 " %A = select <2 x i1> %v0, <2 x i8> %bo, <2 x i8> %v2\n"
517 " ret <2 x i8> %A\n"
518 "}\n");
519 EXPECT_TRUE(isSplatValue(A));
520}
521
522TEST_F(VectorUtilsTest, getSplatValueElt0) {
523 parseAssembly(
524 Assembly: "define <2 x i8> @test(i8 %x) {\n"
525 " %ins = insertelement <2 x i8> undef, i8 %x, i32 0\n"
526 " %A = shufflevector <2 x i8> %ins, <2 x i8> undef, <2 x i32> zeroinitializer\n"
527 " ret <2 x i8> %A\n"
528 "}\n");
529 EXPECT_EQ(getSplatValue(A)->getName(), "x");
530}
531
532TEST_F(VectorUtilsTest, getSplatValueEltMismatch) {
533 parseAssembly(
534 Assembly: "define <2 x i8> @test(i8 %x) {\n"
535 " %ins = insertelement <2 x i8> undef, i8 %x, i32 1\n"
536 " %A = shufflevector <2 x i8> %ins, <2 x i8> undef, <2 x i32> zeroinitializer\n"
537 " ret <2 x i8> %A\n"
538 "}\n");
539 EXPECT_EQ(getSplatValue(A), nullptr);
540}
541
542// TODO: This is a splat, but we don't recognize it.
543
544TEST_F(VectorUtilsTest, getSplatValueElt1) {
545 parseAssembly(
546 Assembly: "define <2 x i8> @test(i8 %x) {\n"
547 " %ins = insertelement <2 x i8> undef, i8 %x, i32 1\n"
548 " %A = shufflevector <2 x i8> %ins, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
549 " ret <2 x i8> %A\n"
550 "}\n");
551 EXPECT_EQ(getSplatValue(A), nullptr);
552}
553
554////////////////////////////////////////////////////////////////////////////////
555// VFShape API tests.
556////////////////////////////////////////////////////////////////////////////////
557
558class VFShapeAPITest : public testing::Test {
559protected:
560 void SetUp() override {
561 M = parseAssemblyString(AsmString: IR, Err, Context&: Ctx);
562 // Get the only call instruction in the block, which is the first
563 // instruction.
564 CI = dyn_cast<CallInst>(Val: &*(instructions(F: M->getFunction(Name: "f")).begin()));
565 }
566
567 const char *IR = "define i32 @f(i32 %a, i64 %b, double %c) {\n"
568 " %1 = call i32 @g(i32 %a, i64 %b, double %c)\n"
569 " ret i32 %1\n"
570 "}\n"
571 "declare i32 @g(i32, i64, double)\n";
572 LLVMContext Ctx;
573 SMDiagnostic Err;
574 std::unique_ptr<Module> M;
575 CallInst *CI;
576 // Dummy shape with no parameters, overwritten by buildShape when invoked.
577 VFShape Shape = {/*VF*/ ElementCount::getFixed(MinVal: 2), /*Parameters*/ {}};
578 VFShape Expected;
579 SmallVector<VFParameter, 8> &ExpectedParams = Expected.Parameters;
580
581 void buildShape(ElementCount VF, bool HasGlobalPred) {
582 Shape = VFShape::get(FTy: CI->getFunctionType(), EC: VF, HasGlobalPred);
583 }
584
585 bool validParams(ArrayRef<VFParameter> Parameters) {
586 Shape.Parameters =
587 SmallVector<VFParameter, 8>(Parameters.begin(), Parameters.end());
588 return Shape.hasValidParameterList();
589 }
590};
591
592TEST_F(VFShapeAPITest, API_buildVFShape) {
593 buildShape(/*VF*/ ElementCount::getFixed(MinVal: 2), /*HasGlobalPred*/ false);
594 Expected = {/*VF*/ ElementCount::getFixed(MinVal: 2), /*Parameters*/ {
595 {.ParamPos: 0, .ParamKind: VFParamKind::Vector},
596 {.ParamPos: 1, .ParamKind: VFParamKind::Vector},
597 {.ParamPos: 2, .ParamKind: VFParamKind::Vector},
598 }};
599 EXPECT_EQ(Shape, Expected);
600
601 buildShape(/*VF*/ ElementCount::getFixed(MinVal: 4), /*HasGlobalPred*/ true);
602 Expected = {/*VF*/ ElementCount::getFixed(MinVal: 4), /*Parameters*/ {
603 {.ParamPos: 0, .ParamKind: VFParamKind::Vector},
604 {.ParamPos: 1, .ParamKind: VFParamKind::Vector},
605 {.ParamPos: 2, .ParamKind: VFParamKind::Vector},
606 {.ParamPos: 3, .ParamKind: VFParamKind::GlobalPredicate},
607 }};
608 EXPECT_EQ(Shape, Expected);
609
610 buildShape(/*VF*/ ElementCount::getScalable(MinVal: 16), /*HasGlobalPred*/ false);
611 Expected = {/*VF*/ ElementCount::getScalable(MinVal: 16), /*Parameters*/ {
612 {.ParamPos: 0, .ParamKind: VFParamKind::Vector},
613 {.ParamPos: 1, .ParamKind: VFParamKind::Vector},
614 {.ParamPos: 2, .ParamKind: VFParamKind::Vector},
615 }};
616 EXPECT_EQ(Shape, Expected);
617}
618
619TEST_F(VFShapeAPITest, API_getScalarShape) {
620 buildShape(/*VF*/ ElementCount::getFixed(MinVal: 1), /*HasGlobalPred*/ false);
621 EXPECT_EQ(VFShape::getScalarShape(CI->getFunctionType()), Shape);
622}
623
624TEST_F(VFShapeAPITest, API_getVectorizedFunction) {
625 VFShape ScalarShape = VFShape::getScalarShape(FTy: CI->getFunctionType());
626 EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(ScalarShape),
627 M->getFunction("g"));
628
629 buildShape(/*VF*/ ElementCount::getScalable(MinVal: 1), /*HasGlobalPred*/ false);
630 EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(Shape), nullptr);
631 buildShape(/*VF*/ ElementCount::getFixed(MinVal: 1), /*HasGlobalPred*/ true);
632 EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(Shape), nullptr);
633 buildShape(/*VF*/ ElementCount::getScalable(MinVal: 1), /*HasGlobalPred*/ true);
634 EXPECT_EQ(VFDatabase(*CI).getVectorizedFunction(Shape), nullptr);
635}
636
637TEST_F(VFShapeAPITest, API_updateVFShape) {
638
639 buildShape(/*VF*/ ElementCount::getFixed(MinVal: 2), /*HasGlobalPred*/ false);
640 Shape.updateParam(P: {.ParamPos: 0 /*Pos*/, .ParamKind: VFParamKind::OMP_Linear, .LinearStepOrPos: 1, .Alignment: Align(4)});
641 Expected = {/*VF*/ ElementCount::getFixed(MinVal: 2), /*Parameters*/ {
642 {.ParamPos: 0, .ParamKind: VFParamKind::OMP_Linear, .LinearStepOrPos: 1, .Alignment: Align(4)},
643 {.ParamPos: 1, .ParamKind: VFParamKind::Vector},
644 {.ParamPos: 2, .ParamKind: VFParamKind::Vector},
645 }};
646 EXPECT_EQ(Shape, Expected);
647
648 // From this point on, we update only the parameters of the VFShape,
649 // so we update only the reference of the expected Parameters.
650 Shape.updateParam(P: {.ParamPos: 1 /*Pos*/, .ParamKind: VFParamKind::OMP_Uniform});
651 ExpectedParams = {
652 {.ParamPos: 0, .ParamKind: VFParamKind::OMP_Linear, .LinearStepOrPos: 1, .Alignment: Align(4)},
653 {.ParamPos: 1, .ParamKind: VFParamKind::OMP_Uniform},
654 {.ParamPos: 2, .ParamKind: VFParamKind::Vector},
655 };
656 EXPECT_EQ(Shape, Expected);
657
658 Shape.updateParam(P: {.ParamPos: 2 /*Pos*/, .ParamKind: VFParamKind::OMP_LinearRefPos, .LinearStepOrPos: 1});
659 ExpectedParams = {
660 {.ParamPos: 0, .ParamKind: VFParamKind::OMP_Linear, .LinearStepOrPos: 1, .Alignment: Align(4)},
661 {.ParamPos: 1, .ParamKind: VFParamKind::OMP_Uniform},
662 {.ParamPos: 2, .ParamKind: VFParamKind::OMP_LinearRefPos, .LinearStepOrPos: 1},
663 };
664 EXPECT_EQ(Shape, Expected);
665}
666
667TEST_F(VFShapeAPITest, API_updateVFShape_GlobalPredicate) {
668
669 buildShape(/*VF*/ ElementCount::getScalable(MinVal: 2), /*HasGlobalPred*/ true);
670 Shape.updateParam(P: {.ParamPos: 1 /*Pos*/, .ParamKind: VFParamKind::OMP_Uniform});
671 Expected = {/*VF*/ ElementCount::getScalable(MinVal: 2),
672 /*Parameters*/ {{.ParamPos: 0, .ParamKind: VFParamKind::Vector},
673 {.ParamPos: 1, .ParamKind: VFParamKind::OMP_Uniform},
674 {.ParamPos: 2, .ParamKind: VFParamKind::Vector},
675 {.ParamPos: 3, .ParamKind: VFParamKind::GlobalPredicate}}};
676 EXPECT_EQ(Shape, Expected);
677}
678
679TEST_F(VFShapeAPITest, Parameters_Valid) {
680 // ParamPos in order.
681 EXPECT_TRUE(validParams({{0, VFParamKind::Vector}}));
682 EXPECT_TRUE(
683 validParams({{0, VFParamKind::Vector}, {1, VFParamKind::Vector}}));
684 EXPECT_TRUE(validParams({{0, VFParamKind::Vector},
685 {1, VFParamKind::Vector},
686 {2, VFParamKind::Vector}}));
687
688 // GlocalPredicate is unique.
689 EXPECT_TRUE(validParams({{0, VFParamKind::Vector},
690 {1, VFParamKind::Vector},
691 {2, VFParamKind::Vector},
692 {3, VFParamKind::GlobalPredicate}}));
693
694 EXPECT_TRUE(validParams({{0, VFParamKind::Vector},
695 {1, VFParamKind::GlobalPredicate},
696 {2, VFParamKind::Vector}}));
697}
698
699TEST_F(VFShapeAPITest, Parameters_ValidOpenMPLinear) {
700// Valid linear constant step (>0).
701#define __BUILD_PARAMETERS(Kind, Val) \
702 { \
703 { 0, Kind, Val } \
704 }
705 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_Linear, 1)));
706 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRef, 2)));
707 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearVal, 4)));
708 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUVal, 33)));
709#undef __BUILD_PARAMETERS
710
711// Valid linear runtime step (the step parameter is marked uniform).
712#define __BUILD_PARAMETERS(Kind) \
713 { \
714 {0, VFParamKind::OMP_Uniform}, {1, VFParamKind::Vector}, { 2, Kind, 0 } \
715 }
716 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));
717 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));
718 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));
719 EXPECT_TRUE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));
720#undef __BUILD_PARAMETERS
721}
722
723TEST_F(VFShapeAPITest, Parameters_Invalid) {
724#ifndef NDEBUG
725 // Wrong order is checked by an assertion: make sure that the
726 // assertion is not removed.
727 EXPECT_DEATH(validParams({{1, VFParamKind::Vector}}),
728 "Broken parameter list.");
729 EXPECT_DEATH(
730 validParams({{1, VFParamKind::Vector}, {0, VFParamKind::Vector}}),
731 "Broken parameter list.");
732#endif
733
734 // GlobalPredicate is not unique
735 EXPECT_FALSE(validParams({{0, VFParamKind::Vector},
736 {1, VFParamKind::GlobalPredicate},
737 {2, VFParamKind::GlobalPredicate}}));
738 EXPECT_FALSE(validParams({{0, VFParamKind::GlobalPredicate},
739 {1, VFParamKind::Vector},
740 {2, VFParamKind::GlobalPredicate}}));
741}
742
743TEST_F(VFShapeAPITest, Parameters_InvalidOpenMPLinear) {
744// Compile time linear steps must be non-zero (compile time invariant).
745#define __BUILD_PARAMETERS(Kind) \
746 { \
747 { 0, Kind, 0 } \
748 }
749 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_Linear)));
750 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRef)));
751 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearVal)));
752 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUVal)));
753#undef __BUILD_PARAMETERS
754
755// The step of a runtime linear parameter must be marked
756// as uniform (runtime invariant).
757#define __BUILD_PARAMETERS(Kind) \
758 { \
759 {0, VFParamKind::OMP_Uniform}, {1, VFParamKind::Vector}, { 2, Kind, 1 } \
760 }
761 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));
762 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));
763 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));
764 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));
765#undef __BUILD_PARAMETERS
766
767// The linear step parameter can't point at itself.
768#define __BUILD_PARAMETERS(Kind) \
769 { \
770 {0, VFParamKind::Vector}, {1, VFParamKind::Vector}, { 2, Kind, 2 } \
771 }
772 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));
773 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));
774 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));
775 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));
776#undef __BUILD_PARAMETERS
777
778// Linear parameter (runtime) is out of range.
779#define __BUILD_PARAMETERS(Kind) \
780 { \
781 {0, VFParamKind::Vector}, {1, VFParamKind::Vector}, { 2, Kind, 3 } \
782 }
783 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearPos)));
784 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearRefPos)));
785 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearValPos)));
786 EXPECT_FALSE(validParams(__BUILD_PARAMETERS(VFParamKind::OMP_LinearUValPos)));
787#undef __BUILD_PARAMETERS
788}
789

source code of llvm/unittests/Analysis/VectorUtilsTest.cpp