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 constexpr const char *L0Library = "libze_loader.so";
100 std::string ErrMsg;
101
102 auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
103 args: llvm::sys::DynamicLibrary::getPermanentLibrary(filename: L0Library, errMsg: &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 {.Name: "zeInitDrivers", .FuncPtr: &zeInitDriversPtr},
118 {.Name: "zeDeviceGet", .FuncPtr: &zeDeviceGetPtr},
119 {.Name: "zeDeviceGetProperties", .FuncPtr: &zeDeviceGetPropertiesPtr},
120 };
121
122 for (auto Entry : Wrappers) {
123 void *P = DynlibHandle->getAddressOfSymbol(symbolName: 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
147int 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 DeviceProperties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
174 DeviceProperties.pNext = nullptr;
175 CALL_ZE_AND_CHECK(zeDeviceGetProperties, Device, &DeviceProperties);
176 llvm::outs() << DeviceProperties.name << '\n';
177 }
178 }
179
180 return 0;
181}
182