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