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 | |
22 | using namespace llvm; |
23 | |
24 | namespace { |
25 | |
26 | class VectorUtilsTest : public testing::Test { |
27 | protected: |
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 | |
60 | struct 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 | |
78 | TEST_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 | |
104 | TEST_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 | |
113 | TEST_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 | |
170 | TEST_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 | |
213 | TEST_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 | |
245 | TEST_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 | |
256 | TEST_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 | |
265 | TEST_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 | |
274 | TEST_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 | |
283 | TEST_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 | |
292 | TEST_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 | |
301 | TEST_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 | |
310 | TEST_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 | |
319 | TEST_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 | |
328 | TEST_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 | |
339 | TEST_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 | |
350 | TEST_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 | |
359 | TEST_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 | |
368 | TEST_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 | |
379 | TEST_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 | |
390 | TEST_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 | |
401 | TEST_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 | |
411 | TEST_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 | |
421 | TEST_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 | |
431 | TEST_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 | |
442 | TEST_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 | |
453 | TEST_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 | |
465 | TEST_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 | |
476 | TEST_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 | |
487 | TEST_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 | |
498 | TEST_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 | |
509 | TEST_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 | |
522 | TEST_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 | |
532 | TEST_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 | |
544 | TEST_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 | |
558 | class VFShapeAPITest : public testing::Test { |
559 | protected: |
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 | |
592 | TEST_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 | |
619 | TEST_F(VFShapeAPITest, API_getScalarShape) { |
620 | buildShape(/*VF*/ ElementCount::getFixed(MinVal: 1), /*HasGlobalPred*/ false); |
621 | EXPECT_EQ(VFShape::getScalarShape(CI->getFunctionType()), Shape); |
622 | } |
623 | |
624 | TEST_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 | |
637 | TEST_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 | |
667 | TEST_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 | |
679 | TEST_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 | |
699 | TEST_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 | |
723 | TEST_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 | |
743 | TEST_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 | |