1343171Sdim//===-- AArch64TargetParser - Parser for AArch64 features -------*- C++ -*-===//
2343171Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6343171Sdim//
7343171Sdim//===----------------------------------------------------------------------===//
8343171Sdim//
9343171Sdim// This file implements a target parser to recognise AArch64 hardware features
10343171Sdim// such as FPU/CPU/ARCH and extension names.
11343171Sdim//
12343171Sdim//===----------------------------------------------------------------------===//
13343171Sdim
14343171Sdim#include "llvm/Support/AArch64TargetParser.h"
15343171Sdim#include "llvm/ADT/StringRef.h"
16343171Sdim#include "llvm/ADT/StringSwitch.h"
17343171Sdim#include <cctype>
18343171Sdim
19343171Sdimusing namespace llvm;
20343171Sdim
21343171Sdimstatic unsigned checkArchVersion(llvm::StringRef Arch) {
22343171Sdim  if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1]))
23343171Sdim    return (Arch[1] - 48);
24343171Sdim  return 0;
25343171Sdim}
26343171Sdim
27343171Sdimunsigned AArch64::getDefaultFPU(StringRef CPU, AArch64::ArchKind AK) {
28343171Sdim  if (CPU == "generic")
29343171Sdim    return AArch64ARCHNames[static_cast<unsigned>(AK)].DefaultFPU;
30343171Sdim
31343171Sdim  return StringSwitch<unsigned>(CPU)
32343171Sdim#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT)       \
33343171Sdim  .Case(NAME, ARM::DEFAULT_FPU)
34343171Sdim#include "../../include/llvm/Support/AArch64TargetParser.def"
35343171Sdim  .Default(ARM::FK_INVALID);
36343171Sdim}
37343171Sdim
38343171Sdimunsigned AArch64::getDefaultExtensions(StringRef CPU, AArch64::ArchKind AK) {
39343171Sdim  if (CPU == "generic")
40343171Sdim    return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions;
41343171Sdim
42343171Sdim  return StringSwitch<unsigned>(CPU)
43343171Sdim#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT)       \
44343171Sdim  .Case(NAME, AArch64ARCHNames[static_cast<unsigned>(ArchKind::ID)]            \
45343171Sdim                      .ArchBaseExtensions |                                    \
46343171Sdim                  DEFAULT_EXT)
47343171Sdim#include "../../include/llvm/Support/AArch64TargetParser.def"
48343171Sdim  .Default(AArch64::AEK_INVALID);
49343171Sdim}
50343171Sdim
51343171SdimAArch64::ArchKind AArch64::getCPUArchKind(StringRef CPU) {
52343171Sdim  if (CPU == "generic")
53343171Sdim    return ArchKind::ARMV8A;
54343171Sdim
55343171Sdim  return StringSwitch<AArch64::ArchKind>(CPU)
56343171Sdim#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT)       \
57343171Sdim  .Case(NAME, ArchKind::ID)
58343171Sdim#include "../../include/llvm/Support/AArch64TargetParser.def"
59343171Sdim  .Default(ArchKind::INVALID);
60343171Sdim}
61343171Sdim
62343171Sdimbool AArch64::getExtensionFeatures(unsigned Extensions,
63343171Sdim                                   std::vector<StringRef> &Features) {
64343171Sdim  if (Extensions == AArch64::AEK_INVALID)
65343171Sdim    return false;
66343171Sdim
67343171Sdim  if (Extensions & AEK_FP)
68343171Sdim    Features.push_back("+fp-armv8");
69343171Sdim  if (Extensions & AEK_SIMD)
70343171Sdim    Features.push_back("+neon");
71343171Sdim  if (Extensions & AEK_CRC)
72343171Sdim    Features.push_back("+crc");
73343171Sdim  if (Extensions & AEK_CRYPTO)
74343171Sdim    Features.push_back("+crypto");
75343171Sdim  if (Extensions & AEK_DOTPROD)
76343171Sdim    Features.push_back("+dotprod");
77343171Sdim  if (Extensions & AEK_FP16FML)
78343171Sdim    Features.push_back("+fp16fml");
79343171Sdim  if (Extensions & AEK_FP16)
80343171Sdim    Features.push_back("+fullfp16");
81343171Sdim  if (Extensions & AEK_PROFILE)
82343171Sdim    Features.push_back("+spe");
83343171Sdim  if (Extensions & AEK_RAS)
84343171Sdim    Features.push_back("+ras");
85343171Sdim  if (Extensions & AEK_LSE)
86343171Sdim    Features.push_back("+lse");
87343171Sdim  if (Extensions & AEK_RDM)
88343171Sdim    Features.push_back("+rdm");
89343171Sdim  if (Extensions & AEK_SVE)
90343171Sdim    Features.push_back("+sve");
91353358Sdim  if (Extensions & AEK_SVE2)
92353358Sdim    Features.push_back("+sve2");
93353358Sdim  if (Extensions & AEK_SVE2AES)
94353358Sdim    Features.push_back("+sve2-aes");
95353358Sdim  if (Extensions & AEK_SVE2SM4)
96353358Sdim    Features.push_back("+sve2-sm4");
97353358Sdim  if (Extensions & AEK_SVE2SHA3)
98353358Sdim    Features.push_back("+sve2-sha3");
99353358Sdim  if (Extensions & AEK_SVE2BITPERM)
100353358Sdim    Features.push_back("+sve2-bitperm");
101343171Sdim  if (Extensions & AEK_RCPC)
102343171Sdim    Features.push_back("+rcpc");
103343171Sdim
104343171Sdim  return true;
105343171Sdim}
106343171Sdim
107343171Sdimbool AArch64::getArchFeatures(AArch64::ArchKind AK,
108343171Sdim                              std::vector<StringRef> &Features) {
109343171Sdim  if (AK == ArchKind::ARMV8_1A)
110343171Sdim    Features.push_back("+v8.1a");
111343171Sdim  if (AK == ArchKind::ARMV8_2A)
112343171Sdim    Features.push_back("+v8.2a");
113343171Sdim  if (AK == ArchKind::ARMV8_3A)
114343171Sdim    Features.push_back("+v8.3a");
115343171Sdim  if (AK == ArchKind::ARMV8_4A)
116343171Sdim    Features.push_back("+v8.4a");
117343171Sdim  if (AK == ArchKind::ARMV8_5A)
118343171Sdim    Features.push_back("+v8.5a");
119343171Sdim
120343171Sdim  return AK != ArchKind::INVALID;
121343171Sdim}
122343171Sdim
123343171SdimStringRef AArch64::getArchName(AArch64::ArchKind AK) {
124343171Sdim  return AArch64ARCHNames[static_cast<unsigned>(AK)].getName();
125343171Sdim}
126343171Sdim
127343171SdimStringRef AArch64::getCPUAttr(AArch64::ArchKind AK) {
128343171Sdim  return AArch64ARCHNames[static_cast<unsigned>(AK)].getCPUAttr();
129343171Sdim}
130343171Sdim
131343171SdimStringRef AArch64::getSubArch(AArch64::ArchKind AK) {
132343171Sdim  return AArch64ARCHNames[static_cast<unsigned>(AK)].getSubArch();
133343171Sdim}
134343171Sdim
135343171Sdimunsigned AArch64::getArchAttr(AArch64::ArchKind AK) {
136343171Sdim  return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchAttr;
137343171Sdim}
138343171Sdim
139343171SdimStringRef AArch64::getArchExtName(unsigned ArchExtKind) {
140343171Sdim  for (const auto &AE : AArch64ARCHExtNames)
141343171Sdim    if (ArchExtKind == AE.ID)
142343171Sdim      return AE.getName();
143343171Sdim  return StringRef();
144343171Sdim}
145343171Sdim
146343171SdimStringRef AArch64::getArchExtFeature(StringRef ArchExt) {
147343171Sdim  if (ArchExt.startswith("no")) {
148343171Sdim    StringRef ArchExtBase(ArchExt.substr(2));
149343171Sdim    for (const auto &AE : AArch64ARCHExtNames) {
150343171Sdim      if (AE.NegFeature && ArchExtBase == AE.getName())
151343171Sdim        return StringRef(AE.NegFeature);
152343171Sdim    }
153343171Sdim  }
154343171Sdim
155343171Sdim  for (const auto &AE : AArch64ARCHExtNames)
156343171Sdim    if (AE.Feature && ArchExt == AE.getName())
157343171Sdim      return StringRef(AE.Feature);
158343171Sdim  return StringRef();
159343171Sdim}
160343171Sdim
161343171SdimStringRef AArch64::getDefaultCPU(StringRef Arch) {
162343171Sdim  ArchKind AK = parseArch(Arch);
163343171Sdim  if (AK == ArchKind::INVALID)
164343171Sdim    return StringRef();
165343171Sdim
166343171Sdim  // Look for multiple AKs to find the default for pair AK+Name.
167343171Sdim  for (const auto &CPU : AArch64CPUNames)
168343171Sdim    if (CPU.ArchID == AK && CPU.Default)
169343171Sdim      return CPU.getName();
170343171Sdim
171343171Sdim  // If we can't find a default then target the architecture instead
172343171Sdim  return "generic";
173343171Sdim}
174343171Sdim
175343171Sdimvoid AArch64::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) {
176343171Sdim  for (const auto &Arch : AArch64CPUNames) {
177343171Sdim    if (Arch.ArchID != ArchKind::INVALID)
178343171Sdim      Values.push_back(Arch.getName());
179343171Sdim  }
180343171Sdim}
181343171Sdim
182343171Sdimbool AArch64::isX18ReservedByDefault(const Triple &TT) {
183343171Sdim  return TT.isAndroid() || TT.isOSDarwin() || TT.isOSFuchsia() ||
184343171Sdim         TT.isOSWindows();
185343171Sdim}
186343171Sdim
187343171Sdim// Allows partial match, ex. "v8a" matches "armv8a".
188343171SdimAArch64::ArchKind AArch64::parseArch(StringRef Arch) {
189343171Sdim  Arch = ARM::getCanonicalArchName(Arch);
190343171Sdim  if (checkArchVersion(Arch) < 8)
191343171Sdim    return ArchKind::INVALID;
192343171Sdim
193343171Sdim  StringRef Syn = ARM::getArchSynonym(Arch);
194343171Sdim  for (const auto A : AArch64ARCHNames) {
195343171Sdim    if (A.getName().endswith(Syn))
196343171Sdim      return A.ID;
197343171Sdim  }
198343171Sdim  return ArchKind::INVALID;
199343171Sdim}
200343171Sdim
201343171SdimAArch64::ArchExtKind AArch64::parseArchExt(StringRef ArchExt) {
202343171Sdim  for (const auto A : AArch64ARCHExtNames) {
203343171Sdim    if (ArchExt == A.getName())
204343171Sdim      return static_cast<ArchExtKind>(A.ID);
205343171Sdim  }
206343171Sdim  return AArch64::AEK_INVALID;
207343171Sdim}
208343171Sdim
209343171SdimAArch64::ArchKind AArch64::parseCPUArch(StringRef CPU) {
210343171Sdim  for (const auto C : AArch64CPUNames) {
211343171Sdim    if (CPU == C.getName())
212343171Sdim      return C.ArchID;
213343171Sdim  }
214343171Sdim  return ArchKind::INVALID;
215343171Sdim}
216360784Sdim
217360784Sdim// Parse a branch protection specification, which has the form
218360784Sdim//   standard | none | [bti,pac-ret[+b-key,+leaf]*]
219360784Sdim// Returns true on success, with individual elements of the specification
220360784Sdim// returned in `PBP`. Returns false in error, with `Err` containing
221360784Sdim// an erroneous part of the spec.
222360784Sdimbool AArch64::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
223360784Sdim                                    StringRef &Err) {
224360784Sdim  PBP = {"none", "a_key", false};
225360784Sdim  if (Spec == "none")
226360784Sdim    return true; // defaults are ok
227360784Sdim
228360784Sdim  if (Spec == "standard") {
229360784Sdim    PBP.Scope = "non-leaf";
230360784Sdim    PBP.BranchTargetEnforcement = true;
231360784Sdim    return true;
232360784Sdim  }
233360784Sdim
234360784Sdim  SmallVector<StringRef, 4> Opts;
235360784Sdim  Spec.split(Opts, "+");
236360784Sdim  for (int I = 0, E = Opts.size(); I != E; ++I) {
237360784Sdim    StringRef Opt = Opts[I].trim();
238360784Sdim    if (Opt == "bti") {
239360784Sdim      PBP.BranchTargetEnforcement = true;
240360784Sdim      continue;
241360784Sdim    }
242360784Sdim    if (Opt == "pac-ret") {
243360784Sdim      PBP.Scope = "non-leaf";
244360784Sdim      for (; I + 1 != E; ++I) {
245360784Sdim        StringRef PACOpt = Opts[I + 1].trim();
246360784Sdim        if (PACOpt == "leaf")
247360784Sdim          PBP.Scope = "all";
248360784Sdim        else if (PACOpt == "b-key")
249360784Sdim          PBP.Key = "b_key";
250360784Sdim        else
251360784Sdim          break;
252360784Sdim      }
253360784Sdim      continue;
254360784Sdim    }
255360784Sdim    if (Opt == "")
256360784Sdim      Err = "<empty>";
257360784Sdim    else
258360784Sdim      Err = Opt;
259360784Sdim    return false;
260360784Sdim  }
261360784Sdim
262360784Sdim  return true;
263360784Sdim}
264