Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/llvm/lib/Analysis/VFABIDemangling.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "llvm/Support/Debug.h"
11
#include "llvm/Support/raw_ostream.h"
12
#include <limits>
13
14
using namespace llvm;
15
16
#define DEBUG_TYPE "vfabi-demangling"
17
18
namespace {
19
/// Utilities for the Vector Function ABI name parser.
20
21
/// Return types for the parser functions.
22
enum class ParseRet {
23
  OK,   // Found.
24
  None, // Not found.
25
  Error // Syntax error.
26
};
27
28
/// Extracts the `<isa>` information from the mangled string, and
29
/// sets the `ISA` accordingly. If successful, the <isa> token is removed
30
/// from the input string `MangledName`.
31
2.23k
static ParseRet tryParseISA(StringRef &MangledName, VFISAKind &ISA) {
32
2.23k
  if (MangledName.empty())
33
1
    return ParseRet::Error;
34
35
2.23k
  if (MangledName.starts_with(VFABI::_LLVM_)) {
36
435
    MangledName = MangledName.drop_front(strlen(VFABI::_LLVM_));
37
435
    ISA = VFISAKind::LLVM;
38
1.79k
  } else {
39
1.79k
    ISA = StringSwitch<VFISAKind>(MangledName.take_front(1))
40
1.79k
              .Case("n", VFISAKind::AdvancedSIMD)
41
1.79k
              .Case("s", VFISAKind::SVE)
42
1.79k
              .Case("b", VFISAKind::SSE)
43
1.79k
              .Case("c", VFISAKind::AVX)
44
1.79k
              .Case("d", VFISAKind::AVX2)
45
1.79k
              .Case("e", VFISAKind::AVX512)
46
1.79k
              .Default(VFISAKind::Unknown);
47
1.79k
    MangledName = MangledName.drop_front(1);
48
1.79k
  }
49
50
2.23k
  return ParseRet::OK;
51
2.23k
}
52
53
/// Extracts the `<mask>` information from the mangled string, and
54
/// sets `IsMasked` accordingly. If successful, the <mask> token is removed
55
/// from the input string `MangledName`.
56
2.23k
static ParseRet tryParseMask(StringRef &MangledName, bool &IsMasked) {
57
2.23k
  if (MangledName.consume_front("M")) {
58
834
    IsMasked = true;
59
834
    return ParseRet::OK;
60
834
  }
61
62
1.40k
  if (MangledName.consume_front("N")) {
63
1.33k
    IsMasked = false;
64
1.33k
    return ParseRet::OK;
65
1.33k
  }
66
67
66
  return ParseRet::Error;
68
1.40k
}
69
70
/// Extract the `<vlen>` information from the mangled string, and
71
/// sets `ParsedVF` accordingly. A `<vlen> == "x"` token is interpreted as a
72
/// scalable vector length and the boolean is set to true, otherwise a nonzero
73
/// unsigned integer will be directly used as a VF. On success, the `<vlen>`
74
/// token is removed from the input string `ParseString`.
75
static ParseRet tryParseVLEN(StringRef &ParseString, VFISAKind ISA,
76
2.16k
                             std::pair<unsigned, bool> &ParsedVF) {
77
2.16k
  if (ParseString.consume_front("x")) {
78
    // SVE is the only scalable ISA currently supported.
79
178
    if (ISA != VFISAKind::SVE) {
80
13
      LLVM_DEBUG(dbgs() << "Vector function variant declared with scalable VF "
81
13
                        << "but ISA is not SVE\n");
82
13
      return ParseRet::Error;
83
13
    }
84
    // We can't determine the VF of a scalable vector by looking at the vlen
85
    // string (just 'x'), so say we successfully parsed it but return a 'true'
86
    // for the scalable field with an invalid VF field so that we know to look
87
    // up the actual VF based on element types from the parameters or return.
88
165
    ParsedVF = {0, true};
89
165
    return ParseRet::OK;
90
178
  }
91
92
1.99k
  unsigned VF = 0;
93
1.99k
  if (ParseString.consumeInteger(10, VF))
94
135
    return ParseRet::Error;
95
96
  // The token `0` is invalid for VLEN.
97
1.85k
  if (VF == 0)
98
4
    return ParseRet::Error;
99
100
1.85k
  ParsedVF = {VF, false};
101
1.85k
  return ParseRet::OK;
102
1.85k
}
103
104
/// The function looks for the following strings at the beginning of
105
/// the input string `ParseString`:
106
///
107
///  <token> <number>
108
///
109
/// On success, it removes the parsed parameter from `ParseString`,
110
/// sets `PKind` to the correspondent enum value, sets `Pos` to
111
/// <number>, and return success.  On a syntax error, it return a
112
/// parsing error. If nothing is parsed, it returns std::nullopt.
113
///
114
/// The function expects <token> to be one of "ls", "Rs", "Us" or
115
/// "Ls".
116
static ParseRet tryParseLinearTokenWithRuntimeStep(StringRef &ParseString,
117
                                                   VFParamKind &PKind, int &Pos,
118
21.7M
                                                   const StringRef Token) {
119
21.7M
  if (ParseString.consume_front(Token)) {
120
3.21k
    PKind = VFABI::getVFParamKindFromString(Token);
121
3.21k
    if (ParseString.consumeInteger(10, Pos))
122
277
      return ParseRet::Error;
123
2.93k
    return ParseRet::OK;
124
3.21k
  }
125
126
21.7M
  return ParseRet::None;
127
21.7M
}
128
129
/// The function looks for the following string at the beginning of
130
/// the input string `ParseString`:
131
///
132
///  <token> <number>
133
///
134
/// <token> is one of "ls", "Rs", "Us" or "Ls".
135
///
136
/// On success, it removes the parsed parameter from `ParseString`,
137
/// sets `PKind` to the correspondent enum value, sets `StepOrPos` to
138
/// <number>, and return success.  On a syntax error, it return a
139
/// parsing error. If nothing is parsed, it returns std::nullopt.
140
static ParseRet tryParseLinearWithRuntimeStep(StringRef &ParseString,
141
                                              VFParamKind &PKind,
142
5.45M
                                              int &StepOrPos) {
143
5.45M
  ParseRet Ret;
144
145
  // "ls" <RuntimeStepPos>
146
5.45M
  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "ls");
147
5.45M
  if (Ret != ParseRet::None)
148
398
    return Ret;
149
150
  // "Rs" <RuntimeStepPos>
151
5.45M
  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Rs");
152
5.45M
  if (Ret != ParseRet::None)
153
403
    return Ret;
154
155
  // "Ls" <RuntimeStepPos>
156
5.44M
  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Ls");
157
5.44M
  if (Ret != ParseRet::None)
158
2.00k
    return Ret;
159
160
  // "Us" <RuntimeStepPos>
161
5.44M
  Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Us");
162
5.44M
  if (Ret != ParseRet::None)
163
408
    return Ret;
164
165
5.44M
  return ParseRet::None;
166
5.44M
}
167
168
/// The function looks for the following strings at the beginning of
169
/// the input string `ParseString`:
170
///
171
///  <token> {"n"} <number>
172
///
173
/// On success, it removes the parsed parameter from `ParseString`,
174
/// sets `PKind` to the correspondent enum value, sets `LinearStep` to
175
/// <number>, and return success.  On a syntax error, it return a
176
/// parsing error. If nothing is parsed, it returns std::nullopt.
177
///
178
/// The function expects <token> to be one of "l", "R", "U" or
179
/// "L".
180
static ParseRet tryParseCompileTimeLinearToken(StringRef &ParseString,
181
                                               VFParamKind &PKind,
182
                                               int &LinearStep,
183
17.1M
                                               const StringRef Token) {
184
17.1M
  if (ParseString.consume_front(Token)) {
185
5.44M
    PKind = VFABI::getVFParamKindFromString(Token);
186
5.44M
    const bool Negate = ParseString.consume_front("n");
187
5.44M
    if (ParseString.consumeInteger(10, LinearStep))
188
5.44M
      LinearStep = 1;
189
5.44M
    if (Negate)
190
313
      LinearStep *= -1;
191
5.44M
    return ParseRet::OK;
192
5.44M
  }
193
194
11.7M
  return ParseRet::None;
195
17.1M
}
196
197
/// The function looks for the following strings at the beginning of
198
/// the input string `ParseString`:
199
///
200
/// ["l" | "R" | "U" | "L"] {"n"} <number>
201
///
202
/// On success, it removes the parsed parameter from `ParseString`,
203
/// sets `PKind` to the correspondent enum value, sets `LinearStep` to
204
/// <number>, and return success.  On a syntax error, it return a
205
/// parsing error. If nothing is parsed, it returns std::nullopt.
206
static ParseRet tryParseLinearWithCompileTimeStep(StringRef &ParseString,
207
                                                  VFParamKind &PKind,
208
5.44M
                                                  int &StepOrPos) {
209
  // "l" {"n"} <CompileTimeStep>
210
5.44M
  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "l") ==
