1 | //===- VFABIDemangler.cpp - Vector Function ABI demangler -----------------===// |
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/SetVector.h" |
11 | #include "llvm/ADT/SmallString.h" |
12 | #include "llvm/ADT/StringSwitch.h" |
13 | #include "llvm/IR/Module.h" |
14 | #include "llvm/Support/Debug.h" |
15 | #include "llvm/Support/raw_ostream.h" |
16 | #include <limits> |
17 | |
18 | using namespace llvm; |
19 | |
20 | #define DEBUG_TYPE "vfabi-demangler" |
21 | |
22 | namespace { |
23 | /// Utilities for the Vector Function ABI name parser. |
24 | |
25 | /// Return types for the parser functions. |
26 | enum class ParseRet { |
27 | OK, // Found. |
28 | None, // Not found. |
29 | Error // Syntax error. |
30 | }; |
31 | |
32 | /// Extracts the `<isa>` information from the mangled string, and |
33 | /// sets the `ISA` accordingly. If successful, the <isa> token is removed |
34 | /// from the input string `MangledName`. |
35 | static ParseRet tryParseISA(StringRef &MangledName, VFISAKind &ISA) { |
36 | if (MangledName.empty()) |
37 | return ParseRet::Error; |
38 | |
39 | if (MangledName.consume_front(Prefix: VFABI::_LLVM_)) { |
40 | ISA = VFISAKind::LLVM; |
41 | } else { |
42 | ISA = StringSwitch<VFISAKind>(MangledName.take_front(N: 1)) |
43 | .Case(S: "n" , Value: VFISAKind::AdvancedSIMD) |
44 | .Case(S: "s" , Value: VFISAKind::SVE) |
45 | .Case(S: "b" , Value: VFISAKind::SSE) |
46 | .Case(S: "c" , Value: VFISAKind::AVX) |
47 | .Case(S: "d" , Value: VFISAKind::AVX2) |
48 | .Case(S: "e" , Value: VFISAKind::AVX512) |
49 | .Default(Value: VFISAKind::Unknown); |
50 | MangledName = MangledName.drop_front(N: 1); |
51 | } |
52 | |
53 | return ParseRet::OK; |
54 | } |
55 | |
56 | /// Extracts the `<mask>` information from the mangled string, and |
57 | /// sets `IsMasked` accordingly. If successful, the <mask> token is removed |
58 | /// from the input string `MangledName`. |
59 | static ParseRet tryParseMask(StringRef &MangledName, bool &IsMasked) { |
60 | if (MangledName.consume_front(Prefix: "M" )) { |
61 | IsMasked = true; |
62 | return ParseRet::OK; |
63 | } |
64 | |
65 | if (MangledName.consume_front(Prefix: "N" )) { |
66 | IsMasked = false; |
67 | return ParseRet::OK; |
68 | } |
69 | |
70 | return ParseRet::Error; |
71 | } |
72 | |
73 | /// Extract the `<vlen>` information from the mangled string, and |
74 | /// sets `ParsedVF` accordingly. A `<vlen> == "x"` token is interpreted as a |
75 | /// scalable vector length and the boolean is set to true, otherwise a nonzero |
76 | /// unsigned integer will be directly used as a VF. On success, the `<vlen>` |
77 | /// token is removed from the input string `ParseString`. |
78 | static ParseRet tryParseVLEN(StringRef &ParseString, VFISAKind ISA, |
79 | std::pair<unsigned, bool> &ParsedVF) { |
80 | if (ParseString.consume_front(Prefix: "x" )) { |
81 | // SVE is the only scalable ISA currently supported. |
82 | if (ISA != VFISAKind::SVE) { |
83 | LLVM_DEBUG(dbgs() << "Vector function variant declared with scalable VF " |
84 | << "but ISA is not SVE\n" ); |
85 | return ParseRet::Error; |
86 | } |
87 | // We can't determine the VF of a scalable vector by looking at the vlen |
88 | // string (just 'x'), so say we successfully parsed it but return a 'true' |
89 | // for the scalable field with an invalid VF field so that we know to look |
90 | // up the actual VF based on element types from the parameters or return. |
91 | ParsedVF = {0, true}; |
92 | return ParseRet::OK; |
93 | } |
94 | |
95 | unsigned VF = 0; |
96 | if (ParseString.consumeInteger(Radix: 10, Result&: VF)) |
97 | return ParseRet::Error; |
98 | |
99 | // The token `0` is invalid for VLEN. |
100 | if (VF == 0) |
101 | return ParseRet::Error; |
102 | |
103 | ParsedVF = {VF, false}; |
104 | return ParseRet::OK; |
105 | } |
106 | |
107 | /// The function looks for the following strings at the beginning of |
108 | /// the input string `ParseString`: |
109 | /// |
110 | /// <token> <number> |
111 | /// |
112 | /// On success, it removes the parsed parameter from `ParseString`, |
113 | /// sets `PKind` to the correspondent enum value, sets `Pos` to |
114 | /// <number>, and return success. On a syntax error, it return a |
115 | /// parsing error. If nothing is parsed, it returns std::nullopt. |
116 | /// |
117 | /// The function expects <token> to be one of "ls", "Rs", "Us" or |
118 | /// "Ls". |
119 | static ParseRet tryParseLinearTokenWithRuntimeStep(StringRef &ParseString, |
120 | VFParamKind &PKind, int &Pos, |
121 | const StringRef Token) { |
122 | if (ParseString.consume_front(Prefix: Token)) { |
123 | PKind = VFABI::getVFParamKindFromString(Token); |
124 | if (ParseString.consumeInteger(Radix: 10, Result&: Pos)) |
125 | return ParseRet::Error; |
126 | return ParseRet::OK; |
127 | } |
128 | |
129 | return ParseRet::None; |
130 | } |
131 | |
132 | /// The function looks for the following string at the beginning of |
133 | /// the input string `ParseString`: |
134 | /// |
135 | /// <token> <number> |
136 | /// |
137 | /// <token> is one of "ls", "Rs", "Us" or "Ls". |
138 | /// |
139 | /// On success, it removes the parsed parameter from `ParseString`, |
140 | /// sets `PKind` to the correspondent enum value, sets `StepOrPos` to |
141 | /// <number>, and return success. On a syntax error, it return a |
142 | /// parsing error. If nothing is parsed, it returns std::nullopt. |
143 | static ParseRet tryParseLinearWithRuntimeStep(StringRef &ParseString, |
144 | VFParamKind &PKind, |
145 | int &StepOrPos) { |
146 | ParseRet Ret; |
147 | |
148 | // "ls" <RuntimeStepPos> |
149 | Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, Pos&: StepOrPos, Token: "ls" ); |
150 | if (Ret != ParseRet::None) |
151 | return Ret; |
152 | |
153 | // "Rs" <RuntimeStepPos> |
154 | Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, Pos&: StepOrPos, Token: "Rs" ); |
155 | if (Ret != ParseRet::None) |
156 | return Ret; |
157 | |
158 | // "Ls" <RuntimeStepPos> |
159 | Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, Pos&: StepOrPos, Token: "Ls" ); |
160 | if (Ret != ParseRet::None) |
161 | return Ret; |
162 | |
163 | // "Us" <RuntimeStepPos> |
164 | Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, Pos&: StepOrPos, Token: "Us" ); |
165 | if (Ret != ParseRet::None) |
166 | return Ret; |
167 | |
168 | return ParseRet::None; |
169 | } |
170 | |
171 | /// The function looks for the following strings at the beginning of |
172 | /// the input string `ParseString`: |
173 | /// |
174 | /// <token> {"n"} <number> |
175 | /// |
176 | /// On success, it removes the parsed parameter from `ParseString`, |
177 | /// sets `PKind` to the correspondent enum value, sets `LinearStep` to |
178 | /// <number>, and return success. On a syntax error, it return a |
179 | /// parsing error. If nothing is parsed, it returns std::nullopt. |
180 | /// |
181 | /// The function expects <token> to be one of "l", "R", "U" or |
182 | /// "L". |
183 | static ParseRet tryParseCompileTimeLinearToken(StringRef &ParseString, |
184 | VFParamKind &PKind, |
185 | int &LinearStep, |
186 | const StringRef Token) { |
187 | if (ParseString.consume_front(Prefix: Token)) { |
188 | PKind = VFABI::getVFParamKindFromString(Token); |
189 | const bool Negate = ParseString.consume_front(Prefix: "n" ); |
190 | if (ParseString.consumeInteger(Radix: 10, Result&: LinearStep)) |
191 | LinearStep = 1; |
192 | if (Negate) |
193 | LinearStep *= -1; |
194 | return ParseRet::OK; |
195 | } |
196 | |
197 | return ParseRet::None; |
198 | } |
199 | |
200 | /// The function looks for the following strings at the beginning of |
201 | /// the input string `ParseString`: |
202 | /// |
203 | /// ["l" | "R" | "U" | "L"] {"n"} <number> |
204 | /// |
205 | /// On success, it removes the parsed parameter from `ParseString`, |
206 | /// sets `PKind` to the correspondent enum value, sets `LinearStep` to |
207 | /// <number>, and return success. On a syntax error, it return a |
208 | /// parsing error. If nothing is parsed, it returns std::nullopt. |
209 | static ParseRet tryParseLinearWithCompileTimeStep(StringRef &ParseString, |
210 | VFParamKind &PKind, |
211 | int &StepOrPos) { |
212 | // "l" {"n"} <CompileTimeStep> |
213 | if (tryParseCompileTimeLinearToken(ParseString, PKind, LinearStep&: StepOrPos, Token: "l" ) == |
214 | ParseRet::OK) |
215 | return ParseRet::OK; |
216 | |
217 | // "R" {"n"} <CompileTimeStep> |
218 | if (tryParseCompileTimeLinearToken(ParseString, PKind, LinearStep&: StepOrPos, Token: "R" ) == |
219 | ParseRet::OK) |
220 | return ParseRet::OK; |
221 | |
222 | // "L" {"n"} <CompileTimeStep> |
223 | if (tryParseCompileTimeLinearToken(ParseString, PKind, LinearStep&: StepOrPos, Token: "L" ) == |
224 | ParseRet::OK) |
225 | return ParseRet::OK; |
226 | |
227 | // "U" {"n"} <CompileTimeStep> |
228 | if (tryParseCompileTimeLinearToken(ParseString, PKind, LinearStep&: StepOrPos, Token: "U" ) == |
229 | ParseRet::OK) |
230 | return ParseRet::OK; |
231 | |
232 | return ParseRet::None; |
233 | } |
234 | |
235 | /// Looks into the <parameters> part of the mangled name in search |
236 | /// for valid paramaters at the beginning of the string |
237 | /// `ParseString`. |
238 | /// |
239 | /// On success, it removes the parsed parameter from `ParseString`, |
240 | /// sets `PKind` to the correspondent enum value, sets `StepOrPos` |
241 | /// accordingly, and return success. On a syntax error, it return a |
242 | /// parsing error. If nothing is parsed, it returns std::nullopt. |
243 | static ParseRet tryParseParameter(StringRef &ParseString, VFParamKind &PKind, |
244 | int &StepOrPos) { |
245 | if (ParseString.consume_front(Prefix: "v" )) { |
246 | PKind = VFParamKind::Vector; |
247 | StepOrPos = 0; |
248 | return ParseRet::OK; |
249 | } |
250 | |
251 | if (ParseString.consume_front(Prefix: "u" )) { |
252 | PKind = VFParamKind::OMP_Uniform; |
253 | StepOrPos = 0; |
254 | return ParseRet::OK; |
255 | } |
256 | |
257 | const ParseRet HasLinearRuntime = |
258 | tryParseLinearWithRuntimeStep(ParseString, PKind, StepOrPos); |
259 | if (HasLinearRuntime != ParseRet::None) |
260 | return HasLinearRuntime; |
261 | |
262 | const ParseRet HasLinearCompileTime = |
263 | tryParseLinearWithCompileTimeStep(ParseString, PKind, StepOrPos); |
264 | if (HasLinearCompileTime != ParseRet::None) |
265 | return HasLinearCompileTime; |
266 | |
267 | return ParseRet::None; |
268 | } |
269 | |
270 | /// Looks into the <parameters> part of the mangled name in search |
271 | /// of a valid 'aligned' clause. The function should be invoked |
272 | /// after parsing a parameter via `tryParseParameter`. |
273 | /// |
274 | /// On success, it removes the parsed parameter from `ParseString`, |
275 | /// sets `PKind` to the correspondent enum value, sets `StepOrPos` |
276 | /// accordingly, and return success. On a syntax error, it return a |
277 | /// parsing error. If nothing is parsed, it returns std::nullopt. |
278 | static ParseRet tryParseAlign(StringRef &ParseString, Align &Alignment) { |
279 | uint64_t Val; |
280 | // "a" <number> |
281 | if (ParseString.consume_front(Prefix: "a" )) { |
282 | if (ParseString.consumeInteger(Radix: 10, Result&: Val)) |
283 | return ParseRet::Error; |
284 | |
285 | if (!isPowerOf2_64(Value: Val)) |
286 | return ParseRet::Error; |
287 | |
288 | Alignment = Align(Val); |
289 | |
290 | return ParseRet::OK; |
291 | } |
292 | |
293 | return ParseRet::None; |
294 | } |
295 | |
296 | // Returns the 'natural' VF for a given scalar element type, based on the |
297 | // current architecture. |
298 | // |
299 | // For SVE (currently the only scalable architecture with a defined name |
300 | // mangling), we assume a minimum vector size of 128b and return a VF based on |
301 | // the number of elements of the given type which would fit in such a vector. |
302 | static std::optional<ElementCount> getElementCountForTy(const VFISAKind ISA, |
303 | const Type *Ty) { |
304 | // Only AArch64 SVE is supported at present. |
305 | assert(ISA == VFISAKind::SVE && |
306 | "Scalable VF decoding only implemented for SVE\n" ); |
307 | |
308 | if (Ty->isIntegerTy(Bitwidth: 64) || Ty->isDoubleTy() || Ty->isPointerTy()) |
309 | return ElementCount::getScalable(MinVal: 2); |
310 | if (Ty->isIntegerTy(Bitwidth: 32) || Ty->isFloatTy()) |
311 | return ElementCount::getScalable(MinVal: 4); |
312 | if (Ty->isIntegerTy(Bitwidth: 16) || Ty->is16bitFPTy()) |
313 | return ElementCount::getScalable(MinVal: 8); |
314 | if (Ty->isIntegerTy(Bitwidth: 8)) |
315 | return ElementCount::getScalable(MinVal: 16); |
316 | |
317 | return std::nullopt; |
318 | } |
319 | |
320 | // Extract the VectorizationFactor from a given function signature, based |
321 | // on the widest scalar element types that will become vector parameters. |
322 | static std::optional<ElementCount> |
323 | getScalableECFromSignature(const FunctionType *Signature, const VFISAKind ISA, |
324 | const SmallVectorImpl<VFParameter> &Params) { |
325 | // Start with a very wide EC and drop when we find smaller ECs based on type. |
326 | ElementCount MinEC = |
327 | ElementCount::getScalable(MinVal: std::numeric_limits<unsigned int>::max()); |
328 | for (auto &Param : Params) { |
329 | // Only vector parameters are used when determining the VF; uniform or |
330 | // linear are left as scalars, so do not affect VF. |
331 | if (Param.ParamKind == VFParamKind::Vector) { |
332 | Type *PTy = Signature->getParamType(i: Param.ParamPos); |
333 | |
334 | std::optional<ElementCount> EC = getElementCountForTy(ISA, Ty: PTy); |
335 | // If we have an unknown scalar element type we can't find a reasonable |
336 | // VF. |
337 | if (!EC) |
338 | return std::nullopt; |
339 | |
340 | // Find the smallest VF, based on the widest scalar type. |
341 | if (ElementCount::isKnownLT(LHS: *EC, RHS: MinEC)) |
342 | MinEC = *EC; |
343 | } |
344 | } |
345 | |
346 | // Also check the return type if not void. |
347 | Type *RetTy = Signature->getReturnType(); |
348 | if (!RetTy->isVoidTy()) { |
349 | std::optional<ElementCount> ReturnEC = getElementCountForTy(ISA, Ty: RetTy); |
350 | // If we have an unknown scalar element type we can't find a reasonable VF. |
351 | if (!ReturnEC) |
352 | return std::nullopt; |
353 | if (ElementCount::isKnownLT(LHS: *ReturnEC, RHS: MinEC)) |
354 | MinEC = *ReturnEC; |
355 | } |
356 | |
357 | // The SVE Vector function call ABI bases the VF on the widest element types |
358 | // present, and vector arguments containing types of that width are always |
359 | // considered to be packed. Arguments with narrower elements are considered |
360 | // to be unpacked. |
361 | if (MinEC.getKnownMinValue() < std::numeric_limits<unsigned int>::max()) |
362 | return MinEC; |
363 | |
364 | return std::nullopt; |
365 | } |
366 | } // namespace |
367 | |
368 | // Format of the ABI name: |
369 | // _ZGV<isa><mask><vlen><parameters>_<scalarname>[(<redirection>)] |
370 | std::optional<VFInfo> VFABI::tryDemangleForVFABI(StringRef MangledName, |
371 | const FunctionType *FTy) { |
372 | const StringRef OriginalName = MangledName; |
373 | // Assume there is no custom name <redirection>, and therefore the |
374 | // vector name consists of |
375 | // _ZGV<isa><mask><vlen><parameters>_<scalarname>. |
376 | StringRef VectorName = MangledName; |
377 | |
378 | // Parse the fixed size part of the mangled name |
379 | if (!MangledName.consume_front(Prefix: "_ZGV" )) |
380 | return std::nullopt; |
381 | |
382 | // Extract ISA. An unknow ISA is also supported, so we accept all |
383 | // values. |
384 | VFISAKind ISA; |
385 | if (tryParseISA(MangledName, ISA) != ParseRet::OK) |
386 | return std::nullopt; |
387 | |
388 | // Extract <mask>. |
389 | bool IsMasked; |
390 | if (tryParseMask(MangledName, IsMasked) != ParseRet::OK) |
391 | return std::nullopt; |
392 | |
393 | // Parse the variable size, starting from <vlen>. |
394 | std::pair<unsigned, bool> ParsedVF; |
395 | if (tryParseVLEN(ParseString&: MangledName, ISA, ParsedVF) != ParseRet::OK) |
396 | return std::nullopt; |
397 | |
398 | // Parse the <parameters>. |
399 | ParseRet ParamFound; |
400 | SmallVector<VFParameter, 8> Parameters; |
401 | do { |
402 | const unsigned ParameterPos = Parameters.size(); |
403 | VFParamKind PKind; |
404 | int StepOrPos; |
405 | ParamFound = tryParseParameter(ParseString&: MangledName, PKind, StepOrPos); |
406 | |
407 | // Bail off if there is a parsing error in the parsing of the parameter. |
408 | if (ParamFound == ParseRet::Error) |
409 | return std::nullopt; |
410 | |
411 | if (ParamFound == ParseRet::OK) { |
412 | Align Alignment; |
413 | // Look for the alignment token "a <number>". |
414 | const ParseRet AlignFound = tryParseAlign(ParseString&: MangledName, Alignment); |
415 | // Bail off if there is a syntax error in the align token. |
416 | if (AlignFound == ParseRet::Error) |
417 | return std::nullopt; |
418 | |
419 | // Add the parameter. |
420 | Parameters.push_back(Elt: {.ParamPos: ParameterPos, .ParamKind: PKind, .LinearStepOrPos: StepOrPos, .Alignment: Alignment}); |
421 | } |
422 | } while (ParamFound == ParseRet::OK); |
423 | |
424 | // A valid MangledName must have at least one valid entry in the |
425 | // <parameters>. |
426 | if (Parameters.empty()) |
427 | return std::nullopt; |
428 | |
429 | // If the number of arguments of the scalar function does not match the |
430 | // vector variant we have just demangled then reject the mapping. |
431 | if (Parameters.size() != FTy->getNumParams()) |
432 | return std::nullopt; |
433 | |
434 | // Figure out the number of lanes in vectors for this function variant. This |
435 | // is easy for fixed length, as the vlen encoding just gives us the value |
436 | // directly. However, if the vlen mangling indicated that this function |
437 | // variant expects scalable vectors we need to work it out based on the |
438 | // demangled parameter types and the scalar function signature. |
439 | std::optional<ElementCount> EC; |
440 | if (ParsedVF.second) { |
441 | EC = getScalableECFromSignature(Signature: FTy, ISA, Params: Parameters); |
442 | if (!EC) |
443 | return std::nullopt; |
444 | } else |
445 | EC = ElementCount::getFixed(MinVal: ParsedVF.first); |
446 | |
447 | // Check for the <scalarname> and the optional <redirection>, which |
448 | // are separated from the prefix with "_" |
449 | if (!MangledName.consume_front(Prefix: "_" )) |
450 | return std::nullopt; |
451 | |
452 | // The rest of the string must be in the format: |
453 | // <scalarname>[(<redirection>)] |
454 | const StringRef ScalarName = |
455 | MangledName.take_while(F: [](char In) { return In != '('; }); |
456 | |
457 | if (ScalarName.empty()) |
458 | return std::nullopt; |
459 | |
460 | // Reduce MangledName to [(<redirection>)]. |
461 | MangledName = MangledName.ltrim(Chars: ScalarName); |
462 | // Find the optional custom name redirection. |
463 | if (MangledName.consume_front(Prefix: "(" )) { |
464 | if (!MangledName.consume_back(Suffix: ")" )) |
465 | return std::nullopt; |
466 | // Update the vector variant with the one specified by the user. |
467 | VectorName = MangledName; |
468 | // If the vector name is missing, bail out. |
469 | if (VectorName.empty()) |
470 | return std::nullopt; |
471 | } |
472 | |
473 | // LLVM internal mapping via the TargetLibraryInfo (TLI) must be |
474 | // redirected to an existing name. |
475 | if (ISA == VFISAKind::LLVM && VectorName == OriginalName) |
476 | return std::nullopt; |
477 | |
478 | // When <mask> is "M", we need to add a parameter that is used as |
479 | // global predicate for the function. |
480 | if (IsMasked) { |
481 | const unsigned Pos = Parameters.size(); |
482 | Parameters.push_back(Elt: {.ParamPos: Pos, .ParamKind: VFParamKind::GlobalPredicate}); |
483 | } |
484 | |
485 | // Asserts for parameters of type `VFParamKind::GlobalPredicate`, as |
486 | // prescribed by the Vector Function ABI specifications supported by |
487 | // this parser: |
488 | // 1. Uniqueness. |
489 | // 2. Must be the last in the parameter list. |
490 | const auto NGlobalPreds = |
491 | llvm::count_if(Range&: Parameters, P: [](const VFParameter &PK) { |
492 | return PK.ParamKind == VFParamKind::GlobalPredicate; |
493 | }); |
494 | assert(NGlobalPreds < 2 && "Cannot have more than one global predicate." ); |
495 | if (NGlobalPreds) |
496 | assert(Parameters.back().ParamKind == VFParamKind::GlobalPredicate && |
497 | "The global predicate must be the last parameter" ); |
498 | |
499 | const VFShape Shape({.VF: *EC, .Parameters: Parameters}); |
500 | return VFInfo({.Shape: Shape, .ScalarName: std::string(ScalarName), .VectorName: std::string(VectorName), .ISA: ISA}); |
501 | } |
502 | |
503 | VFParamKind VFABI::getVFParamKindFromString(const StringRef Token) { |
504 | const VFParamKind ParamKind = StringSwitch<VFParamKind>(Token) |
505 | .Case(S: "v" , Value: VFParamKind::Vector) |
506 | .Case(S: "l" , Value: VFParamKind::OMP_Linear) |
507 | .Case(S: "R" , Value: VFParamKind::OMP_LinearRef) |
508 | .Case(S: "L" , Value: VFParamKind::OMP_LinearVal) |
509 | .Case(S: "U" , Value: VFParamKind::OMP_LinearUVal) |
510 | .Case(S: "ls" , Value: VFParamKind::OMP_LinearPos) |
511 | .Case(S: "Ls" , Value: VFParamKind::OMP_LinearValPos) |
512 | .Case(S: "Rs" , Value: VFParamKind::OMP_LinearRefPos) |
513 | .Case(S: "Us" , Value: VFParamKind::OMP_LinearUValPos) |
514 | .Case(S: "u" , Value: VFParamKind::OMP_Uniform) |
515 | .Default(Value: VFParamKind::Unknown); |
516 | |
517 | if (ParamKind != VFParamKind::Unknown) |
518 | return ParamKind; |
519 | |
520 | // This function should never be invoked with an invalid input. |
521 | llvm_unreachable("This fuction should be invoken only on parameters" |
522 | " that have a textual representation in the mangled name" |
523 | " of the Vector Function ABI" ); |
524 | } |
525 | |
526 | void VFABI::getVectorVariantNames( |
527 | const CallInst &CI, SmallVectorImpl<std::string> &VariantMappings) { |
528 | const StringRef S = CI.getFnAttr(Kind: VFABI::MappingsAttrName).getValueAsString(); |
529 | if (S.empty()) |
530 | return; |
531 | |
532 | SmallVector<StringRef, 8> ListAttr; |
533 | S.split(A&: ListAttr, Separator: "," ); |
534 | |
535 | for (const auto &S : SetVector<StringRef>(ListAttr.begin(), ListAttr.end())) { |
536 | std::optional<VFInfo> Info = |
537 | VFABI::tryDemangleForVFABI(MangledName: S, FTy: CI.getFunctionType()); |
538 | if (Info && CI.getModule()->getFunction(Name: Info->VectorName)) { |
539 | LLVM_DEBUG(dbgs() << "VFABI: Adding mapping '" << S << "' for " << CI |
540 | << "\n" ); |
541 | VariantMappings.push_back(Elt: std::string(S)); |
542 | } else |
543 | LLVM_DEBUG(dbgs() << "VFABI: Invalid mapping '" << S << "'\n" ); |
544 | } |
545 | } |
546 | |
547 | FunctionType *VFABI::createFunctionType(const VFInfo &Info, |
548 | const FunctionType *ScalarFTy) { |
549 | // Create vector parameter types |
550 | SmallVector<Type *, 8> VecTypes; |
551 | ElementCount VF = Info.Shape.VF; |
552 | int ScalarParamIndex = 0; |
553 | for (auto VFParam : Info.Shape.Parameters) { |
554 | if (VFParam.ParamKind == VFParamKind::GlobalPredicate) { |
555 | VectorType *MaskTy = |
556 | VectorType::get(ElementType: Type::getInt1Ty(C&: ScalarFTy->getContext()), EC: VF); |
557 | VecTypes.push_back(Elt: MaskTy); |
558 | continue; |
559 | } |
560 | |
561 | Type *OperandTy = ScalarFTy->getParamType(i: ScalarParamIndex++); |
562 | if (VFParam.ParamKind == VFParamKind::Vector) |
563 | OperandTy = VectorType::get(ElementType: OperandTy, EC: VF); |
564 | VecTypes.push_back(Elt: OperandTy); |
565 | } |
566 | |
567 | auto *RetTy = ScalarFTy->getReturnType(); |
568 | if (!RetTy->isVoidTy()) |
569 | RetTy = VectorType::get(ElementType: RetTy, EC: VF); |
570 | return FunctionType::get(Result: RetTy, Params: VecTypes, isVarArg: false); |
571 | } |
572 | |
573 | void VFABI::setVectorVariantNames(CallInst *CI, |
574 | ArrayRef<std::string> VariantMappings) { |
575 | if (VariantMappings.empty()) |
576 | return; |
577 | |
578 | SmallString<256> Buffer; |
579 | llvm::raw_svector_ostream Out(Buffer); |
580 | for (const std::string &VariantMapping : VariantMappings) |
581 | Out << VariantMapping << "," ; |
582 | // Get rid of the trailing ','. |
583 | assert(!Buffer.str().empty() && "Must have at least one char." ); |
584 | Buffer.pop_back(); |
585 | |
586 | Module *M = CI->getModule(); |
587 | #ifndef NDEBUG |
588 | for (const std::string &VariantMapping : VariantMappings) { |
589 | LLVM_DEBUG(dbgs() << "VFABI: adding mapping '" << VariantMapping << "'\n" ); |
590 | std::optional<VFInfo> VI = |
591 | VFABI::tryDemangleForVFABI(VariantMapping, CI->getFunctionType()); |
592 | assert(VI && "Cannot add an invalid VFABI name." ); |
593 | assert(M->getNamedValue(VI->VectorName) && |
594 | "Cannot add variant to attribute: " |
595 | "vector function declaration is missing." ); |
596 | } |
597 | #endif |
598 | CI->addFnAttr( |
599 | Attr: Attribute::get(Context&: M->getContext(), Kind: MappingsAttrName, Val: Buffer.str())); |
600 | } |
601 | |
602 | bool VFShape::hasValidParameterList() const { |
603 | for (unsigned Pos = 0, NumParams = Parameters.size(); Pos < NumParams; |
604 | ++Pos) { |
605 | assert(Parameters[Pos].ParamPos == Pos && "Broken parameter list." ); |
606 | |
607 | switch (Parameters[Pos].ParamKind) { |
608 | default: // Nothing to check. |
609 | break; |
610 | case VFParamKind::OMP_Linear: |
611 | case VFParamKind::OMP_LinearRef: |
612 | case VFParamKind::OMP_LinearVal: |
613 | case VFParamKind::OMP_LinearUVal: |
614 | // Compile time linear steps must be non-zero. |
615 | if (Parameters[Pos].LinearStepOrPos == 0) |
616 | return false; |
617 | break; |
618 | case VFParamKind::OMP_LinearPos: |
619 | case VFParamKind::OMP_LinearRefPos: |
620 | case VFParamKind::OMP_LinearValPos: |
621 | case VFParamKind::OMP_LinearUValPos: |
622 | // The runtime linear step must be referring to some other |
623 | // parameters in the signature. |
624 | if (Parameters[Pos].LinearStepOrPos >= int(NumParams)) |
625 | return false; |
626 | // The linear step parameter must be marked as uniform. |
627 | if (Parameters[Parameters[Pos].LinearStepOrPos].ParamKind != |
628 | VFParamKind::OMP_Uniform) |
629 | return false; |
630 | // The linear step parameter can't point at itself. |
631 | if (Parameters[Pos].LinearStepOrPos == int(Pos)) |
632 | return false; |
633 | break; |
634 | case VFParamKind::GlobalPredicate: |
635 | // The global predicate must be the unique. Can be placed anywhere in the |
636 | // signature. |
637 | for (unsigned NextPos = Pos + 1; NextPos < NumParams; ++NextPos) |
638 | if (Parameters[NextPos].ParamKind == VFParamKind::GlobalPredicate) |
639 | return false; |
640 | break; |
641 | } |
642 | } |
643 | return true; |
644 | } |
645 | |