This patch overhauls the `libomptarget` and plugin interface. Currently, we define a C API and compile each plugin as a separate shared library. Then, `libomptarget` loads these API functions and forwards its internal calls to them. This was originally designed to allow multiple implementations of a library to be live. However, since then no one has used this functionality and it prevents us from using much nicer interfaces. If the old behavior is desired it should instead be implemented as a separate plugin. This patch replaces the `PluginAdaptorTy` interface with the `GenericPluginTy` that is used by the plugins. Each plugin exports a `createPlugin_<name>` function that is used to get the specific implementation. This code is now shared with `libomptarget`. There are some notable improvements to this. 1. Massively improved lifetimes of life runtime objects 2. The plugins can use a C++ interface 3. Global state does not need to be duplicated for each plugin + libomptarget 4. Easier to use and add features and improve error handling 5. Less function call overhead / Improved LTO performance. Additional changes in this plugin are related to contending with the fact that state is now shared. Initialization and deinitialization is now handled correctly and in phase with the underlying runtime, allowing us to actually know when something is getting deallocated. Depends on https://github.com/llvm/llvm-project/pull/86971 https://github.com/llvm/llvm-project/pull/86875 https://github.com/llvm/llvm-project/pull/86868
172 lines
5.6 KiB
C++
172 lines
5.6 KiB
C++
//===-- PluginManager.h - Plugin loading and communication API --*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Declarations for managing devices that are handled by RTL plugins.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef OMPTARGET_PLUGIN_MANAGER_H
|
|
#define OMPTARGET_PLUGIN_MANAGER_H
|
|
|
|
#include "PluginInterface.h"
|
|
|
|
#include "DeviceImage.h"
|
|
#include "ExclusiveAccess.h"
|
|
#include "Shared/APITypes.h"
|
|
#include "Shared/Requirements.h"
|
|
|
|
#include "device.h"
|
|
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/iterator.h"
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "llvm/Support/DynamicLibrary.h"
|
|
#include "llvm/Support/Error.h"
|
|
|
|
#include <cstdint>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
|
|
using GenericPluginTy = llvm::omp::target::plugin::GenericPluginTy;
|
|
|
|
/// Struct for the data required to handle plugins
|
|
struct PluginManager {
|
|
/// Type of the devices container. We hand out DeviceTy& to queries which are
|
|
/// stable addresses regardless if the container changes.
|
|
using DeviceContainerTy = llvm::SmallVector<std::unique_ptr<DeviceTy>>;
|
|
|
|
/// Exclusive accessor type for the device container.
|
|
using ExclusiveDevicesAccessorTy = Accessor<DeviceContainerTy>;
|
|
|
|
PluginManager() {}
|
|
|
|
void init();
|
|
|
|
void deinit();
|
|
|
|
// Register a shared library with all (compatible) RTLs.
|
|
void registerLib(__tgt_bin_desc *Desc);
|
|
|
|
// Unregister a shared library from all RTLs.
|
|
void unregisterLib(__tgt_bin_desc *Desc);
|
|
|
|
void addDeviceImage(__tgt_bin_desc &TgtBinDesc,
|
|
__tgt_device_image &TgtDeviceImage) {
|
|
DeviceImages.emplace_back(
|
|
std::make_unique<DeviceImageTy>(TgtBinDesc, TgtDeviceImage));
|
|
}
|
|
|
|
/// Initialize as many devices as possible for this plugin. Devices that fail
|
|
/// to initialize are ignored.
|
|
void initDevices(GenericPluginTy &RTL);
|
|
|
|
/// Return the device presented to the user as device \p DeviceNo if it is
|
|
/// initialized and ready. Otherwise return an error explaining the problem.
|
|
llvm::Expected<DeviceTy &> getDevice(uint32_t DeviceNo);
|
|
|
|
/// Iterate over all initialized and ready devices registered with this
|
|
/// plugin.
|
|
auto devices(ExclusiveDevicesAccessorTy &DevicesAccessor) {
|
|
return llvm::make_pointee_range(*DevicesAccessor);
|
|
}
|
|
|
|
/// Iterate over all device images registered with this plugin.
|
|
auto deviceImages() { return llvm::make_pointee_range(DeviceImages); }
|
|
|
|
/// Translation table retreived from the binary
|
|
HostEntriesBeginToTransTableTy HostEntriesBeginToTransTable;
|
|
std::mutex TrlTblMtx; ///< For Translation Table
|
|
/// Host offload entries in order of image registration
|
|
llvm::SmallVector<__tgt_offload_entry *> HostEntriesBeginRegistrationOrder;
|
|
|
|
/// Map from ptrs on the host to an entry in the Translation Table
|
|
HostPtrToTableMapTy HostPtrToTableMap;
|
|
std::mutex TblMapMtx; ///< For HostPtrToTableMap
|
|
|
|
// Work around for plugins that call dlopen on shared libraries that call
|
|
// tgt_register_lib during their initialisation. Stash the pointers in a
|
|
// vector until the plugins are all initialised and then register them.
|
|
bool delayRegisterLib(__tgt_bin_desc *Desc) {
|
|
if (RTLsLoaded)
|
|
return false;
|
|
DelayedBinDesc.push_back(Desc);
|
|
return true;
|
|
}
|
|
|
|
void registerDelayedLibraries() {
|
|
// Only called by libomptarget constructor
|
|
RTLsLoaded = true;
|
|
for (auto *Desc : DelayedBinDesc)
|
|
__tgt_register_lib(Desc);
|
|
DelayedBinDesc.clear();
|
|
}
|
|
|
|
/// Return the number of usable devices.
|
|
int getNumDevices() { return getExclusiveDevicesAccessor()->size(); }
|
|
|
|
/// Return an exclusive handle to access the devices container.
|
|
ExclusiveDevicesAccessorTy getExclusiveDevicesAccessor() {
|
|
return Devices.getExclusiveAccessor();
|
|
}
|
|
|
|
int getNumUsedPlugins() const { return DeviceOffsets.size(); }
|
|
|
|
// Initialize all plugins.
|
|
void initAllPlugins();
|
|
|
|
/// Iterator range for all plugins (in use or not, but always valid).
|
|
auto plugins() { return llvm::make_pointee_range(Plugins); }
|
|
|
|
/// Return the user provided requirements.
|
|
int64_t getRequirements() const { return Requirements.getRequirements(); }
|
|
|
|
/// Add \p Flags to the user provided requirements.
|
|
void addRequirements(int64_t Flags) { Requirements.addRequirements(Flags); }
|
|
|
|
private:
|
|
bool RTLsLoaded = false;
|
|
llvm::SmallVector<__tgt_bin_desc *> DelayedBinDesc;
|
|
|
|
// List of all plugins, in use or not.
|
|
llvm::SmallVector<std::unique_ptr<GenericPluginTy>> Plugins;
|
|
|
|
// Mapping of plugins to offsets in the device table.
|
|
llvm::DenseMap<const GenericPluginTy *, int32_t> DeviceOffsets;
|
|
|
|
// Mapping of plugins to the number of used devices.
|
|
llvm::DenseMap<const GenericPluginTy *, int32_t> DeviceUsed;
|
|
|
|
// Set of all device images currently in use.
|
|
llvm::DenseSet<const __tgt_device_image *> UsedImages;
|
|
|
|
/// Executable images and information extracted from the input images passed
|
|
/// to the runtime.
|
|
llvm::SmallVector<std::unique_ptr<DeviceImageTy>> DeviceImages;
|
|
|
|
/// The user provided requirements.
|
|
RequirementCollection Requirements;
|
|
|
|
std::mutex RTLsMtx; ///< For RTLs
|
|
|
|
/// Devices associated with plugins, accesses to the container are exclusive.
|
|
ProtectedObj<DeviceContainerTy> Devices;
|
|
};
|
|
|
|
/// Initialize the plugin manager and OpenMP runtime.
|
|
void initRuntime();
|
|
|
|
/// Deinitialize the plugin and delete it.
|
|
void deinitRuntime();
|
|
|
|
extern PluginManager *PM;
|
|
|
|
#endif // OMPTARGET_PLUGIN_MANAGER_H
|