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