[clang] bd4f977 - [clang][tools] Add LevelZero support to offload-arch (#160570)

via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 25 14:09:58 PDT 2025


Author: Alex Duran
Date: 2025-09-25T21:09:54Z
New Revision: bd4f97792addc30e9f6aabc143e1ab2b9c7abff7

URL: https://github.com/llvm/llvm-project/commit/bd4f97792addc30e9f6aabc143e1ab2b9c7abff7
DIFF: https://github.com/llvm/llvm-project/commit/bd4f97792addc30e9f6aabc143e1ab2b9c7abff7.diff

LOG: [clang][tools] Add LevelZero support to offload-arch (#160570)

Co-authored-by: Joseph Huber <huberjn at outlook.com>

Added: 
    clang/tools/offload-arch/LevelZeroArch.cpp

Modified: 
    clang/tools/offload-arch/CMakeLists.txt
    clang/tools/offload-arch/OffloadArch.cpp

Removed: 
    


################################################################################
diff  --git a/clang/tools/offload-arch/CMakeLists.txt b/clang/tools/offload-arch/CMakeLists.txt
index cb50b9c1d6dde..f7d7012cf7272 100644
--- a/clang/tools/offload-arch/CMakeLists.txt
+++ b/clang/tools/offload-arch/CMakeLists.txt
@@ -1,7 +1,9 @@
 set(LLVM_LINK_COMPONENTS Support)
 
-add_clang_tool(offload-arch OffloadArch.cpp NVPTXArch.cpp AMDGPUArchByKFD.cpp AMDGPUArchByHIP.cpp)
+add_clang_tool(offload-arch OffloadArch.cpp NVPTXArch.cpp AMDGPUArchByKFD.cpp
+               AMDGPUArchByHIP.cpp LevelZeroArch.cpp)
 
+# Legacy binary names.
 add_clang_symlink(amdgpu-arch offload-arch)
 add_clang_symlink(nvptx-arch offload-arch)
 

diff  --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
new file mode 100644
index 0000000000000..92ead684562e6
--- /dev/null
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -0,0 +1,180 @@
+//===- LevelZeroArch.cpp - list installed Level Zero devices ---*- C++ -*--===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a tool for detecting Level Zero devices installed in the
+// system
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+#include <cstdio>
+
+#define ZE_MAX_DEVICE_NAME 256
+#define ZE_MAX_DEVICE_UUID_SIZE 16
+
+using ze_driver_handle_t = void *;
+using ze_device_handle_t = void *;
+
+enum ze_result_t {
+  ZE_RESULT_SUCCESS = 0,
+  ZE_RESULT_ERROR_UNKNOWN = 0x7ffffffe
+};
+
+enum ze_structure_type_t {
+  ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC = 0x00020021,
+  ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES = 0x3,
+  ZE_STRUCTURE_TYPE_FORCE_UINT32 = 0x7fffffff
+};
+
+enum ze_init_driver_type_flags_t { ZE_INIT_DRIVER_TYPE_FLAG_GPU = 1 };
+
+using ze_device_type_t = uint32_t;
+using ze_device_property_flags_t = uint32_t;
+
+struct ze_init_driver_type_desc_t {
+  ze_structure_type_t stype;
+  const void *pNext;
+  ze_init_driver_type_flags_t flags;
+};
+
+struct ze_device_uuid_t {
+  uint8_t id[ZE_MAX_DEVICE_UUID_SIZE];
+};
+
+struct ze_device_properties_t {
+  ze_structure_type_t stype;
+  void *pNext;
+  ze_device_type_t type;
+  uint32_t vendorId;
+  uint32_t deviceId;
+  ze_device_property_flags_t flags;
+  uint32_t subdeviceId;
+  uint32_t coreClockRate;
+  uint64_t maxMemAllocSize;
+  uint32_t maxHardwareContexts;
+  uint32_t maxCommandQueuePriority;
+  uint32_t numThreadsPerEU;
+  uint32_t physicalEUSimdWidth;
+  uint32_t numEUsPerSubslice;
+  uint32_t numSubslicesPerSlice;
+  uint32_t numSlices;
+  uint64_t timerResolution;
+  uint32_t timestampValidBits;
+  uint32_t kernelTimestampValidBits;
+  ze_device_uuid_t uuid;
+  char name[ZE_MAX_DEVICE_NAME];
+};
+
+ze_result_t zeInitDrivers(uint32_t *pCount, ze_driver_handle_t *phDrivers,
+                          ze_init_driver_type_desc_t *desc);
+ze_result_t zeDeviceGet(ze_driver_handle_t hDriver, uint32_t *pCount,
+                        void *phDevices);
+ze_result_t zeDeviceGetProperties(void *hDevice, void *pProperties);
+
+using namespace llvm;
+extern cl::opt<bool> Verbose;
+
+#define DEFINE_WRAPPER(NAME)                                                   \
+  using NAME##_ty = decltype(NAME);                                            \
+  void *NAME##Ptr = nullptr;                                                   \
+  template <class... Ts> ze_result_t NAME##Wrapper(Ts... args) {               \
+    if (!NAME##Ptr) {                                                          \
+      return ZE_RESULT_ERROR_UNKNOWN;                                          \
+    }                                                                          \
+    return reinterpret_cast<NAME##_ty *>(NAME##Ptr)(args...);                  \
+  };
+
+DEFINE_WRAPPER(zeInitDrivers)
+DEFINE_WRAPPER(zeDeviceGet)
+DEFINE_WRAPPER(zeDeviceGetProperties)
+
+static bool loadLevelZero() {
+  constexpr const char *L0Library = "libze_loader.so";
+  std::string ErrMsg;
+
+  auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
+      llvm::sys::DynamicLibrary::getPermanentLibrary(L0Library, &ErrMsg));
+  if (!DynlibHandle->isValid()) {
+    if (ErrMsg.empty())
+      ErrMsg = "unknown error";
+    if (Verbose)
+      llvm::errs() << "Unable to load library '" << L0Library << "': " << ErrMsg
+                   << "\n";
+    return false;
+  }
+
+  constexpr struct {
+    const char *Name;
+    void **FuncPtr;
+  } Wrappers[] = {
+      {"zeInitDrivers", &zeInitDriversPtr},
+      {"zeDeviceGet", &zeDeviceGetPtr},
+      {"zeDeviceGetProperties", &zeDeviceGetPropertiesPtr},
+  };
+
+  for (auto Entry : Wrappers) {
+    void *P = DynlibHandle->getAddressOfSymbol(Entry.Name);
+    if (P == nullptr) {
+      if (Verbose)
+        llvm::errs() << "Unable to find '" << Entry.Name << "' in '"
+                     << L0Library << "'\n";
+      return false;
+    }
+    *(Entry.FuncPtr) = P;
+  }
+
+  return true;
+}
+
+#define CALL_ZE_AND_CHECK(Fn, ...)                                             \
+  do {                                                                         \
+    ze_result_t Rc = Fn##Wrapper(__VA_ARGS__);                                 \
+    if (Rc != ZE_RESULT_SUCCESS) {                                             \
+      if (Verbose)                                                             \
+        llvm::errs() << "Error: " << __func__ << ":" << #Fn                    \
+                     << " failed with error code " << Rc << "\n";              \
+      return 1;                                                                \
+    }                                                                          \
+  } while (0)
+
+int printGPUsByLevelZero() {
+  if (!loadLevelZero())
+    return 1;
+
+  ze_init_driver_type_desc_t DriverType = {};
+  DriverType.stype = ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC;
+  DriverType.flags = ZE_INIT_DRIVER_TYPE_FLAG_GPU;
+  DriverType.pNext = nullptr;
+  uint32_t DriverCount{0};
+
+  // Initialize and find all drivers.
+  CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, nullptr, &DriverType);
+
+  llvm::SmallVector<ze_driver_handle_t> Drivers(DriverCount);
+  CALL_ZE_AND_CHECK(zeInitDrivers, &DriverCount, Drivers.data(), &DriverType);
+
+  for (auto Driver : Drivers) {
+    // Discover all the devices for a given driver.
+    uint32_t DeviceCount = 0;
+    CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, nullptr);
+
+    llvm::SmallVector<ze_device_handle_t> Devices(DeviceCount);
+    CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, Devices.data());
+
+    for (auto Device : Devices) {
+      ze_device_properties_t DeviceProperties{
+          ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES, nullptr};
+      CALL_ZE_AND_CHECK(zeDeviceGetProperties, Device, &DeviceProperties);
+      llvm::outs() << DeviceProperties.name << '\n';
+    }
+  }
+
+  return 0;
+}