211
5.44M
      ParseRet::OK)
212
109k
    return ParseRet::OK;
213
214
  // "R" {"n"} <CompileTimeStep>
215
5.33M
  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "R") ==
216
5.33M
      ParseRet::OK)
217
37.8k
    return ParseRet::OK;
218
219
  // "L" {"n"} <CompileTimeStep>
220
5.30M
  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "L") ==
221
5.30M
      ParseRet::OK)
222
4.19M
    return ParseRet::OK;
223
224
  // "U" {"n"} <CompileTimeStep>
225
1.10M
  if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "U") ==
226
1.10M
      ParseRet::OK)
227
1.10M
    return ParseRet::OK;
228
229
1.58k
  return ParseRet::None;
230
1.10M
}
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 std::nullopt.
240
static ParseRet tryParseParameter(StringRef &ParseString, VFParamKind &PKind,
241
7.45M
                                  int &StepOrPos) {
242
7.45M
  if (ParseString.consume_front("v")) {
243
1.35M
    PKind = VFParamKind::Vector;
244
1.35M
    StepOrPos = 0;
245
1.35M
    return ParseRet::OK;
246
1.35M
  }
247
248
6.10M
  if (ParseString.consume_front("u")) {
249
650k
    PKind = VFParamKind::OMP_Uniform;
250
650k
    StepOrPos = 0;
251
650k
    return ParseRet::OK;
252
650k
  }
253
254
5.45M
  const ParseRet HasLinearRuntime =
255
5.45M
      tryParseLinearWithRuntimeStep(ParseString, PKind, StepOrPos);
256
5.45M
  if (HasLinearRuntime != ParseRet::None)
257
3.21k
    return HasLinearRuntime;
258
259
5.44M
  const ParseRet HasLinearCompileTime =
260
5.44M
      tryParseLinearWithCompileTimeStep(ParseString, PKind, StepOrPos);
261
5.44M
  if (HasLinearCompileTime != ParseRet::None)
262
5.44M
    return HasLinearCompileTime;
263
264
1.58k
  return ParseRet::None;
265
5.44M
}
266
267
/// Looks into the <parameters> part of the mangled name in search
268
/// of a valid 'aligned' clause. The function should be invoked
269
/// after parsing a parameter via `tryParseParameter`.
270
///
271
/// On success, it removes the parsed parameter from `ParseString`,
272
/// sets `PKind` to the correspondent enum value, sets `StepOrPos`
273
/// accordingly, and return success.  On a syntax error, it return a
274
/// parsing error. If nothing is parsed, it returns std::nullopt.
275
7.45M
static ParseRet tryParseAlign(StringRef &ParseString, Align &Alignment) {
276
7.45M
  uint64_t Val;
277
  //    "a" <number>
278
7.45M
  if (ParseString.consume_front("a")) {
279
1.02k
    if (ParseString.consumeInteger(10, Val))
280
23
      return ParseRet::Error;
281
282
1.00k
    if (!isPowerOf2_64(Val))
283
128
      return ParseRet::Error;
284
285
873
    Alignment = Align(Val);
286
287
873
    return ParseRet::OK;
288
1.00k
  }
289
290
7.44M
  return ParseRet::None;
291
7.45M
}
292
293
// Returns the 'natural' VF for a given scalar element type, based on the
294
// current architecture.
295
//
296
// For SVE (currently the only scalable architecture with a defined name
297
// mangling), we assume a minimum vector size of 128b and return a VF based on
298
// the number of elements of the given type which would fit in such a vector.
299
static std::optional<ElementCount> getElementCountForTy(const VFISAKind ISA,
300
0
                                                        const Type *Ty) {
301
  // Only AArch64 SVE is supported at present.
302
0
  assert(ISA == VFISAKind::SVE &&
303
0
         "Scalable VF decoding only implemented for SVE\n");
304
305
0
  if (Ty->isIntegerTy(64) || Ty->isDoubleTy() || Ty->isPointerTy())
306
0
    return ElementCount::getScalable(2);
307
0
  if (Ty->isIntegerTy(32) || Ty->isFloatTy())
308
0
    return ElementCount::getScalable(4);
309
0
  if (Ty->isIntegerTy(16) || Ty->is16bitFPTy())
310
0
    return ElementCount::getScalable(8);
311
0
  if (Ty->isIntegerTy(8))
312
0
    return ElementCount::getScalable(16);
313
314
0
  return std::nullopt;
315
0
}
316
317
// Extract the VectorizationFactor from a given function signature, based
318
// on the widest scalar element types that will become vector parameters.
319
static std::optional<ElementCount>
320
getScalableECFromSignature(const FunctionType *Signature, const VFISAKind ISA,
321
0
                           const SmallVectorImpl<VFParameter> &Params) {
322
  // Start with a very wide EC and drop when we find smaller ECs based on type.
323
0
  ElementCount MinEC =
324
0
      ElementCount::getScalable(std::numeric_limits<unsigned int>::max());
325
0
  for (auto &Param : Params) {
326
    // Only vector parameters are used when determining the VF; uniform or
327
    // linear are left as scalars, so do not affect VF.
328
0
    if (Param.ParamKind == VFParamKind::Vector) {
329
0
      Type *PTy = Signature->getParamType(Param.ParamPos);
330
331
0
      std::optional<ElementCount> EC = getElementCountForTy(ISA, PTy);
332
      // If we have an unknown scalar element type we can't find a reasonable
333
      // VF.
334
0
      if (!EC)
335
0
        return std::nullopt;
336
337
      // Find the smallest VF, based on the widest scalar type.
338
0
      if (ElementCount::isKnownLT(*EC, MinEC))
339
0
        MinEC = *EC;
340
0
    }
341
0
  }
342
343
  // Also check the return type if not void.
344
0
  Type *RetTy = Signature->getReturnType();
345
0
  if (!RetTy->isVoidTy()) {
346
0
    std::optional<ElementCount> ReturnEC = getElementCountForTy(ISA, RetTy);
347
    // If we have an unknown scalar element type we can't find a reasonable VF.
348
0
    if (!ReturnEC)
349
0
      return std::nullopt;
350
0
    if (ElementCount::isKnownLT(*ReturnEC, MinEC))
351
0
      MinEC = *ReturnEC;
352
0
  }
353
354
  // The SVE Vector function call ABI bases the VF on the widest element types
355
  // present, and vector arguments containing types of that width are always
356
  // considered to be packed. Arguments with narrower elements are considered
357
  // to be unpacked.
358
0
  if (MinEC.getKnownMinValue() < std::numeric_limits<unsigned int>::max())
359
0
    return MinEC;
360
361
0
  return std::nullopt;
362
0
}
363
} // namespace
364
365
// Format of the ABI name:
366
// _ZGV<isa><mask><vlen><parameters>_<scalarname>[(<redirection>)]
367
std::optional<VFInfo> VFABI::tryDemangleForVFABI(StringRef MangledName,
368
2.27k
                                                 const FunctionType *FTy) {
369
2.27k
  const StringRef OriginalName = MangledName;
370
  // Assume there is no custom name <redirection>, and therefore the
371
  // vector name consists of
372
  // _ZGV<isa><mask><vlen><parameters>_<scalarname>.
373
2.27k
  StringRef VectorName = MangledName;
374
375
  // Parse the fixed size part of the mangled name
376
2.27k
  if (!MangledName.consume_front("_ZGV"))
377
38
    return std::nullopt;
378
379
  // Extract ISA. An unknow ISA is also supported, so we accept all
380
  // values.
381
2.23k
  VFISAKind ISA;
382
2.23k
  if (tryParseISA(MangledName, ISA) != ParseRet::OK)
383
1
    return std::nullopt;
384
385
  // Extract <mask>.
386
2.23k
  bool IsMasked;
387
2.23k
  if (tryParseMask(MangledName, IsMasked) != ParseRet::OK)
388
66
    return std::nullopt;
389
390
  // Parse the variable size, starting from <vlen>.
391
2.16k
  std::pair<unsigned, bool> ParsedVF;
392
2.16k
  if (tryParseVLEN(MangledName, ISA, ParsedVF) != ParseRet::OK)
393
152
    return std::nullopt;
394
395
  // Parse the <parameters>.
396
2.01k
  ParseRet ParamFound;
397
2.01k
  SmallVector<VFParameter, 8> Parameters;
398
7.45M
  do {
399
7.45M
    const unsigned ParameterPos = Parameters.size();
400
7.45M
    VFParamKind PKind;
401
7.45M
    int StepOrPos;
402
7.45M
    ParamFound = tryParseParameter(MangledName, PKind, StepOrPos);
403
404
    // Bail off if there is a parsing error in the parsing of the parameter.
405
7.45M
    if (ParamFound == ParseRet::Error)
406
277
      return std::nullopt;
407
408
7.45M
    if (ParamFound == ParseRet::OK) {
409
7.45M
      Align Alignment;
410
      // Look for the alignment token "a <number>".
411
7.45M
      const ParseRet AlignFound = tryParseAlign(MangledName, Alignment);
412
      // Bail off if there is a syntax error in the align token.
413
7.45M
      if (AlignFound == ParseRet::Error)
414
151
        return std::nullopt;
415
416
      // Add the parameter.
417
7.44M
      Parameters.push_back({ParameterPos, PKind, StepOrPos, Alignment});
418
7.44M
    }
419
7.45M
  } while (ParamFound == ParseRet::OK);
420
421
  // A valid MangledName must have at least one valid entry in the
422
  // <parameters>.
423
1.58k
  if (Parameters.empty())
424
114
    return std::nullopt;
425
426
  // If the number of arguments of the scalar function does not match the
427
  // vector variant we have just demangled then reject the mapping.
428
1.47k
  if (Parameters.size() != FTy->getNumParams())
429
1.05k
    return std::nullopt;
430
431
  // Figure out the number of lanes in vectors for this function variant. This
432
  // is easy for fixed length, as the vlen encoding just gives us the value
433
  // directly. However, if the vlen mangling indicated that this function
434
  // variant expects scalable vectors we need to work it out based on the
435
  // demangled parameter types and the scalar function signature.
436
421
  std::optional<ElementCount> EC;
437
421
  if (ParsedVF.second) {
438
0
    EC = getScalableECFromSignature(FTy, ISA, Parameters);
439
0
    if (!EC)
440
0
      return std::nullopt;
441
0
  } else
442
421
    EC = ElementCount::getFixed(ParsedVF.first);
443
444
  // Check for the <scalarname> and the optional <redirection>, which
445
  // are separated from the prefix with "_"
446
421
  if (!MangledName.consume_front("_"))
447
0
    return std::nullopt;
448
449
  // The rest of the string must be in the format:
450
  // <scalarname>[(<redirection>)]
451
421
  const StringRef ScalarName =
452
2.10k
      MangledName.take_while([](char In) { return In != '('; });
453
454
421
  if (ScalarName.empty())
455
0
    return std::nullopt;
456
457
  // Reduce MangledName to [(<redirection>)].
458
421
  MangledName = MangledName.ltrim(ScalarName);
459
  // Find the optional custom name redirection.
460
421
  if (MangledName.consume_front("(")) {
461
421
    if (!MangledName.consume_back(")"))
462
0
      return std::nullopt;
463
    // Update the vector variant with the one specified by the user.
464
421
    VectorName = MangledName;
465
    // If the vector name is missing, bail out.
466
421
    if (VectorName.empty())
467
0
      return std::nullopt;
468
421
  }
469
470
  // LLVM internal mapping via the TargetLibraryInfo (TLI) must be
471
  // redirected to an existing name.
472
421
  if (ISA == VFISAKind::LLVM && VectorName == OriginalName)
473
0
    return std::nullopt;
474
475
  // When <mask> is "M", we need to add a parameter that is used as
476
  // global predicate for the function.
477
421
  if (IsMasked) {
478
0
    const unsigned Pos = Parameters.size();
479
0
    Parameters.push_back({Pos, VFParamKind::GlobalPredicate});
480
0
  }
481
482
  // Asserts for parameters of type `VFParamKind::GlobalPredicate`, as
483
  // prescribed by the Vector Function ABI specifications supported by
484
  // this parser:
485
  // 1. Uniqueness.
486
  // 2. Must be the last in the parameter list.
487
421
  const auto NGlobalPreds =
488
421
      llvm::count_if(Parameters, [](const VFParameter &PK) {
489
421
        return PK.ParamKind == VFParamKind::GlobalPredicate;
490
421
      });
491
421
  assert(NGlobalPreds < 2 && "Cannot have more than one global predicate.");
492
421
  if (NGlobalPreds)
493
0
    assert(Parameters.back().ParamKind == VFParamKind::GlobalPredicate &&
494
421
           "The global predicate must be the last parameter");
495
496
0
  const VFShape Shape({*EC, Parameters});
497
421
  return VFInfo({Shape, std::string(ScalarName), std::string(VectorName), ISA});
498
421
}
499
500
5.44M
VFParamKind VFABI::getVFParamKindFromString(const StringRef Token) {
501
5.44M
  const VFParamKind ParamKind = StringSwitch<VFParamKind>(Token)
502
5.44M
                                    .Case("v", VFParamKind::Vector)
503
5.44M
                                    .Case("l", VFParamKind::OMP_Linear)
504
5.44M
                                    .Case("R", VFParamKind::OMP_LinearRef)
505
5.44M
                                    .Case("L", VFParamKind::OMP_LinearVal)
506
5.44M
                                    .Case("U", VFParamKind::OMP_LinearUVal)
507
5.44M
                                    .Case("ls", VFParamKind::OMP_LinearPos)
508
5.44M
                                    .Case("Ls", VFParamKind::OMP_LinearValPos)
509
5.44M
                                    .Case("Rs", VFParamKind::OMP_LinearRefPos)
510
5.44M
                                    .Case("Us", VFParamKind::OMP_LinearUValPos)
511
5.44M
                                    .Case("u", VFParamKind::OMP_Uniform)
512
5.44M
                                    .Default(VFParamKind::Unknown);
513
514
5.44M
  if (ParamKind != VFParamKind::Unknown)
515
5.44M
    return ParamKind;
516
517
  // This function should never be invoked with an invalid input.
518
0
  llvm_unreachable("This fuction should be invoken only on parameters"
519
0
                   " that have a textual representation in the mangled name"
520
0
                   " of the Vector Function ABI");
521
0
}