1 | //===------- VFABIDemanglerTest.cpp - VFABI 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/IR/VFABIDemangler.h" |
10 | #include "llvm/ADT/StringRef.h" |
11 | #include "llvm/Analysis/VectorUtils.h" |
12 | #include "llvm/AsmParser/Parser.h" |
13 | #include "llvm/IR/DerivedTypes.h" |
14 | #include "llvm/IR/InstIterator.h" |
15 | #include "llvm/IR/Module.h" |
16 | #include "llvm/Support/SourceMgr.h" |
17 | #include "gtest/gtest.h" |
18 | #include <optional> |
19 | |
20 | using namespace llvm; |
21 | |
22 | namespace { |
23 | |
24 | static LLVMContext Ctx; |
25 | |
26 | /// Perform tests against VFABI Rules. `invokeParser` creates a VFInfo object |
27 | /// and a scalar FunctionType, which are used by tests to check that: |
28 | /// 1. The scalar and vector names are correct. |
29 | /// 2. The number of parameters from the parsed mangled name matches the number |
30 | /// of arguments in the scalar function passed as FunctionType string. |
31 | /// 3. The number of vector parameters and their types match the values |
32 | /// specified in the test. |
33 | /// On masked functions it also checks that the last parameter is a mask (ie, |
34 | /// GlobalPredicate). |
35 | /// 4. The vector function is correctly found to have a mask. |
36 | /// |
37 | class VFABIParserTest : public ::testing::Test { |
38 | private: |
39 | // Parser output. |
40 | VFInfo Info; |
41 | /// Reset the data needed for the test. |
42 | void reset(const StringRef ScalarFTyStr) { |
43 | M = parseAssemblyString(AsmString: "declare void @dummy()" , Err, Context&: Ctx); |
44 | EXPECT_NE(M.get(), nullptr) |
45 | << "Loading an invalid module.\n " << Err.getMessage() << "\n" ; |
46 | Type *Ty = parseType(Asm: ScalarFTyStr, Err, M: *(M.get())); |
47 | ScalarFTy = dyn_cast<FunctionType>(Val: Ty); |
48 | EXPECT_NE(ScalarFTy, nullptr) |
49 | << "Invalid function type string: " << ScalarFTyStr << "\n" |
50 | << Err.getMessage() << "\n" ; |
51 | // Reset the VFInfo |
52 | Info = VFInfo(); |
53 | } |
54 | |
55 | // Data needed to load the optional IR passed to invokeParser |
56 | SMDiagnostic Err; |
57 | std::unique_ptr<Module> M; |
58 | FunctionType *ScalarFTy = nullptr; |
59 | |
60 | protected: |
61 | // References to the parser output field. |
62 | ElementCount &VF = Info.Shape.VF; |
63 | VFISAKind &ISA = Info.ISA; |
64 | /// Parameters for the vectorized function |
65 | SmallVector<VFParameter, 8> &Parameters = Info.Shape.Parameters; |
66 | std::string &ScalarName = Info.ScalarName; |
67 | std::string &VectorName = Info.VectorName; |
68 | |
69 | /// Invoke the parser. Every time this method is invoked the state of the test |
70 | /// is reset. |
71 | /// |
72 | /// \p MangledName string the parser has to demangle. |
73 | /// |
74 | /// \p ScalarFTyStr FunctionType string to get the signature of the scalar |
75 | /// function, which is used by `tryDemangleForVFABI` to check for the number |
76 | /// of arguments on scalable vectors, and by `matchParameters` to perform some |
77 | /// additional checking in the tests in this file. |
78 | bool invokeParser(const StringRef MangledName, |
79 | const StringRef ScalarFTyStr = "void()" ) { |
80 | // Reset the VFInfo to be able to call `invokeParser` multiple times in |
81 | // the same test. |
82 | reset(ScalarFTyStr); |
83 | |
84 | const auto OptInfo = VFABI::tryDemangleForVFABI(MangledName, FTy: ScalarFTy); |
85 | if (OptInfo) |
86 | Info = *OptInfo; |
87 | |
88 | return OptInfo.has_value(); |
89 | } |
90 | |
91 | /// Returns whether the parsed function contains a mask. |
92 | bool isMasked() const { return Info.isMasked(); } |
93 | |
94 | FunctionType *getFunctionType() { |
95 | return VFABI::createFunctionType(Info, ScalarFTy); |
96 | } |
97 | }; |
98 | } // unnamed namespace |
99 | |
100 | // Function Types commonly used in tests |
101 | FunctionType *FTyMaskVLen2_i32 = FunctionType::get( |
102 | Result: Type::getVoidTy(C&: Ctx), |
103 | Params: { |
104 | VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getFixed(MinVal: 2)), |
105 | VectorType::get(ElementType: Type::getInt1Ty(C&: Ctx), EC: ElementCount::getFixed(MinVal: 2)), |
106 | }, |
107 | isVarArg: false); |
108 | |
109 | FunctionType *FTyNoMaskVLen2_i32 = FunctionType::get( |
110 | Result: Type::getVoidTy(C&: Ctx), |
111 | Params: { |
112 | VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getFixed(MinVal: 2)), |
113 | }, |
114 | isVarArg: false); |
115 | |
116 | FunctionType *FTyMaskedVLA_i32 = FunctionType::get( |
117 | Result: Type::getVoidTy(C&: Ctx), |
118 | Params: { |
119 | VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 4)), |
120 | VectorType::get(ElementType: Type::getInt1Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 4)), |
121 | }, |
122 | isVarArg: false); |
123 | |
124 | // This test makes sure that the demangling method succeeds only on |
125 | // valid values of the string. |
126 | TEST_F(VFABIParserTest, OnlyValidNames) { |
127 | // Incomplete string. |
128 | EXPECT_FALSE(invokeParser("" )); |
129 | EXPECT_FALSE(invokeParser("_ZGV" )); |
130 | EXPECT_FALSE(invokeParser("_ZGVn" )); |
131 | EXPECT_FALSE(invokeParser("_ZGVnN" )); |
132 | EXPECT_FALSE(invokeParser("_ZGVnN2" )); |
133 | EXPECT_FALSE(invokeParser("_ZGVnN2v" )); |
134 | EXPECT_FALSE(invokeParser("_ZGVnN2v_" )); |
135 | // Missing parameters. |
136 | EXPECT_FALSE(invokeParser("_ZGVnN2_foo" )); |
137 | // Missing _ZGV prefix. |
138 | EXPECT_FALSE(invokeParser("_ZVnN2v_foo" )); |
139 | // Missing <isa>. |
140 | EXPECT_FALSE(invokeParser("_ZGVN2v_foo" )); |
141 | // Missing <mask>. |
142 | EXPECT_FALSE(invokeParser("_ZGVn2v_foo" )); |
143 | // Missing <vlen>. |
144 | EXPECT_FALSE(invokeParser("_ZGVnNv_foo" )); |
145 | // Missing <scalarname>. |
146 | EXPECT_FALSE(invokeParser("_ZGVnN2v_" )); |
147 | // Missing _ separator. |
148 | EXPECT_FALSE(invokeParser("_ZGVnN2vfoo" )); |
149 | // Missing <vectorname>. |
150 | EXPECT_FALSE(invokeParser("_ZGVnN2v_foo()" )); |
151 | // Unterminated name. |
152 | EXPECT_FALSE(invokeParser("_ZGVnN2v_foo(bar" )); |
153 | } |
154 | |
155 | TEST_F(VFABIParserTest, ParamListParsing) { |
156 | EXPECT_TRUE( |
157 | invokeParser("_ZGVnN2vl16Ls32R3l_foo" , "void(i32, i32, i32, ptr, i32)" )); |
158 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
159 | EXPECT_EQ(false, isMasked()); |
160 | FunctionType *FTy = FunctionType::get( |
161 | Result: Type::getVoidTy(C&: Ctx), |
162 | Params: {VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getFixed(MinVal: 2)), |
163 | Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), |
164 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), Type::getInt32Ty(C&: Ctx)}, |
165 | isVarArg: false); |
166 | EXPECT_EQ(getFunctionType(), FTy); |
167 | EXPECT_EQ(Parameters.size(), (unsigned)5); |
168 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0})); |
169 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_Linear, 16})); |
170 | EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearValPos, 32})); |
171 | EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearRef, 3})); |
172 | EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_Linear, 1})); |
173 | EXPECT_EQ(ScalarName, "foo" ); |
174 | EXPECT_EQ(VectorName, "_ZGVnN2vl16Ls32R3l_foo" ); |
175 | } |
176 | |
177 | TEST_F(VFABIParserTest, ScalarNameAndVectorName_01) { |
178 | EXPECT_TRUE(invokeParser("_ZGVnM2v_foo(vector_foo)" , "void(i32)" )); |
179 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
180 | EXPECT_EQ(true, isMasked()); |
181 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
182 | EXPECT_EQ(ScalarName, "foo" ); |
183 | EXPECT_EQ(VectorName, "vector_foo" ); |
184 | } |
185 | |
186 | TEST_F(VFABIParserTest, ScalarNameAndVectorName_02) { |
187 | EXPECT_TRUE(invokeParser("_ZGVnM2v_foo(vector_foo)" , "void(i32)" )); |
188 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
189 | EXPECT_EQ(true, isMasked()); |
190 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
191 | EXPECT_EQ(ScalarName, "foo" ); |
192 | EXPECT_EQ(VectorName, "vector_foo" ); |
193 | } |
194 | |
195 | TEST_F(VFABIParserTest, ScalarNameAndVectorName_03) { |
196 | EXPECT_TRUE( |
197 | invokeParser("_ZGVnM2v___foo_bar_abc(fooBarAbcVec)" , "void(i32)" )); |
198 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
199 | EXPECT_EQ(true, isMasked()); |
200 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
201 | EXPECT_EQ(ScalarName, "__foo_bar_abc" ); |
202 | EXPECT_EQ(VectorName, "fooBarAbcVec" ); |
203 | } |
204 | |
205 | TEST_F(VFABIParserTest, ScalarNameOnly) { |
206 | EXPECT_TRUE(invokeParser("_ZGVnM2v___foo_bar_abc" , "void(i32)" )); |
207 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
208 | EXPECT_EQ(true, isMasked()); |
209 | EXPECT_EQ(ScalarName, "__foo_bar_abc" ); |
210 | // no vector name specified (as it's optional), so it should have the entire |
211 | // mangled name. |
212 | EXPECT_EQ(VectorName, "_ZGVnM2v___foo_bar_abc" ); |
213 | } |
214 | |
215 | TEST_F(VFABIParserTest, Parse) { |
216 | EXPECT_TRUE( |
217 | invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_foo" , |
218 | "void(i32, i32, i32, i32, ptr, i32, i32, i32, ptr)" )); |
219 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
220 | EXPECT_FALSE(isMasked()); |
221 | FunctionType *FTy = FunctionType::get( |
222 | Result: Type::getVoidTy(C&: Ctx), |
223 | Params: { |
224 | VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getFixed(MinVal: 2)), |
225 | Type::getInt32Ty(C&: Ctx), |
226 | Type::getInt32Ty(C&: Ctx), |
227 | Type::getInt32Ty(C&: Ctx), |
228 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), |
229 | Type::getInt32Ty(C&: Ctx), |
230 | Type::getInt32Ty(C&: Ctx), |
231 | Type::getInt32Ty(C&: Ctx), |
232 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), |
233 | }, |
234 | isVarArg: false); |
235 | EXPECT_EQ(getFunctionType(), FTy); |
236 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
237 | EXPECT_EQ(Parameters.size(), (unsigned)9); |
238 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0})); |
239 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearPos, 2})); |
240 | EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearValPos, 27})); |
241 | EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearUValPos, 4})); |
242 | EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_LinearRefPos, 5})); |
243 | EXPECT_EQ(Parameters[5], VFParameter({5, VFParamKind::OMP_Linear, 1})); |
244 | EXPECT_EQ(Parameters[6], VFParameter({6, VFParamKind::OMP_LinearVal, 10})); |
245 | EXPECT_EQ(Parameters[7], VFParameter({7, VFParamKind::OMP_LinearUVal, 100})); |
246 | EXPECT_EQ(Parameters[8], VFParameter({8, VFParamKind::OMP_LinearRef, 1000})); |
247 | EXPECT_EQ(ScalarName, "foo" ); |
248 | EXPECT_EQ(VectorName, "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_foo" ); |
249 | } |
250 | |
251 | TEST_F(VFABIParserTest, ParseVectorName) { |
252 | EXPECT_TRUE(invokeParser("_ZGVnN2v_foo(vector_foo)" , "void(i32)" )); |
253 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
254 | EXPECT_FALSE(isMasked()); |
255 | EXPECT_EQ(getFunctionType(), FTyNoMaskVLen2_i32); |
256 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
257 | EXPECT_EQ(Parameters.size(), (unsigned)1); |
258 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0})); |
259 | EXPECT_EQ(ScalarName, "foo" ); |
260 | EXPECT_EQ(VectorName, "vector_foo" ); |
261 | } |
262 | |
263 | TEST_F(VFABIParserTest, LinearWithCompileTimeNegativeStep) { |
264 | EXPECT_TRUE(invokeParser("_ZGVnN2ln1Ln10Un100Rn1000_foo(vector_foo)" , |
265 | "void(i32, i32, i32, ptr)" )); |
266 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
267 | EXPECT_FALSE(isMasked()); |
268 | FunctionType *FTy = FunctionType::get( |
269 | Result: Type::getVoidTy(C&: Ctx), |
270 | Params: {Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), |
271 | Type::getInt32Ty(C&: Ctx)->getPointerTo()}, |
272 | isVarArg: false); |
273 | EXPECT_EQ(getFunctionType(), FTy); |
274 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
275 | EXPECT_EQ(Parameters.size(), (unsigned)4); |
276 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, -1})); |
277 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearVal, -10})); |
278 | EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearUVal, -100})); |
279 | EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearRef, -1000})); |
280 | EXPECT_EQ(ScalarName, "foo" ); |
281 | EXPECT_EQ(VectorName, "vector_foo" ); |
282 | } |
283 | |
284 | TEST_F(VFABIParserTest, ParseScalableSVE) { |
285 | EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)" , "void(i32)" )); |
286 | EXPECT_EQ(ISA, VFISAKind::SVE); |
287 | EXPECT_TRUE(isMasked()); |
288 | EXPECT_EQ(getFunctionType(), FTyMaskedVLA_i32); |
289 | EXPECT_EQ(VF, ElementCount::getScalable(4)); |
290 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
291 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
292 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
293 | EXPECT_EQ(ScalarName, "foo" ); |
294 | EXPECT_EQ(VectorName, "vector_foo" ); |
295 | } |
296 | |
297 | TEST_F(VFABIParserTest, ParseFixedWidthSVE) { |
298 | EXPECT_TRUE(invokeParser("_ZGVsM2v_foo(vector_foo)" , "void(i32)" )); |
299 | EXPECT_EQ(ISA, VFISAKind::SVE); |
300 | EXPECT_TRUE(isMasked()); |
301 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
302 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
303 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
304 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
305 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
306 | EXPECT_EQ(ScalarName, "foo" ); |
307 | EXPECT_EQ(VectorName, "vector_foo" ); |
308 | } |
309 | |
310 | TEST_F(VFABIParserTest, NotAVectorFunctionABIName) { |
311 | // Vector names should start with `_ZGV`. |
312 | EXPECT_FALSE(invokeParser("ZGVnN2v_foo" )); |
313 | } |
314 | |
315 | TEST_F(VFABIParserTest, LinearWithRuntimeStep) { |
316 | EXPECT_FALSE(invokeParser("_ZGVnN2ls_foo" )) |
317 | << "A number should be present after \"ls\"." ; |
318 | EXPECT_TRUE(invokeParser("_ZGVnN2ls2_foo" , "void(i32)" )); |
319 | EXPECT_FALSE(invokeParser("_ZGVnN2Rs_foo" )) |
320 | << "A number should be present after \"Rs\"." ; |
321 | EXPECT_TRUE(invokeParser("_ZGVnN2Rs4_foo" , "void(i32)" )); |
322 | EXPECT_FALSE(invokeParser("_ZGVnN2Ls_foo" )) |
323 | << "A number should be present after \"Ls\"." ; |
324 | EXPECT_TRUE(invokeParser("_ZGVnN2Ls6_foo" , "void(i32)" )); |
325 | EXPECT_FALSE(invokeParser("_ZGVnN2Us_foo" )) |
326 | << "A number should be present after \"Us\"." ; |
327 | EXPECT_TRUE(invokeParser("_ZGVnN2Us8_foo" , "void(i32)" )); |
328 | } |
329 | |
330 | TEST_F(VFABIParserTest, LinearWithoutCompileTime) { |
331 | EXPECT_TRUE(invokeParser("_ZGVnN3lLRUlnLnRnUn_foo(vector_foo)" , |
332 | "void(i32, i32, ptr, i32, i32, i32, ptr, i32)" )); |
333 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
334 | EXPECT_FALSE(isMasked()); |
335 | FunctionType *FTy = FunctionType::get( |
336 | Result: Type::getVoidTy(C&: Ctx), |
337 | Params: {Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), |
338 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), Type::getInt32Ty(C&: Ctx), |
339 | Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), |
340 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), Type::getInt32Ty(C&: Ctx)}, |
341 | isVarArg: false); |
342 | EXPECT_EQ(getFunctionType(), FTy); |
343 | EXPECT_EQ(Parameters.size(), (unsigned)8); |
344 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, 1})); |
345 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearVal, 1})); |
346 | EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearRef, 1})); |
347 | EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearUVal, 1})); |
348 | EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_Linear, -1})); |
349 | EXPECT_EQ(Parameters[5], VFParameter({5, VFParamKind::OMP_LinearVal, -1})); |
350 | EXPECT_EQ(Parameters[6], VFParameter({6, VFParamKind::OMP_LinearRef, -1})); |
351 | EXPECT_EQ(Parameters[7], VFParameter({7, VFParamKind::OMP_LinearUVal, -1})); |
352 | EXPECT_EQ(ScalarName, "foo" ); |
353 | EXPECT_EQ(VectorName, "vector_foo" ); |
354 | } |
355 | |
356 | TEST_F(VFABIParserTest, LLVM_ISA) { |
357 | EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_foo" )); |
358 | EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_foo(vector_foo)" , "void(i32)" )); |
359 | EXPECT_EQ(ISA, VFISAKind::LLVM); |
360 | EXPECT_FALSE(isMasked()); |
361 | EXPECT_EQ(getFunctionType(), FTyNoMaskVLen2_i32); |
362 | EXPECT_EQ(Parameters.size(), (unsigned)1); |
363 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
364 | EXPECT_EQ(ScalarName, "foo" ); |
365 | EXPECT_EQ(VectorName, "vector_foo" ); |
366 | } |
367 | |
368 | TEST_F(VFABIParserTest, InvalidMask) { |
369 | EXPECT_FALSE(invokeParser("_ZGVsK2v_foo" )); |
370 | } |
371 | |
372 | TEST_F(VFABIParserTest, InvalidParameter) { |
373 | EXPECT_FALSE(invokeParser("_ZGVsM2vX_foo" )); |
374 | } |
375 | |
376 | TEST_F(VFABIParserTest, Align) { |
377 | EXPECT_TRUE(invokeParser("_ZGVsN2l2a2_foo(vector_foo)" , "void(i32)" )); |
378 | EXPECT_EQ(ISA, VFISAKind::SVE); |
379 | EXPECT_FALSE(isMasked()); |
380 | EXPECT_EQ(Parameters.size(), (unsigned)1); |
381 | EXPECT_EQ(Parameters[0].Alignment, Align(2)); |
382 | EXPECT_EQ(ScalarName, "foo" ); |
383 | EXPECT_EQ(VectorName, "vector_foo" ); |
384 | FunctionType *FTy = |
385 | FunctionType::get(Result: Type::getVoidTy(C&: Ctx), Params: {Type::getInt32Ty(C&: Ctx)}, isVarArg: false); |
386 | EXPECT_EQ(getFunctionType(), FTy); |
387 | // Missing alignment value. |
388 | EXPECT_FALSE(invokeParser("_ZGVsM2l2a_foo" )); |
389 | // Invalid alignment token "x". |
390 | EXPECT_FALSE(invokeParser("_ZGVsM2l2ax_foo" )); |
391 | // Alignment MUST be associated to a paramater. |
392 | EXPECT_FALSE(invokeParser("_ZGVsM2a2_foo" )); |
393 | // Alignment must be a power of 2. |
394 | EXPECT_FALSE(invokeParser("_ZGVsN2l2a0_foo" )); |
395 | EXPECT_TRUE(invokeParser("_ZGVsN2l2a1_foo" , "void(i32)" )); |
396 | EXPECT_FALSE(invokeParser("_ZGVsN2l2a3_foo" )); |
397 | EXPECT_FALSE(invokeParser("_ZGVsN2l2a6_foo" )); |
398 | } |
399 | |
400 | TEST_F(VFABIParserTest, ParseUniform) { |
401 | EXPECT_TRUE(invokeParser("_ZGVnN2u_foo(vector_foo)" , "void(i32)" )); |
402 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
403 | EXPECT_FALSE(isMasked()); |
404 | FunctionType *FTy = |
405 | FunctionType::get(Result: Type::getVoidTy(C&: Ctx), Params: {Type::getInt32Ty(C&: Ctx)}, isVarArg: false); |
406 | EXPECT_EQ(getFunctionType(), FTy); |
407 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
408 | EXPECT_EQ(Parameters.size(), (unsigned)1); |
409 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Uniform, 0})); |
410 | EXPECT_EQ(ScalarName, "foo" ); |
411 | EXPECT_EQ(VectorName, "vector_foo" ); |
412 | |
413 | // Uniform doesn't expect extra data. |
414 | EXPECT_FALSE(invokeParser("_ZGVnN2u0_foo" )); |
415 | } |
416 | |
417 | TEST_F(VFABIParserTest, ISAIndependentMangling) { |
418 | // This test makes sure that the mangling of the parameters in |
419 | // independent on the <isa> token. |
420 | const StringRef IRTy = |
421 | "void(i32, i32, i32, i32, ptr, i32, i32, i32, i32, i32)" ; |
422 | FunctionType *FTy = FunctionType::get( |
423 | Result: Type::getVoidTy(C&: Ctx), |
424 | Params: {VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getFixed(MinVal: 2)), |
425 | Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), |
426 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), Type::getInt32Ty(C&: Ctx), |
427 | Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), Type::getInt32Ty(C&: Ctx), |
428 | Type::getInt32Ty(C&: Ctx)}, |
429 | isVarArg: false); |
430 | |
431 | const SmallVector<VFParameter, 8> ExpectedParams = { |
432 | VFParameter({.ParamPos: 0, .ParamKind: VFParamKind::Vector, .LinearStepOrPos: 0}), |
433 | VFParameter({.ParamPos: 1, .ParamKind: VFParamKind::OMP_LinearPos, .LinearStepOrPos: 2}), |
434 | VFParameter({.ParamPos: 2, .ParamKind: VFParamKind::OMP_LinearValPos, .LinearStepOrPos: 27}), |
435 | VFParameter({.ParamPos: 3, .ParamKind: VFParamKind::OMP_LinearUValPos, .LinearStepOrPos: 4}), |
436 | VFParameter({.ParamPos: 4, .ParamKind: VFParamKind::OMP_LinearRefPos, .LinearStepOrPos: 5}), |
437 | VFParameter({.ParamPos: 5, .ParamKind: VFParamKind::OMP_Linear, .LinearStepOrPos: 1}), |
438 | VFParameter({.ParamPos: 6, .ParamKind: VFParamKind::OMP_LinearVal, .LinearStepOrPos: 10}), |
439 | VFParameter({.ParamPos: 7, .ParamKind: VFParamKind::OMP_LinearUVal, .LinearStepOrPos: 100}), |
440 | VFParameter({.ParamPos: 8, .ParamKind: VFParamKind::OMP_LinearRef, .LinearStepOrPos: 1000}), |
441 | VFParameter({.ParamPos: 9, .ParamKind: VFParamKind::OMP_Uniform, .LinearStepOrPos: 0}), |
442 | }; |
443 | |
444 | #define __COMMON_CHECKS \ |
445 | do { \ |
446 | EXPECT_EQ(VF, ElementCount::getFixed(2)); \ |
447 | EXPECT_FALSE(isMasked()); \ |
448 | EXPECT_EQ(getFunctionType(), FTy); \ |
449 | EXPECT_EQ(Parameters.size(), (unsigned)10); \ |
450 | EXPECT_EQ(Parameters, ExpectedParams); \ |
451 | EXPECT_EQ(ScalarName, "foo"); \ |
452 | EXPECT_EQ(VectorName, "vector_foo"); \ |
453 | } while (0) |
454 | |
455 | // Advanced SIMD: <isa> = "n" |
456 | EXPECT_TRUE(invokeParser( |
457 | "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
458 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
459 | __COMMON_CHECKS; |
460 | |
461 | // SVE: <isa> = "s" |
462 | EXPECT_TRUE(invokeParser( |
463 | "_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
464 | EXPECT_EQ(ISA, VFISAKind::SVE); |
465 | __COMMON_CHECKS; |
466 | |
467 | // SSE: <isa> = "b" |
468 | EXPECT_TRUE(invokeParser( |
469 | "_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
470 | EXPECT_EQ(ISA, VFISAKind::SSE); |
471 | __COMMON_CHECKS; |
472 | |
473 | // AVX: <isa> = "c" |
474 | EXPECT_TRUE(invokeParser( |
475 | "_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
476 | EXPECT_EQ(ISA, VFISAKind::AVX); |
477 | __COMMON_CHECKS; |
478 | |
479 | // AVX2: <isa> = "d" |
480 | EXPECT_TRUE(invokeParser( |
481 | "_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
482 | EXPECT_EQ(ISA, VFISAKind::AVX2); |
483 | __COMMON_CHECKS; |
484 | |
485 | // AVX512: <isa> = "e" |
486 | EXPECT_TRUE(invokeParser( |
487 | "_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
488 | EXPECT_EQ(ISA, VFISAKind::AVX512); |
489 | __COMMON_CHECKS; |
490 | |
491 | // LLVM: <isa> = "_LLVM_" internal vector function. |
492 | EXPECT_TRUE(invokeParser( |
493 | "_ZGV_LLVM_N2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
494 | EXPECT_EQ(ISA, VFISAKind::LLVM); |
495 | __COMMON_CHECKS; |
496 | |
497 | // Unknown ISA (randomly using "q"). This test will need update if |
498 | // some targets decide to use "q" as their ISA token. |
499 | EXPECT_TRUE(invokeParser( |
500 | "_ZGVqN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)" , IRTy)); |
501 | EXPECT_EQ(ISA, VFISAKind::Unknown); |
502 | __COMMON_CHECKS; |
503 | |
504 | #undef __COMMON_CHECKS |
505 | } |
506 | |
507 | TEST_F(VFABIParserTest, MissingScalarName) { |
508 | EXPECT_FALSE(invokeParser("_ZGVnN2v_" )); |
509 | } |
510 | |
511 | TEST_F(VFABIParserTest, MissingVectorName) { |
512 | EXPECT_FALSE(invokeParser("_ZGVnN2v_foo()" )); |
513 | } |
514 | |
515 | TEST_F(VFABIParserTest, MissingVectorNameTermination) { |
516 | EXPECT_FALSE(invokeParser("_ZGVnN2v_foo(bar" )); |
517 | } |
518 | |
519 | TEST_F(VFABIParserTest, ParseMaskingNEON) { |
520 | EXPECT_TRUE(invokeParser("_ZGVnM2v_foo(vector_foo)" , "void(i32)" )); |
521 | EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); |
522 | EXPECT_TRUE(isMasked()); |
523 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
524 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
525 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
526 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
527 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
528 | EXPECT_EQ(ScalarName, "foo" ); |
529 | EXPECT_EQ(VectorName, "vector_foo" ); |
530 | } |
531 | |
532 | TEST_F(VFABIParserTest, ParseMaskingSVE) { |
533 | EXPECT_TRUE(invokeParser("_ZGVsM2v_foo(vector_foo)" , "void(i32)" )); |
534 | EXPECT_EQ(ISA, VFISAKind::SVE); |
535 | EXPECT_TRUE(isMasked()); |
536 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
537 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
538 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
539 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
540 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
541 | EXPECT_EQ(ScalarName, "foo" ); |
542 | EXPECT_EQ(VectorName, "vector_foo" ); |
543 | } |
544 | |
545 | TEST_F(VFABIParserTest, ParseMaskingSSE) { |
546 | EXPECT_TRUE(invokeParser("_ZGVbM2v_foo(vector_foo)" , "void(i32)" )); |
547 | EXPECT_EQ(ISA, VFISAKind::SSE); |
548 | EXPECT_TRUE(isMasked()); |
549 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
550 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
551 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
552 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
553 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
554 | EXPECT_EQ(ScalarName, "foo" ); |
555 | EXPECT_EQ(VectorName, "vector_foo" ); |
556 | } |
557 | |
558 | TEST_F(VFABIParserTest, ParseMaskingAVX) { |
559 | EXPECT_TRUE(invokeParser("_ZGVcM2v_foo(vector_foo)" , "void(i32)" )); |
560 | EXPECT_EQ(ISA, VFISAKind::AVX); |
561 | EXPECT_TRUE(isMasked()); |
562 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
563 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
564 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
565 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
566 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
567 | EXPECT_EQ(ScalarName, "foo" ); |
568 | EXPECT_EQ(VectorName, "vector_foo" ); |
569 | } |
570 | |
571 | TEST_F(VFABIParserTest, ParseMaskingAVX2) { |
572 | EXPECT_TRUE(invokeParser("_ZGVdM2v_foo(vector_foo)" , "void(i32)" )); |
573 | EXPECT_EQ(ISA, VFISAKind::AVX2); |
574 | EXPECT_TRUE(isMasked()); |
575 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
576 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
577 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
578 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
579 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
580 | EXPECT_EQ(ScalarName, "foo" ); |
581 | EXPECT_EQ(VectorName, "vector_foo" ); |
582 | } |
583 | |
584 | TEST_F(VFABIParserTest, ParseMaskingAVX512) { |
585 | EXPECT_TRUE(invokeParser("_ZGVeM2v_foo(vector_foo)" , "void(i32)" )); |
586 | EXPECT_EQ(ISA, VFISAKind::AVX512); |
587 | EXPECT_TRUE(isMasked()); |
588 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
589 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
590 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
591 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
592 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
593 | EXPECT_EQ(ScalarName, "foo" ); |
594 | EXPECT_EQ(VectorName, "vector_foo" ); |
595 | } |
596 | |
597 | TEST_F(VFABIParserTest, ParseMaskingLLVM) { |
598 | EXPECT_TRUE(invokeParser("_ZGV_LLVM_M2v_foo(vector_foo)" , "void(i32)" )); |
599 | EXPECT_EQ(ISA, VFISAKind::LLVM); |
600 | EXPECT_TRUE(isMasked()); |
601 | EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32); |
602 | EXPECT_EQ(VF, ElementCount::getFixed(2)); |
603 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
604 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
605 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
606 | EXPECT_EQ(ScalarName, "foo" ); |
607 | EXPECT_EQ(VectorName, "vector_foo" ); |
608 | } |
609 | |
610 | TEST_F(VFABIParserTest, ParseScalableMaskingLLVM) { |
611 | EXPECT_FALSE(invokeParser("_ZGV_LLVM_Mxv_foo(vector_foo)" )); |
612 | } |
613 | |
614 | TEST_F(VFABIParserTest, LLVM_InternalISA) { |
615 | EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_foo" )); |
616 | EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_foo(vector_foo)" , "void(i32)" )); |
617 | EXPECT_EQ(ISA, VFISAKind::LLVM); |
618 | EXPECT_FALSE(isMasked()); |
619 | EXPECT_EQ(getFunctionType(), FTyNoMaskVLen2_i32); |
620 | EXPECT_EQ(Parameters.size(), (unsigned)1); |
621 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
622 | EXPECT_EQ(ScalarName, "foo" ); |
623 | EXPECT_EQ(VectorName, "vector_foo" ); |
624 | } |
625 | |
626 | TEST_F(VFABIParserTest, LLVM_Intrinsics) { |
627 | EXPECT_TRUE(invokeParser("_ZGV_LLVM_N4vv_llvm.pow.f32(__svml_powf4)" , |
628 | "void(float, float)" )); |
629 | EXPECT_EQ(ISA, VFISAKind::LLVM); |
630 | EXPECT_FALSE(isMasked()); |
631 | FunctionType *FTy = FunctionType::get( |
632 | Result: Type::getVoidTy(C&: Ctx), |
633 | Params: { |
634 | VectorType::get(ElementType: Type::getFloatTy(C&: Ctx), EC: ElementCount::getFixed(MinVal: 4)), |
635 | VectorType::get(ElementType: Type::getFloatTy(C&: Ctx), EC: ElementCount::getFixed(MinVal: 4)), |
636 | }, |
637 | isVarArg: false); |
638 | EXPECT_EQ(getFunctionType(), FTy); |
639 | EXPECT_EQ(VF, ElementCount::getFixed(4)); |
640 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
641 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
642 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::Vector})); |
643 | EXPECT_EQ(ScalarName, "llvm.pow.f32" ); |
644 | EXPECT_EQ(VectorName, "__svml_powf4" ); |
645 | } |
646 | |
647 | TEST_F(VFABIParserTest, ParseScalableRequiresDeclaration) { |
648 | const char *MangledName = "_ZGVsMxv_sin(custom_vg)" ; |
649 | EXPECT_FALSE(invokeParser(MangledName)); |
650 | EXPECT_TRUE(invokeParser(MangledName, "void(i32)" )); |
651 | EXPECT_EQ(ISA, VFISAKind::SVE); |
652 | EXPECT_TRUE(isMasked()); |
653 | EXPECT_EQ(getFunctionType(), FTyMaskedVLA_i32); |
654 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
655 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
656 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
657 | EXPECT_EQ(ScalarName, "sin" ); |
658 | EXPECT_EQ(VectorName, "custom_vg" ); |
659 | } |
660 | |
661 | TEST_F(VFABIParserTest, ZeroIsInvalidVLEN) { |
662 | EXPECT_FALSE(invokeParser("_ZGVeM0v_foo" )); |
663 | EXPECT_FALSE(invokeParser("_ZGVeN0v_foo" )); |
664 | EXPECT_FALSE(invokeParser("_ZGVsM0v_foo" )); |
665 | EXPECT_FALSE(invokeParser("_ZGVsN0v_foo" )); |
666 | } |
667 | |
668 | TEST_F(VFABIParserTest, ParseScalableMaskingSVE) { |
669 | EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)" , "i32(i32)" )); |
670 | EXPECT_EQ(ISA, VFISAKind::SVE); |
671 | EXPECT_TRUE(isMasked()); |
672 | FunctionType *FTy = FunctionType::get( |
673 | Result: VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 4)), |
674 | Params: {VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 4)), |
675 | VectorType::get(ElementType: Type::getInt1Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 4))}, |
676 | isVarArg: false); |
677 | EXPECT_EQ(getFunctionType(), FTy); |
678 | EXPECT_EQ(VF, ElementCount::getScalable(4)); |
679 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
680 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
681 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
682 | EXPECT_EQ(ScalarName, "foo" ); |
683 | EXPECT_EQ(VectorName, "vector_foo" ); |
684 | } |
685 | |
686 | TEST_F(VFABIParserTest, ParseScalableMaskingSVESincos) { |
687 | EXPECT_TRUE(invokeParser("_ZGVsMxvl8l8_sincos(custom_vector_sincos)" , |
688 | "void(double, ptr, ptr)" )); |
689 | EXPECT_EQ(ISA, VFISAKind::SVE); |
690 | EXPECT_TRUE(isMasked()); |
691 | FunctionType *FTy = FunctionType::get( |
692 | Result: Type::getVoidTy(C&: Ctx), |
693 | Params: { |
694 | VectorType::get(ElementType: Type::getDoubleTy(C&: Ctx), EC: ElementCount::getScalable(MinVal: 2)), |
695 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), |
696 | Type::getInt32Ty(C&: Ctx)->getPointerTo(), |
697 | VectorType::get(ElementType: Type::getInt1Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 2)), |
698 | }, |
699 | isVarArg: false); |
700 | EXPECT_EQ(getFunctionType(), FTy); |
701 | EXPECT_EQ(VF, ElementCount::getScalable(2)); |
702 | EXPECT_EQ(Parameters.size(), (unsigned)4); |
703 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
704 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_Linear, 8})); |
705 | EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_Linear, 8})); |
706 | EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::GlobalPredicate})); |
707 | EXPECT_EQ(ScalarName, "sincos" ); |
708 | EXPECT_EQ(VectorName, "custom_vector_sincos" ); |
709 | } |
710 | |
711 | // Make sure that we get the correct VF if the return type is wider than any |
712 | // parameter type. |
713 | TEST_F(VFABIParserTest, ParseWiderReturnTypeSVE) { |
714 | EXPECT_TRUE(invokeParser("_ZGVsMxvv_foo(vector_foo)" , "i64(i32, i32)" )); |
715 | EXPECT_EQ(ISA, VFISAKind::SVE); |
716 | EXPECT_TRUE(isMasked()); |
717 | FunctionType *FTy = FunctionType::get( |
718 | Result: VectorType::get(ElementType: Type::getInt64Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 2)), |
719 | Params: { |
720 | VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 2)), |
721 | VectorType::get(ElementType: Type::getInt32Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 2)), |
722 | VectorType::get(ElementType: Type::getInt1Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 2)), |
723 | }, |
724 | isVarArg: false); |
725 | EXPECT_EQ(getFunctionType(), FTy); |
726 | EXPECT_EQ(Parameters.size(), (unsigned)3); |
727 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
728 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::Vector})); |
729 | EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::GlobalPredicate})); |
730 | EXPECT_EQ(VF, ElementCount::getScalable(2)); |
731 | EXPECT_EQ(ScalarName, "foo" ); |
732 | EXPECT_EQ(VectorName, "vector_foo" ); |
733 | } |
734 | |
735 | // Make sure we handle void return types. |
736 | TEST_F(VFABIParserTest, ParseVoidReturnTypeSVE) { |
737 | EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)" , "void(i16)" )); |
738 | EXPECT_EQ(ISA, VFISAKind::SVE); |
739 | EXPECT_TRUE(isMasked()); |
740 | FunctionType *FTy = FunctionType::get( |
741 | Result: Type::getVoidTy(C&: Ctx), |
742 | Params: { |
743 | VectorType::get(ElementType: Type::getInt16Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 8)), |
744 | VectorType::get(ElementType: Type::getInt1Ty(C&: Ctx), EC: ElementCount::getScalable(MinVal: 8)), |
745 | }, |
746 | isVarArg: false); |
747 | EXPECT_EQ(getFunctionType(), FTy); |
748 | EXPECT_EQ(Parameters.size(), (unsigned)2); |
749 | EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); |
750 | EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); |
751 | EXPECT_EQ(VF, ElementCount::getScalable(8)); |
752 | EXPECT_EQ(ScalarName, "foo" ); |
753 | EXPECT_EQ(VectorName, "vector_foo" ); |
754 | } |
755 | |
756 | // Make sure we reject unsupported parameter types. |
757 | TEST_F(VFABIParserTest, ParseUnsupportedElementTypeSVE) { |
758 | EXPECT_FALSE(invokeParser("_ZGVsMxv_foo(vector_foo)" , "void(i128)" )); |
759 | } |
760 | |
761 | // Make sure we reject unsupported return types |
762 | TEST_F(VFABIParserTest, ParseUnsupportedReturnTypeSVE) { |
763 | EXPECT_FALSE(invokeParser("_ZGVsMxv_foo(vector_foo)" , "fp128(float)" )); |
764 | } |
765 | |
766 | class VFABIAttrTest : public testing::Test { |
767 | protected: |
768 | void SetUp() override { |
769 | M = parseAssemblyString(AsmString: IR, Err, Context&: Ctx); |
770 | // Get the only call instruction in the block, which is the first |
771 | // instruction. |
772 | CI = dyn_cast<CallInst>(Val: &*(instructions(F: M->getFunction(Name: "f" )).begin())); |
773 | } |
774 | const char *IR = "define i32 @f(i32 %a) {\n" |
775 | " %1 = call i32 @g(i32 %a) #0\n" |
776 | " ret i32 %1\n" |
777 | "}\n" |
778 | "declare i32 @g(i32)\n" |
779 | "declare <2 x i32> @custom_vg(<2 x i32>)" |
780 | "declare <4 x i32> @_ZGVnN4v_g(<4 x i32>)" |
781 | "declare <8 x i32> @_ZGVnN8v_g(<8 x i32>)" |
782 | "attributes #0 = { " |
783 | "\"vector-function-abi-variant\"=\"" |
784 | "_ZGVnN2v_g(custom_vg),_ZGVnN4v_g\" }" ; |
785 | LLVMContext Ctx; |
786 | SMDiagnostic Err; |
787 | std::unique_ptr<Module> M; |
788 | CallInst *CI; |
789 | SmallVector<std::string, 8> Mappings; |
790 | }; |
791 | |
792 | TEST_F(VFABIAttrTest, Read) { |
793 | VFABI::getVectorVariantNames(CI: *CI, VariantMappings&: Mappings); |
794 | SmallVector<std::string, 8> Exp; |
795 | Exp.push_back(Elt: "_ZGVnN2v_g(custom_vg)" ); |
796 | Exp.push_back(Elt: "_ZGVnN4v_g" ); |
797 | EXPECT_EQ(Mappings, Exp); |
798 | } |
799 | |
800 | TEST_F(VFABIAttrTest, Write) { |
801 | Mappings.push_back(Elt: "_ZGVnN8v_g" ); |
802 | Mappings.push_back(Elt: "_ZGVnN2v_g(custom_vg)" ); |
803 | VFABI::setVectorVariantNames(CI, VariantMappings: Mappings); |
804 | const StringRef S = |
805 | CI->getFnAttr(Kind: "vector-function-abi-variant" ).getValueAsString(); |
806 | EXPECT_EQ(S, "_ZGVnN8v_g,_ZGVnN2v_g(custom_vg)" ); |
807 | } |
808 | |
809 | static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { |
810 | SMDiagnostic Err; |
811 | std::unique_ptr<Module> Mod = parseAssemblyString(AsmString: IR, Err, Context&: C); |
812 | if (!Mod) |
813 | Err.print(ProgName: "VectorFunctionABITests" , S&: errs()); |
814 | return Mod; |
815 | } |
816 | |
817 | TEST(VFABIGetMappingsTest, IndirectCallInst) { |
818 | LLVMContext C; |
819 | std::unique_ptr<Module> M = parseIR(C, IR: R"IR( |
820 | define void @call(void () * %f) { |
821 | entry: |
822 | call void %f() |
823 | ret void |
824 | } |
825 | )IR" ); |
826 | auto *F = dyn_cast_or_null<Function>(Val: M->getNamedValue(Name: "call" )); |
827 | ASSERT_TRUE(F); |
828 | auto *CI = dyn_cast<CallInst>(Val: &F->front().front()); |
829 | ASSERT_TRUE(CI); |
830 | ASSERT_TRUE(CI->isIndirectCall()); |
831 | auto Mappings = VFDatabase::getMappings(CI: *CI); |
832 | EXPECT_EQ(Mappings.size(), (unsigned)0); |
833 | } |
834 | |