blob: 8d2206fb4fed107ab72f1512a0f25c8add9d51f0 [file] [edit]
//===-- IntelGPUTargetParser - Parser for Intel GPU targets ---------------===//
//
// 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 file implements a target parser for the Intel GPU list.
//
//===----------------------------------------------------------------------===//
#include "llvm/TargetParser/IntelGPUTargetParser.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
using namespace llvm;
using namespace IntelGPU;
// A GPU IP version packs four fields, from the most significant bit down:
//
// 31 22 21 14 13 6 5 0
// +---------------+----------+----------+----------+
// | major | minor | reserved | revision |
// +---------------+----------+----------+----------+
// 10 bits 8 bits 8 bits 6 bits
//
// The reserved bits carry no information.
static constexpr uint32_t GPUIPMajorShift = 22;
static constexpr uint32_t GPUIPMinorShift = 14;
[[maybe_unused]] static constexpr uint32_t GPUIPMajorMask = 0x3ff;
static constexpr uint32_t GPUIPMinorMask = 0xff;
static constexpr uint32_t GPUIPRevisionMask = 0x3f;
// The bits that identify a device: the major and the minor version. Neither the
// revision nor the reserved bits take part in the lookup, because every
// revision of a device is one device as far as the compiler is concerned.
static constexpr uint32_t GPUIPDeviceMask = ~0u << GPUIPMinorShift;
// Pack a major and a minor version the way a GPU IP version does, so that a
// row of the table can be compared against a reported version as it is. A value
// too wide for its field would silently corrupt the fields above it, which
// would mean a typo in IntelGPUTargetParser.def going unnoticed.
static constexpr uint32_t packDevice(uint32_t Major, uint32_t Minor) {
assert((Major & ~GPUIPMajorMask) == 0 && "major version too wide");
assert((Minor & ~GPUIPMinorMask) == 0 && "minor version too wide");
return (Major << GPUIPMajorShift) | (Minor << GPUIPMinorShift);
}
StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
const uint32_t Device = GPUIPVersion & GPUIPDeviceMask;
#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \
if (Device == packDevice(MAJOR, MINOR)) \
return NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
return "";
}
std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) {
const uint32_t Major = GPUIPVersion >> GPUIPMajorShift;
const uint32_t Minor = (GPUIPVersion >> GPUIPMinorShift) & GPUIPMinorMask;
const uint32_t Revision = GPUIPVersion & GPUIPRevisionMask;
return ("xe_" + Twine(Major) + "." + Twine(Minor) + "." + Twine(Revision))
.str();
}
StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
switch (Kind) {
case GK_NONE:
return "";
#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \
case GK_##KIND: \
return NAME;
#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS) \
case GK_##KIND: \
return NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
}
llvm_unreachable("invalid Intel GPU kind");
}
GPUKind llvm::IntelGPU::parseArch(StringRef Name) {
// A numeric name ends in a revision, e.g. "xe_12.60.7". Only the major and
// minor versions name a device, so the revision may be any number and is not
// looked up.
if (Name.starts_with("xe_")) {
auto [Device, Revision] = Name.rsplit('.');
if (Device.count('.') != 1 || Revision.empty() ||
!all_of(Revision, isDigit))
return GK_NONE;
Name = Device;
}
return StringSwitch<GPUKind>(Name)
#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \
.Case(NAME, GK_##KIND).Case("xe_" #MAJOR "." #MINOR, GK_##KIND)
#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS) \
.Case(NAME, GK_##KIND)
#define INTEL_GPU_ALIAS(NAME, KIND) .Case(NAME, GK_##KIND)
#include "llvm/TargetParser/IntelGPUTargetParser.def"
.Default(GK_NONE);
}
namespace {
// The IGCA target of each row in IntelGPUTargetParser.def, e.g. {20,
// ComputeExact} for xe-pvc. Several rows can have the same IGCA target, e.g.
// the nine igca_10r devices, so entries repeat.
struct DeviceIGCATarget {
uint16_t Target;
IGCAFeatureSet FeatureSet;
};
constexpr DeviceIGCATarget DeviceIGCATargets[] = {
#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \
{IGCA_TARGET, IGCAFeatureSet::IGCA_FEATURE_SETS},
#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS) \
{IGCA_TARGET, IGCAFeatureSet::IGCA_FEATURE_SETS},
#include "llvm/TargetParser/IntelGPUTargetParser.def"
};
// All five possible names for each device's numeric IGCA target, e.g. igca_20,
// igca_20c, igca_20ca, igca_20r and igca_20ra for xe-pvc. Which of them are
// valid is decided by the IGCATarget constructor, so the rule is in one place.
// The names are string literals, so getIGCATargetName() can return them.
struct IGCATargetNameEntry {
const char *Name;
uint16_t Target;
IGCAFeatureSet FeatureSet;
};
#define IGCA_TARGET_NAMES(TARGET) \
{"igca_" #TARGET, TARGET, IGCAFeatureSet::Core}, \
{"igca_" #TARGET "c", TARGET, IGCAFeatureSet::Compute}, \
{"igca_" #TARGET "ca", TARGET, IGCAFeatureSet::ComputeExact}, \
{"igca_" #TARGET "r", TARGET, IGCAFeatureSet::Render}, \
{"igca_" #TARGET "ra", TARGET, IGCAFeatureSet::RenderExact},
constexpr IGCATargetNameEntry IGCATargetNames[] = {
#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \
IGCA_TARGET_NAMES(IGCA_TARGET)
#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS) \
IGCA_TARGET_NAMES(IGCA_TARGET)
#include "llvm/TargetParser/IntelGPUTargetParser.def"
};
#undef IGCA_TARGET_NAMES
} // namespace
/// \return true if a device with the IGCA feature set \p DeviceFeatureSet can
/// run code built for the IGCA feature set \p RequestedFeatureSet at the
/// device's own numeric IGCA target. For example, xe-pvc (igca_20ca,
/// ComputeExact) can run code built for igca_20ca, igca_20c and igca_20, but
/// not igca_20r.
static bool deviceCanRunIGCAFeatureSet(IGCAFeatureSet DeviceFeatureSet,
IGCAFeatureSet RequestedFeatureSet) {
// Every device runs code built for the Core IGCA feature set, e.g. igca_20.
if (RequestedFeatureSet == IGCAFeatureSet::Core)
return true;
// A device runs code built for its own IGCA feature set, e.g. igca_20ca.
if (RequestedFeatureSet == DeviceFeatureSet)
return true;
// An exact IGCA feature set also runs code built for its non-exact form,
// e.g. igca_20c.
if (DeviceFeatureSet == IGCAFeatureSet::ComputeExact)
return RequestedFeatureSet == IGCAFeatureSet::Compute;
if (DeviceFeatureSet == IGCAFeatureSet::RenderExact)
return RequestedFeatureSet == IGCAFeatureSet::Render;
return false;
}
bool llvm::IntelGPU::IGCATarget::isKnownIGCATarget(uint32_t Packed) {
// A value with any of bits 3-15 set is not an IGCA target, e.g. igca_20c
// (0x00140002) with bit 3 set.
if (Packed & ReservedMask)
return false;
// Split the value into its numeric IGCA target and feature set, e.g. 20 and
// Compute for igca_20c.
uint16_t RequestedTarget = uint16_t(Packed >> TargetShift);
auto RequestedFeatureSet = IGCAFeatureSet(Packed & FeatureSetMask);
for (const DeviceIGCATarget &D : DeviceIGCATargets)
if (D.Target == RequestedTarget &&
deviceCanRunIGCAFeatureSet(D.FeatureSet, RequestedFeatureSet))
return true;
return false;
}
IGCATarget llvm::IntelGPU::parseIGCATarget(StringRef MaybeTarget) {
for (const IGCATargetNameEntry &E : IGCATargetNames)
if (MaybeTarget == E.Name)
return IGCATarget(E.Target, E.FeatureSet);
return IGCATarget::invalid();
}
StringRef llvm::IntelGPU::getIGCATargetName(IGCATarget T) {
for (const IGCATargetNameEntry &E : IGCATargetNames)
if (E.Target == T.getTarget() && E.FeatureSet == T.getFeatureSet())
return E.Name;
return "";
}
IGCATarget llvm::IntelGPU::getIGCATarget(GPUKind Kind) {
switch (Kind) {
case GK_NONE:
return IGCATarget::invalid();
#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \
case GK_##KIND: \
return IGCATarget(IGCA_TARGET, IGCAFeatureSet::IGCA_FEATURE_SETS);
#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS) \
case GK_##KIND: \
return IGCATarget(IGCA_TARGET, IGCAFeatureSet::IGCA_FEATURE_SETS);
#include "llvm/TargetParser/IntelGPUTargetParser.def"
}
llvm_unreachable("invalid Intel GPU kind");
}