mirror-llvm-project/llvm/lib/TargetParser/AMDGPUTargetParser.cpp
Matt Arsenault 0aa3c9e671
AMDGPU: Add missing smem-prefetch-insts to gfx12.5/gfx13 feature map (#213297)
fillAMDGCNFeatureMap omitted smem-prefetch-insts for gfx125x and gfx13,
so clang wrongly rejected __builtin_amdgcn_s_prefetch_data / _inst /
s_buffer_prefetch_data.

Co-authored-by: Claude (Claude-Opus-4.8)
2026-07-31 17:29:31 +00:00

1232 lines
42 KiB
C++

//===-- AMDGPUTargetParser - Parser for AMDGPU features ---------*- C++ -*-===//
//
// 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 to recognise AMDGPU hardware features.
//
//===----------------------------------------------------------------------===//
#include "llvm/TargetParser/AMDGPUTargetParser.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringTable.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Triple.h"
#include <array>
#include <cassert>
using namespace llvm;
using namespace AMDGPU;
namespace {
constexpr unsigned NumAMDGPUSubArches =
Triple::LastAMDGPUSubArch - Triple::FirstAMDGPUSubArch + 1;
// A legacy GPU name (e.g. "tahiti") mapped to the GPUKind it aliases.
struct GPUNameAlias {
StringTable::Offset AltName;
GPUKind Kind;
};
// Per-GPU data for the AMDGCN GPUKinds, from the generated table below.
struct GPUInfo {
StringTable::Offset Name;
Triple::SubArchType SubArch;
unsigned ArchFeatures;
IsaVersion Version;
StringTable::Offset FamilyName;
};
// Per-GPU data for the R600 GPUKinds.
struct R600Info {
StringTable::Offset Name;
R600FeatureKind ArchFeatures;
};
#define GET_AMDGPU_NAME_TABLE
#define GET_AMDGPU_GPU_TABLE
#define GET_AMDGPU_GPU_ALIAS_TABLE
#define GET_AMDGPU_MAJOR_SUBARCH
#define GET_AMDGPU_SUBARCH_NAME
#include "llvm/TargetParser/AMDGPUTargetParserDef.inc"
#define GET_R600_NAME_TABLE
#define GET_R600_GPU_TABLE
#define GET_R600_GPU_ALIAS_TABLE
#include "llvm/TargetParser/R600TargetParserDef.inc"
// The string tables holding GPU-name-derived strings as offsets. R600 and
// AMDGPU come from separate generated headers, each with its own pool.
constexpr StringTable AMDGPUNameStrTab = AMDGPUNameTable;
constexpr StringTable R600NameStrTab = R600NameTable;
// Look up the GPUInfo row for an AMDGCN GPUKind, or nullptr for GK_NONE / a
// non-AMDGCN (R600) kind.
const GPUInfo *getAMDGPUInfo(GPUKind AK) {
if (AK < AMDGPUFirstGPUKind)
return nullptr;
unsigned Idx = AK - AMDGPUFirstGPUKind;
if (Idx >= std::size(AMDGPUGPUTable))
return nullptr;
return &AMDGPUGPUTable[Idx];
}
// Look up the R600Info row for an R600 GPUKind, or nullptr for a non-R600 kind.
const R600Info *getR600Info(GPUKind AK) {
if (AK < R600FirstGPUKind)
return nullptr;
unsigned Idx = AK - R600FirstGPUKind;
if (Idx >= std::size(R600GPUTable))
return nullptr;
return &R600GPUTable[Idx];
}
// Scan a name -> GPUKind table (canonical names, then aliases) for \p CPU.
template <typename InfoT, size_t N, size_t M>
GPUKind parseArchImpl(StringRef CPU, const InfoT (&Table)[N], GPUKind FirstKind,
const StringTable &StrTab,
const GPUNameAlias (&Aliases)[M]) {
for (unsigned I = 0; I != N; ++I) {
if (CPU == StrTab[Table[I].Name])
return static_cast<GPUKind>(FirstKind + I);
}
for (const GPUNameAlias &A : Aliases) {
if (CPU == StrTab[A.AltName])
return A.Kind;
}
return GK_NONE;
}
// Reverse map: SubArch -> GPUKind, indexed by (SubArch - FirstAMDGPUSubArch).
// Subarches with no GPU (incl. the NoSubArch pseudo targets) map to GK_NONE.
constexpr std::array<GPUKind, NumAMDGPUSubArches> AMDGPUSubArchToGPUKind = [] {
std::array<GPUKind, NumAMDGPUSubArches> Map{};
for (unsigned I = 0; I < std::size(AMDGPUGPUTable); ++I) {
Triple::SubArchType SubArch = AMDGPUGPUTable[I].SubArch;
if (SubArch != Triple::NoSubArch) {
Map[SubArch - Triple::FirstAMDGPUSubArch] =
static_cast<GPUKind>(AMDGPUFirstGPUKind + I);
}
}
return Map;
}();
/// SubArch -> major-family, indexed by (SubArch - FirstAMDGPUSubArch).
constexpr std::array<Triple::SubArchType, NumAMDGPUSubArches>
AMDGPUMajorFamilies = [] {
std::array<Triple::SubArchType, NumAMDGPUSubArches> Map{};
for (unsigned I = 0; I < NumAMDGPUSubArches; ++I) {
Map[I] =
static_cast<Triple::SubArchType>(Triple::FirstAMDGPUSubArch + I);
}
for (const AMDGPUMajorSubArchEntry &Entry : AMDGPUMajorSubArch)
Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = Entry.Major;
return Map;
}();
// SubArch -> name-offset, indexed by (SubArch - FirstAMDGPUSubArch). Unmapped
// subarches keep offset 0 (the empty string).
constexpr std::array<StringTable::Offset, NumAMDGPUSubArches>
AMDGPUSubArchNameOffsets = [] {
std::array<StringTable::Offset, NumAMDGPUSubArches> Map{};
for (const AMDGPUSubArchNameEntry &Entry : AMDGPUSubArchNames)
Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] = Entry.NameOffset;
return Map;
}();
// SubArch -> triple-name-offset (e.g. "amdgpu9.00"), like
// AMDGPUSubArchNameOffsets.
constexpr std::array<StringTable::Offset, NumAMDGPUSubArches>
AMDGPUSubArchTripleNameOffsets = [] {
std::array<StringTable::Offset, NumAMDGPUSubArches> Map{};
for (const AMDGPUSubArchNameEntry &Entry : AMDGPUSubArchNames)
Map[Entry.SubArch - Triple::FirstAMDGPUSubArch] =
Entry.TripleNameOffset;
return Map;
}();
} // namespace
StringRef llvm::AMDGPU::getArchFamilyNameAMDGCN(GPUKind AK) {
const GPUInfo *Info = getAMDGPUInfo(AK);
return Info ? AMDGPUNameStrTab[Info->FamilyName] : "";
}
Triple::SubArchType llvm::AMDGPU::getSubArch(GPUKind AK) {
const GPUInfo *Info = getAMDGPUInfo(AK);
return Info ? Info->SubArch : Triple::SubArchType::NoSubArch;
}
AMDGPU::GPUKind
llvm::AMDGPU::getGPUKindFromSubArch(Triple::SubArchType SubArch) {
if (SubArch < Triple::FirstAMDGPUSubArch ||
SubArch > Triple::LastAMDGPUSubArch)
return GK_NONE;
return AMDGPUSubArchToGPUKind[SubArch - Triple::FirstAMDGPUSubArch];
}
Triple::SubArchType AMDGPU::getMajorSubArch(Triple::SubArchType X) {
if (X < Triple::FirstAMDGPUSubArch || X > Triple::LastAMDGPUSubArch)
return Triple::NoSubArch;
return AMDGPUMajorFamilies[X - Triple::FirstAMDGPUSubArch];
}
bool AMDGPU::isSubArchCompatible(Triple::SubArchType A, Triple::SubArchType B) {
if (A == B || A == Triple::NoSubArch || B == Triple::NoSubArch)
return true;
Triple::SubArchType MajorA = AMDGPU::getMajorSubArch(A);
Triple::SubArchType MajorB = AMDGPU::getMajorSubArch(B);
// One side is the major-family subarch covering the other's family.
if (A == MajorA)
return MajorA == MajorB;
if (B == MajorB)
return MajorA == MajorB;
return false;
}
bool AMDGPU::isCPUValidForSubArch(Triple::SubArchType SubArch, GPUKind AK) {
// An unrecognized GPU is never valid.
if (AK == GK_NONE)
return false;
// A legacy triple without a subarch accepts any known GPU.
if (SubArch == Triple::NoSubArch)
return true;
// Reject the dummy "generic" targets
Triple::SubArchType GPUSubArch = getSubArch(AK);
if (GPUSubArch == Triple::NoSubArch)
return false;
return isSubArchCompatible(GPUSubArch, SubArch);
}
bool AMDGPU::isCPUValidForSubArch(Triple::SubArchType SubArch, StringRef CPU) {
return isCPUValidForSubArch(SubArch, parseArchAMDGCN(CPU));
}
bool AMDGPU::isPseudoTarget(GPUKind AK) {
const GPUInfo *Info = getAMDGPUInfo(AK);
return Info && Info->SubArch == Triple::NoSubArch;
}
bool AMDGPU::isPseudoTarget(StringRef CPU) {
return isPseudoTarget(parseArchAMDGCN(CPU));
}
bool AMDGPU::isSubArchCompatible(const Triple &A, const Triple &B) {
// Tolerate subarch mismatch if one entry is none. This is a hack for bitcode
// libraries.
// There's a missing enum entry for an unknown subarch. Make sure the
// subarch is really empty.
if (A.getSubArch() == Triple::NoSubArch)
return A.getArchName().size() == 6;
if (B.getSubArch() == Triple::NoSubArch)
return B.getArchName().size() == 6;
return isSubArchCompatible(A.getSubArch(), B.getSubArch());
}
std::string AMDGPU::mergeSubArch(const Triple &A, const Triple &B) {
if (A.getSubArch() == Triple::NoSubArch)
return B.str();
if (B.getSubArch() == Triple::NoSubArch)
return A.str();
Triple::SubArchType MajorA = AMDGPU::getMajorSubArch(A.getSubArch());
Triple::SubArchType MajorB = AMDGPU::getMajorSubArch(B.getSubArch());
// With a compatible major arch, return the specific subarch.
if (A.getSubArch() == MajorA) {
if (MajorA == MajorB)
return B.str();
}
if (B.getSubArch() == MajorB) {
if (MajorA == MajorB)
return A.str();
}
// Invalid case.
return B.str();
}
StringRef llvm::AMDGPU::getArchNameAMDGCN(GPUKind AK) {
const GPUInfo *Info = getAMDGPUInfo(AK);
return Info ? AMDGPUNameStrTab[Info->Name] : "";
}
StringRef llvm::AMDGPU::getArchNameFromSubArch(Triple::SubArchType SubArch) {
if (SubArch < Triple::FirstAMDGPUSubArch ||
SubArch > Triple::LastAMDGPUSubArch)
return "";
return AMDGPUNameStrTab[AMDGPUSubArchNameOffsets[SubArch -
Triple::FirstAMDGPUSubArch]];
}
StringRef llvm::AMDGPU::getSubArchName(Triple::SubArchType SubArch) {
if (SubArch == Triple::NoSubArch)
return AMDGPUNameStrTab[AMDGPUNoSubArchNameOffset];
assert(SubArch >= Triple::FirstAMDGPUSubArch &&
SubArch <= Triple::LastAMDGPUSubArch &&
"expected an AMDGPU subarch or NoSubArch");
return AMDGPUNameStrTab
[AMDGPUSubArchTripleNameOffsets[SubArch - Triple::FirstAMDGPUSubArch]];
}
StringRef llvm::AMDGPU::getArchNameR600(GPUKind AK) {
const R600Info *Info = getR600Info(AK);
return Info ? R600NameStrTab[Info->Name] : "";
}
AMDGPU::GPUKind llvm::AMDGPU::parseArchAMDGCN(StringRef CPU) {
return parseArchImpl(CPU, AMDGPUGPUTable, AMDGPUFirstGPUKind,
AMDGPUNameStrTab, AMDGPUGPUAliases);
}
AMDGPU::GPUKind llvm::AMDGPU::parseArchR600(StringRef CPU) {
return parseArchImpl(CPU, R600GPUTable, R600FirstGPUKind, R600NameStrTab,
R600GPUAliases);
}
unsigned AMDGPU::getArchAttrAMDGCN(GPUKind AK) {
const GPUInfo *Info = getAMDGPUInfo(AK);
return Info ? Info->ArchFeatures : FEATURE_NONE;
}
unsigned AMDGPU::getArchAttrAMDGCN(Triple::SubArchType SubArch) {
return getArchAttrAMDGCN(getGPUKindFromSubArch(SubArch));
}
R600FeatureKind AMDGPU::getArchAttrR600(GPUKind AK) {
const R600Info *Info = getR600Info(AK);
return Info ? Info->ArchFeatures : R600_FEATURE_NONE;
}
void AMDGPU::fillValidArchListAMDGCN(SmallVectorImpl<StringRef> &Values,
Triple::SubArchType SubArch) {
// XXX: Should this only report unique canonical names?
// An alias shares its GPU's GPUKind, so it is filtered alongside it.
for (unsigned I = 0; I != std::size(AMDGPUGPUTable); ++I) {
GPUKind Kind = static_cast<GPUKind>(AMDGPUFirstGPUKind + I);
if (AMDGPUGPUTable[I].SubArch != Triple::NoSubArch &&
isCPUValidForSubArch(SubArch, Kind))
Values.push_back(AMDGPUNameStrTab[AMDGPUGPUTable[I].Name]);
}
for (const GPUNameAlias &A : AMDGPUGPUAliases) {
if (isCPUValidForSubArch(SubArch, A.Kind))
Values.push_back(AMDGPUNameStrTab[A.AltName]);
}
}
void AMDGPU::fillValidArchListR600(SmallVectorImpl<StringRef> &Values) {
for (const R600Info &Info : R600GPUTable)
Values.push_back(R600NameStrTab[Info.Name]);
for (const GPUNameAlias &A : R600GPUAliases)
Values.push_back(R600NameStrTab[A.AltName]);
}
AMDGPU::IsaVersion AMDGPU::getIsaVersion(StringRef GPU) {
const GPUInfo *Info = getAMDGPUInfo(parseArchAMDGCN(GPU));
return Info ? Info->Version : IsaVersion{0, 0, 0};
}
AMDGPU::IsaVersion AMDGPU::getIsaVersion(Triple::SubArchType SubArch) {
const GPUInfo *Info = getAMDGPUInfo(getGPUKindFromSubArch(SubArch));
return Info ? Info->Version : IsaVersion{0, 0, 0};
}
unsigned AMDGPU::getTotalNumSGPRs(GPUKind AK) {
IsaVersion Version = getIsaVersion(getSubArch(AK));
if (Version.Major >= 8)
return 800;
return 512;
}
unsigned AMDGPU::getTotalNumSGPRs(Triple::SubArchType SubArch) {
IsaVersion Version = getIsaVersion(SubArch);
if (Version.Major >= 8)
return 800;
return 512;
}
unsigned AMDGPU::getAddressableNumSGPRs(GPUKind AK) {
if (getArchAttrAMDGCN(AK) & FEATURE_SGPR_INIT_BUG)
return FIXED_NUM_SGPRS_FOR_INIT_BUG;
IsaVersion Version = getIsaVersion(getSubArch(AK));
if (Version.Major >= 10)
return 106;
if (Version.Major >= 8)
return 102;
return 104;
}
unsigned AMDGPU::getAddressableNumSGPRs(Triple::SubArchType SubArch) {
if (getArchAttrAMDGCN(SubArch) & FEATURE_SGPR_INIT_BUG)
return FIXED_NUM_SGPRS_FOR_INIT_BUG;
IsaVersion Version = getIsaVersion(SubArch);
if (Version.Major >= 10)
return 106;
if (Version.Major >= 8)
return 102;
return 104;
}
unsigned AMDGPU::getSGPRAllocGranule(GPUKind AK) {
IsaVersion Version = getIsaVersion(getSubArch(AK));
if (Version.Major >= 10)
return getAddressableNumSGPRs(AK);
if (Version.Major >= 8)
return 16;
return 8;
}
unsigned AMDGPU::getSGPRAllocGranule(Triple::SubArchType SubArch) {
IsaVersion Version = getIsaVersion(SubArch);
if (Version.Major >= 10)
return getAddressableNumSGPRs(SubArch);
if (Version.Major >= 8)
return 16;
return 8;
}
StringRef AMDGPU::getCanonicalArchName(const Triple &T, StringRef Arch) {
assert(T.isAMDGPU());
auto ProcKind = T.isAMDGCN() ? parseArchAMDGCN(Arch) : parseArchR600(Arch);
if (ProcKind == GK_NONE)
return StringRef();
return T.isAMDGCN() ? getArchNameAMDGCN(ProcKind) : getArchNameR600(ProcKind);
}
static std::pair<FeatureError, StringRef>
insertWaveSizeFeature(StringRef GPU, const Triple &T,
const StringMap<bool> &DefaultFeatures,
StringMap<bool> &Features) {
// A bare subarch triple (no -target-cpu) still pins down the target, so it is
// not a null GPU: DefaultFeatures has already been populated from the
// subarch.
const bool IsNullGPU = T.getSubArch() == Triple::NoSubArch && GPU.empty();
const bool TargetHasWave32 = DefaultFeatures.count("wavefrontsize32");
const bool TargetHasWave64 = DefaultFeatures.count("wavefrontsize64");
auto Wave32Itr = Features.find("wavefrontsize32");
auto Wave64Itr = Features.find("wavefrontsize64");
const bool EnableWave32 =
Wave32Itr != Features.end() && Wave32Itr->getValue();
const bool EnableWave64 =
Wave64Itr != Features.end() && Wave64Itr->getValue();
const bool DisableWave32 =
Wave32Itr != Features.end() && !Wave32Itr->getValue();
const bool DisableWave64 =
Wave64Itr != Features.end() && !Wave64Itr->getValue();
if (EnableWave32 && EnableWave64)
return {AMDGPU::INVALID_FEATURE_COMBINATION,
"'+wavefrontsize32' and '+wavefrontsize64' are mutually exclusive"};
if (DisableWave32 && DisableWave64)
return {AMDGPU::INVALID_FEATURE_COMBINATION,
"'-wavefrontsize32' and '-wavefrontsize64' are mutually exclusive"};
if (!IsNullGPU) {
if (TargetHasWave64) {
if (EnableWave32)
return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "+wavefrontsize32"};
if (DisableWave64)
return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "-wavefrontsize64"};
}
if (TargetHasWave32) {
if (EnableWave64)
return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "+wavefrontsize64"};
if (DisableWave32)
return {AMDGPU::UNSUPPORTED_TARGET_FEATURE, "-wavefrontsize32"};
}
}
// Don't assume any wavesize with an unknown subtarget.
// Default to wave32 if target supports both.
if (!IsNullGPU && !EnableWave32 && !EnableWave64 && !TargetHasWave32 &&
!TargetHasWave64)
Features.insert(std::make_pair("wavefrontsize32", true));
for (const auto &Entry : DefaultFeatures) {
if (!Features.count(Entry.getKey()))
Features[Entry.getKey()] = Entry.getValue();
}
return {NO_ERROR, StringRef()};
}
/// Fills Features map with default values for given target GPU.
/// \p Features contains overriding target features and this function returns
/// default target features with entries overridden by \p Features.
static void fillAMDGCNFeatureMap(StringRef GPU, const Triple &T,
StringMap<bool> &Features) {
// With no explicit GPU, the triple's subarch identifies the target.
AMDGPU::GPUKind Kind = GPU.empty() && T.getSubArch() != Triple::NoSubArch
? getGPUKindFromSubArch(T.getSubArch())
: parseArchAMDGCN(GPU);
switch (Kind) {
case GK_GFX1310:
case GK_GFX13_GENERIC:
Features["ci-insts"] = true;
Features["dot7-insts"] = true;
Features["dot8-insts"] = true;
Features["dl-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["gfx8-insts"] = true;
Features["gfx9-insts"] = true;
Features["gfx10-insts"] = true;
Features["gfx10-3-insts"] = true;
Features["gfx11-insts"] = true;
Features["gfx12-insts"] = true;
Features["gfx1250-insts"] = true;
Features["gfx13-insts"] = true;
Features["flat-global-insts"] = true;
Features["bitop3-insts"] = true;
Features["prng-inst"] = true;
Features["tanh-insts"] = true;
Features["tensor-cvt-lut-insts"] = true;
Features["bf16-trans-insts"] = true;
Features["bf16-cvt-insts"] = true;
Features["cvt-sr-pk-bf16-f32-inst"] = true;
Features["bf16-pk-insts"] = true;
Features["fp8-conversion-insts"] = true;
Features["permlane16-swap"] = true;
Features["ashr-pk-insts"] = true;
Features["atomic-buffer-pk-add-bf16-inst"] = true;
Features["atomic-fadd-rtn-insts"] = true;
Features["atomic-buffer-global-pk-add-f16-insts"] = true;
Features["atomic-flat-pk-add-16-insts"] = true;
Features["atomic-global-pk-add-bf16-inst"] = true;
Features["atomic-ds-pk-add-16-insts"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
Features["s-wakeup-barrier-inst"] = true;
Features["f16bf16-to-fp6bf6-cvt-scale-insts"] = true;
Features["f32-to-fp6bf6-cvt-scale-insts"] = true;
Features["clusters"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
Features["cvt-pknorm-vop3-insts"] = true;
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["async-load-to-lds-insts"] = true;
Features["smem-prefetch-insts"] = true;
break;
case GK_GFX1251:
Features["gfx1251-gemm-insts"] = true;
[[fallthrough]];
case GK_GFX1250:
Features["swmmac-gfx1200-insts"] = true;
Features["swmmac-gfx1250-insts"] = true;
Features["cube-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
Features["lerp-inst"] = true;
Features["qsad-insts"] = true;
Features["sad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
[[fallthrough]];
case GK_GFX12_5_GENERIC:
Features["ci-insts"] = true;
Features["dot7-insts"] = true;
Features["dot8-insts"] = true;
Features["dl-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["gfx8-insts"] = true;
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["gfx10-insts"] = true;
Features["gfx10-3-insts"] = true;
Features["gfx11-insts"] = true;
Features["gfx12-insts"] = true;
Features["gfx1250-insts"] = true;
Features["bitop3-insts"] = true;
Features["prng-inst"] = true;
Features["tanh-insts"] = true;
Features["tensor-cvt-lut-insts"] = true;
Features["transpose-load-f4f6-insts"] = true;
Features["bf16-trans-insts"] = true;
Features["bf16-cvt-insts"] = true;
Features["cvt-sr-pk-bf16-f32-inst"] = true;
Features["bf16-pk-insts"] = true;
Features["fp8-conversion-insts"] = true;
Features["fp8e5m3-insts"] = true;
Features["permlane16-swap"] = true;
Features["ashr-pk-insts"] = true;
Features["add-min-max-insts"] = true;
Features["pk-add-min-max-insts"] = true;
Features["atomic-buffer-pk-add-bf16-inst"] = true;
Features["vmem-pref-insts"] = true;
Features["atomic-fadd-rtn-insts"] = true;
Features["atomic-buffer-global-pk-add-f16-insts"] = true;
Features["atomic-flat-pk-add-16-insts"] = true;
Features["atomic-global-pk-add-bf16-inst"] = true;
Features["atomic-ds-pk-add-16-insts"] = true;
Features["setprio-inc-wg-inst"] = true;
Features["s-wakeup-barrier-inst"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
Features["wavefrontsize32"] = true;
Features["clusters"] = true;
Features["mcast-load-insts"] = true;
Features["async-load-to-lds-insts"] = true;
Features["async-store-from-lds-insts"] = true;
Features["asynccnt"] = true;
Features["smem-prefetch-insts"] = true;
break;
case GK_GFX1201:
case GK_GFX1200:
case GK_GFX12_GENERIC:
Features["ci-insts"] = true;
Features["dot7-insts"] = true;
Features["dot8-insts"] = true;
Features["dot9-insts"] = true;
Features["dot10-insts"] = true;
Features["dot11-insts"] = true;
Features["dot12-insts"] = true;
Features["dl-insts"] = true;
Features["atomic-ds-pk-add-16-insts"] = true;
Features["atomic-flat-pk-add-16-insts"] = true;
Features["atomic-buffer-global-pk-add-f16-insts"] = true;
Features["atomic-buffer-pk-add-bf16-inst"] = true;
Features["atomic-global-pk-add-bf16-inst"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["gfx8-insts"] = true;
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["gfx10-insts"] = true;
Features["gfx10-3-insts"] = true;
Features["gfx11-insts"] = true;
Features["gfx12-insts"] = true;
Features["atomic-fadd-rtn-insts"] = true;
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["bvh-ray-tracing-insts"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
Features["fp8-conversion-insts"] = true;
Features["wmma-128b-insts"] = true;
Features["swmmac-gfx1200-insts"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
Features["smem-prefetch-insts"] = true;
break;
case GK_GFX1170:
case GK_GFX1171:
case GK_GFX1172:
case GK_GFX11_7_GENERIC:
Features["ci-insts"] = true;
Features["dot7-insts"] = true;
Features["dot8-insts"] = true;
Features["dot9-insts"] = true;
Features["dot10-insts"] = true;
Features["dot12-insts"] = true;
Features["dl-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["gfx8-insts"] = true;
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["gfx10-insts"] = true;
Features["gfx10-3-insts"] = true;
Features["gfx11-insts"] = true;
Features["atomic-fadd-rtn-insts"] = true;
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["bvh-ray-tracing-insts"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
Features["gws"] = true;
Features["dot11-insts"] = true;
Features["fp8-conversion-insts"] = true;
Features["wmma-128b-insts"] = true;
Features["swmmac-gfx1200-insts"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
break;
case GK_GFX1154:
case GK_GFX1153:
case GK_GFX1152:
case GK_GFX1151:
case GK_GFX1150:
case GK_GFX1103:
case GK_GFX1102:
case GK_GFX1101:
case GK_GFX1100:
case GK_GFX11_GENERIC:
Features["ci-insts"] = true;
Features["dot5-insts"] = true;
Features["dot7-insts"] = true;
Features["dot8-insts"] = true;
Features["dot9-insts"] = true;
Features["dot10-insts"] = true;
Features["dot12-insts"] = true;
Features["dl-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["gfx8-insts"] = true;
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["gfx10-insts"] = true;
Features["gfx10-3-insts"] = true;
Features["gfx11-insts"] = true;
Features["atomic-fadd-rtn-insts"] = true;
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["bvh-ray-tracing-insts"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
Features["gws"] = true;
Features["wmma-256b-insts"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
break;
case GK_GFX1036:
case GK_GFX1035:
case GK_GFX1034:
case GK_GFX1033:
case GK_GFX1032:
case GK_GFX1031:
case GK_GFX1030:
case GK_GFX10_3_GENERIC:
Features["ci-insts"] = true;
Features["dot1-insts"] = true;
Features["dot2-insts"] = true;
Features["dot5-insts"] = true;
Features["dot6-insts"] = true;
Features["dot7-insts"] = true;
Features["dot10-insts"] = true;
Features["dl-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["gfx8-insts"] = true;
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["gfx10-insts"] = true;
Features["gfx10-3-insts"] = true;
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["bvh-ray-tracing-insts"] = true;
Features["s-memrealtime"] = true;
Features["s-memtime-inst"] = true;
Features["gws"] = true;
Features["vmem-to-lds-load-insts"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
break;
case GK_GFX1012:
case GK_GFX1011:
Features["dot1-insts"] = true;
Features["dot2-insts"] = true;
Features["dot5-insts"] = true;
Features["dot6-insts"] = true;
Features["dot7-insts"] = true;
Features["dot10-insts"] = true;
[[fallthrough]];
case GK_GFX1013:
case GK_GFX1010:
case GK_GFX10_1_GENERIC:
if (Kind == GK_GFX1013)
Features["bvh-ray-tracing-insts"] = true;
Features["dl-insts"] = true;
Features["ci-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["gfx8-insts"] = true;
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["gfx10-insts"] = true;
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["s-memrealtime"] = true;
Features["s-memtime-inst"] = true;
Features["gws"] = true;
Features["vmem-to-lds-load-insts"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
break;
case GK_GFX950:
Features["bitop3-insts"] = true;
Features["fp6bf6-cvt-scale-insts"] = true;
Features["fp4-cvt-scale-insts"] = true;
Features["bf8-cvt-scale-insts"] = true;
Features["fp8-cvt-scale-insts"] = true;
Features["f16bf16-to-fp6bf6-cvt-scale-insts"] = true;
Features["f32-to-f16bf16-cvt-sr-insts"] = true;
Features["prng-inst"] = true;
Features["permlane16-swap"] = true;
Features["permlane32-swap"] = true;
Features["ashr-pk-insts"] = true;
Features["dot12-insts"] = true;
Features["dot13-insts"] = true;
Features["atomic-buffer-pk-add-bf16-inst"] = true;
Features["gfx950-insts"] = true;
[[fallthrough]];
case GK_GFX942:
Features["fp8-insts"] = true;
Features["fp8-conversion-insts"] = true;
if (Kind != GK_GFX950)
Features["xf32-insts"] = true;
[[fallthrough]];
case GK_GFX9_4_GENERIC:
Features["gfx940-insts"] = true;
Features["atomic-ds-pk-add-16-insts"] = true;
Features["atomic-flat-pk-add-16-insts"] = true;
Features["atomic-global-pk-add-bf16-inst"] = true;
Features["gfx90a-insts"] = true;
Features["atomic-buffer-global-pk-add-f16-insts"] = true;
Features["atomic-fadd-rtn-insts"] = true;
Features["dot3-insts"] = true;
Features["dot4-insts"] = true;
Features["dot5-insts"] = true;
Features["dot6-insts"] = true;
Features["mai-insts"] = true;
Features["dl-insts"] = true;
Features["dot1-insts"] = true;
Features["dot2-insts"] = true;
Features["dot7-insts"] = true;
Features["dot10-insts"] = true;
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["gfx8-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["s-memrealtime"] = true;
Features["ci-insts"] = true;
Features["s-memtime-inst"] = true;
Features["gws"] = true;
Features["vmem-to-lds-load-insts"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
Features["wavefrontsize64"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
break;
case GK_GFX90A:
Features["gfx90a-insts"] = true;
Features["atomic-buffer-global-pk-add-f16-insts"] = true;
Features["atomic-fadd-rtn-insts"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
[[fallthrough]];
case GK_GFX908:
Features["dot3-insts"] = true;
Features["dot4-insts"] = true;
Features["dot5-insts"] = true;
Features["dot6-insts"] = true;
Features["mai-insts"] = true;
[[fallthrough]];
case GK_GFX906:
Features["dl-insts"] = true;
Features["dot1-insts"] = true;
Features["dot2-insts"] = true;
Features["dot7-insts"] = true;
Features["dot10-insts"] = true;
[[fallthrough]];
case GK_GFX90C:
case GK_GFX909:
case GK_GFX904:
case GK_GFX902:
case GK_GFX900:
case GK_GFX9_GENERIC:
Features["gfx9-insts"] = true;
Features["flat-global-insts"] = true;
Features["vmem-to-lds-load-insts"] = true;
[[fallthrough]];
case GK_GFX810:
case GK_GFX805:
case GK_GFX803:
case GK_GFX802:
case GK_GFX801:
Features["gfx8-insts"] = true;
Features["16-bit-insts"] = true;
Features["dpp"] = true;
Features["s-memrealtime"] = true;
Features["ci-insts"] = true;
Features["image-insts"] = true;
if (Kind != GK_GFX90A)
Features["extended-image-insts"] = true;
Features["s-memtime-inst"] = true;
Features["gws"] = true;
Features["wavefrontsize64"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
break;
case GK_GFX705:
case GK_GFX704:
case GK_GFX703:
case GK_GFX702:
case GK_GFX701:
case GK_GFX700:
Features["ci-insts"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["qsad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["mqsad-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["s-memtime-inst"] = true;
Features["gws"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
Features["wavefrontsize64"] = true;
break;
case GK_GFX602:
case GK_GFX601:
case GK_GFX600:
Features["image-insts"] = true;
Features["extended-image-insts"] = true;
Features["s-memtime-inst"] = true;
Features["gws"] = true;
Features["atomic-fmin-fmax-global-f32"] = true;
Features["atomic-fmin-fmax-global-f64"] = true;
Features["wavefrontsize64"] = true;
Features["cube-insts"] = true;
Features["lerp-inst"] = true;
Features["sad-insts"] = true;
Features["msad-insts"] = true;
Features["mqsad-pk-insts"] = true;
Features["cvt-pknorm-vop2-insts"] = true;
break;
case GK_NONE:
break;
default:
llvm_unreachable("Unhandled GPU!");
}
}
/// Fills Features map with default values for given target GPU.
/// \p Features contains overriding target features and this function returns
/// default target features with entries overridden by \p Features.
std::pair<FeatureError, StringRef>
AMDGPU::fillAMDGPUFeatureMap(StringRef GPU, const Triple &T,
StringMap<bool> &Features) {
// XXX - What does the member GPU mean if device name string passed here?
if (T.isSPIRV() && T.getOS() == Triple::OSType::AMDHSA) {
// AMDGCN SPIRV must support the union of all AMDGCN features.
SmallVector<StringRef> GPUs;
fillValidArchListAMDGCN(GPUs);
static const Triple AMDGCN("amdgcn-amd-amdhsa");
StringMap<bool> Tmp;
for (auto &&GPU : GPUs) {
fillAMDGCNFeatureMap(GPU, AMDGCN, Tmp);
for (auto &&[F, B] : Tmp)
Features[F] = B;
}
Features["wavefrontsize32"] = true;
Features["wavefrontsize64"] = true;
} else if (T.isAMDGCN()) {
StringMap<bool> DefaultFeatures;
fillAMDGCNFeatureMap(GPU, T, DefaultFeatures);
return insertWaveSizeFeature(GPU, T, DefaultFeatures, Features);
} else {
if (GPU.empty())
GPU = "r600";
switch (llvm::AMDGPU::parseArchR600(GPU)) {
case GK_CAYMAN:
case GK_CYPRESS:
case GK_RV770:
case GK_RV670:
// TODO: Add fp64 when implemented.
break;
case GK_TURKS:
case GK_CAICOS:
case GK_BARTS:
case GK_SUMO:
case GK_REDWOOD:
case GK_JUNIPER:
case GK_CEDAR:
case GK_RV730:
case GK_RV710:
case GK_RS880:
case GK_R630:
case GK_R600:
break;
default:
llvm_unreachable("Unhandled GPU!");
}
}
return {NO_ERROR, StringRef()};
}
TargetID::TargetID(GPUKind Arch, const Triple &TT, TargetIDSetting XnackSetting,
TargetIDSetting SramEccSetting)
: Arch(Arch),
TargetTripleString(TT.normalize(Triple::CanonicalForm::FOUR_IDENT)),
XnackSetting(XnackSetting), SramEccSetting(SramEccSetting),
IsAMDHSA(TT.getOS() == Triple::AMDHSA) {}
// Parse a feature modifier sign ("+"/"-"). Returns "Unsupported" if \p Sign is
// neither (i.e. the modifier is malformed).
static TargetIDSetting getTargetIDSettingFromFeatureString(StringRef Sign) {
if (Sign == "+")
return TargetIDSetting::On;
if (Sign == "-")
return TargetIDSetting::Off;
return TargetIDSetting::Unsupported;
}
// Derive the architecture from the processor name in \p TargetIDStr. "generic"
// and the empty processor name act as a wildcard.
static GPUKind getGPUKindFromTargetID(const Triple &TT, StringRef TargetIDStr) {
StringRef CPUName = TargetIDStr.split(':').first;
return (CPUName.empty() || CPUName == "generic")
? getGPUKindFromSubArch(TT.getSubArch())
: parseArchAMDGCN(CPUName);
}
// Compute the xnack/sramecc settings for processor \p Arch from the
// processor+features string \p TargetIDStr
// (e.g. "gfx90a:xnack+:sramecc-"). Returns false if a modifier names an unknown
// or repeated feature, names one the processor does not support, or has a
// malformed sign.
static bool computeTargetIDFeatures(GPUKind Arch, StringRef TargetIDStr,
TargetIDSetting &XnackSetting,
TargetIDSetting &SramEccSetting) {
unsigned ArchAttr = getArchAttrAMDGCN(Arch);
XnackSetting = (ArchAttr & FEATURE_XNACK_ON_OFF_MODES)
? TargetIDSetting::Any
: TargetIDSetting::Unsupported;
SramEccSetting = (ArchAttr & FEATURE_SRAMECC) ? TargetIDSetting::Any
: TargetIDSetting::Unsupported;
// The first component is the processor; the rest are feature modifiers of the
// form "<feature><+|->".
SmallVector<StringRef, 3> Split;
TargetIDStr.split(Split, ':');
bool SeenXnack = false;
bool SeenSramEcc = false;
bool Valid = true;
for (unsigned I = 1, E = Split.size(); I != E; ++I) {
StringRef FeatureString = Split[I];
if (FeatureString.consume_front("xnack")) {
TargetIDSetting Sign = getTargetIDSettingFromFeatureString(FeatureString);
if (SeenXnack || XnackSetting == TargetIDSetting::Unsupported ||
Sign == TargetIDSetting::Unsupported)
Valid = false;
else
XnackSetting = Sign;
SeenXnack = true;
} else if (FeatureString.consume_front("sramecc")) {
TargetIDSetting Sign = getTargetIDSettingFromFeatureString(FeatureString);
if (SeenSramEcc || SramEccSetting == TargetIDSetting::Unsupported ||
Sign == TargetIDSetting::Unsupported)
Valid = false;
else
SramEccSetting = Sign;
SeenSramEcc = true;
} else {
// Unknown feature name.
Valid = false;
}
}
return Valid;
}
TargetID::TargetID(const Triple &TT, StringRef TargetIDStr)
: TargetID(getGPUKindFromTargetID(TT, TargetIDStr), TT,
TargetIDSetting::Unsupported, TargetIDSetting::Unsupported) {
// Derive the feature settings from the string. Validity is not checked here;
// parseTargetIDString validates untrusted input.
computeTargetIDFeatures(Arch, TargetIDStr, XnackSetting, SramEccSetting);
}
std::optional<TargetID> TargetID::parse(const Triple &TT,
StringRef ProcAndFeatures) {
if (!TT.isAMDGCN())
return std::nullopt;
// Filter out unrecognized subarch suffixes.
if (TT.getSubArch() == Triple::NoSubArch && TT.getArchName() != "amdgcn")
return std::nullopt;
// A named processor (i.e. not the empty/generic wildcard, which is resolved
// from the triple's subarch) must be a recognized GPU that is consistent with
// the triple's subarch.
StringRef CPUName = ProcAndFeatures.split(':').first;
if (!CPUName.empty() && CPUName != "generic" &&
!isCPUValidForSubArch(TT.getSubArch(), CPUName))
return std::nullopt;
// Parse the processor and its feature modifiers, then construct directly from
// the resulting fields.
GPUKind Arch = getGPUKindFromTargetID(TT, ProcAndFeatures);
TargetIDSetting XnackSetting, SramEccSetting;
if (!computeTargetIDFeatures(Arch, ProcAndFeatures, XnackSetting,
SramEccSetting))
return std::nullopt;
return TargetID(Arch, TT, XnackSetting, SramEccSetting);
}
std::optional<TargetID>
TargetID::parseTargetIDString(StringRef TargetIDDirective) {
// Split on '-' to get arch-vendor-os-environment-processor:features. There is
// a single dash separator after the 4-component triple, so the
// processor+features field must be present (even if empty).
SmallVector<StringRef, 5> Parts;
TargetIDDirective.split(Parts, '-', /*MaxSplit=*/4);
if (Parts.size() < 5)
return std::nullopt;
return parse(Triple(Parts[0], Parts[1], Parts[2], Parts[3]), Parts[4]);
}
// Append the explicit (On/Off) sramecc/xnack feature modifiers in canonical
// order, e.g. ":sramecc-:xnack+".
static void printFeatureModifiers(raw_ostream &OS, TargetIDSetting SramEcc,
TargetIDSetting Xnack) {
if (SramEcc == TargetIDSetting::Off)
OS << ":sramecc-";
else if (SramEcc == TargetIDSetting::On)
OS << ":sramecc+";
if (Xnack == TargetIDSetting::Off)
OS << ":xnack-";
else if (Xnack == TargetIDSetting::On)
OS << ":xnack+";
}
void TargetID::print(raw_ostream &StreamRep) const {
StreamRep << TargetTripleString << '-' << getArchNameAMDGCN(Arch);
if (IsAMDHSA)
printFeatureModifiers(StreamRep, getSramEccSetting(), getXnackSetting());
}
std::string TargetID::toString() const {
std::string Str;
raw_string_ostream OS(Str);
OS << *this;
return Str;
}
void TargetID::printCanonicalTargetIDString(raw_ostream &OS) const {
OS << getArchNameAMDGCN(Arch);
printFeatureModifiers(OS, getSramEccSetting(), getXnackSetting());
}
std::string TargetID::getCanonicalFeatureString() const {
std::string Str;
raw_string_ostream OS(Str);
printCanonicalTargetIDString(OS);
return Str;
}
bool TargetID::operator==(const TargetID &Other) const {
return Arch == Other.Arch && XnackSetting == Other.XnackSetting &&
SramEccSetting == Other.SramEccSetting && IsAMDHSA == Other.IsAMDHSA &&
TargetTripleString == Other.TargetTripleString;
}
static bool featureProvidesFor(TargetIDSetting Provided,
TargetIDSetting Requested) {
return Provided == TargetIDSetting::Any ||
Provided == TargetIDSetting::Unsupported || Provided == Requested;
}
bool TargetID::isEquivalent(const TargetID &Other) const {
// The processor and feature settings must match exactly
if (Arch != Other.Arch || XnackSetting != Other.XnackSetting ||
SramEccSetting != Other.SramEccSetting)
return false;
return Triple(getTargetTripleString())
.isCompatibleWith(Triple(Other.getTargetTripleString()));
}
bool TargetID::providesFor(const TargetID &Other) const {
// A major-family/generic processor (e.g. amdgpu9) provides for a specific
// member of its family (e.g. gfx900), but not the reverse. Otherwise the
// processors must match.
if (Arch != Other.Arch && Arch != GK_NONE && Other.Arch != GK_NONE) {
Triple::SubArchType ThisSubArch = getSubArch(Arch);
if (ThisSubArch != getMajorSubArch(ThisSubArch) ||
ThisSubArch != getMajorSubArch(getSubArch(Other.Arch)))
return false;
}
if (!featureProvidesFor(XnackSetting, Other.XnackSetting) ||
!featureProvidesFor(SramEccSetting, Other.SramEccSetting))
return false;
return Triple(getTargetTripleString())
.isCompatibleWith(Triple(Other.getTargetTripleString()));
}