clang: Store vendor GPU kinds in OffloadArch instead of re-listing GPUs (#213362)

OffloadArch was a flat enum that hand-duplicated every AMDGPU and NVPTX
target, plus a few edge cases. This was yet another place that needed
updating every time a new target is added, which should now be avoided.
Replace with a tagged union-like scheme.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply@anthropic.com>
This commit is contained in:
Matt Arsenault 2026-08-01 08:14:04 +02:00 committed by GitHub
parent b94b699857
commit 3c3a18e239
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 304 additions and 381 deletions

View file

@ -10,161 +10,121 @@
#define LLVM_CLANG_BASIC_OFFLOADARCH_H
#include "llvm/ADT/StringRef.h"
#include <cstdint>
#include <tuple>
namespace llvm {
class Triple;
template <typename T> class SmallVectorImpl;
namespace NVPTX {
enum GPUKind : uint8_t;
}
namespace AMDGPU {
enum GPUKind : uint8_t;
}
} // namespace llvm
namespace clang {
enum class OffloadArch {
Unused,
Unknown,
// TODO: Deprecate and remove GPU architectures older than sm_52.
SM_20,
SM_21,
SM_30,
// This has a name conflict with sys/mac.h on AIX, rename it as a workaround.
SM_32_,
SM_35,
SM_37,
SM_50,
SM_52,
SM_53,
SM_60,
SM_61,
SM_62,
SM_70,
SM_72,
SM_75,
SM_80,
SM_86,
SM_87,
SM_88,
SM_89,
SM_90,
SM_90a,
SM_100,
SM_100a,
SM_100f,
SM_101,
SM_101a,
SM_101f,
SM_103,
SM_103a,
SM_103f,
SM_110,
SM_110a,
SM_110f,
SM_120,
SM_120a,
SM_120f,
SM_121,
SM_121a,
SM_121f,
GFX600,
GFX601,
GFX602,
GFX700,
GFX701,
GFX702,
GFX703,
GFX704,
GFX705,
GFX801,
GFX802,
GFX803,
GFX805,
GFX810,
GFX9_GENERIC,
GFX900,
GFX902,
GFX904,
GFX906,
GFX908,
GFX909,
GFX90a,
GFX90c,
GFX9_4_GENERIC,
GFX942,
GFX950,
GFX10_1_GENERIC,
GFX1010,
GFX1011,
GFX1012,
GFX1013,
GFX10_3_GENERIC,
GFX1030,
GFX1031,
GFX1032,
GFX1033,
GFX1034,
GFX1035,
GFX1036,
GFX11_GENERIC,
GFX1100,
GFX1101,
GFX1102,
GFX1103,
GFX1150,
GFX1151,
GFX1152,
GFX1153,
GFX1154,
GFX11_7_GENERIC,
GFX1170,
GFX1171,
GFX1172,
GFX12_GENERIC,
GFX1200,
GFX1201,
GFX12_5_GENERIC,
GFX1250,
GFX1251,
GFX13_GENERIC,
GFX1310,
AMDGCNSPIRV,
Generic, // A processor model named 'generic' if the target backend defines a
// public one.
// Intel CPUs
GRANITERAPIDS,
// Intel GPUs
BMG_G21,
LAST = BMG_G21,
/// A processor an offloading action can target. This is a tagged handle pairing
/// a TargetArch with the matching TargetParser GPU kind; it does not enumerate
/// individual GPUs, so new targets are added in the TargetParser data alone.
class OffloadArch {
public:
enum class TargetArch : uint8_t {
Unused, // Default-constructed; no architecture bound.
Unknown, // A name that matched no known architecture.
NVPTX, // Kind is an llvm::NVPTX::GPUKind.
AMDGPU, // Kind is an llvm::AMDGPU::GPUKind.
SPIRV, // The 'amdgcnspirv' pseudo target.
IntelCPU, // Kind is an IntelArch.
IntelGPU, // Kind is an IntelArch.
Generic, // The 'generic' processor model.
};
CudaDefault = OffloadArch::SM_52,
HIPDefault = OffloadArch::GFX906,
// Intel architectures, which have no TargetParser list yet.
enum class IntelArch : uint32_t {
GRANITERAPIDS,
BMG_G21,
};
private:
// Interpreted according to V; unused for the tagless TargetArch values.
uint32_t Kind = 0;
TargetArch V = TargetArch::Unused;
constexpr OffloadArch(TargetArch V, uint32_t Kind) : Kind(Kind), V(V) {}
public:
constexpr OffloadArch() = default;
static OffloadArch getNVPTX(llvm::NVPTX::GPUKind K) {
return {TargetArch::NVPTX, static_cast<uint32_t>(K)};
}
static OffloadArch getAMDGPU(llvm::AMDGPU::GPUKind K) {
return {TargetArch::AMDGPU, static_cast<uint32_t>(K)};
}
static constexpr OffloadArch getIntel(TargetArch V, IntelArch A) {
return {V, static_cast<uint32_t>(A)};
}
static constexpr OffloadArch getUnused() { return {TargetArch::Unused, 0}; }
static constexpr OffloadArch getUnknown() { return {TargetArch::Unknown, 0}; }
static constexpr OffloadArch getSPIRV() { return {TargetArch::SPIRV, 0}; }
static constexpr OffloadArch getGeneric() { return {TargetArch::Generic, 0}; }
/// Default architectures used when the user does not specify one.
static OffloadArch CudaDefault();
static OffloadArch HIPDefault();
TargetArch targetArch() const { return V; }
bool isNVPTX() const { return V == TargetArch::NVPTX; }
bool isAMDGPU() const { return V == TargetArch::AMDGPU; }
bool isSPIRV() const { return V == TargetArch::SPIRV; }
bool isIntelCPU() const { return V == TargetArch::IntelCPU; }
bool isIntelGPU() const { return V == TargetArch::IntelGPU; }
bool isIntel() const { return isIntelCPU() || isIntelGPU(); }
bool isGeneric() const { return V == TargetArch::Generic; }
bool isUnused() const { return V == TargetArch::Unused; }
bool isUnknown() const { return V == TargetArch::Unknown; }
// Only valid when isNVPTX() / isAMDGPU() respectively.
llvm::NVPTX::GPUKind nvptxKind() const {
return static_cast<llvm::NVPTX::GPUKind>(Kind);
}
llvm::AMDGPU::GPUKind amdgpuKind() const {
return static_cast<llvm::AMDGPU::GPUKind>(Kind);
}
bool operator==(const OffloadArch &Other) const {
return V == Other.V && Kind == Other.Kind;
}
bool operator!=(const OffloadArch &Other) const { return !(*this == Other); }
bool operator<(const OffloadArch &Other) const {
return std::tie(V, Kind) < std::tie(Other.V, Other.Kind);
}
};
static inline bool IsNVIDIAOffloadArch(OffloadArch A) {
return A >= OffloadArch::SM_20 && A < OffloadArch::GFX600;
}
static inline bool IsAMDOffloadArch(OffloadArch A) {
// Generic processor model is for testing only.
return A >= OffloadArch::GFX600 && A < OffloadArch::Generic;
}
static inline bool IsIntelCPUOffloadArch(OffloadArch Arch) {
return Arch >= OffloadArch::GRANITERAPIDS && Arch < OffloadArch::BMG_G21;
}
static inline bool IsIntelGPUOffloadArch(OffloadArch Arch) {
return Arch >= OffloadArch::BMG_G21 && Arch <= OffloadArch::LAST;
}
static inline bool IsIntelOffloadArch(OffloadArch Arch) {
return IsIntelCPUOffloadArch(Arch) || IsIntelGPUOffloadArch(Arch);
inline bool IsNVIDIAOffloadArch(OffloadArch A) { return A.isNVPTX(); }
inline bool IsAMDOffloadArch(OffloadArch A) {
// amdgcnspirv is compiled through the AMDGPU toolchain.
return A.isAMDGPU() || A.isSPIRV();
}
inline bool IsIntelCPUOffloadArch(OffloadArch A) { return A.isIntelCPU(); }
inline bool IsIntelGPUOffloadArch(OffloadArch A) { return A.isIntelGPU(); }
inline bool IsIntelOffloadArch(OffloadArch A) { return A.isIntel(); }
const char *OffloadArchToString(OffloadArch A);
const char *OffloadArchToVirtualArchString(OffloadArch A);
// Convert a string to an OffloadArch enum value. Returns
// OffloadArch::Unknown if the string is not recognized.
// Convert a string to an OffloadArch. Returns an Unknown OffloadArch if the
// string is not recognized.
OffloadArch StringToOffloadArch(llvm::StringRef S);
/// Append the canonical names of all NVIDIA and AMDGPU GPUs.
void fillValidOffloadArchList(llvm::SmallVectorImpl<llvm::StringRef> &Values);
llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple,
OffloadArch ID);
@ -173,19 +133,19 @@ llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple,
struct BoundArch {
llvm::StringRef ArchName;
/// The parsed offload architecture enum.
/// Will be OffloadArch::Unknown if ArchName not recognized.
OffloadArch Arch = OffloadArch::Unused;
/// The parsed offload architecture.
/// Will be an Unknown OffloadArch if ArchName is not recognized.
OffloadArch Arch = OffloadArch::getUnused();
BoundArch() = default;
explicit BoundArch(llvm::StringRef Name)
: ArchName(Name),
Arch(Name.empty() ? OffloadArch::Unknown : StringToOffloadArch(Name)) {}
: ArchName(Name), Arch(Name.empty() ? OffloadArch::getUnknown()
: StringToOffloadArch(Name)) {}
BoundArch(llvm::StringRef Name, OffloadArch A) : ArchName(Name), Arch(A) {}
bool empty() const { return ArchName.empty(); }
explicit operator bool() const { return Arch != OffloadArch::Unused; }
explicit operator bool() const { return !Arch.isUnused(); }
bool operator==(const BoundArch &Other) const {
return Arch == Other.Arch && ArchName == Other.ArchName;

View file

@ -11,6 +11,7 @@
#include "clang/Basic/Cuda.h"
#include "clang/Driver/Driver.h"
#include "llvm/TargetParser/NVPTXTargetParser.h"
#include <bitset>
namespace clang {
@ -28,9 +29,9 @@ private:
std::string IncludePath;
llvm::StringMap<std::string> LibDeviceMap;
// CUDA architectures for which we have raised an error in
// CheckCudaVersionSupportsArch.
mutable std::bitset<(int)OffloadArch::LAST> ArchsWithBadVersion;
// NVIDIA architectures (indexed by GPUKind) for which we have raised an error
// in CheckCudaVersionSupportsArch.
mutable std::bitset<llvm::NVPTX::GK_LAST + 1> ArchsWithBadVersion;
public:
CudaInstallationDetector(const Driver &D, const llvm::Triple &HostTriple,

View file

@ -4,6 +4,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/VersionTuple.h"
#include "llvm/TargetParser/NVPTXTargetParser.h"
#include <cassert>
namespace clang {
@ -78,16 +79,16 @@ CudaVersion ToCudaVersion(llvm::VersionTuple Version) {
}
CudaVersion MinVersionForOffloadArch(OffloadArch A) {
if (A == OffloadArch::Unknown)
if (A.isUnknown())
return CudaVersion::UNKNOWN;
// AMD GPUs do not depend on CUDA versions.
if (IsAMDOffloadArch(A))
return CudaVersion::CUDA_70;
switch (A) {
switch (A.nvptxKind()) {
#define NVPTX_GPU(NAME, KIND, VIRTUAL, SM_ID, MIN_VER, MAX_VER, SUFFIX) \
case OffloadArch::KIND: \
case llvm::NVPTX::GK_##KIND: \
return CudaVersion::MIN_VER;
#include "llvm/TargetParser/NVPTXTargetParser.def"
default:
@ -100,11 +101,12 @@ CudaVersion MaxVersionForOffloadArch(OffloadArch A) {
if (IsAMDOffloadArch(A))
return CudaVersion::NEW;
switch (A) {
case OffloadArch::Unknown:
if (!A.isNVPTX())
return CudaVersion::UNKNOWN;
switch (A.nvptxKind()) {
#define NVPTX_GPU(NAME, KIND, VIRTUAL, SM_ID, MIN_VER, MAX_VER, SUFFIX) \
case OffloadArch::KIND: \
case llvm::NVPTX::GK_##KIND: \
return CudaVersion::MAX_VER;
#include "llvm/TargetParser/NVPTXTargetParser.def"
default:
@ -127,33 +129,15 @@ bool CudaFeatureEnabled(CudaVersion Version, CudaFeature Feature) {
}
unsigned CudaArchToID(OffloadArch Arch) {
switch (Arch) {
#define NVPTX_GPU(NAME, KIND, VIRTUAL, SM_ID, MIN_VER, MAX_VER, SUFFIX) \
case OffloadArch::KIND: \
return SM_ID;
#include "llvm/TargetParser/NVPTXTargetParser.def"
default:
break;
}
llvm_unreachable("invalid NVIDIA GPU architecture");
}
static llvm::NVPTX::GPUKind OffloadArchToNVPTXKind(OffloadArch Arch) {
switch (Arch) {
#define NVPTX_GPU(NAME, KIND, VIRTUAL, SM_ID, MIN_VER, MAX_VER, SUFFIX) \
case OffloadArch::KIND: \
return llvm::NVPTX::GK_##KIND;
#include "llvm/TargetParser/NVPTXTargetParser.def"
default:
return llvm::NVPTX::GK_NONE;
}
assert(Arch.isNVPTX() && "invalid NVIDIA GPU architecture");
return llvm::NVPTX::getSmVersion(Arch.nvptxKind());
}
bool IsNVIDIAAcceleratedOffloadArch(OffloadArch Arch) {
return llvm::NVPTX::isAcceleratedArch(OffloadArchToNVPTXKind(Arch));
return Arch.isNVPTX() && llvm::NVPTX::isAcceleratedArch(Arch.nvptxKind());
}
bool IsNVIDIAFamilySpecificOffloadArch(OffloadArch Arch) {
return llvm::NVPTX::isFamilySpecificArch(OffloadArchToNVPTXKind(Arch));
return Arch.isNVPTX() && llvm::NVPTX::isFamilySpecificArch(Arch.nvptxKind());
}
} // namespace clang

View file

@ -7,164 +7,101 @@
//===----------------------------------------------------------------------===//
#include "clang/Basic/OffloadArch.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/TargetParser/AMDGPUTargetParser.h"
#include "llvm/TargetParser/NVPTXTargetParser.h"
#include "llvm/TargetParser/Triple.h"
namespace clang {
namespace {
struct OffloadArchToStringMap {
OffloadArch Arch;
const char *ArchName;
const char *VirtualArchName;
};
} // namespace
OffloadArch OffloadArch::CudaDefault() {
return getNVPTX(llvm::NVPTX::parseArch("sm_52"));
}
#define SM(sm) {OffloadArch::SM_##sm, "sm_" #sm, "compute_" #sm}
#define GFX(gpu) {OffloadArch::GFX##gpu, "gfx" #gpu, "compute_amdgcn"}
static const OffloadArchToStringMap ArchNames[] = {
// clang-format off
{OffloadArch::Unused, "", ""},
SM(20), {OffloadArch::SM_21, "sm_21", "compute_20"}, // Fermi
SM(30), {OffloadArch::SM_32_, "sm_32", "compute_32"}, SM(35), SM(37), // Kepler
SM(50), SM(52), SM(53), // Maxwell
SM(60), SM(61), SM(62), // Pascal
SM(70), SM(72), // Volta
SM(75), // Turing
SM(80), SM(86), // Ampere
SM(87), // Jetson/Drive AGX Orin
SM(88), // Ampere
SM(89), // Ada Lovelace
SM(90), // Hopper
SM(90a), // Hopper
SM(100), // Blackwell
SM(100a), // Blackwell
SM(100f), // Blackwell
SM(101), // Blackwell
SM(101a), // Blackwell
SM(101f), // Blackwell
SM(103), // Blackwell
SM(103a), // Blackwell
SM(103f), // Blackwell
SM(110), // Blackwell
SM(110a), // Blackwell
SM(110f), // Blackwell
SM(120), // Blackwell
SM(120a), // Blackwell
SM(120f), // Blackwell
SM(121), // Blackwell
SM(121a), // Blackwell
SM(121f), // Blackwell
GFX(600), // gfx600
GFX(601), // gfx601
GFX(602), // gfx602
GFX(700), // gfx700
GFX(701), // gfx701
GFX(702), // gfx702
GFX(703), // gfx703
GFX(704), // gfx704
GFX(705), // gfx705
GFX(801), // gfx801
GFX(802), // gfx802
GFX(803), // gfx803
GFX(805), // gfx805
GFX(810), // gfx810
{OffloadArch::GFX9_GENERIC, "gfx9-generic", "compute_amdgcn"},
GFX(900), // gfx900
GFX(902), // gfx902
GFX(904), // gfx903
GFX(906), // gfx906
GFX(908), // gfx908
GFX(909), // gfx909
GFX(90a), // gfx90a
GFX(90c), // gfx90c
{OffloadArch::GFX9_4_GENERIC, "gfx9-4-generic", "compute_amdgcn"},
GFX(942), // gfx942
GFX(950), // gfx950
{OffloadArch::GFX10_1_GENERIC, "gfx10-1-generic", "compute_amdgcn"},
GFX(1010), // gfx1010
GFX(1011), // gfx1011
GFX(1012), // gfx1012
GFX(1013), // gfx1013
{OffloadArch::GFX10_3_GENERIC, "gfx10-3-generic", "compute_amdgcn"},
GFX(1030), // gfx1030
GFX(1031), // gfx1031
GFX(1032), // gfx1032
GFX(1033), // gfx1033
GFX(1034), // gfx1034
GFX(1035), // gfx1035
GFX(1036), // gfx1036
{OffloadArch::GFX11_GENERIC, "gfx11-generic", "compute_amdgcn"},
GFX(1100), // gfx1100
GFX(1101), // gfx1101
GFX(1102), // gfx1102
GFX(1103), // gfx1103
GFX(1150), // gfx1150
GFX(1151), // gfx1151
GFX(1152), // gfx1152
GFX(1153), // gfx1153
GFX(1154), // gfx1154
{OffloadArch::GFX11_7_GENERIC, "gfx11-7-generic", "compute_amdgcn"},
GFX(1170), // gfx1170
GFX(1171), // gfx1171
GFX(1172), // gfx1172
{OffloadArch::GFX12_GENERIC, "gfx12-generic", "compute_amdgcn"},
GFX(1200), // gfx1200
GFX(1201), // gfx1201
{OffloadArch::GFX12_5_GENERIC, "gfx12-5-generic", "compute_amdgcn"},
GFX(1250), // gfx1250
GFX(1251), // gfx1251
{OffloadArch::GFX13_GENERIC, "gfx13-generic", "compute_amdgcn"},
GFX(1310), // gfx1310
{OffloadArch::AMDGCNSPIRV, "amdgcnspirv", "compute_amdgcn"},
// Intel CPUs
{OffloadArch::GRANITERAPIDS, "graniterapids", ""},
// Intel GPUS
{OffloadArch::BMG_G21, "bmg_g21", ""},
{OffloadArch::Generic, "generic", ""},
// clang-format on
};
#undef SM
#undef GFX
OffloadArch OffloadArch::HIPDefault() {
return getAMDGPU(llvm::AMDGPU::parseArchAMDGCN("gfx906"));
}
const char *OffloadArchToString(OffloadArch A) {
auto Result =
llvm::find_if(ArchNames, [A](const OffloadArchToStringMap &Map) {
return A == Map.Arch;
});
if (Result == std::end(ArchNames))
switch (A.targetArch()) {
case OffloadArch::TargetArch::Unused:
return "";
case OffloadArch::TargetArch::Unknown:
return "unknown";
case OffloadArch::TargetArch::NVPTX:
return llvm::NVPTX::getArchName(A.nvptxKind()).data();
case OffloadArch::TargetArch::AMDGPU:
return llvm::AMDGPU::getArchNameAMDGCN(A.amdgpuKind()).data();
case OffloadArch::TargetArch::SPIRV:
return "amdgcnspirv";
case OffloadArch::TargetArch::IntelCPU:
return "graniterapids";
case OffloadArch::TargetArch::IntelGPU:
return "bmg_g21";
case OffloadArch::TargetArch::Generic:
return "generic";
}
return "unknown";
return Result->ArchName;
}
const char *OffloadArchToVirtualArchString(OffloadArch A) {
auto Result =
llvm::find_if(ArchNames, [A](const OffloadArchToStringMap &Map) {
return A == Map.Arch;
});
if (Result == std::end(ArchNames))
switch (A.targetArch()) {
case OffloadArch::TargetArch::NVPTX:
return llvm::NVPTX::getVirtualArch(A.nvptxKind()).data();
case OffloadArch::TargetArch::AMDGPU:
case OffloadArch::TargetArch::SPIRV:
return "compute_amdgcn";
case OffloadArch::TargetArch::Unknown:
return "unknown";
case OffloadArch::TargetArch::Unused:
case OffloadArch::TargetArch::IntelCPU:
case OffloadArch::TargetArch::IntelGPU:
case OffloadArch::TargetArch::Generic:
return "";
}
return "unknown";
return Result->VirtualArchName;
}
OffloadArch StringToOffloadArch(llvm::StringRef S) {
auto Result =
llvm::find_if(ArchNames, [S](const OffloadArchToStringMap &Map) {
return S == Map.ArchName;
});
if (Result == std::end(ArchNames))
return OffloadArch::Unknown;
return Result->Arch;
// The empty string denotes the "unused" architecture.
if (S.empty())
return OffloadArch::getUnused();
// Non-GPU-table pseudo/sentinel architectures.
if (S == "amdgcnspirv")
return OffloadArch::getSPIRV();
if (S == "generic")
return OffloadArch::getGeneric();
if (S == "graniterapids")
return OffloadArch::getIntel(OffloadArch::TargetArch::IntelCPU,
OffloadArch::IntelArch::GRANITERAPIDS);
if (S == "bmg_g21")
return OffloadArch::getIntel(OffloadArch::TargetArch::IntelGPU,
OffloadArch::IntelArch::BMG_G21);
// Otherwise defer to the vendor TargetParser GPU lists.
if (llvm::NVPTX::GPUKind NV = llvm::NVPTX::parseArch(S))
return OffloadArch::getNVPTX(NV);
if (llvm::AMDGPU::GPUKind AK = llvm::AMDGPU::parseArchAMDGCN(S))
return OffloadArch::getAMDGPU(AK);
return OffloadArch::getUnknown();
}
void fillValidOffloadArchList(llvm::SmallVectorImpl<llvm::StringRef> &Values) {
#define NVPTX_GPU(NAME, KIND, VIRTUAL, SM_ID, MIN_VER, MAX_VER, SUFFIX) \
Values.push_back(NAME);
#include "llvm/TargetParser/NVPTXTargetParser.def"
llvm::AMDGPU::fillValidArchListAMDGCN(Values, llvm::Triple::NoSubArch);
}
llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple,
OffloadArch ID) {
if (ID == OffloadArch::AMDGCNSPIRV)
if (ID.isSPIRV())
return llvm::Triple(llvm::Triple::spirv64, llvm::Triple::NoSubArch,
llvm::Triple::AMD, llvm::Triple::AMDHSA);
if (IsNVIDIAOffloadArch(ID)) {
if (ID.isNVPTX()) {
llvm::Triple::ArchType Arch = DefaultToolchainTriple.isArch64Bit()
? llvm::Triple::nvptx64
: llvm::Triple::nvptx;
@ -172,7 +109,7 @@ llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple,
llvm::Triple::CUDA);
}
if (IsAMDOffloadArch(ID))
if (ID.isAMDGPU())
return llvm::Triple("amdgcn-amd-amdhsa");
return {};

View file

@ -64,7 +64,7 @@ NVPTXTargetInfo::NVPTXTargetInfo(const llvm::Triple &Triple,
// Define available target features
// These must be defined in sorted order!
NoAsmVariants = true;
GPU = OffloadArch::Unused;
GPU = OffloadArch::getUnused();
// PTX supports f16 as a fundamental type.
HasFastHalfType = true;

View file

@ -72,7 +72,7 @@ public:
initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
StringRef CPU,
const std::vector<std::string> &FeaturesVec) const override {
if (GPU != OffloadArch::Unused)
if (!GPU.isUnused())
Features[OffloadArchToString(GPU)] = true;
// Only add PTX feature if explicitly requested. Otherwise, let the backend
// use the minimum required PTX version for the target SM.
@ -131,18 +131,16 @@ public:
}
bool isValidCPUName(StringRef Name) const override {
return StringToOffloadArch(Name) != OffloadArch::Unknown;
return !StringToOffloadArch(Name).isUnknown();
}
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override {
for (int i = static_cast<int>(OffloadArch::SM_20);
i < static_cast<int>(OffloadArch::Generic); ++i)
Values.emplace_back(OffloadArchToString(static_cast<OffloadArch>(i)));
fillValidOffloadArchList(Values);
}
bool setCPU(StringRef Name) override {
GPU = StringToOffloadArch(Name);
return GPU != OffloadArch::Unknown;
return !GPU.isUnknown();
}
void setSupportedOpenCLOpts() override {

View file

@ -961,7 +961,7 @@ static TripleSet inferOffloadToolchains(Compilation &C,
TripleSet Triples;
for (llvm::StringRef Arch : Archs) {
OffloadArch ID = StringToOffloadArch(Arch);
if (ID == OffloadArch::Unknown)
if (ID.isUnknown())
ID = StringToOffloadArch(
getProcessorFromTargetID(llvm::Triple("amdgcn-amd-amdhsa"), Arch));
@ -975,13 +975,12 @@ static TripleSet inferOffloadToolchains(Compilation &C,
<< "CUDA" << Arch;
return {};
}
if (Kind == Action::OFK_OpenMP &&
(ID == OffloadArch::Unknown || ID == OffloadArch::Unused)) {
if (Kind == Action::OFK_OpenMP && (ID.isUnknown() || ID.isUnused())) {
C.getDriver().Diag(clang::diag::err_drv_failed_to_deduce_target_from_arch)
<< Arch;
return {};
}
if (ID == OffloadArch::Unknown || ID == OffloadArch::Unused) {
if (ID.isUnknown() || ID.isUnused()) {
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "offload" << Arch;
return {};
@ -3466,7 +3465,7 @@ class OffloadingActionBuilder final {
bool Relocatable = false;
/// Default GPU architecture if there's no one specified.
OffloadArch DefaultOffloadArch = OffloadArch::Unknown;
OffloadArch DefaultOffloadArch = OffloadArch::getUnknown();
/// Compilation unit ID specified by option '-fuse-cuid=' or'-cuid='.
const CUIDOptions &CUIDOpts;
@ -3644,7 +3643,7 @@ class OffloadingActionBuilder final {
CudaActionBuilder(Compilation &C, DerivedArgList &Args,
const InputList &Inputs)
: CudaActionBuilderBase(C, Args, Inputs, Action::OFK_Cuda) {
DefaultOffloadArch = OffloadArch::CudaDefault;
DefaultOffloadArch = OffloadArch::CudaDefault();
}
ActionBuilderReturnCode
@ -3769,7 +3768,7 @@ class OffloadingActionBuilder final {
const InputList &Inputs)
: CudaActionBuilderBase(C, Args, Inputs, Action::OFK_HIP) {
DefaultOffloadArch = OffloadArch::HIPDefault;
DefaultOffloadArch = OffloadArch::HIPDefault();
if (Args.hasArg(options::OPT_fhip_emit_relocatable,
options::OPT_fno_hip_emit_relocatable)) {
@ -4882,13 +4881,12 @@ static StringRef getCanonicalArchString(Compilation &C,
// expecting the triple to be only NVPTX / AMDGPU.
OffloadArch Arch =
StringToOffloadArch(getProcessorFromTargetID(Triple, ArchStr));
if (Triple.isNVPTX() &&
(Arch == OffloadArch::Unknown || !IsNVIDIAOffloadArch(Arch))) {
if (Triple.isNVPTX() && (Arch.isUnknown() || !IsNVIDIAOffloadArch(Arch))) {
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "CUDA" << ArchStr;
return StringRef();
} else if (Triple.isAMDGPU() &&
(Arch == OffloadArch::Unknown || !IsAMDOffloadArch(Arch))) {
(Arch.isUnknown() || !IsAMDOffloadArch(Arch))) {
C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
<< "HIP" << ArchStr;
return StringRef();
@ -4992,12 +4990,12 @@ Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
if (Archs.empty()) {
if (Kind == Action::OFK_Cuda) {
Archs.insert(OffloadArchToString(TC.getTriple().isSPIRV()
? OffloadArch::Unused
: OffloadArch::CudaDefault));
? OffloadArch::getUnused()
: OffloadArch::CudaDefault()));
} else if (Kind == Action::OFK_HIP) {
Archs.insert(OffloadArchToString(TC.getTriple().isSPIRV()
? OffloadArch::Generic
: OffloadArch::HIPDefault));
? OffloadArch::getGeneric()
: OffloadArch::HIPDefault()));
} else if (Kind == Action::OFK_SYCL) {
Archs.insert(StringRef());
} else if (Kind == Action::OFK_OpenMP) {

View file

@ -83,8 +83,7 @@ OffloadTargetInfo::OffloadTargetInfo(const StringRef Target,
StringRef TargetIdWithFeature =
Components.size() == 6 ? Components.back() : "";
StringRef TargetId = TargetIdWithFeature.split(':').first;
if (!TargetId.empty() &&
clang::StringToOffloadArch(TargetId) != clang::OffloadArch::Unknown)
if (!TargetId.empty() && !clang::StringToOffloadArch(TargetId).isUnknown())
this->TargetID = TargetIdWithFeature;
else
this->TargetID = "";

View file

@ -235,14 +235,12 @@ CudaInstallationDetector::CudaInstallationDetector(
// CUDA-9+ uses single libdevice file for all GPU variants.
std::string FilePath = LibDevicePath + "/libdevice.10.bc";
if (FS.exists(FilePath)) {
for (int Arch = (int)OffloadArch::SM_30, E = (int)OffloadArch::LAST;
Arch < E; ++Arch) {
OffloadArch OA = static_cast<OffloadArch>(Arch);
if (!IsNVIDIAOffloadArch(OA))
continue;
std::string OffloadArchName(OffloadArchToString(OA));
LibDeviceMap[OffloadArchName] = FilePath;
}
// CUDA-9+ uses a single libdevice file for every NVIDIA GPU variant
// (sm_30 and newer).
#define NVPTX_GPU(NAME, KIND, VIRTUAL, SM_ID, MIN_VER, MAX_VER, SUFFIX) \
if ((SM_ID) >= 300) \
LibDeviceMap[NAME] = FilePath;
#include "llvm/TargetParser/NVPTXTargetParser.def"
}
} else {
std::error_code EC;
@ -326,14 +324,15 @@ void CudaInstallationDetector::AddCudaIncludeArgs(
void CudaInstallationDetector::CheckCudaVersionSupportsArch(
OffloadArch Arch) const {
if (Arch == OffloadArch::Unknown || Version == CudaVersion::UNKNOWN ||
ArchsWithBadVersion[(int)Arch])
// Only NVIDIA architectures depend on the CUDA toolkit version.
if (!Arch.isNVPTX() || Version == CudaVersion::UNKNOWN ||
ArchsWithBadVersion[Arch.nvptxKind()])
return;
auto MinVersion = MinVersionForOffloadArch(Arch);
auto MaxVersion = MaxVersionForOffloadArch(Arch);
if (Version < MinVersion || Version > MaxVersion) {
ArchsWithBadVersion[(int)Arch] = true;
ArchsWithBadVersion[Arch.nvptxKind()] = true;
D.Diag(diag::err_drv_cuda_version_unsupported)
<< OffloadArchToString(Arch) << CudaVersionToString(MinVersion)
<< CudaVersionToString(MaxVersion) << InstallPath
@ -414,7 +413,7 @@ void NVPTX::Assembler::ConstructJob(Compilation &C, const JobAction &JA,
}
// Obtain architecture from the action.
assert(GPUArch.Arch != OffloadArch::Unknown &&
assert(!GPUArch.Arch.isUnknown() &&
"Device action expected to have an architecture.");
// Check that our installation's ptxas supports gpu_arch.
@ -778,7 +777,7 @@ NVPTXToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
if (!DAL->hasArg(options::OPT_march_EQ) && OffloadKind != Action::OFK_None) {
DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ),
OffloadArchToString(OffloadArch::CudaDefault));
OffloadArchToString(OffloadArch::CudaDefault()));
} else if (DAL->getLastArgValue(options::OPT_march_EQ) == "generic" &&
OffloadKind == Action::OFK_None) {
DAL->eraseArg(options::OPT_march_EQ);

View file

@ -72,6 +72,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/NVPTXTargetParser.h"
#include "llvm/TargetParser/Triple.h"
#include <optional>
@ -6129,8 +6130,8 @@ Sema::CreateLaunchBoundsAttr(const AttributeCommonInfo &CI, Expr *MaxThreads,
// launch bounds attribute within ompx_attribute to support other archs.
if (!IgnoreArch) {
// '.maxclusterrank' ptx directive requires .target sm_90 or higher.
auto SM = getOffloadArch(Context.getTargetInfo());
if (SM == OffloadArch::Unknown || SM < OffloadArch::SM_90) {
OffloadArch SM = getOffloadArch(Context.getTargetInfo());
if (SM.isUnknown() || llvm::NVPTX::getSmVersion(SM.nvptxKind()) < 900) {
Diag(MaxBlocks->getBeginLoc(), diag::warn_cuda_maxclusterrank_sm_90)
<< OffloadArchToString(SM) << CI << MaxBlocks->getSourceRange();
// Ignore it by setting MaxBlocks to null;
@ -6244,7 +6245,8 @@ void Sema::addNoClusterAttr(Decl *D, const AttributeCommonInfo &CI) {
static void handleClusterDimsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
const TargetInfo &TTI = S.Context.getTargetInfo();
OffloadArch Arch = StringToOffloadArch(TTI.getTargetOpts().CPU);
if ((TTI.getTriple().isNVPTX() && Arch < clang::OffloadArch::SM_90) ||
if ((TTI.getTriple().isNVPTX() &&
llvm::NVPTX::getSmVersion(Arch.nvptxKind()) < 900) ||
(TTI.getTriple().isAMDGPU() &&
!TTI.hasFeatureEnabled(TTI.getTargetOpts().FeatureMap, "clusters"))) {
S.Diag(AL.getLoc(), diag::err_cluster_attr_not_supported) << AL;
@ -6263,7 +6265,8 @@ static void handleClusterDimsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
static void handleNoClusterAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
const TargetInfo &TTI = S.Context.getTargetInfo();
OffloadArch Arch = StringToOffloadArch(TTI.getTargetOpts().CPU);
if ((TTI.getTriple().isNVPTX() && Arch < clang::OffloadArch::SM_90) ||
if ((TTI.getTriple().isNVPTX() &&
llvm::NVPTX::getSmVersion(Arch.nvptxKind()) < 900) ||
(TTI.getTriple().isAMDGPU() &&
!TTI.hasFeatureEnabled(TTI.getTargetOpts().FeatureMap, "clusters"))) {
S.Diag(AL.getLoc(), diag::err_cluster_attr_not_supported) << AL;

View file

@ -59,7 +59,6 @@
// CHECK-SAME: {{^}}, gfx803
// CHECK-SAME: {{^}}, gfx805
// CHECK-SAME: {{^}}, gfx810
// CHECK-SAME: {{^}}, gfx9-generic
// CHECK-SAME: {{^}}, gfx900
// CHECK-SAME: {{^}}, gfx902
// CHECK-SAME: {{^}}, gfx904
@ -68,15 +67,12 @@
// CHECK-SAME: {{^}}, gfx909
// CHECK-SAME: {{^}}, gfx90a
// CHECK-SAME: {{^}}, gfx90c
// CHECK-SAME: {{^}}, gfx9-4-generic
// CHECK-SAME: {{^}}, gfx942
// CHECK-SAME: {{^}}, gfx950
// CHECK-SAME: {{^}}, gfx10-1-generic
// CHECK-SAME: {{^}}, gfx1010
// CHECK-SAME: {{^}}, gfx1011
// CHECK-SAME: {{^}}, gfx1012
// CHECK-SAME: {{^}}, gfx1013
// CHECK-SAME: {{^}}, gfx10-3-generic
// CHECK-SAME: {{^}}, gfx1030
// CHECK-SAME: {{^}}, gfx1031
// CHECK-SAME: {{^}}, gfx1032
@ -84,7 +80,6 @@
// CHECK-SAME: {{^}}, gfx1034
// CHECK-SAME: {{^}}, gfx1035
// CHECK-SAME: {{^}}, gfx1036
// CHECK-SAME: {{^}}, gfx11-generic
// CHECK-SAME: {{^}}, gfx1100
// CHECK-SAME: {{^}}, gfx1101
// CHECK-SAME: {{^}}, gfx1102
@ -94,17 +89,39 @@
// CHECK-SAME: {{^}}, gfx1152
// CHECK-SAME: {{^}}, gfx1153
// CHECK-SAME: {{^}}, gfx1154
// CHECK-SAME: {{^}}, gfx11-7-generic
// CHECK-SAME: {{^}}, gfx1170
// CHECK-SAME: {{^}}, gfx1171
// CHECK-SAME: {{^}}, gfx1172
// CHECK-SAME: {{^}}, gfx12-generic
// CHECK-SAME: {{^}}, gfx1200
// CHECK-SAME: {{^}}, gfx1201
// CHECK-SAME: {{^}}, gfx12-5-generic
// CHECK-SAME: {{^}}, gfx1250
// CHECK-SAME: {{^}}, gfx1251
// CHECK-SAME: {{^}}, gfx13-generic
// CHECK-SAME: {{^}}, gfx1310
// CHECK-SAME: {{^}}, amdgcnspirv
// CHECK-SAME: {{^}}, gfx9-generic
// CHECK-SAME: {{^}}, gfx9-4-generic
// CHECK-SAME: {{^}}, gfx10-1-generic
// CHECK-SAME: {{^}}, gfx10-3-generic
// CHECK-SAME: {{^}}, gfx11-generic
// CHECK-SAME: {{^}}, gfx11-7-generic
// CHECK-SAME: {{^}}, gfx12-generic
// CHECK-SAME: {{^}}, gfx12-5-generic
// CHECK-SAME: {{^}}, gfx13-generic
// CHECK-SAME: {{^}}, tahiti
// CHECK-SAME: {{^}}, pitcairn
// CHECK-SAME: {{^}}, verde
// CHECK-SAME: {{^}}, hainan
// CHECK-SAME: {{^}}, oland
// CHECK-SAME: {{^}}, kaveri
// CHECK-SAME: {{^}}, hawaii
// CHECK-SAME: {{^}}, kabini
// CHECK-SAME: {{^}}, mullins
// CHECK-SAME: {{^}}, bonaire
// CHECK-SAME: {{^}}, carrizo
// CHECK-SAME: {{^}}, iceland
// CHECK-SAME: {{^}}, tonga
// CHECK-SAME: {{^}}, fiji
// CHECK-SAME: {{^}}, polaris10
// CHECK-SAME: {{^}}, polaris11
// CHECK-SAME: {{^}}, tongapro
// CHECK-SAME: {{^}}, stoney
// CHECK-SAME: {{$}}

View file

@ -11,26 +11,49 @@
using namespace clang;
TEST(OffloadArchTest, basic) {
EXPECT_TRUE(IsNVIDIAOffloadArch(OffloadArch::SM_20));
EXPECT_TRUE(IsNVIDIAOffloadArch(OffloadArch::SM_120a));
EXPECT_FALSE(IsNVIDIAOffloadArch(OffloadArch::GFX600));
static OffloadArch parse(llvm::StringRef S) { return StringToOffloadArch(S); }
EXPECT_FALSE(IsAMDOffloadArch(OffloadArch::SM_120a));
EXPECT_TRUE(IsAMDOffloadArch(OffloadArch::GFX600));
EXPECT_TRUE(IsAMDOffloadArch(OffloadArch::GFX1201));
EXPECT_TRUE(IsAMDOffloadArch(OffloadArch::GFX12_GENERIC));
EXPECT_TRUE(IsAMDOffloadArch(OffloadArch::AMDGCNSPIRV));
EXPECT_FALSE(IsAMDOffloadArch(OffloadArch::GRANITERAPIDS));
TEST(OffloadArchTest, VendorClassification) {
EXPECT_TRUE(IsNVIDIAOffloadArch(parse("sm_20")));
EXPECT_TRUE(IsNVIDIAOffloadArch(parse("sm_120a")));
EXPECT_FALSE(IsNVIDIAOffloadArch(parse("gfx600")));
EXPECT_TRUE(IsIntelOffloadArch(OffloadArch::GRANITERAPIDS));
EXPECT_TRUE(IsIntelCPUOffloadArch(OffloadArch::GRANITERAPIDS));
EXPECT_FALSE(IsIntelGPUOffloadArch(OffloadArch::GRANITERAPIDS));
EXPECT_TRUE(IsIntelOffloadArch(OffloadArch::BMG_G21));
EXPECT_FALSE(IsIntelCPUOffloadArch(OffloadArch::BMG_G21));
EXPECT_TRUE(IsIntelGPUOffloadArch(OffloadArch::BMG_G21));
EXPECT_FALSE(IsAMDOffloadArch(parse("sm_120a")));
EXPECT_TRUE(IsAMDOffloadArch(parse("gfx600")));
EXPECT_TRUE(IsAMDOffloadArch(parse("gfx1201")));
EXPECT_TRUE(IsAMDOffloadArch(parse("gfx12-generic")));
EXPECT_TRUE(IsAMDOffloadArch(parse("amdgcnspirv")));
EXPECT_FALSE(IsAMDOffloadArch(parse("graniterapids")));
EXPECT_FALSE(IsNVIDIAOffloadArch(OffloadArch::Generic));
EXPECT_FALSE(IsAMDOffloadArch(OffloadArch::Generic));
EXPECT_FALSE(IsIntelOffloadArch(OffloadArch::Generic));
EXPECT_TRUE(IsIntelOffloadArch(parse("graniterapids")));
EXPECT_TRUE(IsIntelCPUOffloadArch(parse("graniterapids")));
EXPECT_FALSE(IsIntelGPUOffloadArch(parse("graniterapids")));
EXPECT_TRUE(IsIntelOffloadArch(parse("bmg_g21")));
EXPECT_FALSE(IsIntelCPUOffloadArch(parse("bmg_g21")));
EXPECT_TRUE(IsIntelGPUOffloadArch(parse("bmg_g21")));
EXPECT_FALSE(IsNVIDIAOffloadArch(parse("generic")));
EXPECT_FALSE(IsAMDOffloadArch(parse("generic")));
EXPECT_FALSE(IsIntelOffloadArch(parse("generic")));
}
TEST(OffloadArchTest, Unknown) {
EXPECT_TRUE(parse("not-a-real-arch").isUnknown());
EXPECT_TRUE(parse("").isUnused());
}
// Names must round-trip through parse -> string.
TEST(OffloadArchTest, RoundTrip) {
for (const char *Name :
{"sm_52", "sm_90a", "gfx906", "gfx1201", "gfx12-generic", "amdgcnspirv",
"graniterapids", "bmg_g21", "generic"}) {
OffloadArch A = parse(Name);
EXPECT_FALSE(A.isUnknown()) << Name;
EXPECT_STREQ(OffloadArchToString(A), Name);
}
}
TEST(OffloadArchTest, Defaults) {
EXPECT_STREQ(OffloadArchToString(OffloadArch::CudaDefault()), "sm_52");
EXPECT_STREQ(OffloadArchToString(OffloadArch::HIPDefault()), "gfx906");
}

View file

@ -31,7 +31,7 @@ class Triple;
namespace AMDGPU {
/// GPU kinds supported by the AMDGPU target.
enum GPUKind : uint32_t {
enum GPUKind : uint8_t {
// Not specified processor.
GK_NONE = 0,

View file

@ -17,11 +17,15 @@ namespace llvm {
namespace NVPTX {
/// GPU kinds supported by the NVPTX target.
enum GPUKind : uint32_t {
enum GPUKind : uint8_t {
GK_NONE = 0,
#define NVPTX_GPU(NAME, KIND, VIRTUAL, SM_ID, MIN_VER, MAX_VER, SUFFIX) \
GK_##KIND,
#include "llvm/TargetParser/NVPTXTargetParser.def"
// Alias for the last GPUKind. Keep in sync with the final .def row.
// FIXME: Should be generated once the GPU list moves to TableGen.
GK_LAST = GK_SM_121f,
};
/// Suffix class of an NVPTX architecture name. Enumerator spellings match the