Index: clang/lib/Driver/ToolChains/Arch/ARM.h =================================================================== --- clang/lib/Driver/ToolChains/Arch/ARM.h +++ clang/lib/Driver/ToolChains/Arch/ARM.h @@ -13,6 +13,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/Option/Option.h" +#include "llvm/Support/TargetParser.h" #include #include @@ -25,6 +26,8 @@ const llvm::Triple &Triple); const std::string getARMArch(llvm::StringRef Arch, const llvm::Triple &Triple); StringRef getARMCPUForMArch(llvm::StringRef Arch, const llvm::Triple &Triple); +llvm::ARM::ArchKind getLLVMArchKindForARM(StringRef CPU, StringRef Arch, + const llvm::Triple &Triple); StringRef getLLVMArchSuffixForARM(llvm::StringRef CPU, llvm::StringRef Arch, const llvm::Triple &Triple); Index: clang/lib/Driver/ToolChains/Arch/ARM.cpp =================================================================== --- clang/lib/Driver/ToolChains/Arch/ARM.cpp +++ clang/lib/Driver/ToolChains/Arch/ARM.cpp @@ -72,15 +72,13 @@ // Decode ARM features from string like +[no]featureA+[no]featureB+... static bool DecodeARMFeatures(const Driver &D, StringRef text, + StringRef CPU, llvm::ARM::ArchKind ArchKind, std::vector &Features) { SmallVector Split; text.split(Split, StringRef("+"), -1, false); for (StringRef Feature : Split) { - StringRef FeatureName = llvm::ARM::getArchExtFeature(Feature); - if (!FeatureName.empty()) - Features.push_back(FeatureName); - else + if (!appendArchExtFeatures(CPU, ArchKind, Feature, Features)) return false; } return true; @@ -106,8 +104,10 @@ std::pair Split = ArchName.split("+"); std::string MArch = arm::getARMArch(ArchName, Triple); - if (llvm::ARM::parseArch(MArch) == llvm::ARM::ArchKind::INVALID || - (Split.second.size() && !DecodeARMFeatures(D, Split.second, Features))) + llvm::ARM::ArchKind ArchKind = llvm::ARM::parseArch(MArch); + if (ArchKind == llvm::ARM::ArchKind::INVALID || + (Split.second.size() && !DecodeARMFeatures( + D, Split.second, "generic", ArchKind, Features))) D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args); } @@ -119,8 +119,11 @@ std::pair Split = CPUName.split("+"); std::string CPU = arm::getARMTargetCPU(CPUName, ArchName, Triple); - if (arm::getLLVMArchSuffixForARM(CPU, ArchName, Triple).empty() || - (Split.second.size() && !DecodeARMFeatures(D, Split.second, Features))) + llvm::ARM::ArchKind ArchKind = + arm::getLLVMArchKindForARM(CPU, ArchName, Triple); + if (ArchKind == llvm::ARM::ArchKind::INVALID || + (Split.second.size() && !DecodeARMFeatures( + D, Split.second, CPU, ArchKind, Features))) D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args); } @@ -620,11 +623,12 @@ return getARMCPUForMArch(Arch, Triple); } -/// getLLVMArchSuffixForARM - Get the LLVM arch name to use for a particular -/// CPU (or Arch, if CPU is generic). -// FIXME: This is redundant with -mcpu, why does LLVM use this. -StringRef arm::getLLVMArchSuffixForARM(StringRef CPU, StringRef Arch, - const llvm::Triple &Triple) { +/// getLLVMArchSuffixForARM - Get the LLVM ArchKind value to use for a +/// particular CPU (or Arch, if CPU is generic). This is needed to +/// pass to functions like llvm::ARM::getDefaultFPU which need an +/// ArchKind as well as a CPU name. +llvm::ARM::ArchKind arm::getLLVMArchKindForARM(StringRef CPU, StringRef Arch, + const llvm::Triple &Triple) { llvm::ARM::ArchKind ArchKind; if (CPU == "generic") { std::string ARMArch = tools::arm::getARMArch(Arch, Triple); @@ -640,6 +644,15 @@ ? llvm::ARM::ArchKind::ARMV7K : llvm::ARM::parseCPUArch(CPU); } + return ArchKind; +} + +/// getLLVMArchSuffixForARM - Get the LLVM arch name to use for a particular +/// CPU (or Arch, if CPU is generic). +// FIXME: This is redundant with -mcpu, why does LLVM use this. +StringRef arm::getLLVMArchSuffixForARM(StringRef CPU, StringRef Arch, + const llvm::Triple &Triple) { + llvm::ARM::ArchKind ArchKind = getLLVMArchKindForARM(CPU, Arch, Triple); if (ArchKind == llvm::ARM::ArchKind::INVALID) return ""; return llvm::ARM::getSubArch(ArchKind); Index: llvm/include/llvm/Support/ARMTargetParser.h =================================================================== --- llvm/include/llvm/Support/ARMTargetParser.h +++ llvm/include/llvm/Support/ARMTargetParser.h @@ -240,6 +240,8 @@ StringRef getSubArch(ArchKind AK); StringRef getArchExtName(unsigned ArchExtKind); StringRef getArchExtFeature(StringRef ArchExt); +bool appendArchExtFeatures(StringRef CPU, ARM::ArchKind AK, StringRef ArchExt, + std::vector &Features); StringRef getHWDivName(unsigned HWDivKind); // Information by Name Index: llvm/lib/Support/ARMTargetParser.cpp =================================================================== --- llvm/lib/Support/ARMTargetParser.cpp +++ llvm/lib/Support/ARMTargetParser.cpp @@ -484,22 +484,100 @@ return StringRef(); } -StringRef ARM::getArchExtFeature(StringRef ArchExt) { - if (ArchExt.startswith("no")) { - StringRef ArchExtBase(ArchExt.substr(2)); - for (const auto AE : ARCHExtNames) { - if (AE.NegFeature && ArchExtBase == AE.getName()) - return StringRef(AE.NegFeature); - } +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(AE.Feature); + 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; +} + +bool ARM::appendArchExtFeatures( + StringRef CPU, ARM::ArchKind AK, StringRef ArchExt, + std::vector &Features) { + StringRef StandardFeature = getArchExtFeature(ArchExt); + if (!StandardFeature.empty()) { + Features.push_back(StandardFeature); + return true; + } + + bool Negated = stripNegationPrefix(ArchExt); + + if (ArchExt == "fp" || ArchExt == "fp.dp") { + unsigned FPUKind; + + if (ArchExt == "fp.dp") { + unsigned DoubleFPUKind = findDoublePrecisionFPU(getDefaultFPU(CPU, AK)); + + // If the default FPU already supports double-precision, or if + // there is no double-prec FPU that extends it, then "fp.dp" + // doesn't have a separate meaning, and we treat it as an + // invalid extension name. + if (DoubleFPUKind == FK_INVALID) + return false; + + // If there _is_ a separate double-precision FPU, then "nofp.dp" + // should disable just the double-precision extension, leaving + // the base FPU still enabled if it previously was. + if (Negated) { + Features.push_back("-fp64"); + return true; + } + + // Otherwise, select the double-precision FPU. + FPUKind = DoubleFPUKind; + } else if (Negated) { + FPUKind = ARM::FK_NONE; + } else { + FPUKind = getDefaultFPU(CPU, AK); + if (FPUKind == ARM::FK_NONE) + return false; + } + return ARM::getFPUFeatures(FPUKind, Features); + } + + return false; +} + StringRef ARM::getHWDivName(unsigned HWDivKind) { for (const auto D : HWDivNames) { if (HWDivKind == D.ID)