Skip to content

Commit 5a6611b

Browse files
adurangjhuber6
authored andcommitted
[clang][tools] Add LevelZero support to offload-arch (llvm#160570)
Co-authored-by: Joseph Huber <[email protected]>
1 parent fe184b9 commit 5a6611b

File tree

3 files changed

+204
-16
lines changed

3 files changed

+204
-16
lines changed

clang/tools/offload-arch/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
set(LLVM_LINK_COMPONENTS Support)
22

3-
add_clang_tool(offload-arch OffloadArch.cpp NVPTXArch.cpp AMDGPUArchByKFD.cpp AMDGPUArchByHIP.cpp)
3+
add_clang_tool(offload-arch OffloadArch.cpp NVPTXArch.cpp AMDGPUArchByKFD.cpp
4+
AMDGPUArchByHIP.cpp LevelZeroArch.cpp)
45

6+
# Legacy binary names.
57
add_clang_symlink(amdgpu-arch offload-arch)
68
add_clang_symlink(nvptx-arch offload-arch)
79

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//===- LevelZeroArch.cpp - list installed Level Zero devices ---*- C++ -*--===//
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 file implements a tool for detecting Level Zero devices installed in the
10+
// system
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "llvm/Support/CommandLine.h"
15+
#include "llvm/Support/DynamicLibrary.h"
16+
#include "llvm/Support/Error.h"
17+
#include <cstdio>
18+
19+
#define ZE_MAX_DEVICE_NAME 256
20+
#define ZE_MAX_DEVICE_UUID_SIZE 16
21+
22+
using ze_driver_handle_t = void *;
23+
using ze_device_handle_t = void *;
24+
25+
enum ze_result_t {
26+
ZE_RESULT_SUCCESS = 0,
27+
ZE_RESULT_ERROR_UNKNOWN = 0x7ffffffe
28+
};
29+
30+
enum ze_structure_type_t {
31+
ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC = 0x00020021,
32+
ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES = 0x3,
33+
ZE_STRUCTURE_TYPE_FORCE_UINT32 = 0x7fffffff
34+
};
35+
36+
enum ze_init_driver_type_flags_t { ZE_INIT_DRIVER_TYPE_FLAG_GPU = 1 };
37+
38+
using ze_device_type_t = uint32_t;
39+
using ze_device_property_flags_t = uint32_t;
40+
41+
struct ze_init_driver_type_desc_t {
42+
ze_structure_type_t stype;
43+
const void *pNext;
44+
ze_init_driver_type_flags_t flags;
45+
};
46+
47+
struct ze_device_uuid_t {
48+
uint8_t id[ZE_MAX_DEVICE_UUID_SIZE];
49+
};
50+
51+
struct ze_device_properties_t {
52+
ze_structure_type_t stype;
53+
void *pNext;
54+
ze_device_type_t type;
55+
uint32_t vendorId;
56+
uint32_t deviceId;
57+
ze_device_property_flags_t flags;
58+
uint32_t subdeviceId;
59+
uint32_t coreClockRate;
60+
uint64_t maxMemAllocSize;
61+
uint32_t maxHardwareContexts;
62+
uint32_t maxCommandQueuePriority;
63+
uint32_t numThreadsPerEU;
64+
uint32_t physicalEUSimdWidth;
65+
uint32_t numEUsPerSubslice;
66+
uint32_t numSubslicesPerSlice;
67+
uint32_t numSlices;
68+
uint64_t timerResolution;
69+
uint32_t timestampValidBits;
70+
uint32_t kernelTimestampValidBits;
71+
ze_device_uuid_t uuid;
72+
char name[ZE_MAX_DEVICE_NAME];
73+
};
74+
75+
ze_result_t zeInitDrivers(uint32_t *pCount, ze_driver_handle_t *phDrivers,
76+
ze_init_driver_type_desc_t *desc);
77+
ze_result_t zeDeviceGet(ze_driver_handle_t hDriver, uint32_t *pCount,
78+
void *phDevices);
79+
ze_result_t zeDeviceGetProperties(void *hDevice, void *pProperties);
80+
81+
using namespace llvm;
82+
extern cl::opt<bool> Verbose;
83+
84+
#define DEFINE_WRAPPER(NAME) \
85+
using NAME##_ty = decltype(NAME); \
86+
void *NAME##Ptr = nullptr; \
87+
template <class... Ts> ze_result_t NAME##Wrapper(Ts... args) { \
88+
if (!NAME##Ptr) { \
89+
return ZE_RESULT_ERROR_UNKNOWN; \
90+
} \
91+
return reinterpret_cast<NAME##_ty *>(NAME##Ptr)(args...); \
92+
};
93+
94+
DEFINE_WRAPPER(zeInitDrivers)
95+
DEFINE_WRAPPER(zeDeviceGet)
96+
DEFINE_WRAPPER(zeDeviceGetProperties)
97+
98+
static bool loadLevelZero() {
99+
constexpr const char *L0Library = "libze_loader.so";
100+
std::string ErrMsg;
101+
102+
auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
103+
llvm::sys::DynamicLibrary::getPermanentLibrary(L0Library, &ErrMsg));
104+
if (!DynlibHandle->isValid()) {
105+
if (ErrMsg.empty())
106+
ErrMsg = "unknown error";
107+
if (Verbose)
108+
llvm::errs() << "Unable to load library '" << L0Library << "': " << ErrMsg
109+
<< "\n";
110+
return false;
111+
}
112+
113+
constexpr struct {
114+
const char *Name;
115+
void **FuncPtr;
116+
} Wrappers[] = {
117+
{"zeInitDrivers", &zeInitDriversPtr},
118+
{"zeDeviceGet", &zeDeviceGetPtr},
119+
{"zeDeviceGetProperties", &zeDeviceGetPropertiesPtr},
120+
};
121+
122+
for (auto Entry : Wrappers) {
123+
void *P = DynlibHandle->getAddressOfSymbol(Entry.Name);
124+
if (P == nullptr) {
125+
if (Verbose)
126+
llvm::errs() << "Unable to find '" << Entry.Name << "' in '"
127+
<< L0Library << "'\n";
128+
return false;
129+
}
130+
*(Entry.FuncPtr) = P;
131+
}
132+
133+
return true;
134+
}
135+
136+
#define CALL_ZE_AND_CHECK(Fn, ...) \
137+
do { \
138+
ze_result_t Rc = Fn##Wrapper(__VA_ARGS__); \
139+
if (Rc != ZE_RESULT_SUCCESS) { \
140+
if (Verbose) \
141+
llvm::errs() << "Error: " << __func__ << ":" << #Fn \
142+
<< " failed with error code " << Rc << "\n"; \
143+
return 1; \
144+
} \
145+
} while (0)
146+
147+
int printGPUsByLevelZero() {
148+
if (!loadLevelZero())
149+
return 1;
150+
151+
ze_init_driver_type_desc_t DriverType = {};
152+
DriverType.stype = ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC;
153+
DriverType.flags = ZE_INIT_DRIVER_TYPE_FLAG_GPU;
154+
DriverType.pNext = nullptr;
155+
uint32_t DriverCount{0};
156+
157+
// Initialize and find all drivers.
158+
CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, nullptr, &DriverType);
159+
160+
llvm::SmallVector<ze_driver_handle_t> Drivers(DriverCount);
161+
CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, Drivers.data(), &DriverType);
162+
163+
for (auto Driver : Drivers) {
164+
// Discover all the devices for a given driver.
165+
uint32_t DeviceCount = 0;
166+
CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, nullptr);
167+
168+
llvm::SmallVector<ze_device_handle_t> Devices(DeviceCount);
169+
CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, Devices.data());
170+
171+
for (auto Device : Devices) {
172+
ze_device_properties_t DeviceProperties{
173+
ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES, nullptr};
174+
CALL_ZE_AND_CHECK(zeDeviceGetProperties, Device, &DeviceProperties);
175+
llvm::outs() << DeviceProperties.name << '\n';
176+
}
177+
}
178+
179+
return 0;
180+
}

