1//===- AMDGPUTargetDefEmitter.cpp - Generate lists of AMDGPU GPUs ---------===//
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 tablegen backend emits the AMDGPU GPU tables used by
10// AMDGPUTargetParser.cpp.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/SetVector.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringMap.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/Support/MathExtras.h"
22#include "llvm/Support/raw_ostream.h"
23#include "llvm/TableGen/Error.h"
24#include "llvm/TableGen/Record.h"
25#include "llvm/TableGen/StringToOffsetTable.h"
26#include "llvm/TableGen/TableGenBackend.h"
27#include <string>
28#include <utility>
29#include <vector>
30
31using namespace llvm;
32
33// Derive the GPUKind enum from a processor name, e.g. "gfx90a" -> "GK_GFX90A".
34static void emitGPUKindEnum(raw_ostream &OS, StringRef Name) {
35 OS << "GK_";
36 for (char C : Name)
37 OS << ((C == '-') ? '_' : toUpper(x: C));
38}
39
40// Feature string to enumerator, e.g. "16-bit-insts" -> "FEAT_16_BIT_INSTS".
41// AMDGCN uses the "FEAT_" prefix, R600 the "R600_FEAT_" prefix.
42static void emitFeatureEnum(raw_ostream &OS, StringRef Prefix, StringRef Name) {
43 OS << Prefix;
44 for (char C : Name)
45 OS << ((C == '-') ? '_' : toUpper(x: C));
46}
47
48// Emit the Triple::AMDGPUSubArch enumerator suffix for a "gfx..." GPU name,
49// e.g. "gfx90a" -> "90A", "gfx9-generic" -> "9" (the family major).
50static void emitSubArchSuffix(raw_ostream &OS, StringRef Name) {
51 StringRef Suffix = Name;
52 Suffix.consume_front(Prefix: "gfx");
53 Suffix.consume_back(Suffix: "-generic");
54
55 for (char C : Suffix)
56 OS << static_cast<char>((C == '-') ? '_' : toUpper(x: C));
57}
58
59/// Derive the Triple::SubArchType from a "gfx..." GPU name, e.g. "gfx90a" ->
60/// Triple::AMDGPUSubArch90A
61static void emitSubArchForName(raw_ostream &OS, StringRef Name) {
62 OS << "Triple::AMDGPUSubArch";
63 emitSubArchSuffix(OS, Name);
64}
65
66// The explicit subarch spelling for a GPU whose subarch is not derivable from
67// its name, or empty. Optional so test stubs may omit it.
68static std::optional<StringRef> getSubArchSpelling(const Record *Rec) {
69 return Rec->getValueAsOptionalString(FieldName: "SubArchSpelling");
70}
71
72// Emit a subarch enumerator suffix for a spelling, dropping '.' and upcasing,
73// e.g. "12.50s" -> "1250S", matching the sibling name-derived enumerators.
74static void emitSpellingSuffix(raw_ostream &OS, StringRef Spelling) {
75 for (char C : Spelling)
76 if (C != '.')
77 OS << static_cast<char>(toUpper(x: C));
78}
79
80// Derive the Triple::SubArchType for a canonical GPU record. A pseudo target
81// maps to Triple::NoSubArch; an explicit SubArchSpelling maps to that (e.g.
82// "4.67q" -> AMDGPUSubArch4_67Q); otherwise it is derived from the name.
83static void emitSubArch(raw_ostream &OS, const Record *Rec) {
84 if (Rec->getValueAsBit(FieldName: "IsPseudoTarget")) {
85 OS << "Triple::NoSubArch";
86 return;
87 }
88
89 if (std::optional<StringRef> Spelling = getSubArchSpelling(Rec)) {
90 OS << "Triple::AMDGPUSubArch";
91 emitSpellingSuffix(OS, Spelling: *Spelling);
92 return;
93 }
94
95 emitSubArchForName(OS, Name: Rec->getValueAsString(FieldName: "Name"));
96}
97
98// A canonical GPU record is a "gfxN-generic" family target if it covers a set
99// of concrete GPUs (via CoveredGPUs) rather than being a single piece of
100// hardware.
101static bool isGenericTarget(const Record *Rec) {
102 return !Rec->getValueAsListOfDefs(FieldName: "CoveredGPUs").empty();
103}
104
105// Emit the gfx family for a canonical GPU record: "gfx" + the ISA major version
106// (e.g. "gfx90a"/[9,0,10] -> "gfx9", "gfx1250"/[12,5,0] -> "gfx12").
107// Nothing for a pseudo target.
108static void emitArchFamily(raw_ostream &OS, const Record *Rec) {
109 if (Rec->getValueAsBit(FieldName: "IsPseudoTarget"))
110 return;
111 OS << "gfx" << Rec->getValueAsListOfInts(FieldName: "IsaVersion")[0];
112}
113
114// Emit the ISA version tuple as "major, minor, stepping" wrapped in \p Open and
115// \p Close (parens for the AMDGPU_GPU macro's ISAVERSION argument, braces for a
116// struct initializer).
117static void emitIsaVersion(raw_ostream &OS, const Record *Rec, char Open,
118 char Close) {
119 std::vector<int64_t> V = Rec->getValueAsListOfInts(FieldName: "IsaVersion");
120 if (V.size() != 3) {
121 PrintFatalError(ErrorLoc: Rec->getLoc(),
122 Msg: "GPU '" + Rec->getValueAsString(FieldName: "Name") +
123 "' must have a 3-element [major, minor, stepping] "
124 "IsaVersion");
125 }
126
127 // Each component is stored in a uint8_t field, and the stepping is
128 // additionally spelled as a single lowercase hex digit in the device and
129 // subarch names. Reject out-of-range values.
130 for (int64_t Component : V) {
131 if (!isUInt<8>(x: Component)) {
132 PrintFatalError(ErrorLoc: Rec->getLoc(),
133 Msg: "GPU '" + Rec->getValueAsString(FieldName: "Name") +
134 "' IsaVersion components must each fit in a byte");
135 }
136 }
137
138 if (!isUInt<4>(x: V[2])) {
139 PrintFatalError(ErrorLoc: Rec->getLoc(), Msg: "GPU '" + Rec->getValueAsString(FieldName: "Name") +
140 "' stepping must be a single hex digit");
141 }
142
143 OS << Open << V[0] << ", " << V[1] << ", " << V[2] << Close;
144}
145
146// Emit the triple subarch name for a concrete GPU, e.g. gfx90c / [9, 0, 12] ->
147// "amdgpu9.0c". The stepping is spelled as a single lowercase hex digit
148// (validated by emitIsaVersion).
149static void emitConcreteSubArchTripleName(raw_ostream &OS, const Record *Rec) {
150 std::vector<int64_t> V = Rec->getValueAsListOfInts(FieldName: "IsaVersion");
151 OS << "amdgpu" << V[0] << '.' << V[1] << hexdigit(X: V[2], /*LowerCase=*/true);
152}
153
154// Emit the triple subarch name for a major-family subarch, e.g. "9" ->
155// "amdgpu9", "9_4" -> "amdgpu9.4" (the enumerator suffix uses '_', the triple
156// name '.').
157static void emitFamilySubArchTripleName(raw_ostream &OS, StringRef Suffix) {
158 OS << "amdgpu";
159 for (char C : Suffix)
160 OS << static_cast<char>((C == '_') ? '.' : C);
161}
162
163// A canonical GPU or a ProcessorAlias.
164namespace {
165struct GPUEntry {
166 const Record *Rec;
167 bool IsAlias;
168
169 // Whether this entry is (or aliases) a generic family target. \p Canonicals
170 // maps canonical GPU names to their records.
171 bool isGeneric(const StringMap<const Record *> &Canonicals) const {
172 const Record *Canon =
173 IsAlias ? Canonicals.lookup(Key: Rec->getValueAsString(FieldName: "Alias")) : Rec;
174 return Canon && isGenericTarget(Rec: Canon);
175 }
176};
177} // namespace
178
179// The frontend-visible features from def \p ListName, in bit order. Empty if
180// the def is absent.
181static std::vector<const Record *>
182collectFrontendFeatures(const RecordKeeper &RK, StringRef ListName) {
183 const Record *List = RK.getDef(Name: ListName);
184 if (!List)
185 return {};
186 return List->getValueAsListOfDefs(FieldName: "Features");
187}
188
189static void
190emitFeatureBitset(raw_ostream &OS, StringRef BitsetType, StringRef EnumPrefix,
191 const Record *GPU,
192 const DenseMap<const Record *, unsigned> &FeatureIdx);
193
194// The transitive closure of a GPU's SubtargetFeatures, following the Implies
195// edges (a feature enables everything it implies).
196static void collectFeatureClosure(const Record *GPU,
197 SetVector<const Record *> &Closure) {
198 std::vector<const Record *> Worklist = GPU->getValueAsListOfDefs(FieldName: "Features");
199 while (!Worklist.empty()) {
200 const Record *F = Worklist.back();
201 Worklist.pop_back();
202 if (Closure.insert(X: F))
203 append_range(C&: Worklist, R: F->getValueAsListOfDefs(FieldName: "Implies"));
204 }
205}
206
207// Collect canonical GPUs and their aliases, in TableGen definition order. R600
208// GPUs are plain Processor records; AMDGPU GPUs are ProcessorModel records (a
209// Processor subclass), so \p WantR600 selects the family to emit.
210static std::vector<GPUEntry> collectGPUs(const RecordKeeper &RK,
211 bool WantR600) {
212 ArrayRef<const Record *> GPUs = RK.getAllDerivedDefinitions(ClassName: "AMDGPUGPUInfo");
213 std::vector<GPUEntry> Entries;
214 Entries.reserve(n: GPUs.size());
215 for (const Record *Rec : GPUs) {
216 if (Rec->isSubClassOf(Name: "ProcessorModel") == WantR600)
217 continue;
218 Entries.push_back(x: {.Rec: Rec, /*IsAlias=*/false});
219 }
220
221 // Aliases only make sense when their canonical is present, so only gather
222 // them for the family being emitted.
223 if (!Entries.empty()) {
224 for (const Record *Rec :
225 RK.getAllDerivedDefinitionsIfDefined(ClassName: "ProcessorAlias"))
226 Entries.push_back(x: {.Rec: Rec, /*IsAlias=*/true});
227 }
228
229 // Sort to preserve declaration order instead of name order.
230 sort(C&: Entries, Comp: [](const GPUEntry &A, const GPUEntry &B) {
231 return A.Rec->getID() < B.Rec->getID();
232 });
233
234 return Entries;
235}
236
237// Check that every alias resolves to a canonical GPU and no name repeats.
238static void validate(ArrayRef<GPUEntry> Entries) {
239 StringMap<const Record *> Canonicals;
240 for (const GPUEntry &E : Entries)
241 if (!E.IsAlias)
242 Canonicals[E.Rec->getValueAsString(FieldName: "Name")] = E.Rec;
243
244 StringMap<const Record *> Seen;
245 for (const GPUEntry &E : Entries) {
246 StringRef Name = E.Rec->getValueAsString(FieldName: "Name");
247 if (!Seen.insert(KV: {Name, E.Rec}).second) {
248 PrintFatalError(ErrorLoc: E.Rec->getLoc(),
249 Msg: "duplicate AMDGPU processor name '" + Name + "'");
250 }
251
252 if (E.IsAlias) {
253 StringRef Alias = E.Rec->getValueAsString(FieldName: "Alias");
254 if (!Canonicals.count(Key: Alias)) {
255 PrintFatalError(ErrorLoc: E.Rec->getLoc(),
256 Msg: "ProcessorAlias '" + Name + "' aliases '" + Alias +
257 "' which is not a canonical AMDGPU GPU");
258 }
259 }
260 }
261}
262
263// The canonical R600 GPU records, in GPUKind-enum / TableGen definition order.
264static std::vector<const Record *>
265collectR600Canonicals(const RecordKeeper &RK) {
266 std::vector<GPUEntry> Entries = collectGPUs(RK, /*WantR600=*/true);
267 std::vector<const Record *> Canon;
268 Canon.reserve(n: Entries.size());
269
270 for (const GPUEntry &E : Entries) {
271 if (!E.IsAlias)
272 Canon.push_back(x: E.Rec);
273 }
274
275 return Canon;
276}
277
278// Emit the R600 GPUKind enumerators (canonical GPUs only; aliases share a
279// canonical's kind). Guarded by GET_R600_GPU_ENUM.
280static void emitR600Enum(raw_ostream &OS, const RecordKeeper &RK) {
281 std::vector<const Record *> Canon = collectR600Canonicals(RK);
282 if (Canon.empty())
283 return;
284 OS << "#ifdef GET_R600_GPU_ENUM\n"
285 "#undef GET_R600_GPU_ENUM\n";
286 for (const Record *R : Canon) {
287 OS << " ";
288 emitGPUKindEnum(OS, Name: R->getValueAsString(FieldName: "Name"));
289 OS << ",\n";
290 }
291 OS << "#endif // GET_R600_GPU_ENUM\n\n";
292}
293
294// Emit the R600Info table indexed by (GPUKind - R600FirstGPUKind). Names are
295// offsets into the shared \p Names table. Guarded by GET_R600_GPU_TABLE.
296static void
297emitR600Table(raw_ostream &OS, const RecordKeeper &RK,
298 StringToOffsetTable &Names,
299 const DenseMap<const Record *, unsigned> &FeatureIdx) {
300 std::vector<const Record *> Canon = collectR600Canonicals(RK);
301 if (Canon.empty())
302 return;
303
304 OS << "#ifdef GET_R600_GPU_TABLE\n"
305 "#undef GET_R600_GPU_TABLE\n";
306 OS << "static constexpr GPUKind R600FirstGPUKind = ";
307 emitGPUKindEnum(OS, Name: Canon.front()->getValueAsString(FieldName: "Name"));
308 OS << ";\n"
309 "static constexpr R600Info R600GPUTable[] = {\n";
310 for (const Record *R : Canon) {
311 OS << " {" << Names.GetOrAddStringOffset(Str: R->getValueAsString(FieldName: "Name"))
312 << ", ";
313 emitFeatureBitset(OS, BitsetType: "R600FeatureBitset", EnumPrefix: "R600_FEAT_", GPU: R, FeatureIdx);
314 OS << "},\n";
315 }
316 OS << "};\n"
317 "#endif // GET_R600_GPU_TABLE\n\n";
318}
319
320// Emit the R600 name -> GPUKind alias table. Guarded by
321// GET_R600_GPU_ALIAS_TABLE; names are offsets into \p Names.
322static void emitR600Aliases(raw_ostream &OS, const RecordKeeper &RK,
323 StringToOffsetTable &Names) {
324 std::vector<GPUEntry> Entries = collectGPUs(RK, /*WantR600=*/true);
325 validate(Entries);
326 if (Entries.empty())
327 return;
328
329 OS << "#ifdef GET_R600_GPU_ALIAS_TABLE\n"
330 "#undef GET_R600_GPU_ALIAS_TABLE\n"
331 "static constexpr GPUNameAlias R600GPUAliases[] = {\n";
332 for (const GPUEntry &E : Entries) {
333 if (!E.IsAlias)
334 continue;
335 OS << " {" << Names.GetOrAddStringOffset(Str: E.Rec->getValueAsString(FieldName: "Name"))
336 << ", ";
337 emitGPUKindEnum(OS, Name: E.Rec->getValueAsString(FieldName: "Alias"));
338 OS << "},\n";
339 }
340 OS << "};\n"
341 "#endif // GET_R600_GPU_ALIAS_TABLE\n\n";
342}
343
344// Canonical AMDGPU GPUs in GPUKind-enum order: non-generic targets first, then
345// the "gfxN-generic" targets. The enum and the GPUInfo table share this order.
346static std::vector<const Record *>
347collectAMDGPUCanonicals(const RecordKeeper &RK) {
348 std::vector<GPUEntry> Entries = collectGPUs(RK, /*WantR600=*/false);
349 std::vector<const Record *> Canon;
350 Canon.reserve(n: Entries.size());
351
352 for (const GPUEntry &E : Entries) {
353 if (!E.IsAlias && !isGenericTarget(Rec: E.Rec))
354 Canon.push_back(x: E.Rec);
355 }
356
357 for (const GPUEntry &E : Entries) {
358 if (!E.IsAlias && isGenericTarget(Rec: E.Rec))
359 Canon.push_back(x: E.Rec);
360 }
361
362 return Canon;
363}
364
365// Emit the AMDGPU GPUKind enumerators (canonical GPUs only; aliases share a
366// canonical's kind). Guarded by GET_AMDGPU_GPU_ENUM.
367static void emitAMDGPUEnum(raw_ostream &OS, const RecordKeeper &RK) {
368 std::vector<const Record *> Canon = collectAMDGPUCanonicals(RK);
369 if (Canon.empty())
370 return;
371 OS << "#ifdef GET_AMDGPU_GPU_ENUM\n"
372 "#undef GET_AMDGPU_GPU_ENUM\n";
373 for (const Record *R : Canon) {
374 OS << " ";
375 emitGPUKindEnum(OS, Name: R->getValueAsString(FieldName: "Name"));
376 OS << ",\n";
377 }
378 OS << "#endif // GET_AMDGPU_GPU_ENUM\n\n";
379}
380
381// Emit the name -> GPUKind alias table (legacy names such as "tahiti" ->
382// gfx600). Guarded by GET_AMDGPU_GPU_ALIAS_TABLE; names are offsets into \p
383// Names.
384static void emitAMDGPUAliases(raw_ostream &OS, const RecordKeeper &RK,
385 StringToOffsetTable &Names) {
386 std::vector<GPUEntry> Entries = collectGPUs(RK, /*WantR600=*/false);
387 validate(Entries);
388 if (Entries.empty())
389 return;
390
391 OS << "#ifdef GET_AMDGPU_GPU_ALIAS_TABLE\n"
392 "#undef GET_AMDGPU_GPU_ALIAS_TABLE\n"
393 "static constexpr GPUNameAlias AMDGPUGPUAliases[] = {\n";
394 for (const GPUEntry &E : Entries) {
395 if (!E.IsAlias)
396 continue;
397 OS << " {" << Names.GetOrAddStringOffset(Str: E.Rec->getValueAsString(FieldName: "Name"))
398 << ", ";
399 emitGPUKindEnum(OS, Name: E.Rec->getValueAsString(FieldName: "Alias"));
400 OS << "},\n";
401 }
402 OS << "};\n"
403 "#endif // GET_AMDGPU_GPU_ALIAS_TABLE\n\n";
404}
405
406// Per-family spellings for the generated feature enum and name table. R600 and
407// AMDGCN each get their own so the two headers coexist.
408struct FeatureNaming {
409 StringRef EnumGuard;
410 StringRef EnumPrefix;
411 StringRef CountEnumerator;
412 StringRef NameTableGuard;
413 StringRef NameTableSymbol;
414};
415
416static constexpr FeatureNaming AMDGPUFeatureNaming = {
417 .EnumGuard: "GET_AMDGPU_FEATURE_ENUM", .EnumPrefix: "FEAT_", .CountEnumerator: "NUM_FEATURES",
418 .NameTableGuard: "GET_AMDGPU_FEATURE_NAME_TABLE", .NameTableSymbol: "AMDGPUFeatureNames"};
419
420static constexpr FeatureNaming R600FeatureNaming = {
421 .EnumGuard: "GET_R600_FEATURE_ENUM", .EnumPrefix: "R600_FEAT_", .CountEnumerator: "R600_NUM_FEATURES",
422 .NameTableGuard: "GET_R600_FEATURE_NAME_TABLE", .NameTableSymbol: "R600FeatureNames"};
423
424// Emit the frontend feature enum for a family, interning each feature name into
425// \p Names. Returns the name offsets indexed by feature bit.
426static std::vector<unsigned> emitFeatureEnum(raw_ostream &OS,
427 const FeatureNaming &Naming,
428 ArrayRef<const Record *> Features,
429 StringToOffsetTable &Names) {
430 std::vector<unsigned> Offsets;
431 if (Features.empty())
432 return Offsets;
433 Offsets.reserve(n: Features.size());
434
435 OS << "#ifdef " << Naming.EnumGuard << "\n"
436 << "#undef " << Naming.EnumGuard << "\n";
437 for (const Record *F : Features) {
438 StringRef Name = F->getValueAsString(FieldName: "Name");
439 OS << " ";
440 emitFeatureEnum(OS, Prefix: Naming.EnumPrefix, Name);
441 OS << ",\n";
442 Offsets.push_back(x: Names.GetOrAddStringOffset(Str: Name));
443 }
444 OS << " " << Naming.CountEnumerator << "\n"
445 << "#endif // " << Naming.EnumGuard << "\n\n";
446 return Offsets;
447}
448
449// Emit a family's feature-name table (bit -> name offset).
450static void emitFeatureNames(raw_ostream &OS, const FeatureNaming &Naming,
451 ArrayRef<unsigned> Offsets) {
452 if (Offsets.empty())
453 return;
454 OS << "#ifdef " << Naming.NameTableGuard << "\n"
455 << "#undef " << Naming.NameTableGuard << "\n"
456 << "static constexpr StringTable::Offset " << Naming.NameTableSymbol
457 << "[] = {\n";
458 for (unsigned O : Offsets)
459 OS << " " << O << ",\n";
460 OS << "};\n"
461 << "#endif // " << Naming.NameTableGuard << "\n\n";
462}
463
464// Features checked for generic-target compatibility: frontend-visible features
465// and features explicitly opting into the any-covered-GPU rule.
466static SetVector<const Record *>
467collectGenericFeatures(const Record *GPU,
468 const DenseMap<const Record *, unsigned> &FeatureIdx) {
469 SetVector<const Record *> Closure;
470 collectFeatureClosure(GPU, Closure);
471 SetVector<const Record *> Features;
472 for (const Record *F : Closure) {
473 if (FeatureIdx.contains(Val: F) || F->isSubClassOf(Name: "AMDGPUGenericAnyFeature"))
474 Features.insert(X: F);
475 }
476
477 return Features;
478}
479
480// Ordinary frontend-visible features must be present on every covered GPU.
481// AMDGPUGenericAnyFeature features need only be present on one covered GPU.
482static void
483validateGenericFeatures(const Record *GPU,
484 const DenseMap<const Record *, unsigned> &FeatureIdx) {
485 StringRef Name = GPU->getValueAsString(FieldName: "Name");
486 std::vector<const Record *> Covered =
487 GPU->getValueAsListOfDefs(FieldName: "CoveredGPUs");
488 if (Covered.empty()) {
489 if (Name.starts_with(Prefix: "gfx") && Name.ends_with(Suffix: "-generic")) {
490 PrintFatalError(ErrorLoc: GPU->getLoc(), Msg: "generic target '" + Name +
491 "' must cover at least one GPU");
492 }
493 return;
494 }
495
496 SetVector<const Record *> GenericFeatures =
497 collectGenericFeatures(GPU, FeatureIdx);
498 SetVector<const Record *> CoveredFeatures;
499 for (const Record *Member : Covered) {
500 if (!Member->isSubClassOf(Name: "AMDGPUGPUInfo") ||
501 !Member->isSubClassOf(Name: "ProcessorModel") ||
502 Member->getValueAsBit(FieldName: "IsPseudoTarget") || isGenericTarget(Rec: Member)) {
503 PrintFatalError(ErrorLoc: GPU->getLoc(),
504 Msg: "generic target '" + Name + "' covers '" +
505 Member->getValueAsString(FieldName: "Name") +
506 "', which is not a concrete AMDGPU GPU");
507 }
508
509 SetVector<const Record *> MemberFeatures =
510 collectGenericFeatures(GPU: Member, FeatureIdx);
511 CoveredFeatures.insert_range(R&: MemberFeatures);
512 for (const Record *F : GenericFeatures) {
513 if (!F->isSubClassOf(Name: "AMDGPUGenericAnyFeature") &&
514 !MemberFeatures.contains(key: F)) {
515 PrintFatalError(ErrorLoc: GPU->getLoc(),
516 Msg: "generic target '" + GPU->getValueAsString(FieldName: "Name") +
517 "' exposes feature '" +
518 F->getValueAsString(FieldName: "Name") +
519 "' not supported by covered GPU '" +
520 Member->getValueAsString(FieldName: "Name") + "'");
521 }
522 }
523 }
524
525 for (const Record *F : GenericFeatures) {
526 if (!CoveredFeatures.contains(key: F)) {
527 PrintFatalError(ErrorLoc: GPU->getLoc(),
528 Msg: "generic target '" + GPU->getValueAsString(FieldName: "Name") +
529 "' exposes feature '" + F->getValueAsString(FieldName: "Name") +
530 "' not supported by any covered GPU");
531 }
532 }
533}
534
535static void validateAMDGPU(const RecordKeeper &RK) {
536 DenseMap<const Record *, unsigned> FeatureIdx;
537 for (const auto &[Idx, F] :
538 enumerate(First: collectFrontendFeatures(RK, ListName: "AMDGPUFrontendVisibleFeatures")))
539 FeatureIdx[F] = Idx;
540
541 for (const Record *GPU : RK.getAllDerivedDefinitions(ClassName: "AMDGPUGPUInfo"))
542 validateGenericFeatures(GPU, FeatureIdx);
543}
544
545// Emit a GPU's feature bitset initializer: its feature closure intersected with
546// the frontend-visible set \p FeatureIdx, e.g.
547// "AMDGPUFeatureBitset({FEAT_DPP, FEAT_CI_INSTS})".
548static void
549emitFeatureBitset(raw_ostream &OS, StringRef BitsetType, StringRef EnumPrefix,
550 const Record *GPU,
551 const DenseMap<const Record *, unsigned> &FeatureIdx) {
552 SetVector<const Record *> Closure;
553 collectFeatureClosure(GPU, Closure);
554
555 // Sort by bit index for stable output.
556 SmallVector<std::pair<unsigned, StringRef>> Bits;
557 for (const Record *F : Closure) {
558 auto It = FeatureIdx.find(Val: F);
559 if (It != FeatureIdx.end())
560 Bits.emplace_back(Args: It->second, Args: F->getValueAsString(FieldName: "Name"));
561 }
562 sort(C&: Bits);
563
564 OS << BitsetType << "({";
565 ListSeparator LS(", ");
566 for (const auto &[Idx, Name] : Bits) {
567 OS << LS;
568 emitFeatureEnum(OS, Prefix: EnumPrefix, Name);
569 }
570 OS << "})";
571}
572
573// The value of the SubtargetFeature in \p GPU's closure that sets \p FieldName,
574// or \p Default if it has none. Two features setting the same field to
575// different values is an error: SubtargetFeature silently takes the larger.
576static int64_t getFeatureValue(const Record *GPU, StringRef FieldName,
577 int64_t Default) {
578 SetVector<const Record *> Closure;
579 collectFeatureClosure(GPU, Closure);
580
581 const Record *Found = nullptr;
582 int64_t Value = Default;
583 for (const Record *F : Closure) {
584 if (F->getValueAsString(FieldName: "FieldName") != FieldName)
585 continue;
586
587 int64_t V;
588 if (!to_integer(S: F->getValueAsString(FieldName: "Value"), Num&: V)) {
589 PrintFatalError(ErrorLoc: F->getLoc(), Msg: "feature '" + F->getValueAsString(FieldName: "Name") +
590 "' must have an integer value");
591 }
592 if (Found && V != Value) {
593 PrintFatalError(ErrorLoc: GPU->getLoc(),
594 Msg: "GPU '" + GPU->getValueAsString(FieldName: "Name") +
595 "' gets conflicting '" + FieldName +
596 "' values from '" + Found->getValueAsString(FieldName: "Name") +
597 "' and '" + F->getValueAsString(FieldName: "Name") + "'");
598 }
599 Found = F;
600 Value = V;
601 }
602 return Value;
603}
604
605/// Emit a GPUInfo table indexed by (GPUKind - AMDGPUFirstGPUKind). Name and
606/// family strings are stored as offsets into the shared \p Names table.
607static void
608emitAMDGPUTable(raw_ostream &OS, const RecordKeeper &RK,
609 StringToOffsetTable &Names,
610 const DenseMap<const Record *, unsigned> &FeatureIdx) {
611 std::vector<const Record *> Canon = collectAMDGPUCanonicals(RK);
612 if (Canon.empty())
613 return;
614
615 OS << "#ifdef GET_AMDGPU_GPU_TABLE\n"
616 "#undef GET_AMDGPU_GPU_TABLE\n";
617 OS << "static constexpr GPUKind AMDGPUFirstGPUKind = ";
618 emitGPUKindEnum(OS, Name: Canon.front()->getValueAsString(FieldName: "Name"));
619 OS << ";\n"
620 "static constexpr GPUInfo AMDGPUGPUTable[] = {\n";
621 for (const Record *R : Canon) {
622 StringRef Name = R->getValueAsString(FieldName: "Name");
623 OS << " {" << Names.GetOrAddStringOffset(Str: Name) << ", ";
624 emitSubArch(OS, Rec: R);
625 OS << ", ";
626 emitFeatureBitset(OS, BitsetType: "AMDGPUFeatureBitset", EnumPrefix: "FEAT_", GPU: R, FeatureIdx);
627 OS << ", ";
628 emitIsaVersion(OS, Rec: R, Open: '{', Close: '}');
629 SmallString<16> Family;
630 raw_svector_ostream FamilyOS(Family);
631 emitArchFamily(OS&: FamilyOS, Rec: R);
632 OS << ", " << Names.GetOrAddStringOffset(Str: Family) << ", "
633 << getFeatureValue(GPU: R, FieldName: "MaxWavesPerEU", Default: 10) << ", "
634 << getFeatureValue(GPU: R, FieldName: "AddressableLocalMemorySize", Default: 32768) << ", "
635 << getFeatureValue(GPU: R, FieldName: "LDSBankCount", Default: 32) << ", "
636 << getFeatureValue(GPU: R, FieldName: "BufferResourceNumRecordsWidth", Default: 0) << "},\n";
637 }
638 OS << "};\n"
639 "#endif // GET_AMDGPU_GPU_TABLE\n\n";
640}
641
642// Emit the subarch -> major-family-subarch overrides for getMajorSubArch (a
643// subarch not listed here is its own major). Each member GPU maps to its
644// family's major, sourced from a "gfxN-generic" target's CoveredGPUs, or from
645// an AMDGPUFamily's MajorSubArch for the gfx6/gfx7/gfx8 families that have no
646// generic target.
647static void emitAMDGPUMajorSubArch(raw_ostream &OS, const RecordKeeper &RK) {
648 ArrayRef<const Record *> GPUs =
649 RK.getAllDerivedDefinitionsIfDefined(ClassName: "AMDGPUGPUInfo");
650 ArrayRef<const Record *> Families =
651 RK.getAllDerivedDefinitionsIfDefined(ClassName: "AMDGPUFamily");
652
653 // The overrides come from generic targets' CoveredGPUs and AMDGPUFamily
654 // members. std::array makes the R600 case (zero entries) well-formed.
655 size_t NumEntries = 0;
656 for (const Record *G : GPUs)
657 NumEntries += G->getValueAsListOfDefs(FieldName: "CoveredGPUs").size();
658 for (const Record *F : Families)
659 NumEntries += F->getValueAsListOfDefs(FieldName: "Members").size();
660
661 OS << "#ifdef GET_AMDGPU_MAJOR_SUBARCH\n"
662 "#undef GET_AMDGPU_MAJOR_SUBARCH\n"
663 "struct AMDGPUMajorSubArchEntry {\n"
664 " Triple::SubArchType SubArch;\n"
665 " Triple::SubArchType Major;\n"
666 "};\n"
667 "static constexpr std::array<AMDGPUMajorSubArchEntry, "
668 << NumEntries << "> AMDGPUMajorSubArch = {{\n";
669
670 // A "gfxN-generic" target's subarch is the major for every GPU it covers.
671 for (const Record *G : GPUs) {
672 for (const Record *Member : G->getValueAsListOfDefs(FieldName: "CoveredGPUs")) {
673 OS << " {";
674 emitSubArchForName(OS, Name: Member->getValueAsString(FieldName: "Name"));
675 OS << ", ";
676 emitSubArch(OS, Rec: G);
677 OS << "},\n";
678 }
679 }
680
681 // The gfx6/gfx7/gfx8 families have no generic target, so their major comes
682 // from AMDGPUFamily::MajorSubArch.
683 for (const Record *F : Families) {
684 StringRef Major = F->getValueAsString(FieldName: "MajorSubArch");
685 for (const Record *Member : F->getValueAsListOfDefs(FieldName: "Members")) {
686 OS << " {";
687 emitSubArchForName(OS, Name: Member->getValueAsString(FieldName: "Name"));
688 OS << ", Triple::AMDGPUSubArch" << Major << "},\n";
689 }
690 }
691
692 OS << "}};\n"
693 "#endif // GET_AMDGPU_MAJOR_SUBARCH\n\n";
694}
695
696/// Emit the canonical GPU name for each AMDGPU subarch ("gfx900"), and it's
697/// corresponding subarch ("amdgpu9.00")
698static void emitAMDGPUSubArchNames(raw_ostream &OS, const RecordKeeper &RK,
699 StringToOffsetTable &Names) {
700 // A row of the generated table. \p Suffix is emitted verbatim after
701 // "Triple::AMDGPUSubArch"; the two name offsets index the shared string pool.
702 struct SubArchEntry {
703 SmallString<16> Suffix;
704 StringRef GPUName; // e.g. "gfx900".
705 unsigned TripleNameOffset;
706 };
707 std::vector<SubArchEntry> Entries;
708
709 for (const GPUEntry &E : collectGPUs(RK, /*WantR600=*/false)) {
710 if (E.IsAlias || E.Rec->getValueAsBit(FieldName: "IsPseudoTarget"))
711 continue;
712 SubArchEntry Entry;
713 Entry.GPUName = E.Rec->getValueAsString(FieldName: "Name");
714
715 SmallString<16> TripleName;
716 raw_svector_ostream TripleOS(TripleName);
717
718 // An explicit subarch spelling supplies the enumerator suffix and triple
719 // name, rather than the name/ISA version.
720 if (std::optional<StringRef> Spelling = getSubArchSpelling(Rec: E.Rec)) {
721 raw_svector_ostream SubArchOS(Entry.Suffix);
722 emitSpellingSuffix(OS&: SubArchOS, Spelling: *Spelling);
723 TripleOS << "amdgpu" << *Spelling;
724 } else {
725 {
726 raw_svector_ostream SubArchOS(Entry.Suffix);
727 emitSubArchSuffix(OS&: SubArchOS, Name: Entry.GPUName);
728 }
729
730 // A "gfxN-generic" target maps to the major-family subarch, so it takes
731 // the family triple name; a concrete GPU derives it from the ISA version.
732 if (isGenericTarget(Rec: E.Rec))
733 emitFamilySubArchTripleName(OS&: TripleOS, Suffix: Entry.Suffix);
734 else
735 emitConcreteSubArchTripleName(OS&: TripleOS, Rec: E.Rec);
736 }
737 Entry.TripleNameOffset = Names.GetOrAddStringOffset(Str: TripleName);
738
739 Entries.push_back(x: std::move(Entry));
740 }
741
742 for (const Record *F : RK.getAllDerivedDefinitionsIfDefined(ClassName: "AMDGPUFamily")) {
743 std::vector<const Record *> Members = F->getValueAsListOfDefs(FieldName: "Members");
744 StringRef Major = F->getValueAsString(FieldName: "MajorSubArch");
745 SubArchEntry Entry;
746 Entry.Suffix = Major;
747 Entry.GPUName = Members.front()->getValueAsString(FieldName: "Name");
748
749 SmallString<16> TripleName;
750 raw_svector_ostream TripleOS(TripleName);
751 emitFamilySubArchTripleName(OS&: TripleOS, Suffix: Major);
752 Entry.TripleNameOffset = Names.GetOrAddStringOffset(Str: TripleName);
753
754 Entries.push_back(x: std::move(Entry));
755 }
756
757 if (Entries.empty())
758 return;
759
760 unsigned NoSubArchOffset = Names.GetOrAddStringOffset(Str: "amdgpu");
761
762 OS << "#ifdef GET_AMDGPU_SUBARCH_NAME\n"
763 "#undef GET_AMDGPU_SUBARCH_NAME\n";
764 OS << "static constexpr StringTable::Offset AMDGPUNoSubArchNameOffset = "
765 << NoSubArchOffset << ";\n";
766 OS << "struct AMDGPUSubArchNameEntry {\n"
767 " Triple::SubArchType SubArch;\n"
768 " StringTable::Offset NameOffset;\n"
769 " StringTable::Offset TripleNameOffset;\n"
770 "};\n"
771 "static constexpr AMDGPUSubArchNameEntry AMDGPUSubArchNames[] = {\n";
772 for (const SubArchEntry &E : Entries)
773 OS << " {Triple::AMDGPUSubArch" << E.Suffix << ", "
774 << Names.GetOrAddStringOffset(Str: E.GPUName) << ", " << E.TripleNameOffset
775 << "},\n";
776 OS << "};\n"
777 "#endif // GET_AMDGPU_SUBARCH_NAME\n\n";
778}
779
780static void emitAMDGPUTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
781 validateAMDGPU(RK);
782
783 OS << "// Autogenerated by AMDGPUTargetDefEmitter.cpp\n\n";
784 // R600.td and AMDGPU.td are separate top-level files, so a run sees exactly
785 // one family; the other family's sections emit nothing.
786 emitR600Enum(OS, RK);
787 emitAMDGPUEnum(OS, RK);
788 emitAMDGPUMajorSubArch(OS, RK);
789
790 // Each family gets its own string pool with a distinct guard/symbol so the
791 // two generated headers stay independent when a consumer includes both.
792 // Buffer the tables first to intern their strings, then emit the pool ahead.
793 {
794 StringToOffsetTable Names;
795 std::string Tables;
796 raw_string_ostream TablesOS(Tables);
797
798 // The R600 frontend feature enum and per-GPU bitsets share the R600 string
799 // pool (feature names live alongside GPU names).
800 std::vector<const Record *> Features =
801 collectFrontendFeatures(RK, ListName: "R600FrontendVisibleFeatures");
802 DenseMap<const Record *, unsigned> FeatureIdx;
803 for (const auto &[Idx, F] : enumerate(First&: Features))
804 FeatureIdx[F] = Idx;
805
806 std::vector<unsigned> FeatureOffsets =
807 emitFeatureEnum(OS&: TablesOS, Naming: R600FeatureNaming, Features, Names);
808 emitR600Table(OS&: TablesOS, RK, Names, FeatureIdx);
809 emitFeatureNames(OS&: TablesOS, Naming: R600FeatureNaming, Offsets: FeatureOffsets);
810 emitR600Aliases(OS&: TablesOS, RK, Names);
811 if (!Tables.empty()) {
812 OS << "#ifdef GET_R600_NAME_TABLE\n"
813 "#undef GET_R600_NAME_TABLE\n";
814 Names.EmitStringTableDef(OS, Name: "R600NameTable");
815 OS << "#endif // GET_R600_NAME_TABLE\n\n";
816 OS << Tables;
817 }
818 }
819
820 {
821 StringToOffsetTable Names;
822 std::string Tables;
823 raw_string_ostream TablesOS(Tables);
824
825 // The frontend feature enum and per-GPU bitsets share the AMDGPU string
826 // pool (feature names live alongside GPU names).
827 std::vector<const Record *> Features =
828 collectFrontendFeatures(RK, ListName: "AMDGPUFrontendVisibleFeatures");
829 DenseMap<const Record *, unsigned> FeatureIdx;
830 for (const auto &[Idx, F] : enumerate(First&: Features))
831 FeatureIdx[F] = Idx;
832
833 std::vector<unsigned> FeatureOffsets =
834 emitFeatureEnum(OS&: TablesOS, Naming: AMDGPUFeatureNaming, Features, Names);
835 emitAMDGPUTable(OS&: TablesOS, RK, Names, FeatureIdx);
836 emitFeatureNames(OS&: TablesOS, Naming: AMDGPUFeatureNaming, Offsets: FeatureOffsets);
837 emitAMDGPUAliases(OS&: TablesOS, RK, Names);
838 emitAMDGPUSubArchNames(OS&: TablesOS, RK, Names);
839 if (!Tables.empty()) {
840 OS << "#ifdef GET_AMDGPU_NAME_TABLE\n"
841 "#undef GET_AMDGPU_NAME_TABLE\n";
842 Names.EmitStringTableDef(OS, Name: "AMDGPUNameTable");
843 OS << "#endif // GET_AMDGPU_NAME_TABLE\n\n";
844 OS << Tables;
845 }
846 }
847}
848
849static TableGen::Emitter::Opt X("gen-amdgpu-target-def", emitAMDGPUTargetDef,
850 "Generate the list of AMDGPU GPUs");
851