1//===-- AArch64TargetParser - Parser for AArch64 features -------*- C++ -*-===//
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// This file implements a target parser to recognise AArch64 hardware features
10// such as FPU/CPU/ARCH and extension names.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Support/AArch64TargetParser.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/ADT/StringSwitch.h"
17#include <cctype>
18
19using namespace llvm;
20
21static unsigned checkArchVersion(llvm::StringRef Arch) {
22  if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1]))
23    return (Arch[1] - 48);
24  return 0;
25}
26
27unsigned AArch64::getDefaultFPU(StringRef CPU, AArch64::ArchKind AK) {
28  if (CPU == "generic")
29    return AArch64ARCHNames[static_cast<unsigned>(AK)].DefaultFPU;
30
31  return StringSwitch<unsigned>(CPU)
32#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT)       \
33  .Case(NAME, ARM::DEFAULT_FPU)
34#include "../../include/llvm/Support/AArch64TargetParser.def"
35  .Default(ARM::FK_INVALID);
36}
37
38unsigned AArch64::getDefaultExtensions(StringRef CPU, AArch64::ArchKind AK) {
39  if (CPU == "generic")
40    return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions;
41
42  return StringSwitch<unsigned>(CPU)
43#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT)       \
44  .Case(NAME, AArch64ARCHNames[static_cast<unsigned>(ArchKind::ID)]            \
45                      .ArchBaseExtensions |                                    \
46                  DEFAULT_EXT)
47#include "../../include/llvm/Support/AArch64TargetParser.def"
48  .Default(AArch64::AEK_INVALID);
49}
50
51AArch64::ArchKind AArch64::getCPUArchKind(StringRef CPU) {
52  if (CPU == "generic")
53    return ArchKind::ARMV8A;
54
55  return StringSwitch<AArch64::ArchKind>(CPU)
56#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT)       \
57  .Case(NAME, ArchKind::ID)
58#include "../../include/llvm/Support/AArch64TargetParser.def"
59  .Default(ArchKind::INVALID);
60}
61
62bool AArch64::getExtensionFeatures(unsigned Extensions,
63                                   std::vector<StringRef> &Features) {
64  if (Extensions == AArch64::AEK_INVALID)
65    return false;
66
67  if (Extensions & AEK_FP)
68    Features.push_back("+fp-armv8");
69  if (Extensions & AEK_SIMD)
70    Features.push_back("+neon");
71  if (Extensions & AEK_CRC)
72    Features.push_back("+crc");
73  if (Extensions & AEK_CRYPTO)
74    Features.push_back("+crypto");
75  if (Extensions & AEK_DOTPROD)
76    Features.push_back("+dotprod");
77  if (Extensions & AEK_FP16FML)
78    Features.push_back("+fp16fml");
79  if (Extensions & AEK_FP16)
80    Features.push_back("+fullfp16");
81  if (Extensions & AEK_PROFILE)
82    Features.push_back("+spe");
83  if (Extensions & AEK_RAS)
84    Features.push_back("+ras");
85  if (Extensions & AEK_LSE)
86    Features.push_back("+lse");
87  if (Extensions & AEK_RDM)
88    Features.push_back("+rdm");
89  if (Extensions & AEK_SVE)
90    Features.push_back("+sve");
91  if (Extensions & AEK_SVE2)
92    Features.push_back("+sve2");
93  if (Extensions & AEK_SVE2AES)
94    Features.push_back("+sve2-aes");
95  if (Extensions & AEK_SVE2SM4)
96    Features.push_back("+sve2-sm4");
97  if (Extensions & AEK_SVE2SHA3)
98    Features.push_back("+sve2-sha3");
99  if (Extensions & AEK_SVE2BITPERM)
100    Features.push_back("+sve2-bitperm");
101  if (Extensions & AEK_RCPC)
102    Features.push_back("+rcpc");
103
104  return true;
105}
106
107bool AArch64::getArchFeatures(AArch64::ArchKind AK,
108                              std::vector<StringRef> &Features) {
109  if (AK == ArchKind::ARMV8_1A)
110    Features.push_back("+v8.1a");
111  if (AK == ArchKind::ARMV8_2A)
112    Features.push_back("+v8.2a");
113  if (AK == ArchKind::ARMV8_3A)
114    Features.push_back("+v8.3a");
115  if (AK == ArchKind::ARMV8_4A)
116    Features.push_back("+v8.4a");
117  if (AK == ArchKind::ARMV8_5A)
118    Features.push_back("+v8.5a");
119
120  return AK != ArchKind::INVALID;
121}
122
123StringRef AArch64::getArchName(AArch64::ArchKind AK) {
124  return AArch64ARCHNames[static_cast<unsigned>(AK)].getName();
125}
126
127StringRef AArch64::getCPUAttr(AArch64::ArchKind AK) {
128  return AArch64ARCHNames[static_cast<unsigned>(AK)].getCPUAttr();
129}
130
131StringRef AArch64::getSubArch(AArch64::ArchKind AK) {
132  return AArch64ARCHNames[static_cast<unsigned>(AK)].getSubArch();
133}
134
135unsigned AArch64::getArchAttr(AArch64::ArchKind AK) {
136  return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchAttr;
137}
138
139StringRef AArch64::getArchExtName(unsigned ArchExtKind) {
140  for (const auto &AE : AArch64ARCHExtNames)
141    if (ArchExtKind == AE.ID)
142      return AE.getName();
143  return StringRef();
144}
145
146StringRef AArch64::getArchExtFeature(StringRef ArchExt) {
147  if (ArchExt.startswith("no")) {
148    StringRef ArchExtBase(ArchExt.substr(2));
149    for (const auto &AE : AArch64ARCHExtNames) {
150      if (AE.NegFeature && ArchExtBase == AE.getName())
151        return StringRef(AE.NegFeature);
152    }
153  }
154
155  for (const auto &AE : AArch64ARCHExtNames)
156    if (AE.Feature && ArchExt == AE.getName())
157      return StringRef(AE.Feature);
158  return StringRef();
159}
160
161StringRef AArch64::getDefaultCPU(StringRef Arch) {
162  ArchKind AK = parseArch(Arch);
163  if (AK == ArchKind::INVALID)
164    return StringRef();
165
166  // Look for multiple AKs to find the default for pair AK+Name.
167  for (const auto &CPU : AArch64CPUNames)
168    if (CPU.ArchID == AK && CPU.Default)
169      return CPU.getName();
170
171  // If we can't find a default then target the architecture instead
172  return "generic";
173}
174
175void AArch64::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) {
176  for (const auto &Arch : AArch64CPUNames) {
177    if (Arch.ArchID != ArchKind::INVALID)
178      Values.push_back(Arch.getName());
179  }
180}
181
182bool AArch64::isX18ReservedByDefault(const Triple &TT) {
183  return TT.isAndroid() || TT.isOSDarwin() || TT.isOSFuchsia() ||
184         TT.isOSWindows();
185}
186
187// Allows partial match, ex. "v8a" matches "armv8a".
188AArch64::ArchKind AArch64::parseArch(StringRef Arch) {
189  Arch = ARM::getCanonicalArchName(Arch);
190  if (checkArchVersion(Arch) < 8)
191    return ArchKind::INVALID;
192
193  StringRef Syn = ARM::getArchSynonym(Arch);
194  for (const auto A : AArch64ARCHNames) {
195    if (A.getName().endswith(Syn))
196      return A.ID;
197  }
198  return ArchKind::INVALID;
199}
200
201AArch64::ArchExtKind AArch64::parseArchExt(StringRef ArchExt) {
202  for (const auto A : AArch64ARCHExtNames) {
203    if (ArchExt == A.getName())
204      return static_cast<ArchExtKind>(A.ID);
205  }
206  return AArch64::AEK_INVALID;
207}
208
209AArch64::ArchKind AArch64::parseCPUArch(StringRef CPU) {
210  for (const auto C : AArch64CPUNames) {
211    if (CPU == C.getName())
212      return C.ArchID;
213  }
214  return ArchKind::INVALID;
215}
216
217// Parse a branch protection specification, which has the form
218//   standard | none | [bti,pac-ret[+b-key,+leaf]*]
219// Returns true on success, with individual elements of the specification
220// returned in `PBP`. Returns false in error, with `Err` containing
221// an erroneous part of the spec.
222bool AArch64::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
223                                    StringRef &Err) {
224  PBP = {"none", "a_key", false};
225  if (Spec == "none")
226    return true; // defaults are ok
227
228  if (Spec == "standard") {
229    PBP.Scope = "non-leaf";
230    PBP.BranchTargetEnforcement = true;
231    return true;
232  }
233
234  SmallVector<StringRef, 4> Opts;
235  Spec.split(Opts, "+");
236  for (int I = 0, E = Opts.size(); I != E; ++I) {
237    StringRef Opt = Opts[I].trim();
238    if (Opt == "bti") {
239      PBP.BranchTargetEnforcement = true;
240      continue;
241    }
242    if (Opt == "pac-ret") {
243      PBP.Scope = "non-leaf";
244      for (; I + 1 != E; ++I) {
245        StringRef PACOpt = Opts[I + 1].trim();
246        if (PACOpt == "leaf")
247          PBP.Scope = "all";
248        else if (PACOpt == "b-key")
249          PBP.Key = "b_key";
250        else
251          break;
252      }
253      continue;
254    }
255    if (Opt == "")
256      Err = "<empty>";
257    else
258      Err = Opt;
259    return false;
260  }
261
262  return true;
263}
264