1 //===- VFABIDemangling.cpp - Vector Function ABI demangling utilities. ---===//
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
11 using namespace llvm;
12
13 namespace {
14 /// Utilities for the Vector Function ABI name parser.
15
16 /// Return types for the parser functions.
17 enum class ParseRet {
18 OK, // Found.
19 None, // Not found.
20 Error // Syntax error.
21 };
22
23 /// Extracts the `<isa>` information from the mangled string, and
24 /// sets the `ISA` accordingly.
tryParseISA(StringRef & MangledName,VFISAKind & ISA)25 ParseRet tryParseISA(StringRef &MangledName, VFISAKind &ISA) {
26 if (MangledName.empty())
27 return ParseRet::Error;
28
29 if (MangledName.startswith(VFABI::_LLVM_)) {
30 MangledName = MangledName.drop_front(strlen(VFABI::_LLVM_));
31 ISA = VFISAKind::LLVM;
32 } else {
33 ISA = StringSwitch<VFISAKind>(MangledName.take_front(1))
34 .Case("n", VFISAKind::AdvancedSIMD)
35 .Case("s", VFISAKind::SVE)
36 .Case("b", VFISAKind::SSE)
37 .Case("c", VFISAKind::AVX)
38 .Case("d", VFISAKind::AVX2)
39 .Case("e", VFISAKind::AVX512)
40 .Default(VFISAKind::Unknown);
41 MangledName = MangledName.drop_front(1);
42 }
43
44 return ParseRet::OK;
45 }
46
47 /// Extracts the `<mask>` information from the mangled string, and
48 /// sets `IsMasked` accordingly. The input string `MangledName` is
49 /// left unmodified.
tryParseMask(StringRef & MangledName,bool & IsMasked)50 ParseRet tryParseMask(StringRef &MangledName, bool &IsMasked) {
51 if (MangledName.consume_front("M")) {
52 IsMasked = true;
53 return ParseRet::OK;
54 }
55
56 if (MangledName.consume_front("N")) {
57 IsMasked = false;
58 return ParseRet::OK;
59 }
60
61 return ParseRet::Error;
62 }
63
64 /// Extract the `<vlen>` information from the mangled string, and
65 /// sets `VF` accordingly. A `<vlen> == "x"` token is interpreted as a scalable
66 /// vector length. On success, the `<vlen>` token is removed from
67 /// the input string `ParseString`.
68 ///
tryParseVLEN(StringRef & ParseString,unsigned & VF,bool & IsScalable)69 ParseRet tryParseVLEN(StringRef &ParseString, unsigned &VF, bool &IsScalable) {
70 if (ParseString.consume_front("x")) {
71 // Set VF to 0, to be later adjusted to a value grater than zero
72 // by looking at the signature of the vector function with
73 // `getECFromSignature`.
74 VF = 0;
75 IsScalable = true;
76 return ParseRet::OK;
77 }
78
79 if (ParseString.consumeInteger(10, VF))
80 return ParseRet::Error;
81
82 // The token `0` is invalid for VLEN.
83 if (VF == 0)
84 return ParseRet::Error;
85
86 IsScalable = false;
87 return ParseRet::OK;
88 }
89
90 /// The function looks for the following strings at the beginning of
91 /// the input string `ParseString`:
92 ///
93 /// <token> <number>
94 ///
95 /// On success, it removes the parsed parameter from `ParseString`,
96 /// sets `PKind` to the correspondent enum value, sets `Pos` to
97 /// <number>, and return success. On a syntax error, it return a
98 /// parsing error. If nothing is parsed, it returns std::nullopt.
99 ///
100 /// The function expects <token> to be one of "ls", "Rs", "Us" or
101 /// "Ls".
tryParseLinearTokenWithRuntimeStep(StringRef & ParseString,VFParamKind & PKind,int & Pos,const StringRef Token)102 ParseRet tryParseLinearTokenWithRuntimeStep(StringRef &ParseString,
103 VFParamKind &PKind, int &Pos,
104 const StringRef Token) {
105 if (ParseString.consume_front(Token)) {
106 PKind = VFABI::getVFParamKindFromString(Token);
107 if (ParseString.consumeInteger(10, Pos))
108 return ParseRet::Error;
109 return ParseRet::OK;
110 }
111
112 return ParseRet::None;
113 }
114
115 /// The function looks for the following stringt at the beginning of
116 /// the input string `ParseString`:
117 ///
118 /// <token> <number>
119 ///
120 /// <token> is one of "ls", "Rs", "Us" or "Ls".
121 ///
122 /// On success, it removes the parsed parameter from `ParseString`,
123 /// sets `PKind` to the correspondent enum value, sets `StepOrPos` to
124 /// <number>, and return success. On a syntax error, it return a
125 /// parsing error. If nothing is parsed, it returns std::nullopt.
tryParseLinearWithRuntimeStep(StringRef & ParseString,VFParamKind & PKind,int & StepOrPos)126 ParseRet tryParseLinearWithRuntimeStep(StringRef &ParseString,
127 VFParamKind &PKind, int &StepOrPos) {
128 ParseRet Ret;
129
130 // "ls" <RuntimeStepPos>
131 Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "ls");
132 if (Ret != ParseRet::None)
133 return Ret;
134
135 // "Rs" <RuntimeStepPos>
136 Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Rs");
137 if (Ret != ParseRet::None)
138 return Ret;
139
140 // "Ls" <RuntimeStepPos>
141 Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Ls");
142 if (Ret != ParseRet::None)
143 return Ret;
144
145 // "Us" <RuntimeStepPos>
146 Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Us");
147 if (Ret != ParseRet::None)
148 return Ret;
149
150 return ParseRet::None;
151 }
152
153 /// The function looks for the following strings at the beginning of
154 /// the input string `ParseString`:
155 ///
156 /// <token> {"n"} <number>
157 ///
158 /// On success, it removes the parsed parameter from `ParseString`,
159 /// sets `PKind` to the correspondent enum value, sets `LinearStep` to
160 /// <number>, and return success. On a syntax error, it return a
161 /// parsing error. If nothing is parsed, it returns std::nullopt.
162 ///
163 /// The function expects <token> to be one of "l", "R", "U" or
164 /// "L".
tryParseCompileTimeLinearToken(StringRef & ParseString,VFParamKind & PKind,int & LinearStep,const StringRef Token)165 ParseRet tryParseCompileTimeLinearToken(StringRef &ParseString,
166 VFParamKind &PKind, int &LinearStep,
167 const StringRef Token) {
168 if (ParseString.consume_front(Token)) {
169 PKind = VFABI::getVFParamKindFromString(Token);
170 const bool Negate = ParseString.consume_front("n");
171 if (ParseString.consumeInteger(10, LinearStep))
172 LinearStep = 1;
173 if (Negate)
174 LinearStep *= -1;
175 return ParseRet::OK;
176 }
177
178 return ParseRet::None;
179 }
180
181 /// The function looks for the following strings at the beginning of
182 /// the input string `ParseString`:
183 ///
184 /// ["l" | "R" | "U" | "L"] {"n"} <number>
185 ///
186 /// On success, it removes the parsed parameter from `ParseString`,
187 /// sets `PKind` to the correspondent enum value, sets `LinearStep` to
188 /// <number>, and return success. On a syntax error, it return a
189 /// parsing error. If nothing is parsed, it returns std::nullopt.
tryParseLinearWithCompileTimeStep(StringRef & ParseString,VFParamKind & PKind,int & StepOrPos)190 ParseRet tryParseLinearWithCompileTimeStep(StringRef &ParseString,
191 VFParamKind &PKind, int &StepOrPos) {
192 // "l" {"n"} <CompileTimeStep>
193 if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "l") ==
194 ParseRet::OK)
195 return ParseRet::OK;
196
197 // "R" {"n"} <CompileTimeStep>
198 if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "R") ==
199 ParseRet::OK)
200 return ParseRet::OK;
201
202 // "L" {"n"} <CompileTimeStep>
203 if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "L") ==
204 ParseRet::OK)
205 return ParseRet::OK;
206
207 // "U" {"n"} <CompileTimeStep>
208 if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "U") ==
209 ParseRet::OK)
210 return ParseRet::OK;
211
212 return ParseRet::None;
213 }
214
215 /// Looks into the <parameters> part of the mangled name in search
216 /// for valid paramaters at the beginning of the string
217 /// `ParseString`.
218 ///
219 /// On success, it removes the parsed parameter from `ParseString`,
220 /// sets `PKind` to the correspondent enum value, sets `StepOrPos`
221 /// accordingly, and return success. On a syntax error, it return a
222 /// parsing error. If nothing is parsed, it returns std::nullopt.
tryParseParameter(StringRef & ParseString,VFParamKind & PKind,int & StepOrPos)223 ParseRet tryParseParameter(StringRef &ParseString, VFParamKind &PKind,
224 int &StepOrPos) {
225 if (ParseString.consume_front("v")) {
226 PKind = VFParamKind::Vector;
227 StepOrPos = 0;
228 return ParseRet::OK;
229 }
230
231 if (ParseString.consume_front("u")) {
232 PKind = VFParamKind::OMP_Uniform;
233 StepOrPos = 0;
234 return ParseRet::OK;
235 }
236
237 const ParseRet HasLinearRuntime =
238 tryParseLinearWithRuntimeStep(ParseString, PKind, StepOrPos);
239 if (HasLinearRuntime != ParseRet::None)
240 return HasLinearRuntime;
241
242 const ParseRet HasLinearCompileTime =
243 tryParseLinearWithCompileTimeStep(ParseString, PKind, StepOrPos);
244 if (HasLinearCompileTime != ParseRet::None)
245 return HasLinearCompileTime;
246
247 return ParseRet::None;
248 }
249
250 /// Looks into the <parameters> part of the mangled name in search
251 /// of a valid 'aligned' clause. The function should be invoked
252 /// after parsing a parameter via `tryParseParameter`.
253 ///
254 /// On success, it removes the parsed parameter from `ParseString`,
255 /// sets `PKind` to the correspondent enum value, sets `StepOrPos`
256 /// accordingly, and return success. On a syntax error, it return a
257 /// parsing error. If nothing is parsed, it returns std::nullopt.
tryParseAlign(StringRef & ParseString,Align & Alignment)258 ParseRet tryParseAlign(StringRef &ParseString, Align &Alignment) {
259 uint64_t Val;
260 // "a" <number>
261 if (ParseString.consume_front("a")) {
262 if (ParseString.consumeInteger(10, Val))
263 return ParseRet::Error;
264
265 if (!isPowerOf2_64(Val))
266 return ParseRet::Error;
267
268 Alignment = Align(Val);
269
270 return ParseRet::OK;
271 }
272
273 return ParseRet::None;
274 }
275
276 #ifndef NDEBUG
277 // Verify the assumtion that all vectors in the signature of a vector
278 // function have the same number of elements.
verifyAllVectorsHaveSameWidth(FunctionType * Signature)279 bool verifyAllVectorsHaveSameWidth(FunctionType *Signature) {
280 SmallVector<VectorType *, 2> VecTys;
281 if (auto *RetTy = dyn_cast<VectorType>(Signature->getReturnType()))
282 VecTys.push_back(RetTy);
283 for (auto *Ty : Signature->params())
284 if (auto *VTy = dyn_cast<VectorType>(Ty))
285 VecTys.push_back(VTy);
286
287 if (VecTys.size() <= 1)
288 return true;
289
290 assert(VecTys.size() > 1 && "Invalid number of elements.");
291 const ElementCount EC = VecTys[0]->getElementCount();
292 return llvm::all_of(llvm::drop_begin(VecTys), [&EC](VectorType *VTy) {
293 return (EC == VTy->getElementCount());
294 });
295 }
296 #endif // NDEBUG
297
298 // Extract the VectorizationFactor from a given function signature,
299 // under the assumtion that all vectors have the same number of
300 // elements, i.e. same ElementCount.Min.
getECFromSignature(FunctionType * Signature)301 ElementCount getECFromSignature(FunctionType *Signature) {
302 assert(verifyAllVectorsHaveSameWidth(Signature) &&
303 "Invalid vector signature.");
304
305 if (auto *RetTy = dyn_cast<VectorType>(Signature->getReturnType()))
306 return RetTy->getElementCount();
307 for (auto *Ty : Signature->params())
308 if (auto *VTy = dyn_cast<VectorType>(Ty))
309 return VTy->getElementCount();
310
311 return ElementCount::getFixed(/*Min=*/1);
312 }
313 } // namespace
314
315 // Format of the ABI name:
316 // _ZGV<isa><mask><vlen><parameters>_<scalarname>[(<redirection>)]
tryDemangleForVFABI(StringRef MangledName,const Module & M)317 std::optional<VFInfo> VFABI::tryDemangleForVFABI(StringRef MangledName,
318 const Module &M) {
319 const StringRef OriginalName = MangledName;
320 // Assume there is no custom name <redirection>, and therefore the
321 // vector name consists of
322 // _ZGV<isa><mask><vlen><parameters>_<scalarname>.
323 StringRef VectorName = MangledName;
324
325 // Parse the fixed size part of the manled name
326 if (!MangledName.consume_front("_ZGV"))
327 return std::nullopt;
328
329 // Extract ISA. An unknow ISA is also supported, so we accept all
330 // values.
331 VFISAKind ISA;
332 if (tryParseISA(MangledName, ISA) != ParseRet::OK)
333 return std::nullopt;
334
335 // Extract <mask>.
336 bool IsMasked;
337 if (tryParseMask(MangledName, IsMasked) != ParseRet::OK)
338 return std::nullopt;
339
340 // Parse the variable size, starting from <vlen>.
341 unsigned VF;
342 bool IsScalable;
343 if (tryParseVLEN(MangledName, VF, IsScalable) != ParseRet::OK)
344 return std::nullopt;
345
346 // Parse the <parameters>.
347 ParseRet ParamFound;
348 SmallVector<VFParameter, 8> Parameters;
349 do {
350 const unsigned ParameterPos = Parameters.size();
351 VFParamKind PKind;
352 int StepOrPos;
353 ParamFound = tryParseParameter(MangledName, PKind, StepOrPos);
354
355 // Bail off if there is a parsing error in the parsing of the parameter.
356 if (ParamFound == ParseRet::Error)
357 return std::nullopt;
358
359 if (ParamFound == ParseRet::OK) {
360 Align Alignment;
361 // Look for the alignment token "a <number>".
362 const ParseRet AlignFound = tryParseAlign(MangledName, Alignment);
363 // Bail off if there is a syntax error in the align token.
364 if (AlignFound == ParseRet::Error)
365 return std::nullopt;
366
367 // Add the parameter.
368 Parameters.push_back({ParameterPos, PKind, StepOrPos, Alignment});
369 }
370 } while (ParamFound == ParseRet::OK);
371
372 // A valid MangledName must have at least one valid entry in the
373 // <parameters>.
374 if (Parameters.empty())
375 return std::nullopt;
376
377 // Check for the <scalarname> and the optional <redirection>, which
378 // are separated from the prefix with "_"
379 if (!MangledName.consume_front("_"))
380 return std::nullopt;
381
382 // The rest of the string must be in the format:
383 // <scalarname>[(<redirection>)]
384 const StringRef ScalarName =
385 MangledName.take_while([](char In) { return In != '('; });
386
387 if (ScalarName.empty())
388 return std::nullopt;
389
390 // Reduce MangledName to [(<redirection>)].
391 MangledName = MangledName.ltrim(ScalarName);
392 // Find the optional custom name redirection.
393 if (MangledName.consume_front("(")) {
394 if (!MangledName.consume_back(")"))
395 return std::nullopt;
396 // Update the vector variant with the one specified by the user.
397 VectorName = MangledName;
398 // If the vector name is missing, bail out.
399 if (VectorName.empty())
400 return std::nullopt;
401 }
402
403 // LLVM internal mapping via the TargetLibraryInfo (TLI) must be
404 // redirected to an existing name.
405 if (ISA == VFISAKind::LLVM && VectorName == OriginalName)
406 return std::nullopt;
407
408 // When <mask> is "M", we need to add a parameter that is used as
409 // global predicate for the function.
410 if (IsMasked) {
411 const unsigned Pos = Parameters.size();
412 Parameters.push_back({Pos, VFParamKind::GlobalPredicate});
413 }
414
415 // Asserts for parameters of type `VFParamKind::GlobalPredicate`, as
416 // prescribed by the Vector Function ABI specifications supported by
417 // this parser:
418 // 1. Uniqueness.
419 // 2. Must be the last in the parameter list.
420 const auto NGlobalPreds =
421 llvm::count_if(Parameters, [](const VFParameter &PK) {
422 return PK.ParamKind == VFParamKind::GlobalPredicate;
423 });
424 assert(NGlobalPreds < 2 && "Cannot have more than one global predicate.");
425 if (NGlobalPreds)
426 assert(Parameters.back().ParamKind == VFParamKind::GlobalPredicate &&
427 "The global predicate must be the last parameter");
428
429 // Adjust the VF for scalable signatures. The EC.Min is not encoded
430 // in the name of the function, but it is encoded in the IR
431 // signature of the function. We need to extract this information
432 // because it is needed by the loop vectorizer, which reasons in
433 // terms of VectorizationFactor or ElementCount. In particular, we
434 // need to make sure that the VF field of the VFShape class is never
435 // set to 0.
436 if (IsScalable) {
437 const Function *F = M.getFunction(VectorName);
438 // The declaration of the function must be present in the module
439 // to be able to retrieve its signature.
440 if (!F)
441 return std::nullopt;
442 const ElementCount EC = getECFromSignature(F->getFunctionType());
443 VF = EC.getKnownMinValue();
444 }
445
446 // 1. We don't accept a zero lanes vectorization factor.
447 // 2. We don't accept the demangling if the vector function is not
448 // present in the module.
449 if (VF == 0)
450 return std::nullopt;
451 if (!M.getFunction(VectorName))
452 return std::nullopt;
453
454 const VFShape Shape({ElementCount::get(VF, IsScalable), Parameters});
455 return VFInfo({Shape, std::string(ScalarName), std::string(VectorName), ISA});
456 }
457
getVFParamKindFromString(const StringRef Token)458 VFParamKind VFABI::getVFParamKindFromString(const StringRef Token) {
459 const VFParamKind ParamKind = StringSwitch<VFParamKind>(Token)
460 .Case("v", VFParamKind::Vector)
461 .Case("l", VFParamKind::OMP_Linear)
462 .Case("R", VFParamKind::OMP_LinearRef)
463 .Case("L", VFParamKind::OMP_LinearVal)
464 .Case("U", VFParamKind::OMP_LinearUVal)
465 .Case("ls", VFParamKind::OMP_LinearPos)
466 .Case("Ls", VFParamKind::OMP_LinearValPos)
467 .Case("Rs", VFParamKind::OMP_LinearRefPos)
468 .Case("Us", VFParamKind::OMP_LinearUValPos)
469 .Case("u", VFParamKind::OMP_Uniform)
470 .Default(VFParamKind::Unknown);
471
472 if (ParamKind != VFParamKind::Unknown)
473 return ParamKind;
474
475 // This function should never be invoked with an invalid input.
476 llvm_unreachable("This fuction should be invoken only on parameters"
477 " that have a textual representation in the mangled name"
478 " of the Vector Function ABI");
479 }
480