llvm-project/llvm/lib/Support/ARMTargetParser.cpp

629 lines
18 KiB
C++
Raw Normal View History

//===-- ARMTargetParser - Parser for ARM target features --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements a target parser to recognise ARM hardware features
// such as FPU/CPU/ARCH/extensions and specific support such as HWDIV.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/ARMTargetParser.h"
#include "llvm/ADT/StringSwitch.h"
#include <cctype>
using namespace llvm;
static StringRef getHWDivSynonym(StringRef HWDiv) {
return StringSwitch<StringRef>(HWDiv)
.Case("thumb,arm", "arm,thumb")
.Default(HWDiv);
}
// Allows partial match, ex. "v7a" matches "armv7a".
ARM::ArchKind ARM::parseArch(StringRef Arch) {
Arch = getCanonicalArchName(Arch);
StringRef Syn = getArchSynonym(Arch);
for (const auto A : ARCHNames) {
if (A.getName().endswith(Syn))
return A.ID;
}
return ArchKind::INVALID;
}
// Version number (ex. v7 = 7).
unsigned ARM::parseArchVersion(StringRef Arch) {
Arch = getCanonicalArchName(Arch);
switch (parseArch(Arch)) {
case ArchKind::ARMV2:
case ArchKind::ARMV2A:
return 2;
case ArchKind::ARMV3:
case ArchKind::ARMV3M:
return 3;
case ArchKind::ARMV4:
case ArchKind::ARMV4T:
return 4;
case ArchKind::ARMV5T:
case ArchKind::ARMV5TE:
case ArchKind::IWMMXT:
case ArchKind::IWMMXT2:
case ArchKind::XSCALE:
case ArchKind::ARMV5TEJ:
return 5;
case ArchKind::ARMV6:
case ArchKind::ARMV6K:
case ArchKind::ARMV6T2:
case ArchKind::ARMV6KZ:
case ArchKind::ARMV6M:
return 6;
case ArchKind::ARMV7A:
case ArchKind::ARMV7VE:
case ArchKind::ARMV7R:
case ArchKind::ARMV7M:
case ArchKind::ARMV7S:
case ArchKind::ARMV7EM:
case ArchKind::ARMV7K:
return 7;
case ArchKind::ARMV8A:
case ArchKind::ARMV8_1A:
case ArchKind::ARMV8_2A:
case ArchKind::ARMV8_3A:
case ArchKind::ARMV8_4A:
case ArchKind::ARMV8_5A:
case ArchKind::ARMV8R:
case ArchKind::ARMV8MBaseline:
case ArchKind::ARMV8MMainline:
case ArchKind::ARMV8_1MMainline:
return 8;
case ArchKind::INVALID:
return 0;
}
llvm_unreachable("Unhandled architecture");
}
// Profile A/R/M
ARM::ProfileKind ARM::parseArchProfile(StringRef Arch) {
Arch = getCanonicalArchName(Arch);
switch (parseArch(Arch)) {
case ArchKind::ARMV6M:
case ArchKind::ARMV7M:
case ArchKind::ARMV7EM:
case ArchKind::ARMV8MMainline:
case ArchKind::ARMV8MBaseline:
case ArchKind::ARMV8_1MMainline:
return ProfileKind::M;
case ArchKind::ARMV7R:
case ArchKind::ARMV8R:
return ProfileKind::R;
case ArchKind::ARMV7A:
case ArchKind::ARMV7VE:
case ArchKind::ARMV7K:
case ArchKind::ARMV8A:
case ArchKind::ARMV8_1A:
case ArchKind::ARMV8_2A:
case ArchKind::ARMV8_3A:
case ArchKind::ARMV8_4A:
case ArchKind::ARMV8_5A:
return ProfileKind::A;
case ArchKind::ARMV2:
case ArchKind::ARMV2A:
case ArchKind::ARMV3:
case ArchKind::ARMV3M:
case ArchKind::ARMV4:
case ArchKind::ARMV4T:
case ArchKind::ARMV5T:
case ArchKind::ARMV5TE:
case ArchKind::ARMV5TEJ:
case ArchKind::ARMV6:
case ArchKind::ARMV6K:
case ArchKind::ARMV6T2:
case ArchKind::ARMV6KZ:
case ArchKind::ARMV7S:
case ArchKind::IWMMXT:
case ArchKind::IWMMXT2:
case ArchKind::XSCALE:
case ArchKind::INVALID:
return ProfileKind::INVALID;
}
llvm_unreachable("Unhandled architecture");
}
StringRef ARM::getArchSynonym(StringRef Arch) {
return StringSwitch<StringRef>(Arch)
.Case("v5", "v5t")
.Case("v5e", "v5te")
.Case("v6j", "v6")
.Case("v6hl", "v6k")
.Cases("v6m", "v6sm", "v6s-m", "v6-m")
.Cases("v6z", "v6zk", "v6kz")
.Cases("v7", "v7a", "v7hl", "v7l", "v7-a")
.Case("v7r", "v7-r")
.Case("v7m", "v7-m")
.Case("v7em", "v7e-m")
.Cases("v8", "v8a", "v8l", "aarch64", "arm64", "v8-a")
.Case("v8.1a", "v8.1-a")
.Case("v8.2a", "v8.2-a")
.Case("v8.3a", "v8.3-a")
.Case("v8.4a", "v8.4-a")
.Case("v8.5a", "v8.5-a")
.Case("v8r", "v8-r")
.Case("v8m.base", "v8-m.base")
.Case("v8m.main", "v8-m.main")
.Case("v8.1m.main", "v8.1-m.main")
.Default(Arch);
}
bool ARM::getFPUFeatures(unsigned FPUKind, std::vector<StringRef> &Features) {
if (FPUKind >= FK_LAST || FPUKind == FK_INVALID)
return false;
[ARM] Fix bugs introduced by the fp64/d32 rework. Change D60691 caused some knock-on failures that weren't caught by the existing tests. Firstly, selecting a CPU that should have had a restricted FPU (e.g. `-mcpu=cortex-m4`, which should have 16 d-regs and no double precision) could give the unrestricted version, because `ARM::getFPUFeatures` returned a list of features including subtracted ones (here `-fp64`,`-d32`), but `ARMTargetInfo::initFeatureMap` threw away all the ones that didn't start with `+`. Secondly, the preprocessor macros didn't reliably match the actual compilation settings: for example, `-mfpu=softvfp` could still set `__ARM_FP` as if hardware FP was available, because the list of features on the cc1 command line would include things like `+vfp4`,`-vfp4d16` and clang didn't realise that one of those cancelled out the other. I've fixed both of these issues by rewriting `ARM::getFPUFeatures` so that it returns a list that enables every FP-related feature compatible with the selected FPU and disables every feature not compatible, which is more verbose but means clang doesn't have to understand the dependency relationships between the backend features. Meanwhile, `ARMTargetInfo::handleTargetFeatures` is testing for all the various forms of the FP feature names, so that it won't miss cases where it should have set `HW_FP` to feed into feature test macros. That in turn caused an ordering problem when handling `-mcpu=foo+bar` together with `-mfpu=something_that_turns_off_bar`. To fix that, I've arranged that the `+bar` suffixes on the end of `-mcpu` and `-march` cause feature names to be put into a separate vector which is concatenated after the output of `getFPUFeatures`. Another side effect of all this is to fix a bug where `clang -target armv8-eabi` by itself would fail to set `__ARM_FEATURE_FMA`, even though `armv8` (aka Arm v8-A) implies FP-Armv8 which has FMA. That was because `HW_FP` was being set to a value including only the `FPARMV8` bit, but that feature test macro was testing only the `VFP4FPU` bit. Now `HW_FP` ends up with all the bits set, so it gives the right answer. Changes to tests included in this patch: * `arm-target-features.c`: I had to change basically all the expected results. (The Cortex-M4 test in there should function as a regression test for the accidental double-precision bug.) * `arm-mfpu.c`, `armv8.1m.main.c`: switched to using `CHECK-DAG` everywhere so that those tests are no longer sensitive to the order of cc1 feature options on the command line. * `arm-acle-6.5.c`: been updated to expect the right answer to that FMA test. * `Preprocessor/arm-target-features.c`: added a regression test for the `mfpu=softvfp` issue. Reviewers: SjoerdMeijer, dmgreen, ostannard, samparker, JamesNagurne Reviewed By: ostannard Subscribers: srhines, javed.absar, kristof.beyls, hiraditya, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D62998 llvm-svn: 362791
2019-06-07 20:42:54 +08:00
static const struct FPUFeatureNameInfo {
const char *PlusName, *MinusName;
FPUVersion MinVersion;
FPURestriction MaxRestriction;
} FPUFeatureInfoList[] = {
// We have to specify the + and - versions of the name in full so
// that we can return them as static StringRefs.
//
// Also, the SubtargetFeatures ending in just "sp" are listed here
// under FPURestriction::None, which is the only FPURestriction in
// which they would be valid (since FPURestriction::SP doesn't
// exist).
{"+fpregs", "-fpregs", FPUVersion::VFPV2, FPURestriction::SP_D16},
{"+vfp2", "-vfp2", FPUVersion::VFPV2, FPURestriction::None},
{"+vfp2d16", "-vfp2d16", FPUVersion::VFPV2, FPURestriction::D16},
{"+vfp2d16sp", "-vfp2d16sp", FPUVersion::VFPV2, FPURestriction::SP_D16},
{"+vfp2sp", "-vfp2sp", FPUVersion::VFPV2, FPURestriction::None},
{"+vfp3", "-vfp3", FPUVersion::VFPV3, FPURestriction::None},
{"+vfp3d16", "-vfp3d16", FPUVersion::VFPV3, FPURestriction::D16},
{"+vfp3d16sp", "-vfp3d16sp", FPUVersion::VFPV3, FPURestriction::SP_D16},
{"+vfp3sp", "-vfp3sp", FPUVersion::VFPV3, FPURestriction::None},
{"+fp16", "-fp16", FPUVersion::VFPV3_FP16, FPURestriction::SP_D16},
{"+vfp4", "-vfp4", FPUVersion::VFPV4, FPURestriction::None},
{"+vfp4d16", "-vfp4d16", FPUVersion::VFPV4, FPURestriction::D16},
{"+vfp4d16sp", "-vfp4d16sp", FPUVersion::VFPV4, FPURestriction::SP_D16},
{"+vfp4sp", "-vfp4sp", FPUVersion::VFPV4, FPURestriction::None},
{"+fp-armv8", "-fp-armv8", FPUVersion::VFPV5, FPURestriction::None},
{"+fp-armv8d16", "-fp-armv8d16", FPUVersion::VFPV5, FPURestriction::D16},
{"+fp-armv8d16sp", "-fp-armv8d16sp", FPUVersion::VFPV5, FPURestriction::SP_D16},
{"+fp-armv8sp", "-fp-armv8sp", FPUVersion::VFPV5, FPURestriction::None},
{"+fullfp16", "-fullfp16", FPUVersion::VFPV5_FULLFP16, FPURestriction::SP_D16},
{"+fp64", "-fp64", FPUVersion::VFPV2, FPURestriction::D16},
{"+d32", "-d32", FPUVersion::VFPV2, FPURestriction::None},
};
[ARM] Fix bugs introduced by the fp64/d32 rework. Change D60691 caused some knock-on failures that weren't caught by the existing tests. Firstly, selecting a CPU that should have had a restricted FPU (e.g. `-mcpu=cortex-m4`, which should have 16 d-regs and no double precision) could give the unrestricted version, because `ARM::getFPUFeatures` returned a list of features including subtracted ones (here `-fp64`,`-d32`), but `ARMTargetInfo::initFeatureMap` threw away all the ones that didn't start with `+`. Secondly, the preprocessor macros didn't reliably match the actual compilation settings: for example, `-mfpu=softvfp` could still set `__ARM_FP` as if hardware FP was available, because the list of features on the cc1 command line would include things like `+vfp4`,`-vfp4d16` and clang didn't realise that one of those cancelled out the other. I've fixed both of these issues by rewriting `ARM::getFPUFeatures` so that it returns a list that enables every FP-related feature compatible with the selected FPU and disables every feature not compatible, which is more verbose but means clang doesn't have to understand the dependency relationships between the backend features. Meanwhile, `ARMTargetInfo::handleTargetFeatures` is testing for all the various forms of the FP feature names, so that it won't miss cases where it should have set `HW_FP` to feed into feature test macros. That in turn caused an ordering problem when handling `-mcpu=foo+bar` together with `-mfpu=something_that_turns_off_bar`. To fix that, I've arranged that the `+bar` suffixes on the end of `-mcpu` and `-march` cause feature names to be put into a separate vector which is concatenated after the output of `getFPUFeatures`. Another side effect of all this is to fix a bug where `clang -target armv8-eabi` by itself would fail to set `__ARM_FEATURE_FMA`, even though `armv8` (aka Arm v8-A) implies FP-Armv8 which has FMA. That was because `HW_FP` was being set to a value including only the `FPARMV8` bit, but that feature test macro was testing only the `VFP4FPU` bit. Now `HW_FP` ends up with all the bits set, so it gives the right answer. Changes to tests included in this patch: * `arm-target-features.c`: I had to change basically all the expected results. (The Cortex-M4 test in there should function as a regression test for the accidental double-precision bug.) * `arm-mfpu.c`, `armv8.1m.main.c`: switched to using `CHECK-DAG` everywhere so that those tests are no longer sensitive to the order of cc1 feature options on the command line. * `arm-acle-6.5.c`: been updated to expect the right answer to that FMA test. * `Preprocessor/arm-target-features.c`: added a regression test for the `mfpu=softvfp` issue. Reviewers: SjoerdMeijer, dmgreen, ostannard, samparker, JamesNagurne Reviewed By: ostannard Subscribers: srhines, javed.absar, kristof.beyls, hiraditya, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D62998 llvm-svn: 362791
2019-06-07 20:42:54 +08:00
for (const auto &Info: FPUFeatureInfoList) {
if (FPUNames[FPUKind].FPUVer >= Info.MinVersion &&
FPUNames[FPUKind].Restriction <= Info.MaxRestriction)
Features.push_back(Info.PlusName);
else
Features.push_back(Info.MinusName);
}
[ARM] Fix bugs introduced by the fp64/d32 rework. Change D60691 caused some knock-on failures that weren't caught by the existing tests. Firstly, selecting a CPU that should have had a restricted FPU (e.g. `-mcpu=cortex-m4`, which should have 16 d-regs and no double precision) could give the unrestricted version, because `ARM::getFPUFeatures` returned a list of features including subtracted ones (here `-fp64`,`-d32`), but `ARMTargetInfo::initFeatureMap` threw away all the ones that didn't start with `+`. Secondly, the preprocessor macros didn't reliably match the actual compilation settings: for example, `-mfpu=softvfp` could still set `__ARM_FP` as if hardware FP was available, because the list of features on the cc1 command line would include things like `+vfp4`,`-vfp4d16` and clang didn't realise that one of those cancelled out the other. I've fixed both of these issues by rewriting `ARM::getFPUFeatures` so that it returns a list that enables every FP-related feature compatible with the selected FPU and disables every feature not compatible, which is more verbose but means clang doesn't have to understand the dependency relationships between the backend features. Meanwhile, `ARMTargetInfo::handleTargetFeatures` is testing for all the various forms of the FP feature names, so that it won't miss cases where it should have set `HW_FP` to feed into feature test macros. That in turn caused an ordering problem when handling `-mcpu=foo+bar` together with `-mfpu=something_that_turns_off_bar`. To fix that, I've arranged that the `+bar` suffixes on the end of `-mcpu` and `-march` cause feature names to be put into a separate vector which is concatenated after the output of `getFPUFeatures`. Another side effect of all this is to fix a bug where `clang -target armv8-eabi` by itself would fail to set `__ARM_FEATURE_FMA`, even though `armv8` (aka Arm v8-A) implies FP-Armv8 which has FMA. That was because `HW_FP` was being set to a value including only the `FPARMV8` bit, but that feature test macro was testing only the `VFP4FPU` bit. Now `HW_FP` ends up with all the bits set, so it gives the right answer. Changes to tests included in this patch: * `arm-target-features.c`: I had to change basically all the expected results. (The Cortex-M4 test in there should function as a regression test for the accidental double-precision bug.) * `arm-mfpu.c`, `armv8.1m.main.c`: switched to using `CHECK-DAG` everywhere so that those tests are no longer sensitive to the order of cc1 feature options on the command line. * `arm-acle-6.5.c`: been updated to expect the right answer to that FMA test. * `Preprocessor/arm-target-features.c`: added a regression test for the `mfpu=softvfp` issue. Reviewers: SjoerdMeijer, dmgreen, ostannard, samparker, JamesNagurne Reviewed By: ostannard Subscribers: srhines, javed.absar, kristof.beyls, hiraditya, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D62998 llvm-svn: 362791
2019-06-07 20:42:54 +08:00
static const struct NeonFeatureNameInfo {
const char *PlusName, *MinusName;
NeonSupportLevel MinSupportLevel;
} NeonFeatureInfoList[] = {
{"+neon", "-neon", NeonSupportLevel::Neon},
{"+crypto", "-crypto", NeonSupportLevel::Crypto},
};
for (const auto &Info: NeonFeatureInfoList) {
if (FPUNames[FPUKind].NeonSupport >= Info.MinSupportLevel)
Features.push_back(Info.PlusName);
else
Features.push_back(Info.MinusName);
}
return true;
}
// Little/Big endian
ARM::EndianKind ARM::parseArchEndian(StringRef Arch) {
if (Arch.startswith("armeb") || Arch.startswith("thumbeb") ||
Arch.startswith("aarch64_be"))
return EndianKind::BIG;
if (Arch.startswith("arm") || Arch.startswith("thumb")) {
if (Arch.endswith("eb"))
return EndianKind::BIG;
else
return EndianKind::LITTLE;
}
if (Arch.startswith("aarch64") || Arch.startswith("aarch64_32"))
return EndianKind::LITTLE;
return EndianKind::INVALID;
}
// ARM, Thumb, AArch64
ARM::ISAKind ARM::parseArchISA(StringRef Arch) {
return StringSwitch<ISAKind>(Arch)
.StartsWith("aarch64", ISAKind::AARCH64)
.StartsWith("arm64", ISAKind::AARCH64)
.StartsWith("thumb", ISAKind::THUMB)
.StartsWith("arm", ISAKind::ARM)
.Default(ISAKind::INVALID);
}
unsigned ARM::parseFPU(StringRef FPU) {
StringRef Syn = getFPUSynonym(FPU);
for (const auto F : FPUNames) {
if (Syn == F.getName())
return F.ID;
}
return FK_INVALID;
}
ARM::NeonSupportLevel ARM::getFPUNeonSupportLevel(unsigned FPUKind) {
if (FPUKind >= FK_LAST)
return NeonSupportLevel::None;
return FPUNames[FPUKind].NeonSupport;
}
// MArch is expected to be of the form (arm|thumb)?(eb)?(v.+)?(eb)?, but
// (iwmmxt|xscale)(eb)? is also permitted. If the former, return
// "v.+", if the latter, return unmodified string, minus 'eb'.
// If invalid, return empty string.
StringRef ARM::getCanonicalArchName(StringRef Arch) {
size_t offset = StringRef::npos;
StringRef A = Arch;
StringRef Error = "";
// Begins with "arm" / "thumb", move past it.
if (A.startswith("arm64_32"))
offset = 8;
else if (A.startswith("arm64"))
offset = 5;
else if (A.startswith("aarch64_32"))
offset = 10;
else if (A.startswith("arm"))
offset = 3;
else if (A.startswith("thumb"))
offset = 5;
else if (A.startswith("aarch64")) {
offset = 7;
// AArch64 uses "_be", not "eb" suffix.
if (A.find("eb") != StringRef::npos)
return Error;
if (A.substr(offset, 3) == "_be")
offset += 3;
}
// Ex. "armebv7", move past the "eb".
if (offset != StringRef::npos && A.substr(offset, 2) == "eb")
offset += 2;
// Or, if it ends with eb ("armv7eb"), chop it off.
else if (A.endswith("eb"))
A = A.substr(0, A.size() - 2);
// Trim the head
if (offset != StringRef::npos)
A = A.substr(offset);
// Empty string means offset reached the end, which means it's valid.
if (A.empty())
return Arch;
// Only match non-marketing names
if (offset != StringRef::npos) {
// Must start with 'vN'.
if (A.size() >= 2 && (A[0] != 'v' || !std::isdigit(A[1])))
return Error;
// Can't have an extra 'eb'.
if (A.find("eb") != StringRef::npos)
return Error;
}
// Arch will either be a 'v' name (v7a) or a marketing name (xscale).
return A;
}
StringRef ARM::getFPUSynonym(StringRef FPU) {
return StringSwitch<StringRef>(FPU)
.Cases("fpa", "fpe2", "fpe3", "maverick", "invalid") // Unsupported
.Case("vfp2", "vfpv2")
.Case("vfp3", "vfpv3")
.Case("vfp4", "vfpv4")
.Case("vfp3-d16", "vfpv3-d16")
.Case("vfp4-d16", "vfpv4-d16")
.Cases("fp4-sp-d16", "vfpv4-sp-d16", "fpv4-sp-d16")
.Cases("fp4-dp-d16", "fpv4-dp-d16", "vfpv4-d16")
.Case("fp5-sp-d16", "fpv5-sp-d16")
.Cases("fp5-dp-d16", "fpv5-dp-d16", "fpv5-d16")
// FIXME: Clang uses it, but it's bogus, since neon defaults to vfpv3.
.Case("neon-vfpv3", "neon")
.Default(FPU);
}
StringRef ARM::getFPUName(unsigned FPUKind) {
if (FPUKind >= FK_LAST)
return StringRef();
return FPUNames[FPUKind].getName();
}
ARM::FPUVersion ARM::getFPUVersion(unsigned FPUKind) {
if (FPUKind >= FK_LAST)
return FPUVersion::NONE;
return FPUNames[FPUKind].FPUVer;
}
ARM::FPURestriction ARM::getFPURestriction(unsigned FPUKind) {
if (FPUKind >= FK_LAST)
return FPURestriction::None;
return FPUNames[FPUKind].Restriction;
}
unsigned ARM::getDefaultFPU(StringRef CPU, ARM::ArchKind AK) {
if (CPU == "generic")
return ARM::ARCHNames[static_cast<unsigned>(AK)].DefaultFPU;
return StringSwitch<unsigned>(CPU)
#define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \
.Case(NAME, DEFAULT_FPU)
#include "llvm/Support/ARMTargetParser.def"
.Default(ARM::FK_INVALID);
}
unsigned ARM::getDefaultExtensions(StringRef CPU, ARM::ArchKind AK) {
if (CPU == "generic")
return ARM::ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions;
return StringSwitch<unsigned>(CPU)
#define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \
.Case(NAME, \
ARCHNames[static_cast<unsigned>(ArchKind::ID)].ArchBaseExtensions | \
DEFAULT_EXT)
#include "llvm/Support/ARMTargetParser.def"
.Default(ARM::AEK_INVALID);
}
bool ARM::getHWDivFeatures(unsigned HWDivKind,
std::vector<StringRef> &Features) {
if (HWDivKind == AEK_INVALID)
return false;
if (HWDivKind & AEK_HWDIVARM)
Features.push_back("+hwdiv-arm");
else
Features.push_back("-hwdiv-arm");
if (HWDivKind & AEK_HWDIVTHUMB)
Features.push_back("+hwdiv");
else
Features.push_back("-hwdiv");
return true;
}
bool ARM::getExtensionFeatures(unsigned Extensions,
std::vector<StringRef> &Features) {
if (Extensions == AEK_INVALID)
return false;
for (const auto AE : ARCHExtNames) {
if ((Extensions & AE.ID) == AE.ID && AE.Feature)
Features.push_back(AE.Feature);
else if (AE.NegFeature)
Features.push_back(AE.NegFeature);
}
return getHWDivFeatures(Extensions, Features);
}
StringRef ARM::getArchName(ARM::ArchKind AK) {
return ARCHNames[static_cast<unsigned>(AK)].getName();
}
StringRef ARM::getCPUAttr(ARM::ArchKind AK) {
return ARCHNames[static_cast<unsigned>(AK)].getCPUAttr();
}
StringRef ARM::getSubArch(ARM::ArchKind AK) {
return ARCHNames[static_cast<unsigned>(AK)].getSubArch();
}
unsigned ARM::getArchAttr(ARM::ArchKind AK) {
return ARCHNames[static_cast<unsigned>(AK)].ArchAttr;
}
StringRef ARM::getArchExtName(unsigned ArchExtKind) {
for (const auto AE : ARCHExtNames) {
if (ArchExtKind == AE.ID)
return AE.getName();
}
return StringRef();
}
static bool stripNegationPrefix(StringRef &Name) {
if (Name.startswith("no")) {
Name = Name.substr(2);
return true;
}
return false;
}
StringRef ARM::getArchExtFeature(StringRef ArchExt) {
bool Negated = stripNegationPrefix(ArchExt);
for (const auto AE : ARCHExtNames) {
if (AE.Feature && ArchExt == AE.getName())
return StringRef(Negated ? AE.NegFeature : AE.Feature);
}
return StringRef();
}
static unsigned findDoublePrecisionFPU(unsigned InputFPUKind) {
const ARM::FPUName &InputFPU = ARM::FPUNames[InputFPUKind];
// If the input FPU already supports double-precision, then there
// isn't any different FPU we can return here.
//
// The current available FPURestriction values are None (no
// restriction), D16 (only 16 d-regs) and SP_D16 (16 d-regs
// and single precision only); there's no value representing
// SP restriction without D16. So this test just means 'is it
// SP only?'.
if (InputFPU.Restriction != ARM::FPURestriction::SP_D16)
return ARM::FK_INVALID;
// Otherwise, look for an FPU entry with all the same fields, except
// that SP_D16 has been replaced with just D16, representing adding
// double precision and not changing anything else.
for (const ARM::FPUName &CandidateFPU : ARM::FPUNames) {
if (CandidateFPU.FPUVer == InputFPU.FPUVer &&
CandidateFPU.NeonSupport == InputFPU.NeonSupport &&
CandidateFPU.Restriction == ARM::FPURestriction::D16) {
return CandidateFPU.ID;
}
}
// nothing found
return ARM::FK_INVALID;
}
static unsigned getAEKID(StringRef ArchExtName) {
for (const auto AE : ARM::ARCHExtNames)
if (AE.getName() == ArchExtName)
return AE.ID;
return ARM::AEK_INVALID;
}
bool ARM::appendArchExtFeatures(
StringRef CPU, ARM::ArchKind AK, StringRef ArchExt,
std::vector<StringRef> &Features) {
size_t StartingNumFeatures = Features.size();
const bool Negated = stripNegationPrefix(ArchExt);
unsigned ID = getAEKID(ArchExt);
if (ID == AEK_INVALID)
return false;
for (const auto AE : ARCHExtNames) {
if (Negated && (AE.ID & ID) == ID && AE.NegFeature)
Features.push_back(AE.NegFeature);
else if (AE.ID == ID && AE.Feature)
Features.push_back(AE.Feature);
}
if (CPU == "")
CPU = "generic";
if (ArchExt == "fp" || ArchExt == "fp.dp") {
unsigned FPUKind;
if (ArchExt == "fp.dp") {
if (Negated) {
Features.push_back("-fp64");
return true;
}
FPUKind = findDoublePrecisionFPU(getDefaultFPU(CPU, AK));
} else if (Negated) {
FPUKind = ARM::FK_NONE;
} else {
FPUKind = getDefaultFPU(CPU, AK);
}
return ARM::getFPUFeatures(FPUKind, Features);
}
return StartingNumFeatures != Features.size();
}
StringRef ARM::getHWDivName(unsigned HWDivKind) {
for (const auto D : HWDivNames) {
if (HWDivKind == D.ID)
return D.getName();
}
return StringRef();
}
StringRef ARM::getDefaultCPU(StringRef Arch) {
ArchKind AK = parseArch(Arch);
if (AK == ArchKind::INVALID)
return StringRef();
// Look for multiple AKs to find the default for pair AK+Name.
for (const auto CPU : CPUNames) {
if (CPU.ArchID == AK && CPU.Default)
return CPU.getName();
}
// If we can't find a default then target the architecture instead
return "generic";
}
unsigned ARM::parseHWDiv(StringRef HWDiv) {
StringRef Syn = getHWDivSynonym(HWDiv);
for (const auto D : HWDivNames) {
if (Syn == D.getName())
return D.ID;
}
return AEK_INVALID;
}
unsigned ARM::parseArchExt(StringRef ArchExt) {
for (const auto A : ARCHExtNames) {
if (ArchExt == A.getName())
return A.ID;
}
return AEK_INVALID;
}
ARM::ArchKind ARM::parseCPUArch(StringRef CPU) {
for (const auto C : CPUNames) {
if (CPU == C.getName())
return C.ArchID;
}
return ArchKind::INVALID;
}
void ARM::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) {
for (const CpuNames<ArchKind> &Arch : CPUNames) {
if (Arch.ArchID != ArchKind::INVALID)
Values.push_back(Arch.getName());
}
}
StringRef ARM::computeDefaultTargetABI(const Triple &TT, StringRef CPU) {
StringRef ArchName =
CPU.empty() ? TT.getArchName() : getArchName(parseCPUArch(CPU));
if (TT.isOSBinFormatMachO()) {
if (TT.getEnvironment() == Triple::EABI ||
TT.getOS() == Triple::UnknownOS ||
parseArchProfile(ArchName) == ProfileKind::M)
return "aapcs";
if (TT.isWatchABI())
return "aapcs16";
return "apcs-gnu";
} else if (TT.isOSWindows())
// FIXME: this is invalid for WindowsCE.
return "aapcs";
// Select the default based on the platform.
switch (TT.getEnvironment()) {
case Triple::Android:
case Triple::GNUEABI:
case Triple::GNUEABIHF:
case Triple::MuslEABI:
case Triple::MuslEABIHF:
return "aapcs-linux";
case Triple::EABIHF:
case Triple::EABI:
return "aapcs";
default:
if (TT.isOSNetBSD())
return "apcs-gnu";
if (TT.isOSOpenBSD())
return "aapcs-linux";
return "aapcs";
}
}