summaryrefslogtreecommitdiff
path: root/offload/liboffload/src/OffloadImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'offload/liboffload/src/OffloadImpl.cpp')
-rw-r--r--offload/liboffload/src/OffloadImpl.cpp451
1 files changed, 368 insertions, 83 deletions
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 457f1053f163..d956d274b5eb 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -19,27 +19,6 @@
#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
@@ -55,6 +34,97 @@ ompt_function_lookup_t lookupCallbackByName = nullptr;
} // namespace llvm::omp::target
#endif
+using namespace llvm::omp::target;
+using namespace llvm::omp::target::plugin;
+
+// Handle type definitions. Ideally these would be 1:1 with the plugins, but
+// we add some additional data here for now to avoid churn in the plugin
+// interface.
+struct ol_device_impl_t {
+ ol_device_impl_t(int DeviceNum, GenericDeviceTy *Device,
+ ol_platform_handle_t Platform)
+ : DeviceNum(DeviceNum), Device(Device), Platform(Platform) {}
+ int DeviceNum;
+ GenericDeviceTy *Device;
+ ol_platform_handle_t Platform;
+};
+
+struct ol_platform_impl_t {
+ ol_platform_impl_t(std::unique_ptr<GenericPluginTy> Plugin,
+ std::vector<ol_device_impl_t> Devices,
+ ol_platform_backend_t BackendType)
+ : Plugin(std::move(Plugin)), Devices(Devices), BackendType(BackendType) {}
+ std::unique_ptr<GenericPluginTy> Plugin;
+ std::vector<ol_device_impl_t> Devices;
+ ol_platform_backend_t BackendType;
+};
+
+struct ol_queue_impl_t {
+ ol_queue_impl_t(__tgt_async_info *AsyncInfo, ol_device_handle_t Device)
+ : AsyncInfo(AsyncInfo), Device(Device) {}
+ __tgt_async_info *AsyncInfo;
+ ol_device_handle_t Device;
+};
+
+struct ol_event_impl_t {
+ ol_event_impl_t(void *EventInfo, ol_queue_handle_t Queue)
+ : EventInfo(EventInfo), Queue(Queue) {}
+ ~ol_event_impl_t() { (void)Queue->Device->Device->destroyEvent(EventInfo); }
+ void *EventInfo;
+ ol_queue_handle_t Queue;
+};
+
+struct ol_program_impl_t {
+ ol_program_impl_t(plugin::DeviceImageTy *Image,
+ std::unique_ptr<llvm::MemoryBuffer> ImageData,
+ const __tgt_device_image &DeviceImage)
+ : Image(Image), ImageData(std::move(ImageData)),
+ DeviceImage(DeviceImage) {}
+ plugin::DeviceImageTy *Image;
+ std::unique_ptr<llvm::MemoryBuffer> ImageData;
+ __tgt_device_image DeviceImage;
+};
+
+namespace llvm {
+namespace offload {
+
+struct AllocInfo {
+ ol_device_handle_t Device;
+ ol_alloc_type_t Type;
+};
+
+using AllocInfoMapT = DenseMap<void *, AllocInfo>;
+AllocInfoMapT &allocInfoMap() {
+ static AllocInfoMapT AllocInfoMap{};
+ return AllocInfoMap;
+}
+
+using PlatformVecT = SmallVector<ol_platform_impl_t, 4>;
+PlatformVecT &Platforms() {
+ static PlatformVecT Platforms;
+ return Platforms;
+}
+
+ol_device_handle_t HostDevice() {
+ // The host platform is always inserted last
+ return &Platforms().back().Devices[0];
+}
+
+template <typename HandleT> ol_impl_result_t olDestroy(HandleT Handle) {
+ delete Handle;
+ return OL_SUCCESS;
+}
+
+constexpr ol_platform_backend_t pluginNameToBackend(StringRef Name) {
+ if (Name == "amdgpu") {
+ return OL_PLATFORM_BACKEND_AMDGPU;
+ } else if (Name == "cuda") {
+ return OL_PLATFORM_BACKEND_CUDA;
+ } else {
+ return OL_PLATFORM_BACKEND_UNKNOWN;
+ }
+}
+
// 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"
@@ -63,26 +133,36 @@ 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()), {}}); \
+ Platforms().emplace_back(ol_platform_impl_t{ \
+ std::unique_ptr<GenericPluginTy>(createPlugin_##Name()), \
+ {}, \
+ pluginNameToBackend(#Name)}); \
} while (false);
#include "Shared/Targets.def"
- // Preemptively initialize all devices in the plugin so we can just return
- // them from deviceGet
+ // Preemptively initialize all devices in the plugin
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});
+ Platform.Devices.emplace_back(ol_device_impl_t{
+ DevNum, &Platform.Plugin->getDevice(DevNum), &Platform});
}
}
}
+ // Add the special host device
+ auto &HostPlatform = Platforms().emplace_back(
+ ol_platform_impl_t{nullptr,
+ {ol_device_impl_t{-1, nullptr, nullptr}},
+ OL_PLATFORM_BACKEND_HOST});
+ HostDevice()->Platform = &HostPlatform;
+
offloadConfig().TracingEnabled = std::getenv("OFFLOAD_TRACE");
+ offloadConfig().ValidationEnabled =
+ !std::getenv("OFFLOAD_DISABLE_VALIDATION");
}
// TODO: We can properly reference count here and manage the resources in a more
@@ -95,36 +175,16 @@ ol_impl_result_t olInit_impl() {
}
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);
+ bool IsHost = Platform->BackendType == OL_PLATFORM_BACKEND_HOST;
switch (PropName) {
case OL_PLATFORM_INFO_NAME:
- return ReturnValue(Platform->Plugin->getName());
+ return ReturnValue(IsHost ? "Host" : Platform->Plugin->getName());
case OL_PLATFORM_INFO_VENDOR_NAME:
// TODO: Implement this
return ReturnValue("Unknown platform vendor");
@@ -135,14 +195,7 @@ ol_impl_result_t olGetPlatformInfoImplDetail(ol_platform_handle_t Platform,
.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);
- }
+ return ReturnValue(Platform->BackendType);
}
default:
return OL_ERRC_INVALID_ENUMERATION;
@@ -165,27 +218,6 @@ ol_impl_result_t olGetPlatformInfoSize_impl(ol_platform_handle_t Platform,
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,
@@ -193,12 +225,12 @@ ol_impl_result_t olGetDeviceInfoImplDetail(ol_device_handle_t Device,
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) {
+ auto GetInfo = [&](std::vector<std::string> Names) {
+ InfoQueueTy DevInfo;
+ if (auto Err = Device->Device->obtainInfoImpl(DevInfo))
+ return std::string("");
+
for (auto Name : Names) {
auto InfoKeyMatches = [&](const InfoQueueTy::InfoQueueEntryTy &Info) {
return Info.Key == Name;
@@ -245,3 +277,256 @@ ol_impl_result_t olGetDeviceInfoSize_impl(ol_device_handle_t Device,
size_t *PropSizeRet) {
return olGetDeviceInfoImplDetail(Device, PropName, 0, nullptr, PropSizeRet);
}
+
+ol_impl_result_t olIterateDevices_impl(ol_device_iterate_cb_t Callback,
+ void *UserData) {
+ for (auto &Platform : Platforms()) {
+ for (auto &Device : Platform.Devices) {
+ if (!Callback(&Device, UserData)) {
+ break;
+ }
+ }
+ }
+
+ return OL_SUCCESS;
+}
+
+TargetAllocTy convertOlToPluginAllocTy(ol_alloc_type_t Type) {
+ switch (Type) {
+ case OL_ALLOC_TYPE_DEVICE:
+ return TARGET_ALLOC_DEVICE;
+ case OL_ALLOC_TYPE_HOST:
+ return TARGET_ALLOC_HOST;
+ case OL_ALLOC_TYPE_MANAGED:
+ default:
+ return TARGET_ALLOC_SHARED;
+ }
+}
+
+ol_impl_result_t olMemAlloc_impl(ol_device_handle_t Device,
+ ol_alloc_type_t Type, size_t Size,
+ void **AllocationOut) {
+ auto Alloc =
+ Device->Device->dataAlloc(Size, nullptr, convertOlToPluginAllocTy(Type));
+ if (!Alloc)
+ return {OL_ERRC_OUT_OF_RESOURCES,
+ formatv("Could not create allocation on device {0}", Device).str()};
+
+ *AllocationOut = *Alloc;
+ allocInfoMap().insert_or_assign(*Alloc, AllocInfo{Device, Type});
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t olMemFree_impl(void *Address) {
+ if (!allocInfoMap().contains(Address))
+ return {OL_ERRC_INVALID_ARGUMENT, "Address is not a known allocation"};
+
+ auto AllocInfo = allocInfoMap().at(Address);
+ auto Device = AllocInfo.Device;
+ auto Type = AllocInfo.Type;
+
+ auto Res =
+ Device->Device->dataDelete(Address, convertOlToPluginAllocTy(Type));
+ if (Res)
+ return {OL_ERRC_OUT_OF_RESOURCES, "Could not free allocation"};
+
+ allocInfoMap().erase(Address);
+
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t olCreateQueue_impl(ol_device_handle_t Device,
+ ol_queue_handle_t *Queue) {
+ auto CreatedQueue = std::make_unique<ol_queue_impl_t>(nullptr, Device);
+ auto Err = Device->Device->initAsyncInfo(&(CreatedQueue->AsyncInfo));
+ if (Err)
+ return {OL_ERRC_UNKNOWN, "Could not initialize stream resource"};
+
+ *Queue = CreatedQueue.release();
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t olDestroyQueue_impl(ol_queue_handle_t Queue) {
+ return olDestroy(Queue);
+}
+
+ol_impl_result_t olWaitQueue_impl(ol_queue_handle_t Queue) {
+ // Host plugin doesn't have a queue set so it's not safe to call synchronize
+ // on it, but we have nothing to synchronize in that situation anyway.
+ if (Queue->AsyncInfo->Queue) {
+ auto Err = Queue->Device->Device->synchronize(Queue->AsyncInfo);
+ if (Err)
+ return {OL_ERRC_INVALID_QUEUE, "The queue failed to synchronize"};
+ }
+
+ // Recreate the stream resource so the queue can be reused
+ // TODO: Would be easier for the synchronization to (optionally) not release
+ // it to begin with.
+ auto Res = Queue->Device->Device->initAsyncInfo(&Queue->AsyncInfo);
+ if (Res)
+ return {OL_ERRC_UNKNOWN, "Could not reinitialize the stream resource"};
+
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t olWaitEvent_impl(ol_event_handle_t Event) {
+ auto Res = Event->Queue->Device->Device->syncEvent(Event->EventInfo);
+ if (Res)
+ return {OL_ERRC_INVALID_EVENT, "The event failed to synchronize"};
+
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t olDestroyEvent_impl(ol_event_handle_t Event) {
+ return olDestroy(Event);
+}
+
+ol_event_handle_t makeEvent(ol_queue_handle_t Queue) {
+ auto EventImpl = std::make_unique<ol_event_impl_t>(nullptr, Queue);
+ auto Res = Queue->Device->Device->createEvent(&EventImpl->EventInfo);
+ if (Res)
+ return nullptr;
+
+ Res = Queue->Device->Device->recordEvent(EventImpl->EventInfo,
+ Queue->AsyncInfo);
+ if (Res)
+ return nullptr;
+
+ return EventImpl.release();
+}
+
+ol_impl_result_t olMemcpy_impl(ol_queue_handle_t Queue, void *DstPtr,
+ ol_device_handle_t DstDevice, void *SrcPtr,
+ ol_device_handle_t SrcDevice, size_t Size,
+ ol_event_handle_t *EventOut) {
+ if (DstDevice == HostDevice() && SrcDevice == HostDevice()) {
+ if (!Queue) {
+ std::memcpy(DstPtr, SrcPtr, Size);
+ return OL_SUCCESS;
+ } else {
+ return {OL_ERRC_INVALID_ARGUMENT,
+ "One of DstDevice and SrcDevice must be a non-host device if "
+ "Queue is specified"};
+ }
+ }
+
+ // If no queue is given the memcpy will be synchronous
+ auto QueueImpl = Queue ? Queue->AsyncInfo : nullptr;
+
+ if (DstDevice == HostDevice()) {
+ auto Res = SrcDevice->Device->dataRetrieve(DstPtr, SrcPtr, Size, QueueImpl);
+ if (Res)
+ return {OL_ERRC_UNKNOWN, "The data retrieve operation failed"};
+ } else if (SrcDevice == HostDevice()) {
+ auto Res = DstDevice->Device->dataSubmit(DstPtr, SrcPtr, Size, QueueImpl);
+ if (Res)
+ return {OL_ERRC_UNKNOWN, "The data submit operation failed"};
+ } else {
+ auto Res = SrcDevice->Device->dataExchange(SrcPtr, *DstDevice->Device,
+ DstPtr, Size, QueueImpl);
+ if (Res)
+ return {OL_ERRC_UNKNOWN, "The data exchange operation failed"};
+ }
+
+ if (EventOut)
+ *EventOut = makeEvent(Queue);
+
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t olCreateProgram_impl(ol_device_handle_t Device,
+ const void *ProgData, size_t ProgDataSize,
+ ol_program_handle_t *Program) {
+ // Make a copy of the program binary in case it is released by the caller.
+ auto ImageData = MemoryBuffer::getMemBufferCopy(
+ StringRef(reinterpret_cast<const char *>(ProgData), ProgDataSize));
+
+ auto DeviceImage = __tgt_device_image{
+ const_cast<char *>(ImageData->getBuffer().data()),
+ const_cast<char *>(ImageData->getBuffer().data()) + ProgDataSize, nullptr,
+ nullptr};
+
+ ol_program_handle_t Prog =
+ new ol_program_impl_t(nullptr, std::move(ImageData), DeviceImage);
+
+ auto Res =
+ Device->Device->loadBinary(Device->Device->Plugin, &Prog->DeviceImage);
+ if (!Res) {
+ delete Prog;
+ return OL_ERRC_INVALID_VALUE;
+ }
+
+ Prog->Image = *Res;
+ *Program = Prog;
+
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t olDestroyProgram_impl(ol_program_handle_t Program) {
+ return olDestroy(Program);
+}
+
+ol_impl_result_t olGetKernel_impl(ol_program_handle_t Program,
+ const char *KernelName,
+ ol_kernel_handle_t *Kernel) {
+
+ auto &Device = Program->Image->getDevice();
+ auto KernelImpl = Device.constructKernel(KernelName);
+ if (!KernelImpl)
+ return OL_ERRC_INVALID_KERNEL_NAME;
+
+ auto Err = KernelImpl->init(Device, *Program->Image);
+ if (Err)
+ return {OL_ERRC_UNKNOWN, "Could not initialize the kernel"};
+
+ *Kernel = &*KernelImpl;
+
+ return OL_SUCCESS;
+}
+
+ol_impl_result_t
+olLaunchKernel_impl(ol_queue_handle_t Queue, ol_device_handle_t Device,
+ ol_kernel_handle_t Kernel, const void *ArgumentsData,
+ size_t ArgumentsSize,
+ const ol_kernel_launch_size_args_t *LaunchSizeArgs,
+ ol_event_handle_t *EventOut) {
+ auto *DeviceImpl = Device->Device;
+ if (Queue && Device != Queue->Device) {
+ return {OL_ERRC_INVALID_DEVICE,
+ "Device specified does not match the device of the given queue"};
+ }
+
+ auto *QueueImpl = Queue ? Queue->AsyncInfo : nullptr;
+ AsyncInfoWrapperTy AsyncInfoWrapper(*DeviceImpl, QueueImpl);
+ KernelArgsTy LaunchArgs{};
+ LaunchArgs.NumTeams[0] = LaunchSizeArgs->NumGroupsX;
+ LaunchArgs.NumTeams[1] = LaunchSizeArgs->NumGroupsY;
+ LaunchArgs.NumTeams[2] = LaunchSizeArgs->NumGroupsZ;
+ LaunchArgs.ThreadLimit[0] = LaunchSizeArgs->GroupSizeX;
+ LaunchArgs.ThreadLimit[1] = LaunchSizeArgs->GroupSizeY;
+ LaunchArgs.ThreadLimit[2] = LaunchSizeArgs->GroupSizeZ;
+ LaunchArgs.DynCGroupMem = LaunchSizeArgs->DynSharedMemory;
+
+ KernelLaunchParamsTy Params;
+ Params.Data = const_cast<void *>(ArgumentsData);
+ Params.Size = ArgumentsSize;
+ LaunchArgs.ArgPtrs = reinterpret_cast<void **>(&Params);
+ // Don't do anything with pointer indirection; use arg data as-is
+ LaunchArgs.Flags.IsCUDA = true;
+
+ auto *KernelImpl = reinterpret_cast<GenericKernelTy *>(Kernel);
+ auto Err = KernelImpl->launch(*DeviceImpl, LaunchArgs.ArgPtrs, nullptr,
+ LaunchArgs, AsyncInfoWrapper);
+
+ AsyncInfoWrapper.finalize(Err);
+ if (Err)
+ return {OL_ERRC_UNKNOWN, "Could not finalize the AsyncInfoWrapper"};
+
+ if (EventOut)
+ *EventOut = makeEvent(Queue);
+
+ return OL_SUCCESS;
+}
+
+} // namespace offload
+} // namespace llvm