clang/tools/offload-arch/OffloadArch.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@ enum VendorName {
2121
all,
2222
amdgpu,
2323
nvptx,
24+
intel,
2425
};
2526

2627
static cl::opt<VendorName>
2728
Only("only", cl::desc("Restrict to vendor:"), cl::cat(OffloadArchCategory),
2829
cl::init(all),
2930
cl::values(clEnumVal(all, "Print all GPUs (default)"),
3031
clEnumVal(amdgpu, "Only print AMD GPUs"),
31-
clEnumVal(nvptx, "Only print NVIDIA GPUs")));
32+
clEnumVal(nvptx, "Only print NVIDIA GPUs"),
33+
clEnumVal(intel, "Only print Intel GPUs")));
3234

3335
cl::opt<bool> Verbose("verbose", cl::desc("Enable verbose output"),
3436
cl::init(false), cl::cat(OffloadArchCategory));
@@ -40,6 +42,7 @@ static void PrintVersion(raw_ostream &OS) {
4042
int printGPUsByKFD();
4143
int printGPUsByHIP();
4244
int printGPUsByCUDA();
45+
int printGPUsByLevelZero();
4346

4447
static int printAMD() {
4548
#ifndef _WIN32
@@ -51,6 +54,12 @@ static int printAMD() {
5154
}
5255

5356
static int printNVIDIA() { return printGPUsByCUDA(); }
57+
static int printIntel() { return printGPUsByLevelZero(); }
58+
59+
const std::array<std::pair<VendorName, function_ref<int()>>, 3> VendorTable{
60+
{{VendorName::amdgpu, printAMD},
61+
{VendorName::nvptx, printNVIDIA},
62+
{VendorName::intel, printIntel}}};
5463

5564
int main(int argc, char *argv[]) {
5665
cl::HideUnrelatedOptions(OffloadArchCategory);
@@ -68,20 +77,17 @@ int main(int argc, char *argv[]) {
6877
return 0;
6978
}
7079

71-
// If this was invoked from the legacy symlinks provide the same behavior.
72-
bool AMDGPUOnly = Only == VendorName::amdgpu ||
73-
sys::path::stem(argv[0]).starts_with("amdgpu-arch");
74-
bool NVIDIAOnly = Only == VendorName::nvptx ||
75-
sys::path::stem(argv[0]).starts_with("nvptx-arch");
76-
77-
int NVIDIAResult = 0;
78-
if (!AMDGPUOnly)
79-
NVIDIAResult = printNVIDIA();
80+
// Support legacy binaries.
81+
if (sys::path::stem(argv[0]).starts_with("amdgpu-arch"))
82+
Only = VendorName::amdgpu;
83+
if (sys::path::stem(argv[0]).starts_with("nvptx-arch"))
84+
Only = VendorName::nvptx;
8085

81-
int AMDResult = 0;
82-
if (!NVIDIAOnly)
83-
AMDResult = printAMD();
86+
int Result = 1;
87+
for (auto [Name, Func] : VendorTable) {
88+
if (Only == VendorName::all || Only == Name)
89+
Result &= Func();
90+
}
8491

85-
// We only failed if all cases returned an error.
86-
return AMDResult && NVIDIAResult;
92+
return Result;
8793
}

0 commit comments

Comments
 (0)