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