| //===- ol_impl.cpp - Implementation of the new LLVM/Offload API ------===// | |
| // | |
| // 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 contains the definitions of the new LLVM/Offload API entry points. See | |
| // new-api/API/README.md for more information. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "OffloadImpl.hpp" | |
| #include "Helpers.hpp" | |
| #include "PluginManager.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include <OffloadAPI.h> | |
| #include <mutex> | |
| using namespace llvm; | |
| using namespace llvm::omp::target::plugin; | |
| // Handle type definitions. Ideally these would be 1:1 with the plugins | |
| struct ol_device_handle_t_ { | |
| int DeviceNum; | |
| GenericDeviceTy &Device; | |
| ol_platform_handle_t Platform; | |
| }; | |
| struct ol_platform_handle_t_ { | |
| std::unique_ptr<GenericPluginTy> Plugin; | |
| std::vector<ol_device_handle_t_> Devices; | |
| }; | |
| using PlatformVecT = SmallVector<ol_platform_handle_t_, 4>; | |
| PlatformVecT &Platforms() { | |
| static PlatformVecT Platforms; | |
| return Platforms; | |
| } | |
| // TODO: Some plugins expect to be linked into libomptarget which defines these | |
| // symbols to implement ompt callbacks. The least invasive workaround here is to | |
| // define them in libLLVMOffload as false/null so they are never used. In future | |
| // it would be better to allow the plugins to implement callbacks without | |
| // pulling in details from libomptarget. | |
| #ifdef OMPT_SUPPORT | |
| namespace llvm::omp::target { | |
| namespace ompt { | |
| bool Initialized = false; | |
| ompt_get_callback_t lookupCallbackByCode = nullptr; | |
| ompt_function_lookup_t lookupCallbackByName = nullptr; | |
| } // namespace ompt | |
| } // namespace llvm::omp::target | |
| #endif | |
| // Every plugin exports this method to create an instance of the plugin type. | |
| #define PLUGIN_TARGET(Name) extern "C" GenericPluginTy *createPlugin_##Name(); | |
| #include "Shared/Targets.def" | |
| void initPlugins() { | |
| // Attempt to create an instance of each supported plugin. | |
| #define PLUGIN_TARGET(Name) \ | |
| do { \ | |
| Platforms().emplace_back(ol_platform_handle_t_{ \ | |
| std::unique_ptr<GenericPluginTy>(createPlugin_##Name()), {}}); \ | |
| } while (false); | |
| #include "Shared/Targets.def" | |
| // Preemptively initialize all devices in the plugin so we can just return | |
| // them from deviceGet | |
| for (auto &Platform : Platforms()) { | |
| auto Err = Platform.Plugin->init(); | |
| [[maybe_unused]] std::string InfoMsg = toString(std::move(Err)); | |
| for (auto DevNum = 0; DevNum < Platform.Plugin->number_of_devices(); | |
| DevNum++) { | |
| if (Platform.Plugin->init_device(DevNum) == OFFLOAD_SUCCESS) { | |
| Platform.Devices.emplace_back(ol_device_handle_t_{ | |
| DevNum, Platform.Plugin->getDevice(DevNum), &Platform}); | |
| } | |
| } | |
| } | |
| offloadConfig().TracingEnabled = std::getenv("OFFLOAD_TRACE"); | |
| } | |
| // TODO: We can properly reference count here and manage the resources in a more | |
| // clever way | |
| ol_impl_result_t olInit_impl() { | |
| static std::once_flag InitFlag; | |
| std::call_once(InitFlag, initPlugins); | |
| return OL_SUCCESS; | |
| } | |
| ol_impl_result_t olShutDown_impl() { return OL_SUCCESS; } | |
| ol_impl_result_t olGetPlatformCount_impl(uint32_t *NumPlatforms) { | |
| *NumPlatforms = Platforms().size(); | |
| return OL_SUCCESS; | |
| } | |
| ol_impl_result_t olGetPlatform_impl(uint32_t NumEntries, | |
| ol_platform_handle_t *PlatformsOut) { | |
| if (NumEntries > Platforms().size()) { | |
| return {OL_ERRC_INVALID_SIZE, | |
| std::string{formatv("{0} platform(s) available but {1} requested.", | |
| Platforms().size(), NumEntries)}}; | |
| } | |
| for (uint32_t PlatformIndex = 0; PlatformIndex < NumEntries; | |
| PlatformIndex++) { | |
| PlatformsOut[PlatformIndex] = &(Platforms())[PlatformIndex]; | |
| } | |
| return OL_SUCCESS; | |
| } | |
| ol_impl_result_t olGetPlatformInfoImplDetail(ol_platform_handle_t Platform, | |
| ol_platform_info_t PropName, | |
| size_t PropSize, void *PropValue, | |
| size_t *PropSizeRet) { | |
| ReturnHelper ReturnValue(PropSize, PropValue, PropSizeRet); | |
| switch (PropName) { | |
| case OL_PLATFORM_INFO_NAME: | |
| return ReturnValue(Platform->Plugin->getName()); | |
| case OL_PLATFORM_INFO_VENDOR_NAME: | |
| // TODO: Implement this | |
| return ReturnValue("Unknown platform vendor"); | |
| case OL_PLATFORM_INFO_VERSION: { | |
| return ReturnValue(formatv("v{0}.{1}.{2}", OL_VERSION_MAJOR, | |
| OL_VERSION_MINOR, OL_VERSION_PATCH) | |
| .str() | |
| .c_str()); | |
| } | |
| case OL_PLATFORM_INFO_BACKEND: { | |
| auto PluginName = Platform->Plugin->getName(); | |
| if (PluginName == StringRef("CUDA")) { | |
| return ReturnValue(OL_PLATFORM_BACKEND_CUDA); | |
| } else if (PluginName == StringRef("AMDGPU")) { | |
| return ReturnValue(OL_PLATFORM_BACKEND_AMDGPU); | |
| } else { | |
| return ReturnValue(OL_PLATFORM_BACKEND_UNKNOWN); | |
| } | |
| } | |
| default: | |
| return OL_ERRC_INVALID_ENUMERATION; | |
| } | |
| return OL_SUCCESS; | |
| } | |
| ol_impl_result_t olGetPlatformInfo_impl(ol_platform_handle_t Platform, | |
| ol_platform_info_t PropName, | |
| size_t PropSize, void *PropValue) { | |
| return olGetPlatformInfoImplDetail(Platform, PropName, PropSize, PropValue, | |
| nullptr); | |
| } | |
| ol_impl_result_t olGetPlatformInfoSize_impl(ol_platform_handle_t Platform, | |
| ol_platform_info_t PropName, | |
| size_t *PropSizeRet) { | |
| return olGetPlatformInfoImplDetail(Platform, PropName, 0, nullptr, | |
| PropSizeRet); | |
| } | |
| ol_impl_result_t olGetDeviceCount_impl(ol_platform_handle_t Platform, | |
| uint32_t *pNumDevices) { | |
| *pNumDevices = static_cast<uint32_t>(Platform->Devices.size()); | |
| return OL_SUCCESS; | |
| } | |
| ol_impl_result_t olGetDevice_impl(ol_platform_handle_t Platform, | |
| uint32_t NumEntries, | |
| ol_device_handle_t *Devices) { | |
| if (NumEntries > Platform->Devices.size()) { | |
| return OL_ERRC_INVALID_SIZE; | |
| } | |
| for (uint32_t DeviceIndex = 0; DeviceIndex < NumEntries; DeviceIndex++) { | |
| Devices[DeviceIndex] = &(Platform->Devices[DeviceIndex]); | |
| } | |
| return OL_SUCCESS; | |
| } | |
| ol_impl_result_t olGetDeviceInfoImplDetail(ol_device_handle_t Device, | |
| ol_device_info_t PropName, | |
| size_t PropSize, void *PropValue, | |
| size_t *PropSizeRet) { | |
| ReturnHelper ReturnValue(PropSize, PropValue, PropSizeRet); | |
| InfoQueueTy DevInfo; | |
| if (auto Err = Device->Device.obtainInfoImpl(DevInfo)) | |
| return OL_ERRC_OUT_OF_RESOURCES; | |
| // Find the info if it exists under any of the given names | |
| auto GetInfo = [&DevInfo](std::vector<std::string> Names) { | |
| for (auto Name : Names) { | |
| auto InfoKeyMatches = [&](const InfoQueueTy::InfoQueueEntryTy &Info) { | |
| return Info.Key == Name; | |
| }; | |
| auto Item = std::find_if(DevInfo.getQueue().begin(), | |
| DevInfo.getQueue().end(), InfoKeyMatches); | |
| if (Item != std::end(DevInfo.getQueue())) { | |
| return Item->Value; | |
| } | |
| } | |
| return std::string(""); | |
| }; | |
| switch (PropName) { | |
| case OL_DEVICE_INFO_PLATFORM: | |
| return ReturnValue(Device->Platform); | |
| case OL_DEVICE_INFO_TYPE: | |
| return ReturnValue(OL_DEVICE_TYPE_GPU); | |
| case OL_DEVICE_INFO_NAME: | |
| return ReturnValue(GetInfo({"Device Name"}).c_str()); | |
| case OL_DEVICE_INFO_VENDOR: | |
| return ReturnValue(GetInfo({"Vendor Name"}).c_str()); | |
| case OL_DEVICE_INFO_DRIVER_VERSION: | |
| return ReturnValue( | |
| GetInfo({"CUDA Driver Version", "HSA Runtime Version"}).c_str()); | |
| default: | |
| return OL_ERRC_INVALID_ENUMERATION; | |
| } | |
| return OL_SUCCESS; | |
| } | |
| ol_impl_result_t olGetDeviceInfo_impl(ol_device_handle_t Device, | |
| ol_device_info_t PropName, | |
| size_t PropSize, void *PropValue) { | |
| return olGetDeviceInfoImplDetail(Device, PropName, PropSize, PropValue, | |
| nullptr); | |
| } | |
| ol_impl_result_t olGetDeviceInfoSize_impl(ol_device_handle_t Device, | |
| ol_device_info_t PropName, | |
| size_t *PropSizeRet) { | |
| return olGetDeviceInfoImplDetail(Device, PropName, 0, nullptr, PropSizeRet); | |
| } |