diff  --git a/clang/tools/offload-arch/OffloadArch.cpp b/clang/tools/offload-arch/OffloadArch.cpp
index 74be40214a0ec..3c5131eb7c06c 100644
--- a/clang/tools/offload-arch/OffloadArch.cpp
+++ b/clang/tools/offload-arch/OffloadArch.cpp
@@ -21,6 +21,7 @@ enum VendorName {
   all,
   amdgpu,
   nvptx,
+  intel,
 };
 
 static cl::opt<VendorName>
@@ -28,7 +29,8 @@ static cl::opt<VendorName>
          cl::init(all),
          cl::values(clEnumVal(all, "Print all GPUs (default)"),
                     clEnumVal(amdgpu, "Only print AMD GPUs"),
-                    clEnumVal(nvptx, "Only print NVIDIA GPUs")));
+                    clEnumVal(nvptx, "Only print NVIDIA GPUs"),
+                    clEnumVal(intel, "Only print Intel GPUs")));
 
 cl::opt<bool> Verbose("verbose", cl::desc("Enable verbose output"),
                       cl::init(false), cl::cat(OffloadArchCategory));
@@ -40,6 +42,7 @@ static void PrintVersion(raw_ostream &OS) {
 int printGPUsByKFD();
 int printGPUsByHIP();
 int printGPUsByCUDA();
+int printGPUsByLevelZero();
 
 static int printAMD() {
 #ifndef _WIN32
@@ -51,6 +54,12 @@ static int printAMD() {
 }
 
 static int printNVIDIA() { return printGPUsByCUDA(); }
+static int printIntel() { return printGPUsByLevelZero(); }
+
+const std::array<std::pair<VendorName, function_ref<int()>>, 3> VendorTable{
+    {{VendorName::amdgpu, printAMD},
+     {VendorName::nvptx, printNVIDIA},
+     {VendorName::intel, printIntel}}};
 
 int main(int argc, char *argv[]) {
   cl::HideUnrelatedOptions(OffloadArchCategory);
@@ -68,20 +77,17 @@ int main(int argc, char *argv[]) {
     return 0;
   }
 
-  // If this was invoked from the legacy symlinks provide the same behavior.
-  bool AMDGPUOnly = Only == VendorName::amdgpu ||
-                    sys::path::stem(argv[0]).starts_with("amdgpu-arch");
-  bool NVIDIAOnly = Only == VendorName::nvptx ||
-                    sys::path::stem(argv[0]).starts_with("nvptx-arch");
-
-  int NVIDIAResult = 0;
-  if (!AMDGPUOnly)
-    NVIDIAResult = printNVIDIA();
+  // Support legacy binaries.
+  if (sys::path::stem(argv[0]).starts_with("amdgpu-arch"))
+    Only = VendorName::amdgpu;
+  if (sys::path::stem(argv[0]).starts_with("nvptx-arch"))
+    Only = VendorName::nvptx;
 
-  int AMDResult = 0;
-  if (!NVIDIAOnly)
-    AMDResult = printAMD();
+  int Result = 1;
+  for (auto [Name, Func] : VendorTable) {
+    if (Only == VendorName::all || Only == Name)
+      Result &= Func();
+  }
 
-  // We only failed if all cases returned an error.
-  return AMDResult && NVIDIAResult;
+  return Result;
 }


        


More information about the cfe-commits mailing list