blob: 66d9d3b66350c03f3768c7eff61e6f1554327449 [file]
//===- AMDGPUTargetDefEmitter.cpp - Generate lists of AMDGPU GPUs ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend emits the AMDGPU GPU tables used by
// AMDGPUTargetParser.cpp.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <vector>
using namespace llvm;
// Derive the GPUKind enum from a processor name, e.g. "gfx90a" -> "GK_GFX90A".
static void emitGPUKindEnum(raw_ostream &OS, StringRef Name) {
OS << "GK_";
for (char C : Name)
OS << ((C == '-') ? '_' : toUpper(C));
}
// Derive the Triple::SubArchType from an AMDGPU processor name, e.g. "gfx90a"
// -> Triple::AMDGPUSubArch90A. A generic target uses its family's major
// subarch, e.g. "gfx9-generic" -> Triple::AMDGPUSubArch9.
static void emitSubArch(raw_ostream &OS, StringRef Name) {
StringRef Suffix = Name;
Suffix.consume_front("gfx");
Suffix.consume_back("-generic");
OS << "Triple::AMDGPUSubArch";
for (char C : Suffix)
OS << ((C == '-') ? '_' : toUpper(C));
}
// Emit the ISA version tuple "(major, minor, stepping)".
static void emitIsaVersion(raw_ostream &OS, const Record *Rec) {
std::vector<int64_t> V = Rec->getValueAsListOfInts("IsaVersion");
if (V.size() != 3) {
PrintFatalError(Rec->getLoc(),
"GPU '" + Rec->getValueAsString("Name") +
"' must have a 3-element [major, minor, stepping] "
"IsaVersion");
}
OS << '(' << V[0] << ", " << V[1] << ", " << V[2] << ')';
}
// A canonical GPU or a ProcessorAlias.
namespace {
struct GPUEntry {
const Record *Rec;
bool IsAlias;
// An entry is generic if it is (or aliases) a "gfxN-generic" family target,
// i.e. a canonical that covers a set of concrete GPUs (non-empty
// CoveredGPUs).
// \p Canonicals maps canonical GPU names to their records.
bool isGeneric(const StringMap<const Record *> &Canonicals) const {
const Record *Canon =
IsAlias ? Canonicals.lookup(Rec->getValueAsString("Alias")) : Rec;
return Canon && !Canon->getValueAsListOfDefs("CoveredGPUs").empty();
}
};
} // namespace
// Emit the ArchFeature spellings joined with '|', or \p NoneSpelling when
// empty.
static void emitFeatureExpr(raw_ostream &OS, const Record *Rec,
StringRef NoneSpelling) {
ListSeparator LS("|");
bool Any = false;
for (const Record *F : Rec->getValueAsListOfDefs("ArchFeatures")) {
OS << LS << F->getValueAsString("Spelling");
Any = true;
}
if (!Any)
OS << NoneSpelling;
}
// Collect canonical GPUs and their aliases, in TableGen definition order. R600
// GPUs are plain Processor records; AMDGPU GPUs are ProcessorModel records (a
// Processor subclass), so \p WantR600 selects the family to emit.
static std::vector<GPUEntry> collectGPUs(const RecordKeeper &RK,
bool WantR600) {
ArrayRef<const Record *> GPUs = RK.getAllDerivedDefinitions("AMDGPUGPUInfo");
std::vector<GPUEntry> Entries;
Entries.reserve(GPUs.size());
for (const Record *Rec : GPUs) {
if (Rec->isSubClassOf("ProcessorModel") == WantR600)
continue;
Entries.push_back({Rec, /*IsAlias=*/false});
}
// Aliases only make sense when their canonical is present, so only gather
// them for the family being emitted.
if (!Entries.empty()) {
for (const Record *Rec :
RK.getAllDerivedDefinitionsIfDefined("ProcessorAlias"))
Entries.push_back({Rec, /*IsAlias=*/true});
}
// Sort to preserve declaration order instead of name order.
sort(Entries, [](const GPUEntry &A, const GPUEntry &B) {
return A.Rec->getID() < B.Rec->getID();
});
return Entries;
}
// Check that every alias resolves to a canonical GPU and no name repeats.
static void validate(ArrayRef<GPUEntry> Entries) {
StringMap<const Record *> Canonicals;
for (const GPUEntry &E : Entries)
if (!E.IsAlias)
Canonicals[E.Rec->getValueAsString("Name")] = E.Rec;
StringMap<const Record *> Seen;
for (const GPUEntry &E : Entries) {
StringRef Name = E.Rec->getValueAsString("Name");
if (!Seen.insert({Name, E.Rec}).second) {
PrintFatalError(E.Rec->getLoc(),
"duplicate AMDGPU processor name '" + Name + "'");
}
if (E.IsAlias) {
StringRef Alias = E.Rec->getValueAsString("Alias");
if (!Canonicals.count(Alias)) {
PrintFatalError(E.Rec->getLoc(),
"ProcessorAlias '" + Name + "' aliases '" + Alias +
"' which is not a canonical AMDGPU GPU");
}
}
}
}
static void emitR600(raw_ostream &OS, const RecordKeeper &RK) {
std::vector<GPUEntry> Entries = collectGPUs(RK, /*WantR600=*/true);
validate(Entries);
if (Entries.empty())
return;
OS << "#ifndef R600_GPU\n"
"#define R600_GPU(NAME, ENUM, FEATURES)\n"
"#endif\n\n"
"#ifndef R600_GPU_ALIAS\n"
"#define R600_GPU_ALIAS(NAME, ENUM)\n"
"#endif\n\n";
for (const GPUEntry &E : Entries) {
StringRef Name = E.Rec->getValueAsString("Name");
if (E.IsAlias) {
OS << "R600_GPU_ALIAS(\"" << Name << "\", ";
emitGPUKindEnum(OS, E.Rec->getValueAsString("Alias"));
OS << ")\n";
} else {
OS << "R600_GPU(\"" << Name << "\", ";
emitGPUKindEnum(OS, Name);
OS << ", ";
emitFeatureExpr(OS, E.Rec, "R600_FEATURE_NONE");
OS << ")\n";
}
}
OS << "\n#undef R600_GPU\n"
"#undef R600_GPU_ALIAS\n";
}
static void emitAMDGPUEntry(raw_ostream &OS, const GPUEntry &E) {
StringRef Name = E.Rec->getValueAsString("Name");
if (E.IsAlias) {
OS << "AMDGPU_GPU_ALIAS(\"" << Name << "\", ";
emitGPUKindEnum(OS, E.Rec->getValueAsString("Alias"));
OS << ")\n";
} else {
OS << "AMDGPU_GPU(\"" << Name << "\", ";
emitGPUKindEnum(OS, Name);
OS << ", ";
emitSubArch(OS, Name);
OS << ", ";
emitIsaVersion(OS, E.Rec);
OS << ", ";
emitFeatureExpr(OS, E.Rec, "FEATURE_NONE");
OS << ")\n";
}
}
static void emitAMDGPU(raw_ostream &OS, const RecordKeeper &RK) {
std::vector<GPUEntry> Entries = collectGPUs(RK, /*WantR600=*/false);
validate(Entries);
if (Entries.empty())
return;
StringMap<const Record *> Canonicals;
for (const GPUEntry &E : Entries) {
if (!E.IsAlias)
Canonicals[E.Rec->getValueAsString("Name")] = E.Rec;
}
OS << "#ifndef AMDGPU_GPU\n"
"#define AMDGPU_GPU(NAME, ENUM, SUBARCH, ISAVERSION, FEATURES)\n"
"#endif\n\n"
"#ifndef AMDGPU_GPU_ALIAS\n"
"#define AMDGPU_GPU_ALIAS(NAME, ENUM)\n"
"#endif\n\n";
// The GPUKind enum is positional and code relies on the generic targets
// being a contiguous block at the end (GK_AMDGPU_GENERIC_FIRST/LAST), so emit
// all non-generic entries first, then the generics, each group preserving
// TableGen definition order.
for (const GPUEntry &E : Entries) {
if (!E.isGeneric(Canonicals))
emitAMDGPUEntry(OS, E);
}
for (const GPUEntry &E : Entries) {
if (E.isGeneric(Canonicals))
emitAMDGPUEntry(OS, E);
}
OS << "\n#undef AMDGPU_GPU\n"
"#undef AMDGPU_GPU_ALIAS\n";
}
static void emitAMDGPUTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
OS << "// Autogenerated by AMDGPUTargetDefEmitter.cpp\n\n";
// R600 processors are Processor records; AMDGPU processors are
// ProcessorModel records. R600.td and AMDGPU.td are separate top-level files
// (neither includes the other), so exactly one family is present in a given
// run; the other section emits nothing.
emitR600(OS, RK);
emitAMDGPU(OS, RK);
}
static TableGen::Emitter::Opt X("gen-amdgpu-target-def", emitAMDGPUTargetDef,
"Generate the list of AMDGPU GPUs");