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
23using namespace llvm;
24using namespace AMDGPU;
25
26namespace {
27constexpr unsigned NumAMDGPUSubArches =
28 Triple::LastAMDGPUSubArch - Triple::FirstAMDGPUSubArch + 1;
29
30// A legacy GPU name (e.g. "tahiti") mapped to the GPUKind it aliases.
31struct GPUNameAlias {
32 StringTable::Offset AltName;
33 GPUKind Kind;
34};
35
36// Per-GPU data for the AMDGCN GPUKinds, from the generated table below.
37struct 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.
50struct 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.
70constexpr StringTable AMDGPUNameStrTab = AMDGPUNameTable;
71constexpr 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.
75const 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.
85const 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.
95template <typename InfoT, size_t N, size_t M>
96GPUKind 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.
114constexpr 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).
128constexpr 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).
144constexpr 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.
154constexpr 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
164StringRef llvm::AMDGPU::getArchFamilyNameAMDGCN(GPUKind AK) {
165 const GPUInfo *Info = getAMDGPUInfo(AK);
166 return Info ? AMDGPUNameStrTab[Info->FamilyName] : "";
167}
168
169Triple::SubArchType llvm::AMDGPU::getSubArch(GPUKind AK) {
170 const GPUInfo *Info = getAMDGPUInfo(AK);
171 return Info ? Info->SubArch : Triple::SubArchType::NoSubArch;
172}
173
174StringRef llvm::AMDGPU::getBaseArchNameAMDGCN(GPUKind AK) {
175 const GPUInfo *Info = getAMDGPUInfo(AK);
176 return Info ? AMDGPUNameStrTab[Info->BaseName] : "";
177}
178
179AMDGPU::GPUKind
180llvm::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
187Triple::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
193bool 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
209bool 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
225bool AMDGPU::isCPUValidForSubArch(Triple::SubArchType SubArch, StringRef CPU) {
226 return isCPUValidForSubArch(SubArch, AK: parseArchAMDGCN(CPU));
227}
228
229bool AMDGPU::isPseudoTarget(GPUKind AK) {
230 const GPUInfo *Info = getAMDGPUInfo(AK);
231 return Info && Info->SubArch == Triple::NoSubArch;
232}
233
234bool AMDGPU::isPseudoTarget(StringRef CPU) {
235 return isPseudoTarget(AK: parseArchAMDGCN(CPU));
236}
237
238bool 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
252std::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
276StringRef llvm::AMDGPU::getArchNameAMDGCN(GPUKind AK) {
277 const GPUInfo *Info = getAMDGPUInfo(AK);
278 return Info ? AMDGPUNameStrTab[Info->Name] : "";
279}
280
281StringRef 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
289StringRef 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
300StringRef llvm::AMDGPU::getArchNameR600(GPUKind AK) {
301 const R600Info *Info = getR600Info(AK);
302 return Info ? R600NameStrTab[Info->Name] : "";
303}
304
305AMDGPU::GPUKind llvm::AMDGPU::parseArchAMDGCN(StringRef CPU) {
306 return parseArchImpl(CPU, Table: AMDGPUGPUTable, FirstKind: AMDGPUFirstGPUKind,
307 StrTab: AMDGPUNameStrTab, Aliases: AMDGPUGPUAliases);
308}
309
310AMDGPU::GPUKind llvm::AMDGPU::parseArchR600(StringRef CPU) {
311 return parseArchImpl(CPU, Table: R600GPUTable, FirstKind: R600FirstGPUKind, StrTab: R600NameStrTab,
312 Aliases: R600GPUAliases);
313}
314
315unsigned AMDGPU::getArchAttrAMDGCN(GPUKind AK) {
316 const GPUInfo *Info = getAMDGPUInfo(AK);
317 return Info ? Info->ArchFeatures : FEATURE_NONE;
318}
319
320unsigned AMDGPU::getArchAttrAMDGCN(Triple::SubArchType SubArch) {
321 const GPUInfo *Info = getAMDGPUInfo(AK: getGPUKindFromSubArch(SubArch));
322 return Info ? Info->ArchFeatures : FEATURE_NONE;
323}
324
325R600FeatureKind AMDGPU::getArchAttrR600(GPUKind AK) {
326 const R600Info *Info = getR600Info(AK);
327 return Info ? Info->ArchFeatures : R600_FEATURE_NONE;
328}
329
330const AMDGPUFeatureBitset &AMDGPU::getFeatureBitset(GPUKind AK) {
331 static constexpr AMDGPUFeatureBitset Empty{};
332 const GPUInfo *Info = getAMDGPUInfo(AK);
333 return Info ? Info->Features : Empty;
334}
335
336void 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
344void 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
361void 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
368AMDGPU::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
373AMDGPU::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
378unsigned AMDGPU::getTotalNumSGPRs(GPUKind AK) {
379 IsaVersion Version = getIsaVersion(SubArch: getSubArch(AK));
380 if (Version.Major >= 8)
381 return 800;
382 return 512;
383}
384
385unsigned AMDGPU::getTotalNumSGPRs(Triple::SubArchType SubArch) {
386 IsaVersion Version = getIsaVersion(SubArch);
387 if (Version.Major >= 8)
388 return 800;
389 return 512;
390}
391
392unsigned 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
404unsigned 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
416unsigned 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
425unsigned 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
434unsigned AMDGPU::getMaxHWAddressableLocalMemorySize(GPUKind AK) {
435 const GPUInfo *Info = getAMDGPUInfo(AK);
436 return Info ? Info->MaxHWAddressableLocalMemorySize : 32768;
437}
438
439unsigned
440AMDGPU::getMaxHWAddressableLocalMemorySize(Triple::SubArchType SubArch) {
441 return getMaxHWAddressableLocalMemorySize(AK: getGPUKindFromSubArch(SubArch));
442}
443
444unsigned AMDGPU::getMaxWavesPerEU(GPUKind AK) {
445 const GPUInfo *Info = getAMDGPUInfo(AK);
446 return Info ? Info->MaxWavesPerEU : 10;
447}
448
449unsigned AMDGPU::getMaxWavesPerEU(Triple::SubArchType SubArch) {
450 return getMaxWavesPerEU(AK: getGPUKindFromSubArch(SubArch));
451}
452
453StringRef 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.
467static 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.
474static 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.
488static std::pair<FeatureError, StringRef>
489fillAMDGCNFeatureMap(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.
556std::pair<FeatureError, StringRef>
557AMDGPU::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
602TargetID::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).
611static 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.
622static 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.
634static 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
678TargetID::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
686std::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
714std::optional<TargetID>
715TargetID::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+".
729static 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
742void 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
749std::string TargetID::toString() const {
750 std::string Str;
751 raw_string_ostream OS(Str);
752 OS << *this;
753 return Str;
754}
755
756void TargetID::printCanonicalTargetIDString(raw_ostream &OS) const {
757 OS << getArchNameAMDGCN(AK: Arch);
758 printFeatureModifiers(OS, SramEcc: getSramEccSetting(), Xnack: getXnackSetting());
759}
760
761std::string TargetID::getCanonicalFeatureString() const {
762 std::string Str;
763 raw_string_ostream OS(Str);
764 printCanonicalTargetIDString(OS);
765 return Str;
766}
767
768bool 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
774static bool featureProvidesFor(TargetIDSetting Provided,
775 TargetIDSetting Requested) {
776 return Provided == TargetIDSetting::Any ||
777 Provided == TargetIDSetting::Unsupported || Provided == Requested;
778}
779
780bool 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
790bool 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