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
22using ze_driver_handle_t = void *;
23using ze_device_handle_t = void *;
24
25enum ze_result_t {
26 ZE_RESULT_SUCCESS = 0,
27 ZE_RESULT_ERROR_UNKNOWN = 0x7ffffffe
28};
29
30enum 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
36enum ze_init_driver_type_flags_t { ZE_INIT_DRIVER_TYPE_FLAG_GPU = 1 };
37
38using ze_device_type_t = uint32_t;
39using ze_device_property_flags_t = uint32_t;
40
41struct 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
47struct ze_device_uuid_t {
48 uint8_t id[ZE_MAX_DEVICE_UUID_SIZE];
49};
50
51struct 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
75ze_result_t zeInitDrivers(uint32_t *pCount, ze_driver_handle_t *phDrivers,
76 ze_init_driver_type_desc_t *desc);
77ze_result_t zeDeviceGet(ze_driver_handle_t hDriver, uint32_t *pCount,
78 void *phDevices);
79ze_result_t zeDeviceGetProperties(void *hDevice, void *pProperties);
80
81using namespace llvm;
82extern 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
94DEFINE_WRAPPER(zeInitDrivers)
95DEFINE_WRAPPER(zeDeviceGet)
96DEFINE_WRAPPER(zeDeviceGetProperties)
97
98static bool loadLevelZero() {
99#ifdef _WIN32
100 constexpr const char *L0Library = "ze_loader.dll";
101#else
102 constexpr const char *L0Library = "libze_loader.so";
103#endif
104 std::string ErrMsg;
105
106 auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
107 args: llvm::sys::DynamicLibrary::getPermanentLibrary(filename: L0Library, errMsg: &ErrMsg));
108 if (!DynlibHandle->isValid()) {
109 if (ErrMsg.empty())
110 ErrMsg = "unknown error";
111 if (Verbose)
112 llvm::errs() << "Unable to load library '" << L0Library << "': " << ErrMsg
113 << "\n";
114 return false;
115 }
116
117 constexpr struct {
118 const char *Name;
119 void **FuncPtr;
120 } Wrappers[] = {
121 {.Name: "zeInitDrivers", .FuncPtr: &zeInitDriversPtr},
122 {.Name: "zeDeviceGet", .FuncPtr: &zeDeviceGetPtr},
123 {.Name: "zeDeviceGetProperties", .FuncPtr: &zeDeviceGetPropertiesPtr},
124 };
125
126 for (auto Entry : Wrappers) {
127 void *P = DynlibHandle->getAddressOfSymbol(symbolName: Entry.Name);
128 if (P == nullptr) {
129 if (Verbose)
130 llvm::errs() << "Unable to find '" << Entry.Name << "' in '"
131 << L0Library << "'\n";
132 return false;
133 }
134 *(Entry.FuncPtr) = P;
135 }
136
137 return true;
138}
139
140#define CALL_ZE_AND_CHECK(Fn, ...) \
141 do { \
142 ze_result_t Rc = Fn##Wrapper(__VA_ARGS__); \
143 if (Rc != ZE_RESULT_SUCCESS) { \
144 if (Verbose) \
145 llvm::errs() << "Error: " << __func__ << ":" << #Fn \
146 << " failed with error code " << Rc << "\n"; \
147 return 1; \
148 } \
149 } while (0)
150
151int printGPUsByLevelZero() {
152 if (!loadLevelZero())
153 return 1;
154
155 ze_init_driver_type_desc_t DriverType = {};
156 DriverType.stype = ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC;
157 DriverType.flags = ZE_INIT_DRIVER_TYPE_FLAG_GPU;
158 DriverType.pNext = nullptr;
159 uint32_t DriverCount{0};
160
161 // Initialize and find all drivers.
162 CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, nullptr, &DriverType);
163
164 llvm::SmallVector<ze_driver_handle_t> Drivers(DriverCount);
165 CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, Drivers.data(), &DriverType);
166
167 for (auto Driver : Drivers) {
168 // Discover all the devices for a given driver.
169 uint32_t DeviceCount = 0;
170 CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, nullptr);
171
172 llvm::SmallVector<ze_device_handle_t> Devices(DeviceCount);
173 CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, Devices.data());
174
175 for (auto Device : Devices) {
176 ze_device_properties_t DeviceProperties = {};
177 DeviceProperties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
178 DeviceProperties.pNext = nullptr;
179 CALL_ZE_AND_CHECK(zeDeviceGetProperties, Device, &DeviceProperties);
180 llvm::outs() << DeviceProperties.name << '\n';
181 }
182 }
183
184 return 0;
185}
186