1353940Sdim//===- VFABIDemangling.cpp - Vector Function ABI demangling utilities. ---===//
2353940Sdim//
3353940Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353940Sdim// See https://llvm.org/LICENSE.txt for license information.
5353940Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353940Sdim//
7353940Sdim//===----------------------------------------------------------------------===//
8353940Sdim
9357095Sdim#include "llvm/ADT/SmallSet.h"
10357095Sdim#include "llvm/ADT/SmallString.h"
11353940Sdim#include "llvm/Analysis/VectorUtils.h"
12353940Sdim
13353940Sdimusing namespace llvm;
14353940Sdim
15353940Sdimnamespace {
16353940Sdim/// Utilities for the Vector Function ABI name parser.
17353940Sdim
18353940Sdim/// Return types for the parser functions.
19353940Sdimenum class ParseRet {
20353940Sdim  OK,   // Found.
21353940Sdim  None, // Not found.
22353940Sdim  Error // Syntax error.
23353940Sdim};
24353940Sdim
25353940Sdim/// Extracts the `<isa>` information from the mangled string, and
26353940Sdim/// sets the `ISA` accordingly.
27353940SdimParseRet tryParseISA(StringRef &MangledName, VFISAKind &ISA) {
28353940Sdim  if (MangledName.empty())
29353940Sdim    return ParseRet::Error;
30353940Sdim
31357095Sdim  if (MangledName.startswith(VFABI::_LLVM_)) {
32357095Sdim    MangledName = MangledName.drop_front(strlen(VFABI::_LLVM_));
33357095Sdim    ISA = VFISAKind::LLVM;
34357095Sdim  } else {
35357095Sdim    ISA = StringSwitch<VFISAKind>(MangledName.take_front(1))
36357095Sdim              .Case("n", VFISAKind::AdvancedSIMD)
37357095Sdim              .Case("s", VFISAKind::SVE)
38357095Sdim              .Case("b", VFISAKind::SSE)
39357095Sdim              .Case("c", VFISAKind::AVX)
40357095Sdim              .Case("d", VFISAKind::AVX2)
41357095Sdim              .Case("e", VFISAKind::AVX512)
42357095Sdim              .Default(VFISAKind::Unknown);
43357095Sdim    MangledName = MangledName.drop_front(1);
44357095Sdim  }
45353940Sdim
46353940Sdim  return ParseRet::OK;
47353940Sdim}
48353940Sdim
49353940Sdim/// Extracts the `<mask>` information from the mangled string, and
50353940Sdim/// sets `IsMasked` accordingly. The input string `MangledName` is
51353940Sdim/// left unmodified.
52353940SdimParseRet tryParseMask(StringRef &MangledName, bool &IsMasked) {
53353940Sdim  if (MangledName.consume_front("M")) {
54353940Sdim    IsMasked = true;
55353940Sdim    return ParseRet::OK;
56353940Sdim  }
57353940Sdim
58353940Sdim  if (MangledName.consume_front("N")) {
59353940Sdim    IsMasked = false;
60353940Sdim    return ParseRet::OK;
61353940Sdim  }
62353940Sdim
63353940Sdim  return ParseRet::Error;
64353940Sdim}
65353940Sdim
66353940Sdim/// Extract the `<vlen>` information from the mangled string, and
67353940Sdim/// sets `VF` accordingly. A `<vlen> == "x"` token is interpreted as a scalable
68353940Sdim/// vector length. On success, the `<vlen>` token is removed from
69353940Sdim/// the input string `ParseString`.
70353940Sdim///
71353940SdimParseRet tryParseVLEN(StringRef &ParseString, unsigned &VF, bool &IsScalable) {
72353940Sdim  if (ParseString.consume_front("x")) {
73353940Sdim    VF = 0;
74353940Sdim    IsScalable = true;
75353940Sdim    return ParseRet::OK;
76353940Sdim  }
77353940Sdim
78353940Sdim  if (ParseString.consumeInteger(10, VF))
79353940Sdim    return ParseRet::Error;
80353940Sdim
81353940Sdim  IsScalable = false;
82353940Sdim  return ParseRet::OK;
83353940Sdim}
84353940Sdim
85353940Sdim/// The function looks for the following strings at the beginning of
86353940Sdim/// the input string `ParseString`:
87353940Sdim///
88353940Sdim///  <token> <number>
89353940Sdim///
90353940Sdim/// On success, it removes the parsed parameter from `ParseString`,
91353940Sdim/// sets `PKind` to the correspondent enum value, sets `Pos` to
92353940Sdim/// <number>, and return success.  On a syntax error, it return a
93353940Sdim/// parsing error. If nothing is parsed, it returns None.
94353940Sdim///
95353940Sdim/// The function expects <token> to be one of "ls", "Rs", "Us" or
96353940Sdim/// "Ls".
97353940SdimParseRet tryParseLinearTokenWithRuntimeStep(StringRef &ParseString,
98353940Sdim                                            VFParamKind &PKind, int &Pos,
99353940Sdim                                            const StringRef Token) {
100353940Sdim  if (ParseString.consume_front(Token)) {
101353940Sdim    PKind = VFABI::getVFParamKindFromString(Token);
102353940Sdim    if (ParseString.consumeInteger(10, Pos))
103353940Sdim      return ParseRet::Error;
104353940Sdim    return ParseRet::OK;
105353940Sdim  }
106353940Sdim
107353940Sdim  return ParseRet::None;
108353940Sdim}
109353940Sdim
110353940Sdim/// The function looks for the following stringt at the beginning of
111353940Sdim/// the input string `ParseString`:
112353940Sdim///
113353940Sdim///  <token> <number>
114353940Sdim///
115353940Sdim/// <token> is one of "ls", "Rs", "Us" or "Ls".
116353940Sdim///
117353940Sdim/// On success, it removes the parsed parameter from `ParseString`,
118353940Sdim/// sets `PKind` to the correspondent enum value, sets `StepOrPos` to
119353940Sdim/// <number>, and return success.  On a syntax error, it return a
120353940Sdim/// parsing error. If nothing is parsed, it returns None.
121353940SdimParseRet tryParseLinearWithRuntimeStep(StringRef &ParseString,
122353940Sdim                                       VFParamKind &PKind, int &StepOrPos) {
123353940Sdim  ParseRet Ret;
124353940Sdim
125353940Sdim  // "ls" <RuntimeStepPos>
126353940Sdim  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "ls");
127353940Sdim  if (Ret != ParseRet::None)
128353940Sdim    return Ret;
129353940Sdim
130353940Sdim  // "Rs" <RuntimeStepPos>
131353940Sdim  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Rs");
132353940Sdim  if (Ret != ParseRet::None)
133353940Sdim    return Ret;
134353940Sdim
135353940Sdim  // "Ls" <RuntimeStepPos>
136353940Sdim  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Ls");
137353940Sdim  if (Ret != ParseRet::None)
138353940Sdim    return Ret;
139353940Sdim
140353940Sdim  // "Us" <RuntimeStepPos>
141353940Sdim  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Us");
142353940Sdim  if (Ret != ParseRet::None)
143353940Sdim    return Ret;
144353940Sdim
145353940Sdim  return ParseRet::None;
146353940Sdim}
147353940Sdim
148353940Sdim/// The function looks for the following strings at the beginning of
149353940Sdim/// the input string `ParseString`:
150353940Sdim///
151353940Sdim///  <token> {"n"} <number>
152353940Sdim///
153353940Sdim/// On success, it removes the parsed parameter from `ParseString`,
154353940Sdim/// sets `PKind` to the correspondent enum value, sets `LinearStep` to
155353940Sdim/// <number>, and return success.  On a syntax error, it return a
156353940Sdim/// parsing error. If nothing is parsed, it returns None.
157353940Sdim///
158353940Sdim/// The function expects <token> to be one of "l", "R", "U" or
159353940Sdim/// "L".
160353940SdimParseRet tryParseCompileTimeLinearToken(StringRef &ParseString,
161353940Sdim                                        VFParamKind &PKind, int &LinearStep,
162353940Sdim                                        const StringRef Token) {
163353940Sdim  if (ParseString.consume_front(Token)) {
164353940Sdim    PKind = VFABI::getVFParamKindFromString(Token);
165353940Sdim    const bool Negate = ParseString.consume_front("n");
166353940Sdim    if (ParseString.consumeInteger(10, LinearStep))
167353940Sdim      LinearStep = 1;
168353940Sdim    if (Negate)
169353940Sdim      LinearStep *= -1;
170353940Sdim    return ParseRet::OK;
171353940Sdim  }
172353940Sdim
173353940Sdim  return ParseRet::None;
174353940Sdim}
175353940Sdim
176353940Sdim/// The function looks for the following strings at the beginning of
177353940Sdim/// the input string `ParseString`:
178353940Sdim///
179353940Sdim/// ["l" | "R" | "U" | "L"] {"n"} <number>
180353940Sdim///
181353940Sdim/// On success, it removes the parsed parameter from `ParseString`,
182353940Sdim/// sets `PKind` to the correspondent enum value, sets `LinearStep` to
183353940Sdim/// <number>, and return success.  On a syntax error, it return a
184353940Sdim/// parsing error. If nothing is parsed, it returns None.
185353940SdimParseRet tryParseLinearWithCompileTimeStep(StringRef &ParseString,
186353940Sdim                                           VFParamKind &PKind, int &StepOrPos) {
187353940Sdim  // "l" {"n"} <CompileTimeStep>
188353940Sdim  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "l") ==
189353940Sdim      ParseRet::OK)
190353940Sdim    return ParseRet::OK;
191353940Sdim
192353940Sdim  // "R" {"n"} <CompileTimeStep>
193353940Sdim  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "R") ==
194353940Sdim      ParseRet::OK)
195353940Sdim    return ParseRet::OK;
196353940Sdim
197353940Sdim  // "L" {"n"} <CompileTimeStep>
198353940Sdim  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "L") ==
199353940Sdim      ParseRet::OK)
200353940Sdim    return ParseRet::OK;
201353940Sdim
202353940Sdim  // "U" {"n"} <CompileTimeStep>
203353940Sdim  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "U") ==
204353940Sdim      ParseRet::OK)
205353940Sdim    return ParseRet::OK;
206353940Sdim
207353940Sdim  return ParseRet::None;
208353940Sdim}
209353940Sdim
210353940Sdim/// The function looks for the following strings at the beginning of
211353940Sdim/// the input string `ParseString`:
212353940Sdim///
213353940Sdim/// "u" <number>
214353940Sdim///
215353940Sdim/// On success, it removes the parsed parameter from `ParseString`,
216353940Sdim/// sets `PKind` to the correspondent enum value, sets `Pos` to
217353940Sdim/// <number>, and return success.  On a syntax error, it return a
218353940Sdim/// parsing error. If nothing is parsed, it returns None.
219353940SdimParseRet tryParseUniform(StringRef &ParseString, VFParamKind &PKind, int &Pos) {
220353940Sdim  // "u" <Pos>
221353940Sdim  const char *UniformToken = "u";
222353940Sdim  if (ParseString.consume_front(UniformToken)) {
223353940Sdim    PKind = VFABI::getVFParamKindFromString(UniformToken);
224353940Sdim    if (ParseString.consumeInteger(10, Pos))
225353940Sdim      return ParseRet::Error;
226353940Sdim
227353940Sdim    return ParseRet::OK;
228353940Sdim  }
229353940Sdim  return ParseRet::None;
230353940Sdim}
231353940Sdim
232353940Sdim/// Looks into the <parameters> part of the mangled name in search
233353940Sdim/// for valid paramaters at the beginning of the string
234353940Sdim/// `ParseString`.
235353940Sdim///
236353940Sdim/// On success, it removes the parsed parameter from `ParseString`,
237353940Sdim/// sets `PKind` to the correspondent enum value, sets `StepOrPos`
238353940Sdim/// accordingly, and return success.  On a syntax error, it return a
239353940Sdim/// parsing error. If nothing is parsed, it returns None.
240353940SdimParseRet tryParseParameter(StringRef &ParseString, VFParamKind &PKind,
241353940Sdim                           int &StepOrPos) {
242353940Sdim  if (ParseString.consume_front("v")) {
243353940Sdim    PKind = VFParamKind::Vector;
244353940Sdim    StepOrPos = 0;
245353940Sdim    return ParseRet::OK;
246353940Sdim  }
247353940Sdim
248353940Sdim  const ParseRet HasLinearRuntime =
249353940Sdim      tryParseLinearWithRuntimeStep(ParseString, PKind, StepOrPos);
250353940Sdim  if (HasLinearRuntime != ParseRet::None)
251353940Sdim    return HasLinearRuntime;
252353940Sdim
253353940Sdim  const ParseRet HasLinearCompileTime =
254353940Sdim      tryParseLinearWithCompileTimeStep(ParseString, PKind, StepOrPos);
255353940Sdim  if (HasLinearCompileTime != ParseRet::None)
256353940Sdim    return HasLinearCompileTime;
257353940Sdim
258353940Sdim  const ParseRet HasUniform = tryParseUniform(ParseString, PKind, StepOrPos);
259353940Sdim  if (HasUniform != ParseRet::None)
260353940Sdim    return HasUniform;
261353940Sdim
262353940Sdim  return ParseRet::None;
263353940Sdim}
264353940Sdim
265353940Sdim/// Looks into the <parameters> part of the mangled name in search
266353940Sdim/// of a valid 'aligned' clause. The function should be invoked
267353940Sdim/// after parsing a parameter via `tryParseParameter`.
268353940Sdim///
269353940Sdim/// On success, it removes the parsed parameter from `ParseString`,
270353940Sdim/// sets `PKind` to the correspondent enum value, sets `StepOrPos`
271353940Sdim/// accordingly, and return success.  On a syntax error, it return a
272353940Sdim/// parsing error. If nothing is parsed, it returns None.
273353940SdimParseRet tryParseAlign(StringRef &ParseString, Align &Alignment) {
274353940Sdim  uint64_t Val;
275353940Sdim  //    "a" <number>
276353940Sdim  if (ParseString.consume_front("a")) {
277353940Sdim    if (ParseString.consumeInteger(10, Val))
278353940Sdim      return ParseRet::Error;
279353940Sdim
280353940Sdim    if (!isPowerOf2_64(Val))
281353940Sdim      return ParseRet::Error;
282353940Sdim
283353940Sdim    Alignment = Align(Val);
284353940Sdim
285353940Sdim    return ParseRet::OK;
286353940Sdim  }
287353940Sdim
288353940Sdim  return ParseRet::None;
289353940Sdim}
290353940Sdim} // namespace
291353940Sdim
292353940Sdim// Format of the ABI name:
293353940Sdim// _ZGV<isa><mask><vlen><parameters>_<scalarname>[(<redirection>)]
294353940SdimOptional<VFInfo> VFABI::tryDemangleForVFABI(StringRef MangledName) {
295357095Sdim  const StringRef OriginalName = MangledName;
296353940Sdim  // Assume there is no custom name <redirection>, and therefore the
297353940Sdim  // vector name consists of
298353940Sdim  // _ZGV<isa><mask><vlen><parameters>_<scalarname>.
299353940Sdim  StringRef VectorName = MangledName;
300353940Sdim
301353940Sdim  // Parse the fixed size part of the manled name
302353940Sdim  if (!MangledName.consume_front("_ZGV"))
303353940Sdim    return None;
304353940Sdim
305353940Sdim  // Extract ISA. An unknow ISA is also supported, so we accept all
306353940Sdim  // values.
307353940Sdim  VFISAKind ISA;
308353940Sdim  if (tryParseISA(MangledName, ISA) != ParseRet::OK)
309353940Sdim    return None;
310353940Sdim
311353940Sdim  // Extract <mask>.
312353940Sdim  bool IsMasked;
313353940Sdim  if (tryParseMask(MangledName, IsMasked) != ParseRet::OK)
314353940Sdim    return None;
315353940Sdim
316353940Sdim  // Parse the variable size, starting from <vlen>.
317353940Sdim  unsigned VF;
318353940Sdim  bool IsScalable;
319353940Sdim  if (tryParseVLEN(MangledName, VF, IsScalable) != ParseRet::OK)
320353940Sdim    return None;
321353940Sdim
322353940Sdim  // Parse the <parameters>.
323353940Sdim  ParseRet ParamFound;
324353940Sdim  SmallVector<VFParameter, 8> Parameters;
325353940Sdim  do {
326353940Sdim    const unsigned ParameterPos = Parameters.size();
327353940Sdim    VFParamKind PKind;
328353940Sdim    int StepOrPos;
329353940Sdim    ParamFound = tryParseParameter(MangledName, PKind, StepOrPos);
330353940Sdim
331353940Sdim    // Bail off if there is a parsing error in the parsing of the parameter.
332353940Sdim    if (ParamFound == ParseRet::Error)
333353940Sdim      return None;
334353940Sdim
335353940Sdim    if (ParamFound == ParseRet::OK) {
336353940Sdim      Align Alignment;
337353940Sdim      // Look for the alignment token "a <number>".
338353940Sdim      const ParseRet AlignFound = tryParseAlign(MangledName, Alignment);
339353940Sdim      // Bail off if there is a syntax error in the align token.
340353940Sdim      if (AlignFound == ParseRet::Error)
341353940Sdim        return None;
342353940Sdim
343353940Sdim      // Add the parameter.
344353940Sdim      Parameters.push_back({ParameterPos, PKind, StepOrPos, Alignment});
345353940Sdim    }
346353940Sdim  } while (ParamFound == ParseRet::OK);
347353940Sdim
348357095Sdim  // A valid MangledName must have at least one valid entry in the
349353940Sdim  // <parameters>.
350353940Sdim  if (Parameters.empty())
351353940Sdim    return None;
352353940Sdim
353353940Sdim  // Check for the <scalarname> and the optional <redirection>, which
354353940Sdim  // are separated from the prefix with "_"
355353940Sdim  if (!MangledName.consume_front("_"))
356353940Sdim    return None;
357353940Sdim
358353940Sdim  // The rest of the string must be in the format:
359353940Sdim  // <scalarname>[(<redirection>)]
360353940Sdim  const StringRef ScalarName =
361353940Sdim      MangledName.take_while([](char In) { return In != '('; });
362353940Sdim
363353940Sdim  if (ScalarName.empty())
364353940Sdim    return None;
365353940Sdim
366353940Sdim  // Reduce MangledName to [(<redirection>)].
367353940Sdim  MangledName = MangledName.ltrim(ScalarName);
368353940Sdim  // Find the optional custom name redirection.
369353940Sdim  if (MangledName.consume_front("(")) {
370353940Sdim    if (!MangledName.consume_back(")"))
371353940Sdim      return None;
372353940Sdim    // Update the vector variant with the one specified by the user.
373353940Sdim    VectorName = MangledName;
374353940Sdim    // If the vector name is missing, bail out.
375353940Sdim    if (VectorName.empty())
376353940Sdim      return None;
377353940Sdim  }
378353940Sdim
379357095Sdim  // LLVM internal mapping via the TargetLibraryInfo (TLI) must be
380357095Sdim  // redirected to an existing name.
381357095Sdim  if (ISA == VFISAKind::LLVM && VectorName == OriginalName)
382357095Sdim    return None;
383357095Sdim
384353940Sdim  // When <mask> is "M", we need to add a parameter that is used as
385353940Sdim  // global predicate for the function.
386353940Sdim  if (IsMasked) {
387353940Sdim    const unsigned Pos = Parameters.size();
388353940Sdim    Parameters.push_back({Pos, VFParamKind::GlobalPredicate});
389353940Sdim  }
390353940Sdim
391353940Sdim  // Asserts for parameters of type `VFParamKind::GlobalPredicate`, as
392353940Sdim  // prescribed by the Vector Function ABI specifications supported by
393353940Sdim  // this parser:
394353940Sdim  // 1. Uniqueness.
395353940Sdim  // 2. Must be the last in the parameter list.
396353940Sdim  const auto NGlobalPreds = std::count_if(
397353940Sdim      Parameters.begin(), Parameters.end(), [](const VFParameter PK) {
398353940Sdim        return PK.ParamKind == VFParamKind::GlobalPredicate;
399353940Sdim      });
400353940Sdim  assert(NGlobalPreds < 2 && "Cannot have more than one global predicate.");
401353940Sdim  if (NGlobalPreds)
402353940Sdim    assert(Parameters.back().ParamKind == VFParamKind::GlobalPredicate &&
403353940Sdim           "The global predicate must be the last parameter");
404353940Sdim
405357095Sdim  const VFShape Shape({VF, IsScalable, Parameters});
406357095Sdim  return VFInfo({Shape, ScalarName, VectorName, ISA});
407353940Sdim}
408353940Sdim
409353940SdimVFParamKind VFABI::getVFParamKindFromString(const StringRef Token) {
410353940Sdim  const VFParamKind ParamKind = StringSwitch<VFParamKind>(Token)
411353940Sdim                                    .Case("v", VFParamKind::Vector)
412353940Sdim                                    .Case("l", VFParamKind::OMP_Linear)
413353940Sdim                                    .Case("R", VFParamKind::OMP_LinearRef)
414353940Sdim                                    .Case("L", VFParamKind::OMP_LinearVal)
415353940Sdim                                    .Case("U", VFParamKind::OMP_LinearUVal)
416353940Sdim                                    .Case("ls", VFParamKind::OMP_LinearPos)
417353940Sdim                                    .Case("Ls", VFParamKind::OMP_LinearValPos)
418353940Sdim                                    .Case("Rs", VFParamKind::OMP_LinearRefPos)
419353940Sdim                                    .Case("Us", VFParamKind::OMP_LinearUValPos)
420353940Sdim                                    .Case("u", VFParamKind::OMP_Uniform)
421353940Sdim                                    .Default(VFParamKind::Unknown);
422353940Sdim
423353940Sdim  if (ParamKind != VFParamKind::Unknown)
424353940Sdim    return ParamKind;
425353940Sdim
426353940Sdim  // This function should never be invoked with an invalid input.
427353940Sdim  llvm_unreachable("This fuction should be invoken only on parameters"
428353940Sdim                   " that have a textual representation in the mangled name"
429353940Sdim                   " of the Vector Function ABI");
430353940Sdim}
431