| 1 | //===-- AMDGPUTargetParser - Parser for AMDGPU 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 AMDGPU hardware features. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "llvm/TargetParser/AMDGPUTargetParser.h" |
| 14 | #include "llvm/ADT/ArrayRef.h" |
| 15 | #include "llvm/ADT/SmallVector.h" |
| 16 | #include "llvm/ADT/StringTable.h" |
| 17 | #include "llvm/ADT/Twine.h" |
| 18 | #include "llvm/Support/raw_ostream.h" |
| 19 | #include "llvm/TargetParser/Triple.h" |
| 20 | #include <array> |
| 21 | #include <cassert> |
| 22 | |
| 23 | using namespace llvm; |
| 24 | using namespace AMDGPU; |
| 25 | |
| 26 | namespace { |
| 27 | constexpr unsigned NumAMDGPUSubArches = |
| 28 | Triple::LastAMDGPUSubArch - Triple::FirstAMDGPUSubArch + 1; |
| 29 | |
| 30 | // A legacy GPU name (e.g. "tahiti") mapped to the GPUKind it aliases. |
| 31 | struct GPUNameAlias { |
| 32 | StringTable::Offset AltName; |
| 33 | GPUKind Kind; |
| 34 | }; |
| 35 | |
| 36 | // Per-GPU data for the AMDGCN GPUKinds, from the generated table below. |
| 37 | struct GPUInfo { |
| 38 | StringTable::Offset Name; |
| 39 | Triple::SubArchType SubArch; |
| 40 | unsigned ArchFeatures; |
| 41 | AMDGPUFeatureBitset Features; |
| 42 | IsaVersion Version; |
| 43 | StringTable::Offset FamilyName; |
| 44 | StringTable::Offset BaseName; // The canonical device name for a variant. |
| 45 | uint8_t MaxWavesPerEU; |
| 46 | uint32_t MaxHWAddressableLocalMemorySize; |
| 47 | }; |
| 48 | |
| 49 | // Per-GPU data for the R600 GPUKinds. |
| 50 | struct R600Info { |
| 51 | StringTable::Offset Name; |
| 52 | R600FeatureKind ArchFeatures; |
| 53 | }; |
| 54 | |
| 55 | #define GET_AMDGPU_NAME_TABLE |
| 56 | #define GET_AMDGPU_GPU_TABLE |
| 57 | #define GET_AMDGPU_GPU_ALIAS_TABLE |
| 58 | #define GET_AMDGPU_MAJOR_SUBARCH |
| 59 | #define GET_AMDGPU_SUBARCH_NAME |
| 60 | #define GET_AMDGPU_FEATURE_NAME_TABLE |
| 61 | #include "llvm/TargetParser/AMDGPUTargetParserDef.inc" |
| 62 | |
| 63 | #define GET_R600_NAME_TABLE |
| 64 | #define GET_R600_GPU_TABLE |
| 65 | #define GET_R600_GPU_ALIAS_TABLE |
| 66 | #include "llvm/TargetParser/R600TargetParserDef.inc" |
| 67 | |
| 68 | // The string tables holding GPU-name-derived strings as offsets. R600 and |
| 69 | // AMDGPU come from separate generated headers, each with its own pool. |
| 70 | constexpr StringTable AMDGPUNameStrTab = AMDGPUNameTable; |
| 71 | constexpr StringTable R600NameStrTab = R600NameTable; |
| 72 | |
| 73 | // Look up the GPUInfo row for an AMDGCN GPUKind, or nullptr for GK_NONE / a |
| 74 | // non-AMDGCN (R600) kind. |
| 75 | const GPUInfo *getAMDGPUInfo(GPUKind AK) { |
| 76 | if (AK < AMDGPUFirstGPUKind) |
| 77 | return nullptr; |
| 78 | unsigned Idx = AK - AMDGPUFirstGPUKind; |
| 79 | if (Idx >= std::size(AMDGPUGPUTable)) |
| 80 | return nullptr; |
| 81 | return &AMDGPUGPUTable[Idx]; |
| 82 | } |
| 83 | |
| 84 | // Look up the R600Info row for an R600 GPUKind, or nullptr for a non-R600 kind. |
| 85 | const R600Info *getR600Info(GPUKind AK) { |
| 86 | if (AK < R600FirstGPUKind) |
| 87 | return nullptr; |
| 88 | unsigned Idx = AK - R600FirstGPUKind; |
| 89 | if (Idx >= std::size(R600GPUTable)) |
| 90 | return nullptr; |
| 91 | return &R600GPUTable[Idx]; |
| 92 | } |
| 93 | |
| 94 | // Scan a name -> GPUKind table (canonical names, then aliases) for \p CPU. |
| 95 | template <typename InfoT, size_t N, size_t M> |
| 96 | GPUKind parseArchImpl(StringRef CPU, const InfoT (&Table)[N], GPUKind FirstKind, |
| 97 | const StringTable &StrTab, |
| 98 | const GPUNameAlias (&Aliases)[M]) { |
| 99 | for (unsigned I = 0; I != N; ++I) { |
| 100 | if (CPU == StrTab[Table[I].Name]) |
| 101 | return static_cast<GPUKind>(FirstKind + I); |
| 102 | } |
| 103 | |
| 104 | for (const GPUNameAlias &A : Aliases) { |
| 105 | if (CPU == StrTab[A.AltName]) |
| 106 | return A.Kind; |
| 107 | } |
| 108 | |
| 109 | return GK_NONE; |
| 110 | } |
| 111 | |
| 112 | // Reverse map: SubArch -> GPUKind, indexed by (SubArch - FirstAMDGPUSubArch). |
| 113 | // Subarches with no GPU (incl. the NoSubArch pseudo targets) map to GK_NONE. |
| 114 | constexpr std::array<GPUKind, NumAMDGPUSubArches> AMDGPUSubArchToGPUKind = [] { |
| 115 | std::array<GPUKind, NumAMDGPUSubArches> Map{}; |
| 116 | |
| 117 | for (unsigned I = 0; I < std::size(AMDGPUGPUTable); ++I) { |
| 118 | Triple::SubArchType SubArch = AMDGPUGPUTable[I].SubArch; |
| 119 | if (SubArch != Triple::NoSubArch) { |
| 120 | Map[SubArch - Triple::FirstAMDGPUSubArch] = |
| 121 | static_cast<GPUKind>(AMDGPUFirstGPUKind + I); |
| 122 | } |
| 123 | } |
| 124 | return Map; |
| 125 | }(); |
| 126 | |
| 127 | /// SubArch -> major-family, indexed by (SubArch - FirstAMDGPUSubArch). |
| 128 | constexpr std::array<Triple::SubArchType, NumAMDGPUSubArches> |
| 129 | AMDGPUMajorFamilies = [] { |
| 130 | std::array<Triple::SubArchType, NumAMDGPUSubArches> Map{}; |
| 131 | |
| 132 | for (unsigned I = 0; I < NumAMDGPUSubArches; ++I) { |
| 133 | Map[I] = |
| 134 | static_cast<Triple::SubArchType>(Triple::FirstAMDGPUSubArch + I); |
| 135 | } |
| 136 | |
| 137 | for (const AMDGPUMajorSubArchEntry &Entry : AMDGPUMajorSubArch) |
| 138 | Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = Entry.Major; |
| 139 | return Map; |
| 140 | }(); |
| 141 | |
| 142 | // SubArch -> name-offset, indexed by (SubArch - FirstAMDGPUSubArch). Unmapped |
| 143 | // subarches keep offset 0 (the empty string). |
| 144 | constexpr std::array<StringTable::Offset, NumAMDGPUSubArches> |
| 145 | AMDGPUSubArchNameOffsets = [] { |
| 146 | std::array<StringTable::Offset, NumAMDGPUSubArches> Map{}; |
| 147 | for (const AMDGPUSubArchNameEntry &Entry : AMDGPUSubArchNames) |
| 148 | Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = Entry.NameOffset; |
| 149 | return Map; |
| 150 | }(); |
| 151 | |
| 152 | // SubArch -> triple-name-offset (e.g. "amdgpu9.00"), like |
| 153 | // AMDGPUSubArchNameOffsets. |
| 154 | constexpr std::array<StringTable::Offset, NumAMDGPUSubArches> |
| 155 | AMDGPUSubArchTripleNameOffsets = [] { |
| 156 | std::array<StringTable::Offset, NumAMDGPUSubArches> Map{}; |
| 157 | for (const AMDGPUSubArchNameEntry &Entry : AMDGPUSubArchNames) |
| 158 | Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = |
| 159 | Entry.TripleNameOffset; |
| 160 | return Map; |
| 161 | }(); |
| 162 | } // namespace |
| 163 | |
| 164 | StringRef llvm::AMDGPU::getArchFamilyNameAMDGCN(GPUKind AK) { |
| 165 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 166 | return Info ? AMDGPUNameStrTab[Info->FamilyName] : "" ; |
| 167 | } |
| 168 | |
| 169 | Triple::SubArchType llvm::AMDGPU::getSubArch(GPUKind AK) { |
| 170 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 171 | return Info ? Info->SubArch : Triple::SubArchType::NoSubArch; |
| 172 | } |
| 173 | |
| 174 | StringRef llvm::AMDGPU::getBaseArchNameAMDGCN(GPUKind AK) { |
| 175 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 176 | return Info ? AMDGPUNameStrTab[Info->BaseName] : "" ; |
| 177 | } |
| 178 | |
| 179 | AMDGPU::GPUKind |
| 180 | llvm::AMDGPU::getGPUKindFromSubArch(Triple::SubArchType SubArch) { |
| 181 | if (SubArch < Triple::FirstAMDGPUSubArch || |
| 182 | SubArch > Triple::LastAMDGPUSubArch) |
| 183 | return GK_NONE; |
| 184 | return AMDGPUSubArchToGPUKind[SubArch - Triple::FirstAMDGPUSubArch]; |
| 185 | } |
| 186 | |
| 187 | Triple::SubArchType AMDGPU::getMajorSubArch(Triple::SubArchType X) { |
| 188 | if (X < Triple::FirstAMDGPUSubArch || X > Triple::LastAMDGPUSubArch) |
| 189 | return Triple::NoSubArch; |
| 190 | return AMDGPUMajorFamilies[X - Triple::FirstAMDGPUSubArch]; |
| 191 | } |
| 192 | |
| 193 | bool AMDGPU::isSubArchCompatible(Triple::SubArchType A, Triple::SubArchType B) { |
| 194 | if (A == B || A == Triple::NoSubArch || B == Triple::NoSubArch) |
| 195 | return true; |
| 196 | |
| 197 | Triple::SubArchType MajorA = AMDGPU::getMajorSubArch(X: A); |
| 198 | Triple::SubArchType MajorB = AMDGPU::getMajorSubArch(X: B); |
| 199 | |
| 200 | // One side is the major-family subarch covering the other's family. |
| 201 | if (A == MajorA) |
| 202 | return MajorA == MajorB; |
| 203 | if (B == MajorB) |
| 204 | return MajorA == MajorB; |
| 205 | |
| 206 | return false; |
| 207 | } |
| 208 | |
| 209 | bool AMDGPU::isCPUValidForSubArch(Triple::SubArchType SubArch, GPUKind AK) { |
| 210 | // An unrecognized GPU is never valid. |
| 211 | if (AK == GK_NONE) |
| 212 | return false; |
| 213 | // A legacy triple without a subarch accepts any known GPU. |
| 214 | if (SubArch == Triple::NoSubArch) |
| 215 | return true; |
| 216 | |
| 217 | // Reject the dummy "generic" targets |
| 218 | Triple::SubArchType GPUSubArch = getSubArch(AK); |
| 219 | if (GPUSubArch == Triple::NoSubArch) |
| 220 | return false; |
| 221 | |
| 222 | return isSubArchCompatible(A: GPUSubArch, B: SubArch); |
| 223 | } |
| 224 | |
| 225 | bool AMDGPU::isCPUValidForSubArch(Triple::SubArchType SubArch, StringRef CPU) { |
| 226 | return isCPUValidForSubArch(SubArch, AK: parseArchAMDGCN(CPU)); |
| 227 | } |
| 228 | |
| 229 | bool AMDGPU::isPseudoTarget(GPUKind AK) { |
| 230 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 231 | return Info && Info->SubArch == Triple::NoSubArch; |
| 232 | } |
| 233 | |
| 234 | bool AMDGPU::isPseudoTarget(StringRef CPU) { |
| 235 | return isPseudoTarget(AK: parseArchAMDGCN(CPU)); |
| 236 | } |
| 237 | |
| 238 | bool AMDGPU::isSubArchCompatible(const Triple &A, const Triple &B) { |
| 239 | // Tolerate subarch mismatch if one entry is none. This is a hack for bitcode |
| 240 | // libraries. |
| 241 | // There's a missing enum entry for an unknown subarch. Make sure the |
| 242 | // subarch is really empty. |
| 243 | if (A.getSubArch() == Triple::NoSubArch) |
| 244 | return A.getArchName().size() == 6; |
| 245 | |
| 246 | if (B.getSubArch() == Triple::NoSubArch) |
| 247 | return B.getArchName().size() == 6; |
| 248 | |
| 249 | return isSubArchCompatible(A: A.getSubArch(), B: B.getSubArch()); |
| 250 | } |
| 251 | |
| 252 | std::string AMDGPU::mergeSubArch(const Triple &A, const Triple &B) { |
| 253 | if (A.getSubArch() == Triple::NoSubArch) |
| 254 | return B.str(); |
| 255 | if (B.getSubArch() == Triple::NoSubArch) |
| 256 | return A.str(); |
| 257 | |
| 258 | Triple::SubArchType MajorA = AMDGPU::getMajorSubArch(X: A.getSubArch()); |
| 259 | Triple::SubArchType MajorB = AMDGPU::getMajorSubArch(X: B.getSubArch()); |
| 260 | |
| 261 | // With a compatible major arch, return the specific subarch. |
| 262 | if (A.getSubArch() == MajorA) { |
| 263 | if (MajorA == MajorB) |
| 264 | return B.str(); |
| 265 | } |
| 266 | |
| 267 | if (B.getSubArch() == MajorB) { |
| 268 | if (MajorA == MajorB) |
| 269 | return A.str(); |
| 270 | } |
| 271 | |
| 272 | // Invalid case. |
| 273 | return B.str(); |
| 274 | } |
| 275 | |
| 276 | StringRef llvm::AMDGPU::getArchNameAMDGCN(GPUKind AK) { |
| 277 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 278 | return Info ? AMDGPUNameStrTab[Info->Name] : "" ; |
| 279 | } |
| 280 | |
| 281 | StringRef llvm::AMDGPU::getArchNameFromSubArch(Triple::SubArchType SubArch) { |
| 282 | if (SubArch < Triple::FirstAMDGPUSubArch || |
| 283 | SubArch > Triple::LastAMDGPUSubArch) |
| 284 | return "" ; |
| 285 | return AMDGPUNameStrTab[AMDGPUSubArchNameOffsets[SubArch - |
| 286 | Triple::FirstAMDGPUSubArch]]; |
| 287 | } |
| 288 | |
| 289 | StringRef llvm::AMDGPU::getSubArchName(Triple::SubArchType SubArch) { |
| 290 | if (SubArch == Triple::NoSubArch) |
| 291 | return AMDGPUNameStrTab[AMDGPUNoSubArchNameOffset]; |
| 292 | |
| 293 | assert(SubArch >= Triple::FirstAMDGPUSubArch && |
| 294 | SubArch <= Triple::LastAMDGPUSubArch && |
| 295 | "expected an AMDGPU subarch or NoSubArch" ); |
| 296 | return AMDGPUNameStrTab |
| 297 | [AMDGPUSubArchTripleNameOffsets[SubArch - Triple::FirstAMDGPUSubArch]]; |
| 298 | } |
| 299 | |
| 300 | StringRef llvm::AMDGPU::getArchNameR600(GPUKind AK) { |
| 301 | const R600Info *Info = getR600Info(AK); |
| 302 | return Info ? R600NameStrTab[Info->Name] : "" ; |
| 303 | } |
| 304 | |
| 305 | AMDGPU::GPUKind llvm::AMDGPU::parseArchAMDGCN(StringRef CPU) { |
| 306 | return parseArchImpl(CPU, Table: AMDGPUGPUTable, FirstKind: AMDGPUFirstGPUKind, |
| 307 | StrTab: AMDGPUNameStrTab, Aliases: AMDGPUGPUAliases); |
| 308 | } |
| 309 | |
| 310 | AMDGPU::GPUKind llvm::AMDGPU::parseArchR600(StringRef CPU) { |
| 311 | return parseArchImpl(CPU, Table: R600GPUTable, FirstKind: R600FirstGPUKind, StrTab: R600NameStrTab, |
| 312 | Aliases: R600GPUAliases); |
| 313 | } |
| 314 | |
| 315 | unsigned AMDGPU::getArchAttrAMDGCN(GPUKind AK) { |
| 316 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 317 | return Info ? Info->ArchFeatures : FEATURE_NONE; |
| 318 | } |
| 319 | |
| 320 | unsigned AMDGPU::getArchAttrAMDGCN(Triple::SubArchType SubArch) { |
| 321 | const GPUInfo *Info = getAMDGPUInfo(AK: getGPUKindFromSubArch(SubArch)); |
| 322 | return Info ? Info->ArchFeatures : FEATURE_NONE; |
| 323 | } |
| 324 | |
| 325 | R600FeatureKind AMDGPU::getArchAttrR600(GPUKind AK) { |
| 326 | const R600Info *Info = getR600Info(AK); |
| 327 | return Info ? Info->ArchFeatures : R600_FEATURE_NONE; |
| 328 | } |
| 329 | |
| 330 | const AMDGPUFeatureBitset &AMDGPU::getFeatureBitset(GPUKind AK) { |
| 331 | static constexpr AMDGPUFeatureBitset Empty{}; |
| 332 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 333 | return Info ? Info->Features : Empty; |
| 334 | } |
| 335 | |
| 336 | void AMDGPU::getFeatureNames(const AMDGPUFeatureBitset &Features, |
| 337 | SmallVectorImpl<StringRef> &Names) { |
| 338 | for (unsigned I = 0; I != NUM_FEATURES; ++I) { |
| 339 | if (Features.test(I)) |
| 340 | Names.push_back(Elt: AMDGPUNameStrTab[AMDGPUFeatureNames[I]]); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | void AMDGPU::fillValidArchListAMDGCN(SmallVectorImpl<StringRef> &Values, |
| 345 | Triple::SubArchType SubArch) { |
| 346 | // XXX: Should this only report unique canonical names? |
| 347 | // An alias shares its GPU's GPUKind, so it is filtered alongside it. |
| 348 | for (unsigned I = 0; I != std::size(AMDGPUGPUTable); ++I) { |
| 349 | GPUKind Kind = static_cast<GPUKind>(AMDGPUFirstGPUKind + I); |
| 350 | if (AMDGPUGPUTable[I].SubArch != Triple::NoSubArch && |
| 351 | isCPUValidForSubArch(SubArch, AK: Kind)) |
| 352 | Values.push_back(Elt: AMDGPUNameStrTab[AMDGPUGPUTable[I].Name]); |
| 353 | } |
| 354 | |
| 355 | for (const GPUNameAlias &A : AMDGPUGPUAliases) { |
| 356 | if (isCPUValidForSubArch(SubArch, AK: A.Kind)) |
| 357 | Values.push_back(Elt: AMDGPUNameStrTab[A.AltName]); |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | void AMDGPU::fillValidArchListR600(SmallVectorImpl<StringRef> &Values) { |
| 362 | for (const R600Info &Info : R600GPUTable) |
| 363 | Values.push_back(Elt: R600NameStrTab[Info.Name]); |
| 364 | for (const GPUNameAlias &A : R600GPUAliases) |
| 365 | Values.push_back(Elt: R600NameStrTab[A.AltName]); |
| 366 | } |
| 367 | |
| 368 | AMDGPU::IsaVersion AMDGPU::getIsaVersion(StringRef GPU) { |
| 369 | const GPUInfo *Info = getAMDGPUInfo(AK: parseArchAMDGCN(CPU: GPU)); |
| 370 | return Info ? Info->Version : IsaVersion{.Major: 0, .Minor: 0, .Stepping: 0}; |
| 371 | } |
| 372 | |
| 373 | AMDGPU::IsaVersion AMDGPU::getIsaVersion(Triple::SubArchType SubArch) { |
| 374 | const GPUInfo *Info = getAMDGPUInfo(AK: getGPUKindFromSubArch(SubArch)); |
| 375 | return Info ? Info->Version : IsaVersion{.Major: 0, .Minor: 0, .Stepping: 0}; |
| 376 | } |
| 377 | |
| 378 | unsigned AMDGPU::getTotalNumSGPRs(GPUKind AK) { |
| 379 | IsaVersion Version = getIsaVersion(SubArch: getSubArch(AK)); |
| 380 | if (Version.Major >= 8) |
| 381 | return 800; |
| 382 | return 512; |
| 383 | } |
| 384 | |
| 385 | unsigned AMDGPU::getTotalNumSGPRs(Triple::SubArchType SubArch) { |
| 386 | IsaVersion Version = getIsaVersion(SubArch); |
| 387 | if (Version.Major >= 8) |
| 388 | return 800; |
| 389 | return 512; |
| 390 | } |
| 391 | |
| 392 | unsigned AMDGPU::getAddressableNumSGPRs(GPUKind AK) { |
| 393 | if (getFeatureBitset(AK).test(I: FEAT_SGPR_INIT_BUG)) |
| 394 | return FIXED_NUM_SGPRS_FOR_INIT_BUG; |
| 395 | |
| 396 | IsaVersion Version = getIsaVersion(SubArch: getSubArch(AK)); |
| 397 | if (Version.Major >= 10) |
| 398 | return 106; |
| 399 | if (Version.Major >= 8) |
| 400 | return 102; |
| 401 | return 104; |
| 402 | } |
| 403 | |
| 404 | unsigned AMDGPU::getAddressableNumSGPRs(Triple::SubArchType SubArch) { |
| 405 | if (getFeatureBitset(AK: getGPUKindFromSubArch(SubArch)).test(I: FEAT_SGPR_INIT_BUG)) |
| 406 | return FIXED_NUM_SGPRS_FOR_INIT_BUG; |
| 407 | |
| 408 | IsaVersion Version = getIsaVersion(SubArch); |
| 409 | if (Version.Major >= 10) |
| 410 | return 106; |
| 411 | if (Version.Major >= 8) |
| 412 | return 102; |
| 413 | return 104; |
| 414 | } |
| 415 | |
| 416 | unsigned AMDGPU::getSGPRAllocGranule(GPUKind AK) { |
| 417 | IsaVersion Version = getIsaVersion(SubArch: getSubArch(AK)); |
| 418 | if (Version.Major >= 10) |
| 419 | return getAddressableNumSGPRs(AK); |
| 420 | if (Version.Major >= 8) |
| 421 | return 16; |
| 422 | return 8; |
| 423 | } |
| 424 | |
| 425 | unsigned AMDGPU::getSGPRAllocGranule(Triple::SubArchType SubArch) { |
| 426 | IsaVersion Version = getIsaVersion(SubArch); |
| 427 | if (Version.Major >= 10) |
| 428 | return getAddressableNumSGPRs(SubArch); |
| 429 | if (Version.Major >= 8) |
| 430 | return 16; |
| 431 | return 8; |
| 432 | } |
| 433 | |
| 434 | unsigned AMDGPU::getMaxHWAddressableLocalMemorySize(GPUKind AK) { |
| 435 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 436 | return Info ? Info->MaxHWAddressableLocalMemorySize : 32768; |
| 437 | } |
| 438 | |
| 439 | unsigned |
| 440 | AMDGPU::getMaxHWAddressableLocalMemorySize(Triple::SubArchType SubArch) { |
| 441 | return getMaxHWAddressableLocalMemorySize(AK: getGPUKindFromSubArch(SubArch)); |
| 442 | } |
| 443 | |
| 444 | unsigned AMDGPU::getMaxWavesPerEU(GPUKind AK) { |
| 445 | const GPUInfo *Info = getAMDGPUInfo(AK); |
| 446 | return Info ? Info->MaxWavesPerEU : 10; |
| 447 | } |
| 448 | |
| 449 | unsigned AMDGPU::getMaxWavesPerEU(Triple::SubArchType SubArch) { |
| 450 | return getMaxWavesPerEU(AK: getGPUKindFromSubArch(SubArch)); |
| 451 | } |
| 452 | |
| 453 | StringRef AMDGPU::getCanonicalArchName(const Triple &T, StringRef Arch) { |
| 454 | assert(T.isAMDGPU()); |
| 455 | auto ProcKind = T.isAMDGCN() ? parseArchAMDGCN(CPU: Arch) : parseArchR600(CPU: Arch); |
| 456 | if (ProcKind == GK_NONE) |
| 457 | return StringRef(); |
| 458 | |
| 459 | return T.isAMDGCN() ? getArchNameAMDGCN(AK: ProcKind) : getArchNameR600(AK: ProcKind); |
| 460 | } |
| 461 | |
| 462 | // Capability features clang queries via the feature bitset but must not |
| 463 | // serialize into the target-feature string. |
| 464 | // |
| 465 | // FIXME: This is hacky, we shouldn't have mismatches between the bitset and |
| 466 | // feature string map. |
| 467 | static const AMDGPUFeatureBitset FrontendOnlyFeatures = { |
| 468 | FEAT_FAST_FMAF, FEAT_FAST_DENORMAL_F32, FEAT_SUPPORTS_WAVE32, |
| 469 | FEAT_SUPPORTS_WGP, FEAT_XNACK_SUPPORT, FEAT_SRAMECC_SUPPORT, |
| 470 | FEAT_XNACK_ON_OFF_MODES}; |
| 471 | |
| 472 | // Add a GPU's features (minus the frontend-only ones) to \p Features. With \p |
| 473 | // Overwrite false, existing entries are kept so user -mattr overrides win. |
| 474 | static void addGPUFeatures(const GPUInfo &Info, bool Overwrite, |
| 475 | StringMap<bool> &Features) { |
| 476 | SmallVector<StringRef, NUM_FEATURES> Names; |
| 477 | getFeatureNames(Features: Info.Features & ~FrontendOnlyFeatures, Names); |
| 478 | for (StringRef Name : Names) { |
| 479 | if (Overwrite) |
| 480 | Features[Name] = true; |
| 481 | else |
| 482 | Features.insert(KV: {Name, true}); |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | /// Add a GPU's default features to \p Features (preserving user overrides) and |
| 487 | /// validate any requested wavesize. |
| 488 | static std::pair<FeatureError, StringRef> |
| 489 | fillAMDGCNFeatureMap(StringRef GPU, const Triple &T, |
| 490 | StringMap<bool> &Features) { |
| 491 | // With no explicit GPU, the triple's subarch identifies the target. |
| 492 | GPUKind Kind = GPU.empty() && T.getSubArch() != Triple::NoSubArch |
| 493 | ? getGPUKindFromSubArch(SubArch: T.getSubArch()) |
| 494 | : parseArchAMDGCN(CPU: GPU); |
| 495 | const GPUInfo *Info = getAMDGPUInfo(AK: Kind); |
| 496 | |
| 497 | // A bare subarch triple (no -target-cpu) still pins down the target, so it is |
| 498 | // not a null GPU. The target's native wavesize (if single-mode) is in the |
| 499 | // feature bitset; a dual-mode GPU has neither wave bit set. |
| 500 | const bool IsNullGPU = T.getSubArch() == Triple::NoSubArch && GPU.empty(); |
| 501 | const bool TargetHasWave32 = |
| 502 | Info && Info->Features.test(I: FEAT_WAVEFRONTSIZE32); |
| 503 | const bool TargetHasWave64 = |
| 504 | Info && Info->Features.test(I: FEAT_WAVEFRONTSIZE64); |
| 505 | |
| 506 | auto Wave32Itr = Features.find(Key: "wavefrontsize32" ); |
| 507 | auto Wave64Itr = Features.find(Key: "wavefrontsize64" ); |
| 508 | const bool EnableWave32 = |
| 509 | Wave32Itr != Features.end() && Wave32Itr->getValue(); |
| 510 | const bool EnableWave64 = |
| 511 | Wave64Itr != Features.end() && Wave64Itr->getValue(); |
| 512 | const bool DisableWave32 = |
| 513 | Wave32Itr != Features.end() && !Wave32Itr->getValue(); |
| 514 | const bool DisableWave64 = |
| 515 | Wave64Itr != Features.end() && !Wave64Itr->getValue(); |
| 516 | |
| 517 | if (EnableWave32 && EnableWave64) |
| 518 | return {AMDGPU::INVALID_FEATURE_COMBINATION, |
| 519 | "'+wavefrontsize32' and '+wavefrontsize64' are mutually exclusive" }; |
| 520 | if (DisableWave32 && DisableWave64) |
| 521 | return {AMDGPU::INVALID_FEATURE_COMBINATION, |
| 522 | "'-wavefrontsize32' and '-wavefrontsize64' are mutually exclusive" }; |
| 523 | |
| 524 | if (!IsNullGPU) { |
| 525 | if (TargetHasWave64) { |
| 526 | if (EnableWave32) |
| 527 | return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "+wavefrontsize32" }; |
| 528 | if (DisableWave64) |
| 529 | return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "-wavefrontsize64" }; |
| 530 | } |
| 531 | |
| 532 | if (TargetHasWave32) { |
| 533 | if (EnableWave64) |
| 534 | return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "+wavefrontsize64" }; |
| 535 | if (DisableWave32) |
| 536 | return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "-wavefrontsize32" }; |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | // Don't assume any wavesize with an unknown subtarget. |
| 541 | // Default to wave32 if target supports both. |
| 542 | if (!IsNullGPU && !EnableWave32 && !EnableWave64 && !TargetHasWave32 && |
| 543 | !TargetHasWave64) |
| 544 | Features.insert(KV: {"wavefrontsize32" , true}); |
| 545 | |
| 546 | // Merge the target defaults, keeping any user -mattr overrides. |
| 547 | if (Info) |
| 548 | addGPUFeatures(Info: *Info, /*Overwrite=*/false, Features); |
| 549 | |
| 550 | return {NO_ERROR, StringRef()}; |
| 551 | } |
| 552 | |
| 553 | /// Fills Features map with default values for given target GPU. |
| 554 | /// \p Features contains overriding target features and this function returns |
| 555 | /// default target features with entries overridden by \p Features. |
| 556 | std::pair<FeatureError, StringRef> |
| 557 | AMDGPU::fillAMDGPUFeatureMap(StringRef GPU, const Triple &T, |
| 558 | StringMap<bool> &Features) { |
| 559 | // XXX - What does the member GPU mean if device name string passed here? |
| 560 | if (T.isSPIRV() && T.getOS() == Triple::OSType::AMDHSA) { |
| 561 | // AMDGCN SPIRV must support the union of all AMDGCN features. |
| 562 | SmallVector<StringRef> GPUs; |
| 563 | fillValidArchListAMDGCN(Values&: GPUs); |
| 564 | for (StringRef G : GPUs) |
| 565 | if (const GPUInfo *Info = getAMDGPUInfo(AK: parseArchAMDGCN(CPU: G))) |
| 566 | addGPUFeatures(Info: *Info, /*Overwrite=*/true, Features); |
| 567 | Features["wavefrontsize32" ] = true; |
| 568 | Features["wavefrontsize64" ] = true; |
| 569 | } else if (T.isAMDGCN()) { |
| 570 | return fillAMDGCNFeatureMap(GPU, T, Features); |
| 571 | } else { |
| 572 | if (GPU.empty()) |
| 573 | GPU = "r600" ; |
| 574 | |
| 575 | switch (llvm::AMDGPU::parseArchR600(CPU: GPU)) { |
| 576 | case GK_CAYMAN: |
| 577 | case GK_CYPRESS: |
| 578 | case GK_RV770: |
| 579 | case GK_RV670: |
| 580 | // TODO: Add fp64 when implemented. |
| 581 | break; |
| 582 | case GK_TURKS: |
| 583 | case GK_CAICOS: |
| 584 | case GK_BARTS: |
| 585 | case GK_SUMO: |
| 586 | case GK_REDWOOD: |
| 587 | case GK_JUNIPER: |
| 588 | case GK_CEDAR: |
| 589 | case GK_RV730: |
| 590 | case GK_RV710: |
| 591 | case GK_RS880: |
| 592 | case GK_R630: |
| 593 | case GK_R600: |
| 594 | break; |
| 595 | default: |
| 596 | llvm_unreachable("Unhandled GPU!" ); |
| 597 | } |
| 598 | } |
| 599 | return {NO_ERROR, StringRef()}; |
| 600 | } |
| 601 | |
| 602 | TargetID::TargetID(GPUKind Arch, const Triple &TT, TargetIDSetting XnackSetting, |
| 603 | TargetIDSetting SramEccSetting) |
| 604 | : Arch(Arch), |
| 605 | TargetTripleString(TT.normalize(Form: Triple::CanonicalForm::FOUR_IDENT)), |
| 606 | XnackSetting(XnackSetting), SramEccSetting(SramEccSetting), |
| 607 | IsAMDHSA(TT.getOS() == Triple::AMDHSA) {} |
| 608 | |
| 609 | // Parse a feature modifier sign ("+"/"-"). Returns "Unsupported" if \p Sign is |
| 610 | // neither (i.e. the modifier is malformed). |
| 611 | static TargetIDSetting getTargetIDSettingFromFeatureString(StringRef Sign) { |
| 612 | if (Sign == "+" ) |
| 613 | return TargetIDSetting::On; |
| 614 | if (Sign == "-" ) |
| 615 | return TargetIDSetting::Off; |
| 616 | |
| 617 | return TargetIDSetting::Unsupported; |
| 618 | } |
| 619 | |
| 620 | // Derive the architecture from the processor name in \p TargetIDStr. "generic" |
| 621 | // and the empty processor name act as a wildcard. |
| 622 | static GPUKind getGPUKindFromTargetID(const Triple &TT, StringRef TargetIDStr) { |
| 623 | StringRef CPUName = TargetIDStr.split(Separator: ':').first; |
| 624 | return (CPUName.empty() || CPUName == "generic" ) |
| 625 | ? getGPUKindFromSubArch(SubArch: TT.getSubArch()) |
| 626 | : parseArchAMDGCN(CPU: CPUName); |
| 627 | } |
| 628 | |
| 629 | // Compute the xnack/sramecc settings for processor \p Arch from the |
| 630 | // processor+features string \p TargetIDStr |
| 631 | // (e.g. "gfx90a:xnack+:sramecc-"). Returns false if a modifier names an unknown |
| 632 | // or repeated feature, names one the processor does not support, or has a |
| 633 | // malformed sign. |
| 634 | static bool computeTargetIDFeatures(GPUKind Arch, StringRef TargetIDStr, |
| 635 | TargetIDSetting &XnackSetting, |
| 636 | TargetIDSetting &SramEccSetting) { |
| 637 | const AMDGPUFeatureBitset &Features = getFeatureBitset(AK: Arch); |
| 638 | XnackSetting = Features.test(I: FEAT_XNACK_ON_OFF_MODES) |
| 639 | ? TargetIDSetting::Any |
| 640 | : TargetIDSetting::Unsupported; |
| 641 | SramEccSetting = Features.test(I: FEAT_SRAMECC_SUPPORT) |
| 642 | ? TargetIDSetting::Any |
| 643 | : TargetIDSetting::Unsupported; |
| 644 | |
| 645 | // The first component is the processor; the rest are feature modifiers of the |
| 646 | // form "<feature><+|->". |
| 647 | SmallVector<StringRef, 3> Split; |
| 648 | TargetIDStr.split(A&: Split, Separator: ':'); |
| 649 | bool SeenXnack = false; |
| 650 | bool SeenSramEcc = false; |
| 651 | bool Valid = true; |
| 652 | for (unsigned I = 1, E = Split.size(); I != E; ++I) { |
| 653 | StringRef FeatureString = Split[I]; |
| 654 | if (FeatureString.consume_front(Prefix: "xnack" )) { |
| 655 | TargetIDSetting Sign = getTargetIDSettingFromFeatureString(Sign: FeatureString); |
| 656 | if (SeenXnack || XnackSetting == TargetIDSetting::Unsupported || |
| 657 | Sign == TargetIDSetting::Unsupported) |
| 658 | Valid = false; |
| 659 | else |
| 660 | XnackSetting = Sign; |
| 661 | SeenXnack = true; |
| 662 | } else if (FeatureString.consume_front(Prefix: "sramecc" )) { |
| 663 | TargetIDSetting Sign = getTargetIDSettingFromFeatureString(Sign: FeatureString); |
| 664 | if (SeenSramEcc || SramEccSetting == TargetIDSetting::Unsupported || |
| 665 | Sign == TargetIDSetting::Unsupported) |
| 666 | Valid = false; |
| 667 | else |
| 668 | SramEccSetting = Sign; |
| 669 | SeenSramEcc = true; |
| 670 | } else { |
| 671 | // Unknown feature name. |
| 672 | Valid = false; |
| 673 | } |
| 674 | } |
| 675 | return Valid; |
| 676 | } |
| 677 | |
| 678 | TargetID::TargetID(const Triple &TT, StringRef TargetIDStr) |
| 679 | : TargetID(getGPUKindFromTargetID(TT, TargetIDStr), TT, |
| 680 | TargetIDSetting::Unsupported, TargetIDSetting::Unsupported) { |
| 681 | // Derive the feature settings from the string. Validity is not checked here; |
| 682 | // parseTargetIDString validates untrusted input. |
| 683 | computeTargetIDFeatures(Arch, TargetIDStr, XnackSetting, SramEccSetting); |
| 684 | } |
| 685 | |
| 686 | std::optional<TargetID> TargetID::parse(const Triple &TT, |
| 687 | StringRef ProcAndFeatures) { |
| 688 | if (!TT.isAMDGCN()) |
| 689 | return std::nullopt; |
| 690 | |
| 691 | // Filter out unrecognized subarch suffixes. |
| 692 | if (TT.getSubArch() == Triple::NoSubArch && TT.getArchName() != "amdgcn" ) |
| 693 | return std::nullopt; |
| 694 | |
| 695 | // A named processor (i.e. not the empty/generic wildcard, which is resolved |
| 696 | // from the triple's subarch) must be a recognized GPU that is consistent with |
| 697 | // the triple's subarch. |
| 698 | StringRef CPUName = ProcAndFeatures.split(Separator: ':').first; |
| 699 | if (!CPUName.empty() && CPUName != "generic" && |
| 700 | !isCPUValidForSubArch(SubArch: TT.getSubArch(), CPU: CPUName)) |
| 701 | return std::nullopt; |
| 702 | |
| 703 | // Parse the processor and its feature modifiers, then construct directly from |
| 704 | // the resulting fields. |
| 705 | GPUKind Arch = getGPUKindFromTargetID(TT, TargetIDStr: ProcAndFeatures); |
| 706 | TargetIDSetting XnackSetting, SramEccSetting; |
| 707 | if (!computeTargetIDFeatures(Arch, TargetIDStr: ProcAndFeatures, XnackSetting, |
| 708 | SramEccSetting)) |
| 709 | return std::nullopt; |
| 710 | |
| 711 | return TargetID(Arch, TT, XnackSetting, SramEccSetting); |
| 712 | } |
| 713 | |
| 714 | std::optional<TargetID> |
| 715 | TargetID::parseTargetIDString(StringRef TargetIDDirective) { |
| 716 | // Split on '-' to get arch-vendor-os-environment-processor:features. There is |
| 717 | // a single dash separator after the 4-component triple, so the |
| 718 | // processor+features field must be present (even if empty). |
| 719 | SmallVector<StringRef, 5> Parts; |
| 720 | TargetIDDirective.split(A&: Parts, Separator: '-', /*MaxSplit=*/4); |
| 721 | if (Parts.size() < 5) |
| 722 | return std::nullopt; |
| 723 | |
| 724 | return parse(TT: Triple(Parts[0], Parts[1], Parts[2], Parts[3]), ProcAndFeatures: Parts[4]); |
| 725 | } |
| 726 | |
| 727 | // Append the explicit (On/Off) sramecc/xnack feature modifiers in canonical |
| 728 | // order, e.g. ":sramecc-:xnack+". |
| 729 | static void printFeatureModifiers(raw_ostream &OS, TargetIDSetting SramEcc, |
| 730 | TargetIDSetting Xnack) { |
| 731 | if (SramEcc == TargetIDSetting::Off) |
| 732 | OS << ":sramecc-" ; |
| 733 | else if (SramEcc == TargetIDSetting::On) |
| 734 | OS << ":sramecc+" ; |
| 735 | |
| 736 | if (Xnack == TargetIDSetting::Off) |
| 737 | OS << ":xnack-" ; |
| 738 | else if (Xnack == TargetIDSetting::On) |
| 739 | OS << ":xnack+" ; |
| 740 | } |
| 741 | |
| 742 | void TargetID::print(raw_ostream &StreamRep) const { |
| 743 | StreamRep << TargetTripleString << '-' << getArchNameAMDGCN(AK: Arch); |
| 744 | |
| 745 | if (IsAMDHSA) |
| 746 | printFeatureModifiers(OS&: StreamRep, SramEcc: getSramEccSetting(), Xnack: getXnackSetting()); |
| 747 | } |
| 748 | |
| 749 | std::string TargetID::toString() const { |
| 750 | std::string Str; |
| 751 | raw_string_ostream OS(Str); |
| 752 | OS << *this; |
| 753 | return Str; |
| 754 | } |
| 755 | |
| 756 | void TargetID::printCanonicalTargetIDString(raw_ostream &OS) const { |
| 757 | OS << getArchNameAMDGCN(AK: Arch); |
| 758 | printFeatureModifiers(OS, SramEcc: getSramEccSetting(), Xnack: getXnackSetting()); |
| 759 | } |
| 760 | |
| 761 | std::string TargetID::getCanonicalFeatureString() const { |
| 762 | std::string Str; |
| 763 | raw_string_ostream OS(Str); |
| 764 | printCanonicalTargetIDString(OS); |
| 765 | return Str; |
| 766 | } |
| 767 | |
| 768 | bool TargetID::operator==(const TargetID &Other) const { |
| 769 | return Arch == Other.Arch && XnackSetting == Other.XnackSetting && |
| 770 | SramEccSetting == Other.SramEccSetting && IsAMDHSA == Other.IsAMDHSA && |
| 771 | TargetTripleString == Other.TargetTripleString; |
| 772 | } |
| 773 | |
| 774 | static bool featureProvidesFor(TargetIDSetting Provided, |
| 775 | TargetIDSetting Requested) { |
| 776 | return Provided == TargetIDSetting::Any || |
| 777 | Provided == TargetIDSetting::Unsupported || Provided == Requested; |
| 778 | } |
| 779 | |
| 780 | bool TargetID::isEquivalent(const TargetID &Other) const { |
| 781 | // The processor and feature settings must match exactly |
| 782 | if (Arch != Other.Arch || XnackSetting != Other.XnackSetting || |
| 783 | SramEccSetting != Other.SramEccSetting) |
| 784 | return false; |
| 785 | |
| 786 | return Triple(getTargetTripleString()) |
| 787 | .isCompatibleWith(Other: Triple(Other.getTargetTripleString())); |
| 788 | } |
| 789 | |
| 790 | bool TargetID::providesFor(const TargetID &Other) const { |
| 791 | // A major-family/generic processor (e.g. amdgpu9) provides for a specific |
| 792 | // member of its family (e.g. gfx900), but not the reverse. Otherwise the |
| 793 | // processors must match. |
| 794 | if (Arch != Other.Arch && Arch != GK_NONE && Other.Arch != GK_NONE) { |
| 795 | Triple::SubArchType ThisSubArch = getSubArch(AK: Arch); |
| 796 | if (ThisSubArch != getMajorSubArch(X: ThisSubArch) || |
| 797 | ThisSubArch != getMajorSubArch(X: getSubArch(AK: Other.Arch))) |
| 798 | return false; |
| 799 | } |
| 800 | |
| 801 | if (!featureProvidesFor(Provided: XnackSetting, Requested: Other.XnackSetting) || |
| 802 | !featureProvidesFor(Provided: SramEccSetting, Requested: Other.SramEccSetting)) |
| 803 | return false; |
| 804 | |
| 805 | return Triple(getTargetTripleString()) |
| 806 | .isCompatibleWith(Other: Triple(Other.getTargetTripleString())); |
| 807 | } |
| 808 | |