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
13using namespace llvm;
14
15namespace {
16/// Utilities for the Vector Function ABI name parser.
17
18/// Return types for the parser functions.
19enum 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.
27ParseRet 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.
52ParseRet 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///
71ParseRet 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".
97ParseRet 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.
121ParseRet 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".
160ParseRet 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.
185ParseRet 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.
219ParseRet 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.
240ParseRet 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.
273ParseRet 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>)]
294Optional<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
409VFParamKind 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