| //===-- AMDGPUTargetParser - Parser for AMDGPU 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 AMDGPU hardware features. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/TargetParser/AMDGPUTargetParser.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringTable.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/TargetParser/Triple.h" |
| #include <array> |
| #include <cassert> |
| |
| using namespace llvm; |
| using namespace AMDGPU; |
| |
| namespace { |
| constexpr unsigned NumAMDGPUSubArches = |
| Triple::LastAMDGPUSubArch - Triple::FirstAMDGPUSubArch + 1; |
| |
| // A legacy GPU name (e.g. "tahiti") mapped to the GPUKind it aliases. |
| struct GPUNameAlias { |
| StringTable::Offset AltName; |
| GPUKind Kind; |
| }; |
| |
| // Per-GPU data for the AMDGCN GPUKinds, from the generated table below. |
| struct GPUInfo { |
| StringTable::Offset Name; |
| Triple::SubArchType SubArch; |
| unsigned ArchFeatures; |
| AMDGPUFeatureBitset Features; |
| IsaVersion Version; |
| StringTable::Offset FamilyName; |
| StringTable::Offset BaseName; // The canonical device name for a variant. |
| uint8_t MaxWavesPerEU; |
| uint32_t MaxHWAddressableLocalMemorySize; |
| }; |
| |
| // Per-GPU data for the R600 GPUKinds. |
| struct R600Info { |
| StringTable::Offset Name; |
| R600FeatureKind ArchFeatures; |
| }; |
| |
| #define GET_AMDGPU_NAME_TABLE |
| #define GET_AMDGPU_GPU_TABLE |
| #define GET_AMDGPU_GPU_ALIAS_TABLE |
| #define GET_AMDGPU_MAJOR_SUBARCH |
| #define GET_AMDGPU_SUBARCH_NAME |
| #define GET_AMDGPU_FEATURE_NAME_TABLE |
| #include "llvm/TargetParser/AMDGPUTargetParserDef.inc" |
| |
| #define GET_R600_NAME_TABLE |
| #define GET_R600_GPU_TABLE |
| #define GET_R600_GPU_ALIAS_TABLE |
| #include "llvm/TargetParser/R600TargetParserDef.inc" |
| |
| // The string tables holding GPU-name-derived strings as offsets. R600 and |
| // AMDGPU come from separate generated headers, each with its own pool. |
| constexpr StringTable AMDGPUNameStrTab = AMDGPUNameTable; |
| constexpr StringTable R600NameStrTab = R600NameTable; |
| |
| // Look up the GPUInfo row for an AMDGCN GPUKind, or nullptr for GK_NONE / a |
| // non-AMDGCN (R600) kind. |
| const GPUInfo *getAMDGPUInfo(GPUKind AK) { |
| if (AK < AMDGPUFirstGPUKind) |
| return nullptr; |
| unsigned Idx = AK - AMDGPUFirstGPUKind; |
| if (Idx >= std::size(AMDGPUGPUTable)) |
| return nullptr; |
| return &AMDGPUGPUTable[Idx]; |
| } |
| |
| // Look up the R600Info row for an R600 GPUKind, or nullptr for a non-R600 kind. |
| const R600Info *getR600Info(GPUKind AK) { |
| if (AK < R600FirstGPUKind) |
| return nullptr; |
| unsigned Idx = AK - R600FirstGPUKind; |
| if (Idx >= std::size(R600GPUTable)) |
| return nullptr; |
| return &R600GPUTable[Idx]; |
| } |
| |
| // Scan a name -> GPUKind table (canonical names, then aliases) for \p CPU. |
| template <typename InfoT, size_t N, size_t M> |
| GPUKind parseArchImpl(StringRef CPU, const InfoT (&Table)[N], GPUKind FirstKind, |
| const StringTable &StrTab, |
| const GPUNameAlias (&Aliases)[M]) { |
| for (unsigned I = 0; I != N; ++I) { |
| if (CPU == StrTab[Table[I].Name]) |
| return static_cast<GPUKind>(FirstKind + I); |
| } |
| |
| for (const GPUNameAlias &A : Aliases) { |
| if (CPU == StrTab[A.AltName]) |
| return A.Kind; |
| } |
| |
| return GK_NONE; |
| } |
| |
| // Reverse map: SubArch -> GPUKind, indexed by (SubArch - FirstAMDGPUSubArch). |
| // Subarches with no GPU (incl. the NoSubArch pseudo targets) map to GK_NONE. |
| constexpr std::array<GPUKind, NumAMDGPUSubArches> AMDGPUSubArchToGPUKind = [] { |
| std::array<GPUKind, NumAMDGPUSubArches> Map{}; |
| |
| for (unsigned I = 0; I < std::size(AMDGPUGPUTable); ++I) { |
| Triple::SubArchType SubArch = AMDGPUGPUTable[I].SubArch; |
| if (SubArch != Triple::NoSubArch) { |
| Map[SubArch - Triple::FirstAMDGPUSubArch] = |
| static_cast<GPUKind>(AMDGPUFirstGPUKind + I); |
| } |
| } |
| return Map; |
| }(); |
| |
| /// SubArch -> major-family, indexed by (SubArch - FirstAMDGPUSubArch). |
| constexpr std::array<Triple::SubArchType, NumAMDGPUSubArches> |
| AMDGPUMajorFamilies = [] { |
| std::array<Triple::SubArchType, NumAMDGPUSubArches> Map{}; |
| |
| for (unsigned I = 0; I < NumAMDGPUSubArches; ++I) { |
| Map[I] = |
| static_cast<Triple::SubArchType>(Triple::FirstAMDGPUSubArch + I); |
| } |
| |
| for (const AMDGPUMajorSubArchEntry &Entry : AMDGPUMajorSubArch) |
| Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = Entry.Major; |
| return Map; |
| }(); |
| |
| // SubArch -> name-offset, indexed by (SubArch - FirstAMDGPUSubArch). Unmapped |
| // subarches keep offset 0 (the empty string). |
| constexpr std::array<StringTable::Offset, NumAMDGPUSubArches> |
| AMDGPUSubArchNameOffsets = [] { |
| std::array<StringTable::Offset, NumAMDGPUSubArches> Map{}; |
| for (const AMDGPUSubArchNameEntry &Entry : AMDGPUSubArchNames) |
| Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = Entry.NameOffset; |
| return Map; |
| }(); |
| |
| // SubArch -> triple-name-offset (e.g. "amdgpu9.00"), like |
| // AMDGPUSubArchNameOffsets. |
| constexpr std::array<StringTable::Offset, NumAMDGPUSubArches> |
| AMDGPUSubArchTripleNameOffsets = [] { |
| std::array<StringTable::Offset, NumAMDGPUSubArches> Map{}; |
| for (const AMDGPUSubArchNameEntry &Entry : AMDGPUSubArchNames) |
| Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = |
| Entry.TripleNameOffset; |
| return Map; |
| }(); |
| } // namespace |
| |
| StringRef llvm::AMDGPU::getArchFamilyNameAMDGCN(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? AMDGPUNameStrTab[Info->FamilyName] : ""; |
| } |
| |
| Triple::SubArchType llvm::AMDGPU::getSubArch(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? Info->SubArch : Triple::SubArchType::NoSubArch; |
| } |
| |
| StringRef llvm::AMDGPU::getBaseArchNameAMDGCN(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? AMDGPUNameStrTab[Info->BaseName] : ""; |
| } |
| |
| AMDGPU::GPUKind |
| llvm::AMDGPU::getGPUKindFromSubArch(Triple::SubArchType SubArch) { |
| if (SubArch < Triple::FirstAMDGPUSubArch || |
| SubArch > Triple::LastAMDGPUSubArch) |
| return GK_NONE; |
| return AMDGPUSubArchToGPUKind[SubArch - Triple::FirstAMDGPUSubArch]; |
| } |
| |
| Triple::SubArchType AMDGPU::getMajorSubArch(Triple::SubArchType X) { |
| if (X < Triple::FirstAMDGPUSubArch || X > Triple::LastAMDGPUSubArch) |
| return Triple::NoSubArch; |
| return AMDGPUMajorFamilies[X - Triple::FirstAMDGPUSubArch]; |
| } |
| |
| bool AMDGPU::isSubArchCompatible(Triple::SubArchType A, Triple::SubArchType B) { |
| if (A == B || A == Triple::NoSubArch || B == Triple::NoSubArch) |
| return true; |
| |
| Triple::SubArchType MajorA = AMDGPU::getMajorSubArch(A); |
| Triple::SubArchType MajorB = AMDGPU::getMajorSubArch(B); |
| |
| // One side is the major-family subarch covering the other's family. |
| if (A == MajorA) |
| return MajorA == MajorB; |
| if (B == MajorB) |
| return MajorA == MajorB; |
| |
| return false; |
| } |
| |
| bool AMDGPU::isCPUValidForSubArch(Triple::SubArchType SubArch, GPUKind AK) { |
| // An unrecognized GPU is never valid. |
| if (AK == GK_NONE) |
| return false; |
| // A legacy triple without a subarch accepts any known GPU. |
| if (SubArch == Triple::NoSubArch) |
| return true; |
| |
| // Reject the dummy "generic" targets |
| Triple::SubArchType GPUSubArch = getSubArch(AK); |
| if (GPUSubArch == Triple::NoSubArch) |
| return false; |
| |
| return isSubArchCompatible(GPUSubArch, SubArch); |
| } |
| |
| bool AMDGPU::isCPUValidForSubArch(Triple::SubArchType SubArch, StringRef CPU) { |
| return isCPUValidForSubArch(SubArch, parseArchAMDGCN(CPU)); |
| } |
| |
| bool AMDGPU::isPseudoTarget(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info && Info->SubArch == Triple::NoSubArch; |
| } |
| |
| bool AMDGPU::isPseudoTarget(StringRef CPU) { |
| return isPseudoTarget(parseArchAMDGCN(CPU)); |
| } |
| |
| bool AMDGPU::isSubArchCompatible(const Triple &A, const Triple &B) { |
| // Tolerate subarch mismatch if one entry is none. This is a hack for bitcode |
| // libraries. |
| // There's a missing enum entry for an unknown subarch. Make sure the |
| // subarch is really empty. |
| if (A.getSubArch() == Triple::NoSubArch) |
| return A.getArchName().size() == 6; |
| |
| if (B.getSubArch() == Triple::NoSubArch) |
| return B.getArchName().size() == 6; |
| |
| return isSubArchCompatible(A.getSubArch(), B.getSubArch()); |
| } |
| |
| std::string AMDGPU::mergeSubArch(const Triple &A, const Triple &B) { |
| if (A.getSubArch() == Triple::NoSubArch) |
| return B.str(); |
| if (B.getSubArch() == Triple::NoSubArch) |
| return A.str(); |
| |
| Triple::SubArchType MajorA = AMDGPU::getMajorSubArch(A.getSubArch()); |
| Triple::SubArchType MajorB = AMDGPU::getMajorSubArch(B.getSubArch()); |
| |
| // With a compatible major arch, return the specific subarch. |
| if (A.getSubArch() == MajorA) { |
| if (MajorA == MajorB) |
| return B.str(); |
| } |
| |
| if (B.getSubArch() == MajorB) { |
| if (MajorA == MajorB) |
| return A.str(); |
| } |
| |
| // Invalid case. |
| return B.str(); |
| } |
| |
| StringRef llvm::AMDGPU::getArchNameAMDGCN(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? AMDGPUNameStrTab[Info->Name] : ""; |
| } |
| |
| StringRef llvm::AMDGPU::getArchNameFromSubArch(Triple::SubArchType SubArch) { |
| if (SubArch < Triple::FirstAMDGPUSubArch || |
| SubArch > Triple::LastAMDGPUSubArch) |
| return ""; |
| return AMDGPUNameStrTab[AMDGPUSubArchNameOffsets[SubArch - |
| Triple::FirstAMDGPUSubArch]]; |
| } |
| |
| StringRef llvm::AMDGPU::getSubArchName(Triple::SubArchType SubArch) { |
| if (SubArch == Triple::NoSubArch) |
| return AMDGPUNameStrTab[AMDGPUNoSubArchNameOffset]; |
| |
| assert(SubArch >= Triple::FirstAMDGPUSubArch && |
| SubArch <= Triple::LastAMDGPUSubArch && |
| "expected an AMDGPU subarch or NoSubArch"); |
| return AMDGPUNameStrTab |
| [AMDGPUSubArchTripleNameOffsets[SubArch - Triple::FirstAMDGPUSubArch]]; |
| } |
| |
| StringRef llvm::AMDGPU::getArchNameR600(GPUKind AK) { |
| const R600Info *Info = getR600Info(AK); |
| return Info ? R600NameStrTab[Info->Name] : ""; |
| } |
| |
| AMDGPU::GPUKind llvm::AMDGPU::parseArchAMDGCN(StringRef CPU) { |
| return parseArchImpl(CPU, AMDGPUGPUTable, AMDGPUFirstGPUKind, |
| AMDGPUNameStrTab, AMDGPUGPUAliases); |
| } |
| |
| AMDGPU::GPUKind llvm::AMDGPU::parseArchR600(StringRef CPU) { |
| return parseArchImpl(CPU, R600GPUTable, R600FirstGPUKind, R600NameStrTab, |
| R600GPUAliases); |
| } |
| |
| unsigned AMDGPU::getArchAttrAMDGCN(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? Info->ArchFeatures : FEATURE_NONE; |
| } |
| |
| unsigned AMDGPU::getArchAttrAMDGCN(Triple::SubArchType SubArch) { |
| const GPUInfo *Info = getAMDGPUInfo(getGPUKindFromSubArch(SubArch)); |
| return Info ? Info->ArchFeatures : FEATURE_NONE; |
| } |
| |
| R600FeatureKind AMDGPU::getArchAttrR600(GPUKind AK) { |
| const R600Info *Info = getR600Info(AK); |
| return Info ? Info->ArchFeatures : R600_FEATURE_NONE; |
| } |
| |
| const AMDGPUFeatureBitset &AMDGPU::getFeatureBitset(GPUKind AK) { |
| static constexpr AMDGPUFeatureBitset Empty{}; |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? Info->Features : Empty; |
| } |
| |
| void AMDGPU::getFeatureNames(const AMDGPUFeatureBitset &Features, |
| SmallVectorImpl<StringRef> &Names) { |
| for (unsigned I = 0; I != NUM_FEATURES; ++I) { |
| if (Features.test(I)) |
| Names.push_back(AMDGPUNameStrTab[AMDGPUFeatureNames[I]]); |
| } |
| } |
| |
| void AMDGPU::fillValidArchListAMDGCN(SmallVectorImpl<StringRef> &Values, |
| Triple::SubArchType SubArch) { |
| // XXX: Should this only report unique canonical names? |
| // An alias shares its GPU's GPUKind, so it is filtered alongside it. |
| for (unsigned I = 0; I != std::size(AMDGPUGPUTable); ++I) { |
| GPUKind Kind = static_cast<GPUKind>(AMDGPUFirstGPUKind + I); |
| if (AMDGPUGPUTable[I].SubArch != Triple::NoSubArch && |
| isCPUValidForSubArch(SubArch, Kind)) |
| Values.push_back(AMDGPUNameStrTab[AMDGPUGPUTable[I].Name]); |
| } |
| |
| for (const GPUNameAlias &A : AMDGPUGPUAliases) { |
| if (isCPUValidForSubArch(SubArch, A.Kind)) |
| Values.push_back(AMDGPUNameStrTab[A.AltName]); |
| } |
| } |
| |
| void AMDGPU::fillValidArchListR600(SmallVectorImpl<StringRef> &Values) { |
| for (const R600Info &Info : R600GPUTable) |
| Values.push_back(R600NameStrTab[Info.Name]); |
| for (const GPUNameAlias &A : R600GPUAliases) |
| Values.push_back(R600NameStrTab[A.AltName]); |
| } |
| |
| AMDGPU::IsaVersion AMDGPU::getIsaVersion(StringRef GPU) { |
| const GPUInfo *Info = getAMDGPUInfo(parseArchAMDGCN(GPU)); |
| return Info ? Info->Version : IsaVersion{0, 0, 0}; |
| } |
| |
| AMDGPU::IsaVersion AMDGPU::getIsaVersion(Triple::SubArchType SubArch) { |
| const GPUInfo *Info = getAMDGPUInfo(getGPUKindFromSubArch(SubArch)); |
| return Info ? Info->Version : IsaVersion{0, 0, 0}; |
| } |
| |
| unsigned AMDGPU::getTotalNumSGPRs(GPUKind AK) { |
| IsaVersion Version = getIsaVersion(getSubArch(AK)); |
| if (Version.Major >= 8) |
| return 800; |
| return 512; |
| } |
| |
| unsigned AMDGPU::getTotalNumSGPRs(Triple::SubArchType SubArch) { |
| IsaVersion Version = getIsaVersion(SubArch); |
| if (Version.Major >= 8) |
| return 800; |
| return 512; |
| } |
| |
| unsigned AMDGPU::getAddressableNumSGPRs(GPUKind AK) { |
| if (getFeatureBitset(AK).test(FEAT_SGPR_INIT_BUG)) |
| return FIXED_NUM_SGPRS_FOR_INIT_BUG; |
| |
| IsaVersion Version = getIsaVersion(getSubArch(AK)); |
| if (Version.Major >= 10) |
| return 106; |
| if (Version.Major >= 8) |
| return 102; |
| return 104; |
| } |
| |
| unsigned AMDGPU::getAddressableNumSGPRs(Triple::SubArchType SubArch) { |
| if (getFeatureBitset(getGPUKindFromSubArch(SubArch)).test(FEAT_SGPR_INIT_BUG)) |
| return FIXED_NUM_SGPRS_FOR_INIT_BUG; |
| |
| IsaVersion Version = getIsaVersion(SubArch); |
| if (Version.Major >= 10) |
| return 106; |
| if (Version.Major >= 8) |
| return 102; |
| return 104; |
| } |
| |
| unsigned AMDGPU::getSGPRAllocGranule(GPUKind AK) { |
| IsaVersion Version = getIsaVersion(getSubArch(AK)); |
| if (Version.Major >= 10) |
| return getAddressableNumSGPRs(AK); |
| if (Version.Major >= 8) |
| return 16; |
| return 8; |
| } |
| |
| unsigned AMDGPU::getSGPRAllocGranule(Triple::SubArchType SubArch) { |
| IsaVersion Version = getIsaVersion(SubArch); |
| if (Version.Major >= 10) |
| return getAddressableNumSGPRs(SubArch); |
| if (Version.Major >= 8) |
| return 16; |
| return 8; |
| } |
| |
| unsigned AMDGPU::getMaxHWAddressableLocalMemorySize(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? Info->MaxHWAddressableLocalMemorySize : 32768; |
| } |
| |
| unsigned |
| AMDGPU::getMaxHWAddressableLocalMemorySize(Triple::SubArchType SubArch) { |
| return getMaxHWAddressableLocalMemorySize(getGPUKindFromSubArch(SubArch)); |
| } |
| |
| unsigned AMDGPU::getMaxWavesPerEU(GPUKind AK) { |
| const GPUInfo *Info = getAMDGPUInfo(AK); |
| return Info ? Info->MaxWavesPerEU : 10; |
| } |
| |
| unsigned AMDGPU::getMaxWavesPerEU(Triple::SubArchType SubArch) { |
| return getMaxWavesPerEU(getGPUKindFromSubArch(SubArch)); |
| } |
| |
| StringRef AMDGPU::getCanonicalArchName(const Triple &T, StringRef Arch) { |
| assert(T.isAMDGPU()); |
| auto ProcKind = T.isAMDGCN() ? parseArchAMDGCN(Arch) : parseArchR600(Arch); |
| if (ProcKind == GK_NONE) |
| return StringRef(); |
| |
| return T.isAMDGCN() ? getArchNameAMDGCN(ProcKind) : getArchNameR600(ProcKind); |
| } |
| |
| // Capability features clang queries via the feature bitset but must not |
| // serialize into the target-feature string. |
| // |
| // FIXME: This is hacky, we shouldn't have mismatches between the bitset and |
| // feature string map. |
| static const AMDGPUFeatureBitset FrontendOnlyFeatures = { |
| FEAT_FAST_FMAF, FEAT_FAST_DENORMAL_F32, FEAT_SUPPORTS_WAVE32, |
| FEAT_SUPPORTS_WGP, FEAT_XNACK_SUPPORT, FEAT_SRAMECC_SUPPORT, |
| FEAT_XNACK_ON_OFF_MODES}; |
| |
| // Add a GPU's features (minus the frontend-only ones) to \p Features. With \p |
| // Overwrite false, existing entries are kept so user -mattr overrides win. |
| static void addGPUFeatures(const GPUInfo &Info, bool Overwrite, |
| StringMap<bool> &Features) { |
| SmallVector<StringRef, NUM_FEATURES> Names; |
| getFeatureNames(Info.Features & ~FrontendOnlyFeatures, Names); |
| for (StringRef Name : Names) { |
| if (Overwrite) |
| Features[Name] = true; |
| else |
| Features.insert({Name, true}); |
| } |
| } |
| |
| /// Add a GPU's default features to \p Features (preserving user overrides) and |
| /// validate any requested wavesize. |
| static std::pair<FeatureError, StringRef> |
| fillAMDGCNFeatureMap(StringRef GPU, const Triple &T, |
| StringMap<bool> &Features) { |
| // With no explicit GPU, the triple's subarch identifies the target. |
| GPUKind Kind = GPU.empty() && T.getSubArch() != Triple::NoSubArch |
| ? getGPUKindFromSubArch(T.getSubArch()) |
| : parseArchAMDGCN(GPU); |
| const GPUInfo *Info = getAMDGPUInfo(Kind); |
| |
| // A bare subarch triple (no -target-cpu) still pins down the target, so it is |
| // not a null GPU. The target's native wavesize (if single-mode) is in the |
| // feature bitset; a dual-mode GPU has neither wave bit set. |
| const bool IsNullGPU = T.getSubArch() == Triple::NoSubArch && GPU.empty(); |
| const bool TargetHasWave32 = |
| Info && Info->Features.test(FEAT_WAVEFRONTSIZE32); |
| const bool TargetHasWave64 = |
| Info && Info->Features.test(FEAT_WAVEFRONTSIZE64); |
| |
| auto Wave32Itr = Features.find("wavefrontsize32"); |
| auto Wave64Itr = Features.find("wavefrontsize64"); |
| const bool EnableWave32 = |
| Wave32Itr != Features.end() && Wave32Itr->getValue(); |
| const bool EnableWave64 = |
| Wave64Itr != Features.end() && Wave64Itr->getValue(); |
| const bool DisableWave32 = |
| Wave32Itr != Features.end() && !Wave32Itr->getValue(); |
| const bool DisableWave64 = |
| Wave64Itr != Features.end() && !Wave64Itr->getValue(); |
| |
| if (EnableWave32 && EnableWave64) |
| return {AMDGPU::INVALID_FEATURE_COMBINATION, |
| "'+wavefrontsize32' and '+wavefrontsize64' are mutually exclusive"}; |
| if (DisableWave32 && DisableWave64) |
| return {AMDGPU::INVALID_FEATURE_COMBINATION, |
| "'-wavefrontsize32' and '-wavefrontsize64' are mutually exclusive"}; |
| |
| if (!IsNullGPU) { |
| if (TargetHasWave64) { |
| if (EnableWave32) |
| return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "+wavefrontsize32"}; |
| if (DisableWave64) |
| return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "-wavefrontsize64"}; |
| } |
| |
| if (TargetHasWave32) { |
| if (EnableWave64) |
| return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "+wavefrontsize64"}; |
| if (DisableWave32) |
| return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "-wavefrontsize32"}; |
| } |
| } |
| |
| // Don't assume any wavesize with an unknown subtarget. |
| // Default to wave32 if target supports both. |
| if (!IsNullGPU && !EnableWave32 && !EnableWave64 && !TargetHasWave32 && |
| !TargetHasWave64) |
| Features.insert({"wavefrontsize32", true}); |
| |
| // Merge the target defaults, keeping any user -mattr overrides. |
| if (Info) |
| addGPUFeatures(*Info, /*Overwrite=*/false, Features); |
| |
| return {NO_ERROR, StringRef()}; |
| } |
| |
| /// Fills Features map with default values for given target GPU. |
| /// \p Features contains overriding target features and this function returns |
| /// default target features with entries overridden by \p Features. |
| std::pair<FeatureError, StringRef> |
| AMDGPU::fillAMDGPUFeatureMap(StringRef GPU, const Triple &T, |
| StringMap<bool> &Features) { |
| // XXX - What does the member GPU mean if device name string passed here? |
| if (T.isSPIRV() && T.getOS() == Triple::OSType::AMDHSA) { |
| // AMDGCN SPIRV must support the union of all AMDGCN features. |
| SmallVector<StringRef> GPUs; |
| fillValidArchListAMDGCN(GPUs); |
| for (StringRef G : GPUs) |
| if (const GPUInfo *Info = getAMDGPUInfo(parseArchAMDGCN(G))) |
| addGPUFeatures(*Info, /*Overwrite=*/true, Features); |
| Features["wavefrontsize32"] = true; |
| Features["wavefrontsize64"] = true; |
| } else if (T.isAMDGCN()) { |
| return fillAMDGCNFeatureMap(GPU, T, Features); |
| } else { |
| if (GPU.empty()) |
| GPU = "r600"; |
| |
| switch (llvm::AMDGPU::parseArchR600(GPU)) { |
| case GK_CAYMAN: |
| case GK_CYPRESS: |
| case GK_RV770: |
| case GK_RV670: |
| // TODO: Add fp64 when implemented. |
| break; |
| case GK_TURKS: |
| case GK_CAICOS: |
| case GK_BARTS: |
| case GK_SUMO: |
| case GK_REDWOOD: |
| case GK_JUNIPER: |
| case GK_CEDAR: |
| case GK_RV730: |
| case GK_RV710: |
| case GK_RS880: |
| case GK_R630: |
| case GK_R600: |
| break; |
| default: |
| llvm_unreachable("Unhandled GPU!"); |
| } |
| } |
| return {NO_ERROR, StringRef()}; |
| } |
| |
| TargetID::TargetID(GPUKind Arch, const Triple &TT, TargetIDSetting XnackSetting, |
| TargetIDSetting SramEccSetting) |
| : Arch(Arch), |
| TargetTripleString(TT.normalize(Triple::CanonicalForm::FOUR_IDENT)), |
| XnackSetting(XnackSetting), SramEccSetting(SramEccSetting), |
| IsAMDHSA(TT.getOS() == Triple::AMDHSA) {} |
| |
| // Parse a feature modifier sign ("+"/"-"). Returns "Unsupported" if \p Sign is |
| // neither (i.e. the modifier is malformed). |
| static TargetIDSetting getTargetIDSettingFromFeatureString(StringRef Sign) { |
| if (Sign == "+") |
| return TargetIDSetting::On; |
| if (Sign == "-") |
| return TargetIDSetting::Off; |
| |
| return TargetIDSetting::Unsupported; |
| } |
| |
| // Derive the architecture from the processor name in \p TargetIDStr. "generic" |
| // and the empty processor name act as a wildcard. |
| static GPUKind getGPUKindFromTargetID(const Triple &TT, StringRef TargetIDStr) { |
| StringRef CPUName = TargetIDStr.split(':').first; |
| return (CPUName.empty() || CPUName == "generic") |
| ? getGPUKindFromSubArch(TT.getSubArch()) |
| : parseArchAMDGCN(CPUName); |
| } |
| |
| // Compute the xnack/sramecc settings for processor \p Arch from the |
| // processor+features string \p TargetIDStr |
| // (e.g. "gfx90a:xnack+:sramecc-"). Returns false if a modifier names an unknown |
| // or repeated feature, names one the processor does not support, or has a |
| // malformed sign. |
| static bool computeTargetIDFeatures(GPUKind Arch, StringRef TargetIDStr, |
| TargetIDSetting &XnackSetting, |
| TargetIDSetting &SramEccSetting) { |
| const AMDGPUFeatureBitset &Features = getFeatureBitset(Arch); |
| XnackSetting = Features.test(FEAT_XNACK_ON_OFF_MODES) |
| ? TargetIDSetting::Any |
| : TargetIDSetting::Unsupported; |
| SramEccSetting = Features.test(FEAT_SRAMECC_SUPPORT) |
| ? TargetIDSetting::Any |
| : TargetIDSetting::Unsupported; |
| |
| // The first component is the processor; the rest are feature modifiers of the |
| // form "<feature><+|->". |
| SmallVector<StringRef, 3> Split; |
| TargetIDStr.split(Split, ':'); |
| bool SeenXnack = false; |
| bool SeenSramEcc = false; |
| bool Valid = true; |
| for (unsigned I = 1, E = Split.size(); I != E; ++I) { |
| StringRef FeatureString = Split[I]; |
| if (FeatureString.consume_front("xnack")) { |
| TargetIDSetting Sign = getTargetIDSettingFromFeatureString(FeatureString); |
| if (SeenXnack || XnackSetting == TargetIDSetting::Unsupported || |
| Sign == TargetIDSetting::Unsupported) |
| Valid = false; |
| else |
| XnackSetting = Sign; |
| SeenXnack = true; |
| } else if (FeatureString.consume_front("sramecc")) { |
| TargetIDSetting Sign = getTargetIDSettingFromFeatureString(FeatureString); |
| if (SeenSramEcc || SramEccSetting == TargetIDSetting::Unsupported || |
| Sign == TargetIDSetting::Unsupported) |
| Valid = false; |
| else |
| SramEccSetting = Sign; |
| SeenSramEcc = true; |
| } else { |
| // Unknown feature name. |
| Valid = false; |
| } |
| } |
| return Valid; |
| } |
| |
| TargetID::TargetID(const Triple &TT, StringRef TargetIDStr) |
| : TargetID(getGPUKindFromTargetID(TT, TargetIDStr), TT, |
| TargetIDSetting::Unsupported, TargetIDSetting::Unsupported) { |
| // Derive the feature settings from the string. Validity is not checked here; |
| // parseTargetIDString validates untrusted input. |
| computeTargetIDFeatures(Arch, TargetIDStr, XnackSetting, SramEccSetting); |
| } |
| |
| std::optional<TargetID> TargetID::parse(const Triple &TT, |
| StringRef ProcAndFeatures) { |
| if (!TT.isAMDGCN()) |
| return std::nullopt; |
| |
| // Filter out unrecognized subarch suffixes. |
| if (TT.getSubArch() == Triple::NoSubArch && TT.getArchName() != "amdgcn") |
| return std::nullopt; |
| |
| // A named processor (i.e. not the empty/generic wildcard, which is resolved |
| // from the triple's subarch) must be a recognized GPU that is consistent with |
| // the triple's subarch. |
| StringRef CPUName = ProcAndFeatures.split(':').first; |
| if (!CPUName.empty() && CPUName != "generic" && |
| !isCPUValidForSubArch(TT.getSubArch(), CPUName)) |
| return std::nullopt; |
| |
| // Parse the processor and its feature modifiers, then construct directly from |
| // the resulting fields. |
| GPUKind Arch = getGPUKindFromTargetID(TT, ProcAndFeatures); |
| TargetIDSetting XnackSetting, SramEccSetting; |
| if (!computeTargetIDFeatures(Arch, ProcAndFeatures, XnackSetting, |
| SramEccSetting)) |
| return std::nullopt; |
| |
| return TargetID(Arch, TT, XnackSetting, SramEccSetting); |
| } |
| |
| std::optional<TargetID> |
| TargetID::parseTargetIDString(StringRef TargetIDDirective) { |
| // Split on '-' to get arch-vendor-os-environment-processor:features. There is |
| // a single dash separator after the 4-component triple, so the |
| // processor+features field must be present (even if empty). |
| SmallVector<StringRef, 5> Parts; |
| TargetIDDirective.split(Parts, '-', /*MaxSplit=*/4); |
| if (Parts.size() < 5) |
| return std::nullopt; |
| |
| return parse(Triple(Parts[0], Parts[1], Parts[2], Parts[3]), Parts[4]); |
| } |
| |
| // Append the explicit (On/Off) sramecc/xnack feature modifiers in canonical |
| // order, e.g. ":sramecc-:xnack+". |
| static void printFeatureModifiers(raw_ostream &OS, TargetIDSetting SramEcc, |
| TargetIDSetting Xnack) { |
| if (SramEcc == TargetIDSetting::Off) |
| OS << ":sramecc-"; |
| else if (SramEcc == TargetIDSetting::On) |
| OS << ":sramecc+"; |
| |
| if (Xnack == TargetIDSetting::Off) |
| OS << ":xnack-"; |
| else if (Xnack == TargetIDSetting::On) |
| OS << ":xnack+"; |
| } |
| |
| void TargetID::print(raw_ostream &StreamRep) const { |
| StreamRep << TargetTripleString << '-' << getArchNameAMDGCN(Arch); |
| |
| if (IsAMDHSA) |
| printFeatureModifiers(StreamRep, getSramEccSetting(), getXnackSetting()); |
| } |
| |
| std::string TargetID::toString() const { |
| std::string Str; |
| raw_string_ostream OS(Str); |
| OS << *this; |
| return Str; |
| } |
| |
| void TargetID::printCanonicalTargetIDString(raw_ostream &OS) const { |
| OS << getArchNameAMDGCN(Arch); |
| printFeatureModifiers(OS, getSramEccSetting(), getXnackSetting()); |
| } |
| |
| std::string TargetID::getCanonicalFeatureString() const { |
| std::string Str; |
| raw_string_ostream OS(Str); |
| printCanonicalTargetIDString(OS); |
| return Str; |
| } |
| |
| bool TargetID::operator==(const TargetID &Other) const { |
| return Arch == Other.Arch && XnackSetting == Other.XnackSetting && |
| SramEccSetting == Other.SramEccSetting && IsAMDHSA == Other.IsAMDHSA && |
| TargetTripleString == Other.TargetTripleString; |
| } |
| |
| static bool featureProvidesFor(TargetIDSetting Provided, |
| TargetIDSetting Requested) { |
| return Provided == TargetIDSetting::Any || |
| Provided == TargetIDSetting::Unsupported || Provided == Requested; |
| } |
| |
| bool TargetID::isEquivalent(const TargetID &Other) const { |
| // The processor and feature settings must match exactly |
| if (Arch != Other.Arch || XnackSetting != Other.XnackSetting || |
| SramEccSetting != Other.SramEccSetting) |
| return false; |
| |
| return Triple(getTargetTripleString()) |
| .isCompatibleWith(Triple(Other.getTargetTripleString())); |
| } |
| |
| bool TargetID::providesFor(const TargetID &Other) const { |
| // A major-family/generic processor (e.g. amdgpu9) provides for a specific |
| // member of its family (e.g. gfx900), but not the reverse. Otherwise the |
| // processors must match. |
| if (Arch != Other.Arch && Arch != GK_NONE && Other.Arch != GK_NONE) { |
| Triple::SubArchType ThisSubArch = getSubArch(Arch); |
| if (ThisSubArch != getMajorSubArch(ThisSubArch) || |
| ThisSubArch != getMajorSubArch(getSubArch(Other.Arch))) |
| return false; |
| } |
| |
| if (!featureProvidesFor(XnackSetting, Other.XnackSetting) || |
| !featureProvidesFor(SramEccSetting, Other.SramEccSetting)) |
| return false; |
| |
| return Triple(getTargetTripleString()) |
| .isCompatibleWith(Triple(Other.getTargetTripleString())); |
| } |