| //===- llvm-offload-device-info.cpp - Print liboffload properties ---------===// | |
| // | |
| // 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 is a command line utility that, by using the new liboffload API, prints | |
| // all devices and properties | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include <OffloadAPI.h> | |
| #include <iostream> | |
| #include <vector> | |
| #define OFFLOAD_ERR(X) \ | |
| if (auto Err = X) { \ | |
| return Err; \ | |
| } | |
| enum class PrintKind { | |
| NORMAL, | |
| FP_FLAGS, | |
| }; | |
| template <typename T, PrintKind PK = PrintKind::NORMAL> | |
| void doWrite(std::ostream &S, T &&Val) { | |
| S << Val; | |
| } | |
| template <> | |
| void doWrite<ol_platform_backend_t>(std::ostream &S, | |
| ol_platform_backend_t &&Val) { | |
| switch (Val) { | |
| case OL_PLATFORM_BACKEND_UNKNOWN: | |
| S << "UNKNOWN"; | |
| break; | |
| case OL_PLATFORM_BACKEND_CUDA: | |
| S << "CUDA"; | |
| break; | |
| case OL_PLATFORM_BACKEND_AMDGPU: | |
| S << "AMDGPU"; | |
| break; | |
| case OL_PLATFORM_BACKEND_LEVEL_ZERO: | |
| S << "LEVEL_ZERO"; | |
| break; | |
| case OL_PLATFORM_BACKEND_HOST: | |
| S << "HOST"; | |
| break; | |
| default: | |
| S << "<< INVALID >>"; | |
| break; | |
| } | |
| } | |
| template <> | |
| void doWrite<ol_device_type_t>(std::ostream &S, ol_device_type_t &&Val) { | |
| switch (Val) { | |
| case OL_DEVICE_TYPE_GPU: | |
| S << "GPU"; | |
| break; | |
| case OL_DEVICE_TYPE_CPU: | |
| S << "CPU"; | |
| break; | |
| case OL_DEVICE_TYPE_HOST: | |
| S << "HOST"; | |
| break; | |
| default: | |
| S << "<< INVALID >>"; | |
| break; | |
| } | |
| } | |
| template <> | |
| void doWrite<ol_dimensions_t>(std::ostream &S, ol_dimensions_t &&Val) { | |
| S << "{x: " << Val.x << ", y: " << Val.y << ", z: " << Val.z << "}"; | |
| } | |
| template <> | |
| void doWrite<ol_device_fp_capability_flags_t, PrintKind::FP_FLAGS>( | |
| std::ostream &S, ol_device_fp_capability_flags_t &&Val) { | |
| S << Val << " {"; | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_CORRECTLY_ROUNDED_DIVIDE_SQRT) { | |
| S << " CORRECTLY_ROUNDED_DIVIDE_SQRT"; | |
| } | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_ROUND_TO_NEAREST) { | |
| S << " ROUND_TO_NEAREST"; | |
| } | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_ROUND_TO_ZERO) { | |
| S << " ROUND_TO_ZERO"; | |
| } | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_ROUND_TO_INF) { | |
| S << " ROUND_TO_INF"; | |
| } | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_INF_NAN) { | |
| S << " INF_NAN"; | |
| } | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_DENORM) { | |
| S << " DENORM"; | |
| } | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_FMA) { | |
| S << " FMA"; | |
| } | |
| if (Val & OL_DEVICE_FP_CAPABILITY_FLAG_SOFT_FLOAT) { | |
| S << " SOFT_FLOAT"; | |
| } | |
| S << " }"; | |
| } | |
| template <typename T> | |
| ol_result_t printPlatformValue(std::ostream &S, ol_platform_handle_t Plat, | |
| ol_platform_info_t Info, const char *Desc) { | |
| S << Desc << ": "; | |
| if constexpr (std::is_pointer_v<T>) { | |
| std::vector<uint8_t> Val; | |
| size_t Size; | |
| OFFLOAD_ERR(olGetPlatformInfoSize(Plat, Info, &Size)); | |
| Val.resize(Size); | |
| OFFLOAD_ERR(olGetPlatformInfo(Plat, Info, sizeof(Val), Val.data())); | |
| doWrite(S, reinterpret_cast<T>(Val.data())); | |
| } else { | |
| T Val; | |
| OFFLOAD_ERR(olGetPlatformInfo(Plat, Info, sizeof(Val), &Val)); | |
| doWrite(S, std::move(Val)); | |
| } | |
| S << "\n"; | |
| return OL_SUCCESS; | |
| } | |
| template <typename T, PrintKind PK = PrintKind::NORMAL> | |
| ol_result_t printDeviceValue(std::ostream &S, ol_device_handle_t Dev, | |
| ol_device_info_t Info, const char *Desc, | |
| const char *Units = nullptr) { | |
| S << Desc << ": "; | |
| if constexpr (std::is_pointer_v<T>) { | |
| std::vector<uint8_t> Val; | |
| size_t Size; | |
| OFFLOAD_ERR(olGetDeviceInfoSize(Dev, Info, &Size)); | |
| Val.resize(Size); | |
| OFFLOAD_ERR(olGetDeviceInfo(Dev, Info, Size, Val.data())); | |
| doWrite<T, PK>(S, reinterpret_cast<T>(Val.data())); | |
| } else { | |
| T Val; | |
| OFFLOAD_ERR(olGetDeviceInfo(Dev, Info, sizeof(Val), &Val)); | |
| doWrite<T, PK>(S, std::move(Val)); | |
| } | |
| if (Units) | |
| S << " " << Units; | |
| S << "\n"; | |
| return OL_SUCCESS; | |
| } | |
| ol_result_t printDevice(std::ostream &S, ol_device_handle_t D) { | |
| ol_platform_handle_t Platform; | |
| OFFLOAD_ERR( | |
| olGetDeviceInfo(D, OL_DEVICE_INFO_PLATFORM, sizeof(Platform), &Platform)); | |
| std::vector<char> Name; | |
| size_t NameSize; | |
| OFFLOAD_ERR(olGetDeviceInfoSize(D, OL_DEVICE_INFO_PRODUCT_NAME, &NameSize)) | |
| Name.resize(NameSize); | |
| OFFLOAD_ERR( | |
| olGetDeviceInfo(D, OL_DEVICE_INFO_PRODUCT_NAME, NameSize, Name.data())); | |
| S << "[" << Name.data() << "]\n"; | |
| OFFLOAD_ERR(printPlatformValue<const char *>( | |
| S, Platform, OL_PLATFORM_INFO_NAME, "Platform Name")); | |
| OFFLOAD_ERR(printPlatformValue<const char *>( | |
| S, Platform, OL_PLATFORM_INFO_VENDOR_NAME, "Platform Vendor Name")); | |
| OFFLOAD_ERR(printPlatformValue<const char *>( | |
| S, Platform, OL_PLATFORM_INFO_VERSION, "Platform Version")); | |
| OFFLOAD_ERR(printPlatformValue<ol_platform_backend_t>( | |
| S, Platform, OL_PLATFORM_INFO_BACKEND, "Platform Backend")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<const char *>(S, D, OL_DEVICE_INFO_NAME, "Name")); | |
| OFFLOAD_ERR(printDeviceValue<const char *>(S, D, OL_DEVICE_INFO_PRODUCT_NAME, | |
| "Product Name")); | |
| OFFLOAD_ERR(printDeviceValue<const char *>(S, D, OL_DEVICE_INFO_UID, "UID")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<ol_device_type_t>(S, D, OL_DEVICE_INFO_TYPE, "Type")); | |
| OFFLOAD_ERR(printDeviceValue<const char *>( | |
| S, D, OL_DEVICE_INFO_DRIVER_VERSION, "Driver Version")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>( | |
| S, D, OL_DEVICE_INFO_MAX_WORK_GROUP_SIZE, "Max Work Group Size")); | |
| OFFLOAD_ERR(printDeviceValue<ol_dimensions_t>( | |
| S, D, OL_DEVICE_INFO_MAX_WORK_GROUP_SIZE_PER_DIMENSION, | |
| "Max Work Group Size Per Dimension")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_MAX_WORK_SIZE, | |
| "Max Work Size")); | |
| OFFLOAD_ERR(printDeviceValue<ol_dimensions_t>( | |
| S, D, OL_DEVICE_INFO_MAX_WORK_SIZE_PER_DIMENSION, | |
| "Max Work Size Per Dimension")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_VENDOR_ID, "Vendor ID")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_NUM_COMPUTE_UNITS, | |
| "Num Compute Units")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>( | |
| S, D, OL_DEVICE_INFO_MAX_CLOCK_FREQUENCY, "Max Clock Frequency", "MHz")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_MEMORY_CLOCK_RATE, | |
| "Memory Clock Rate", "MHz")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_ADDRESS_BITS, | |
| "Address Bits")); | |
| OFFLOAD_ERR(printDeviceValue<uint64_t>( | |
| S, D, OL_DEVICE_INFO_MAX_MEM_ALLOC_SIZE, "Max Mem Allocation Size", "B")); | |
| OFFLOAD_ERR(printDeviceValue<uint64_t>(S, D, OL_DEVICE_INFO_GLOBAL_MEM_SIZE, | |
| "Global Mem Size", "B")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<uint64_t>(S, D, OL_DEVICE_INFO_WORK_GROUP_LOCAL_MEM_SIZE, | |
| "Work Group Shared Mem Size", "B")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<bool>(S, D, OL_DEVICE_INFO_SINGLE_FP_SUPPORT, | |
| "Single Precision Floating Point Support")); | |
| OFFLOAD_ERR( | |
| (printDeviceValue<ol_device_fp_capability_flags_t, PrintKind::FP_FLAGS>( | |
| S, D, OL_DEVICE_INFO_SINGLE_FP_CONFIG, | |
| "Single Precision Floating Point Capability"))); | |
| OFFLOAD_ERR( | |
| printDeviceValue<bool>(S, D, OL_DEVICE_INFO_DOUBLE_FP_SUPPORT, | |
| "Double Precision Floating Point Support")); | |
| OFFLOAD_ERR( | |
| (printDeviceValue<ol_device_fp_capability_flags_t, PrintKind::FP_FLAGS>( | |
| S, D, OL_DEVICE_INFO_DOUBLE_FP_CONFIG, | |
| "Double Precision Floating Point Capability"))); | |
| OFFLOAD_ERR(printDeviceValue<bool>(S, D, OL_DEVICE_INFO_HALF_FP_SUPPORT, | |
| "Half Precision Floating Point Support")); | |
| OFFLOAD_ERR( | |
| (printDeviceValue<ol_device_fp_capability_flags_t, PrintKind::FP_FLAGS>( | |
| S, D, OL_DEVICE_INFO_HALF_FP_CONFIG, | |
| "Half Precision Floating Point Capability"))); | |
| OFFLOAD_ERR( | |
| printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_NATIVE_VECTOR_WIDTH_CHAR, | |
| "Native Vector Width For Char")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_NATIVE_VECTOR_WIDTH_SHORT, | |
| "Native Vector Width For Short")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>(S, D, | |
| OL_DEVICE_INFO_NATIVE_VECTOR_WIDTH_INT, | |
| "Native Vector Width For Int")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_NATIVE_VECTOR_WIDTH_LONG, | |
| "Native Vector Width For Long")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_NATIVE_VECTOR_WIDTH_FLOAT, | |
| "Native Vector Width For Float")); | |
| OFFLOAD_ERR(printDeviceValue<uint32_t>( | |
| S, D, OL_DEVICE_INFO_NATIVE_VECTOR_WIDTH_DOUBLE, | |
| "Native Vector Width For Double")); | |
| OFFLOAD_ERR( | |
| printDeviceValue<uint32_t>(S, D, OL_DEVICE_INFO_NATIVE_VECTOR_WIDTH_HALF, | |
| "Native Vector Width For Half")); | |
| return OL_SUCCESS; | |
| } | |
| ol_result_t printRoot(std::ostream &S) { | |
| OFFLOAD_ERR(olInit(nullptr)); | |
| S << "Liboffload Version: " << OL_VERSION_MAJOR << "." << OL_VERSION_MINOR | |
| << "." << OL_VERSION_PATCH << "\n"; | |
| std::vector<ol_device_handle_t> Devices; | |
| OFFLOAD_ERR(olIterateDevices( | |
| [](ol_device_handle_t Device, void *UserData) { | |
| reinterpret_cast<decltype(Devices) *>(UserData)->push_back(Device); | |
| return true; | |
| }, | |
| &Devices)); | |
| S << "Num Devices: " << Devices.size() << "\n"; | |
| for (auto &D : Devices) { | |
| S << "\n"; | |
| OFFLOAD_ERR(printDevice(S, D)); | |
| } | |
| OFFLOAD_ERR(olShutDown()); | |
| return OL_SUCCESS; | |
| } | |
| int main(int argc, char **argv) { | |
| auto Err = printRoot(std::cout); | |
| if (Err) { | |
| std::cerr << "[Liboffload error " << Err->Code << "]: " << Err->Details | |
| << "\n"; | |
| return 1; | |
| } | |
| return 0; | |
| } |