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 <[email protected]> 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; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
