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 "llvm/TargetParser/IntelGPUTargetParser.h"
18#include <cstdio>
19#include <string>
20
21#define ZE_MAX_DEVICE_NAME 256
22#define ZE_MAX_DEVICE_UUID_SIZE 16
23
24using ze_driver_handle_t = void *;
25using ze_device_handle_t = void *;
26
27enum ze_result_t {
28 ZE_RESULT_SUCCESS = 0,
29 ZE_RESULT_ERROR_UNKNOWN = 0x7ffffffe
30};
31
32enum ze_structure_type_t {
33 ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC = 0x00020021,
34 ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES = 0x3,
35 ZE_STRUCTURE_TYPE_DEVICE_IP_VERSION_EXT = 0x1000f,
36 ZE_STRUCTURE_TYPE_FORCE_UINT32 = 0x7fffffff
37};
38
39enum ze_init_driver_type_flags_t { ZE_INIT_DRIVER_TYPE_FLAG_GPU = 1 };
40
41using ze_device_type_t = uint32_t;
42using ze_device_property_flags_t = uint32_t;
43
44struct ze_init_driver_type_desc_t {
45 ze_structure_type_t stype;
46 const void *pNext;
47 ze_init_driver_type_flags_t flags;
48};
49
50struct ze_device_uuid_t {
51 uint8_t id[ZE_MAX_DEVICE_UUID_SIZE];
52};
53
54struct ze_device_properties_t {
55 ze_structure_type_t stype;
56 void *pNext;
57 ze_device_type_t type;
58 uint32_t vendorId;
59 uint32_t deviceId;
60 ze_device_property_flags_t flags;
61 uint32_t subdeviceId;
62 uint32_t coreClockRate;
63 uint64_t maxMemAllocSize;
64 uint32_t maxHardwareContexts;
65 uint32_t maxCommandQueuePriority;
66 uint32_t numThreadsPerEU;
67 uint32_t physicalEUSimdWidth;
68 uint32_t numEUsPerSubslice;
69 uint32_t numSubslicesPerSlice;
70 uint32_t numSlices;
71 uint64_t timerResolution;
72 uint32_t timestampValidBits;
73 uint32_t kernelTimestampValidBits;
74 ze_device_uuid_t uuid;
75 char name[ZE_MAX_DEVICE_NAME];
76};
77
78// Chained onto ze_device_properties_t::pNext to request the device IP version.
79struct ze_device_ip_version_ext_t {
80 ze_structure_type_t stype;
81 const void *pNext;
82 uint32_t ipVersion;
83};
84
85ze_result_t zeInitDrivers(uint32_t *pCount, ze_driver_handle_t *phDrivers,
86 ze_init_driver_type_desc_t *desc);
87ze_result_t zeDeviceGet(ze_driver_handle_t hDriver, uint32_t *pCount,
88 void *phDevices);
89ze_result_t zeDeviceGetProperties(void *hDevice, void *pProperties);
90
91using namespace llvm;
92extern cl::opt<bool> Verbose;
93
94#define DEFINE_WRAPPER(NAME) \
95 using NAME##_ty = decltype(NAME); \
96 void *NAME##Ptr = nullptr; \
97 template <class... Ts> ze_result_t NAME##Wrapper(Ts... args) { \
98 if (!NAME##Ptr) { \
99 return ZE_RESULT_ERROR_UNKNOWN; \
100 } \
101 return reinterpret_cast<NAME##_ty *>(NAME##Ptr)(args...); \
102 }
103
104DEFINE_WRAPPER(zeInitDrivers)
105DEFINE_WRAPPER(zeDeviceGet)
106DEFINE_WRAPPER(zeDeviceGetProperties)
107
108static bool loadLevelZero() {
109#ifdef _WIN32
110 constexpr const char *L0Library = "ze_loader.dll";
111#else
112 constexpr const char *L0Library = "libze_loader.so";
113#endif
114 std::string ErrMsg;
115
116 auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
117 args: llvm::sys::DynamicLibrary::getPermanentLibrary(filename: L0Library, errMsg: &ErrMsg));
118 if (!DynlibHandle->isValid()) {
119 if (ErrMsg.empty())
120 ErrMsg = "unknown error";
121 if (Verbose)
122 llvm::errs() << "Unable to load library '" << L0Library << "': " << ErrMsg
123 << "\n";
124 return false;
125 }
126
127 constexpr struct {
128 const char *Name;
129 void **FuncPtr;
130 } Wrappers[] = {
131 {.Name: "zeInitDrivers", .FuncPtr: &zeInitDriversPtr},
132 {.Name: "zeDeviceGet", .FuncPtr: &zeDeviceGetPtr},
133 {.Name: "zeDeviceGetProperties", .FuncPtr: &zeDeviceGetPropertiesPtr},
134 };
135
136 for (auto Entry : Wrappers) {
137 void *P = DynlibHandle->getAddressOfSymbol(symbolName: Entry.Name);
138 if (P == nullptr) {
139 if (Verbose)
140 llvm::errs() << "Unable to find '" << Entry.Name << "' in '"
141 << L0Library << "'\n";
142 return false;
143 }
144 *(Entry.FuncPtr) = P;
145 }
146
147 return true;
148}
149
150#define CALL_ZE_AND_CHECK(Fn, ...) \
151 do { \
152 ze_result_t Rc = Fn##Wrapper(__VA_ARGS__); \
153 if (Rc != ZE_RESULT_SUCCESS) { \
154 if (Verbose) \
155 llvm::errs() << "Error: " << __func__ << ":" << #Fn \
156 << " failed with error code " << Rc << "\n"; \
157 return 1; \
158 } \
159 } while (0)
160
161// Translate a GPU IP version into an architecture name that is a legal
162// --offload-arch parameter. A device that has no name in the table is named
163// after its version.
164std::string getIntelGPUArchName(uint32_t IPVersion) {
165 StringRef Name = IntelGPU::getArchName(GPUIPVersion: IPVersion);
166 return Name.empty() ? IntelGPU::getNumericArchName(GPUIPVersion: IPVersion) : Name.str();
167}
168
169int printGPUsByLevelZero() {
170 if (!loadLevelZero())
171 return 1;
172
173 ze_init_driver_type_desc_t DriverType = {};
174 DriverType.stype = ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC;
175 DriverType.flags = ZE_INIT_DRIVER_TYPE_FLAG_GPU;
176 DriverType.pNext = nullptr;
177 uint32_t DriverCount{0};
178
179 // Initialize and find all drivers.
180 CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, nullptr, &DriverType);
181
182 llvm::SmallVector<ze_driver_handle_t> Drivers(DriverCount);
183 CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, Drivers.data(), &DriverType);
184
185 for (auto Driver : Drivers) {
186 // Discover all the devices for a given driver.
187 uint32_t DeviceCount = 0;
188 CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, nullptr);
189
190 llvm::SmallVector<ze_device_handle_t> Devices(DeviceCount);
191 CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, Devices.data());
192
193 for (auto Device : Devices) {
194 ze_device_ip_version_ext_t IPVersion = {};
195 IPVersion.stype = ZE_STRUCTURE_TYPE_DEVICE_IP_VERSION_EXT;
196 IPVersion.pNext = nullptr;
197
198 ze_device_properties_t DeviceProperties = {};
199 DeviceProperties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
200 DeviceProperties.pNext = &IPVersion;
201 CALL_ZE_AND_CHECK(zeDeviceGetProperties, Device, &DeviceProperties);
202
203 if (IPVersion.ipVersion == 0) {
204 if (Verbose)
205 llvm::errs()
206 << "warning: skipping device '" << DeviceProperties.name
207 << "': the device does not support Device IP Version Extension\n";
208 continue;
209 }
210
211 if (Verbose)
212 llvm::errs() << "Found device '" << DeviceProperties.name << "'\n";
213
214 llvm::outs() << getIntelGPUArchName(IPVersion: IPVersion.ipVersion) << '\n';
215 }
216 }
217
218 return 0;
219}
220