brintos

brintos / llvm-project-archived public Read only

0
0
Text · 68.9 KiB · 1d52c96 Raw
1790 lines · c
1//===- PluginInterface.h - Target independent plugin device interface -----===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9//===----------------------------------------------------------------------===//10 11#ifndef OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_PLUGININTERFACE_H12#define OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_PLUGININTERFACE_H13 14#include <cstddef>15#include <cstdint>16#include <deque>17#include <list>18#include <map>19#include <shared_mutex>20#include <variant>21#include <vector>22 23#include "ExclusiveAccess.h"24#include "OpenMP/InteropAPI.h"25#include "Shared/APITypes.h"26#include "Shared/Debug.h"27#include "Shared/Environment.h"28#include "Shared/EnvironmentVar.h"29#include "Shared/Requirements.h"30#include "Shared/Utils.h"31 32#include "GlobalHandler.h"33#include "JIT.h"34#include "MemoryManager.h"35#include "OffloadError.h"36#include "RPC.h"37#include "omptarget.h"38 39#ifdef OMPT_SUPPORT40#include "omp-tools.h"41#endif42 43#include "llvm/ADT/SmallVector.h"44#include "llvm/Frontend/OpenMP/OMPConstants.h"45#include "llvm/Frontend/OpenMP/OMPGridValues.h"46#include "llvm/Support/Allocator.h"47#include "llvm/Support/Error.h"48#include "llvm/Support/ErrorHandling.h"49#include "llvm/Support/MemoryBufferRef.h"50#include "llvm/Support/raw_ostream.h"51#include "llvm/TargetParser/Triple.h"52 53namespace llvm {54namespace omp {55namespace target {56 57namespace plugin {58 59struct GenericPluginTy;60struct GenericKernelTy;61struct GenericDeviceTy;62struct RecordReplayTy;63template <typename ResourceRef> class GenericDeviceResourceManagerTy;64 65namespace Plugin {66/// Create a success error. This is the same as calling Error::success(), but67/// it is recommended to use this one for consistency with Plugin::error() and68/// Plugin::check().69static inline Error success() { return Error::success(); }70 71/// Create an Offload error.72template <typename... ArgsTy>73static Error error(error::ErrorCode Code, const char *ErrFmt, ArgsTy... Args) {74  return error::createOffloadError(Code, ErrFmt, Args...);75}76 77inline Error error(error::ErrorCode Code, const char *S) {78  return make_error<error::OffloadError>(Code, S);79}80 81inline Error error(error::ErrorCode Code, Error &&OtherError,82                   const char *Context) {83  return error::createOffloadError(Code, std::move(OtherError), Context);84}85 86/// Check the plugin-specific error code and return an error or success87/// accordingly. In case of an error, create a string error with the error88/// description. The ErrFmt should follow the format:89///     "Error in <function name>[<optional info>]: %s"90/// The last format specifier "%s" is mandatory and will be used to place the91/// error code's description. Notice this function should be only called from92/// the plugin-specific code.93/// TODO: Refactor this, must be defined individually by each plugin.94template <typename... ArgsTy>95static Error check(int32_t ErrorCode, const char *ErrFmt, ArgsTy... Args);96} // namespace Plugin97 98/// Class that wraps the __tgt_async_info to simply its usage. In case the99/// object is constructed without a valid __tgt_async_info, the object will use100/// an internal one and will synchronize the current thread with the pending101/// operations when calling AsyncInfoWrapperTy::finalize(). This latter function102/// must be called before destroying the wrapper object.103struct AsyncInfoWrapperTy {104  AsyncInfoWrapperTy(GenericDeviceTy &Device, __tgt_async_info *AsyncInfoPtr);105 106  ~AsyncInfoWrapperTy() {107    assert(!AsyncInfoPtr && "AsyncInfoWrapperTy not finalized");108  }109 110  /// Get the raw __tgt_async_info pointer.111  operator __tgt_async_info *() const { return AsyncInfoPtr; }112 113  /// Indicate whether there is queue.114  bool hasQueue() const { return (AsyncInfoPtr->Queue != nullptr); }115 116  /// Get the queue.117  template <typename Ty> Ty getQueueAs() {118    static_assert(sizeof(Ty) == sizeof(AsyncInfoPtr->Queue),119                  "Queue is not of the same size as target type");120    return static_cast<Ty>(AsyncInfoPtr->Queue);121  }122 123  /// Set the queue.124  template <typename Ty> void setQueueAs(Ty Queue) {125    static_assert(sizeof(Ty) == sizeof(AsyncInfoPtr->Queue),126                  "Queue is not of the same size as target type");127    assert(!AsyncInfoPtr->Queue && "Overwriting queue");128    AsyncInfoPtr->Queue = Queue;129  }130 131  /// Get the queue, using the provided resource manager to initialise it if it132  /// doesn't exist.133  template <typename Ty, typename RMTy>134  Expected<Ty>135  getOrInitQueue(GenericDeviceResourceManagerTy<RMTy> &ResourceManager) {136    std::lock_guard<std::mutex> Lock(AsyncInfoPtr->Mutex);137    if (!AsyncInfoPtr->Queue) {138      if (auto Err = ResourceManager.getResource(139              *reinterpret_cast<Ty *>(&AsyncInfoPtr->Queue)))140        return Err;141    }142    return getQueueAs<Ty>();143  }144 145  /// Synchronize with the __tgt_async_info's pending operations if it's the146  /// internal async info. The error associated to the asynchronous operations147  /// issued in this queue must be provided in \p Err. This function will update148  /// the error parameter with the result of the synchronization if it was149  /// actually executed. This function must be called before destroying the150  /// object and only once.151  void finalize(Error &Err);152 153  /// Register \p Ptr as an associated allocation that is freed after154  /// finalization.155  void freeAllocationAfterSynchronization(void *Ptr) {156    std::lock_guard<std::mutex> AllocationGuard(AsyncInfoPtr->Mutex);157    AsyncInfoPtr->AssociatedAllocations.push_back(Ptr);158  }159 160private:161  GenericDeviceTy &Device;162  __tgt_async_info LocalAsyncInfo;163  __tgt_async_info *AsyncInfoPtr;164};165 166enum class DeviceInfo {167#define OFFLOAD_DEVINFO(Name, _, Value) Name = Value,168#include "OffloadInfo.inc"169#undef OFFLOAD_DEVINFO170};171 172/// Tree node for device information173///174/// This information is either printed or used by liboffload to extract certain175/// device queries. Each property has an optional key, an optional value176/// and optional children. The children can be used to store additional177/// information (such as x, y and z components of ranges).178struct InfoTreeNode {179  static constexpr uint64_t IndentSize = 4;180 181  std::string Key;182  using VariantType = std::variant<uint64_t, std::string, bool, std::monostate>;183  VariantType Value;184  std::string Units;185  // Need to specify a default value number of elements here as `InfoTreeNode`'s186  // size is unknown. This is a vector (rather than a Key->Value map) since:187  // * The keys need to be owned and thus `std::string`s188  // * The order of keys is important189  // * The same key can appear multiple times190  std::unique_ptr<llvm::SmallVector<InfoTreeNode, 8>> Children;191 192  llvm::DenseMap<DeviceInfo, size_t> DeviceInfoMap;193 194  InfoTreeNode() : InfoTreeNode("", std::monostate{}, "") {}195  InfoTreeNode(std::string Key, VariantType Value, std::string Units)196      : Key(std::move(Key)), Value(Value), Units(std::move(Units)) {}197 198  /// Add a new info entry as a child of this node. The entry requires at least199  /// a key string in \p Key. The value in \p Value is optional and can be any200  /// type that is representable as a string. The units in \p Units is optional201  /// and must be a string. Providing a device info key allows liboffload to202  /// use that value for an appropriate olGetDeviceInfo query203  template <typename T = std::monostate>204  InfoTreeNode *add(std::string Key, T Value = T(),205                    std::string Units = std::string(),206                    std::optional<DeviceInfo> DeviceInfoKey = std::nullopt) {207    assert(!Key.empty() && "Invalid info key");208 209    if (!Children)210      Children = std::make_unique<llvm::SmallVector<InfoTreeNode, 8>>();211 212    VariantType ValueVariant;213    if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, std::monostate>)214      ValueVariant = Value;215    else if constexpr (std::is_arithmetic_v<T>)216      ValueVariant = static_cast<uint64_t>(Value);217    else218      ValueVariant = std::string{Value};219 220    auto Ptr =221        &Children->emplace_back(std::move(Key), ValueVariant, std::move(Units));222 223    if (DeviceInfoKey)224      DeviceInfoMap[*DeviceInfoKey] = Children->size() - 1;225 226    return Ptr;227  }228 229  std::optional<InfoTreeNode *> get(StringRef Key) {230    if (!Children)231      return std::nullopt;232 233    auto It = std::find_if(Children->begin(), Children->end(),234                           [&](auto &V) { return V.Key == Key; });235    if (It == Children->end())236      return std::nullopt;237    return It;238  }239 240  std::optional<InfoTreeNode *> get(DeviceInfo Info) {241    auto Result = DeviceInfoMap.find(Info);242    if (Result != DeviceInfoMap.end())243      return &(*Children)[Result->second];244    return std::nullopt;245  }246 247  /// Print all info entries in the tree248  void print() const {249    // Fake an additional indent so that values are offset from the keys250    doPrint(0, maxKeySize(1));251  }252 253private:254  void doPrint(int Level, uint64_t MaxKeySize) const {255    if (Key.size()) {256      // Compute the indentations for the current entry.257      uint64_t KeyIndentSize = Level * IndentSize;258      uint64_t ValIndentSize =259          MaxKeySize - (Key.size() + KeyIndentSize) + IndentSize;260 261      llvm::outs() << std::string(KeyIndentSize, ' ') << Key262                   << std::string(ValIndentSize, ' ');263      std::visit(264          [](auto &&V) {265            using T = std::decay_t<decltype(V)>;266            if constexpr (std::is_same_v<T, std::string>)267              llvm::outs() << V;268            else if constexpr (std::is_same_v<T, bool>)269              llvm::outs() << (V ? "Yes" : "No");270            else if constexpr (std::is_same_v<T, uint64_t>)271              llvm::outs() << V;272            else if constexpr (std::is_same_v<T, std::monostate>) {273              // Do nothing274            } else275              static_assert(false, "doPrint visit not exhaustive");276          },277          Value);278      llvm::outs() << (Units.empty() ? "" : " ") << Units << "\n";279    }280 281    // Print children282    if (Children)283      for (const auto &Entry : *Children)284        Entry.doPrint(Level + 1, MaxKeySize);285  }286 287  // Recursively calculates the maximum width of each key, including indentation288  uint64_t maxKeySize(int Level) const {289    uint64_t MaxKeySize = 0;290 291    if (Children)292      for (const auto &Entry : *Children) {293        uint64_t KeySize = Entry.Key.size() + Level * IndentSize;294        MaxKeySize = std::max(MaxKeySize, KeySize);295        MaxKeySize = std::max(MaxKeySize, Entry.maxKeySize(Level + 1));296      }297 298    return MaxKeySize;299  }300};301 302/// Class wrapping a __tgt_device_image and its offload entry table on a303/// specific device. This class is responsible for storing and managing304/// the offload entries for an image on a device.305class DeviceImageTy {306  /// Image identifier within the corresponding device. Notice that this id is307  /// not unique between different device; they may overlap.308  int32_t ImageId;309 310  /// The managed image data.311  std::unique_ptr<MemoryBuffer> Image;312 313  /// Reference to the device this image is loaded on.314  GenericDeviceTy &Device;315 316public:317  virtual ~DeviceImageTy() = default;318 319  DeviceImageTy(int32_t Id, GenericDeviceTy &Device,320                std::unique_ptr<MemoryBuffer> &&Image)321      : ImageId(Id), Image(std::move(Image)), Device(Device) {}322 323  /// Get the image identifier within the device.324  int32_t getId() const { return ImageId; }325 326  /// Get the device that this image is loaded onto.327  GenericDeviceTy &getDevice() const { return Device; }328 329  /// Get the image starting address.330  const void *getStart() const { return Image->getBufferStart(); }331 332  /// Get the image size.333  size_t getSize() const { return Image->getBufferSize(); }334 335  /// Get a memory buffer reference to the whole image.336  MemoryBufferRef getMemoryBuffer() const {337    return MemoryBufferRef(StringRef((const char *)getStart(), getSize()),338                           "Image");339  }340};341 342/// Class implementing common functionalities of offload kernels. Each plugin343/// should define the specific kernel class, derive from this generic one, and344/// implement the necessary virtual function members.345struct GenericKernelTy {346  /// Construct a kernel with a name and a execution mode.347  GenericKernelTy(const char *Name)348      : Name(Name), PreferredNumThreads(0), MaxNumThreads(0) {}349 350  virtual ~GenericKernelTy() {}351 352  /// Initialize the kernel object from a specific device.353  Error init(GenericDeviceTy &GenericDevice, DeviceImageTy &Image);354  virtual Error initImpl(GenericDeviceTy &GenericDevice,355                         DeviceImageTy &Image) = 0;356 357  /// Launch the kernel on the specific device. The device must be the same358  /// one used to initialize the kernel.359  Error launch(GenericDeviceTy &GenericDevice, void **ArgPtrs,360               ptrdiff_t *ArgOffsets, KernelArgsTy &KernelArgs,361               AsyncInfoWrapperTy &AsyncInfoWrapper) const;362  virtual Error launchImpl(GenericDeviceTy &GenericDevice,363                           uint32_t NumThreads[3], uint32_t NumBlocks[3],364                           KernelArgsTy &KernelArgs,365                           KernelLaunchParamsTy LaunchParams,366                           AsyncInfoWrapperTy &AsyncInfoWrapper) const = 0;367 368  virtual Expected<uint64_t> maxGroupSize(GenericDeviceTy &GenericDevice,369                                          uint64_t DynamicMemSize) const = 0;370 371  /// Get the kernel name.372  const char *getName() const { return Name.c_str(); }373 374  /// Get the kernel image.375  DeviceImageTy &getImage() const {376    assert(ImagePtr && "Kernel is not initialized!");377    return *ImagePtr;378  }379 380  /// Return the kernel environment object for kernel \p Name.381  const KernelEnvironmentTy &getKernelEnvironmentForKernel() {382    return KernelEnvironment;383  }384 385  /// Return a device pointer to a new kernel launch environment.386  Expected<KernelLaunchEnvironmentTy *>387  getKernelLaunchEnvironment(GenericDeviceTy &GenericDevice, uint32_t Version,388                             AsyncInfoWrapperTy &AsyncInfo) const;389 390  /// Indicate whether an execution mode is valid.391  static bool isValidExecutionMode(OMPTgtExecModeFlags ExecutionMode) {392    switch (ExecutionMode) {393    case OMP_TGT_EXEC_MODE_BARE:394    case OMP_TGT_EXEC_MODE_SPMD:395    case OMP_TGT_EXEC_MODE_GENERIC:396    case OMP_TGT_EXEC_MODE_GENERIC_SPMD:397    case OMP_TGT_EXEC_MODE_SPMD_NO_LOOP:398      return true;399    }400    return false;401  }402 403protected:404  /// Get the execution mode name of the kernel.405  const char *getExecutionModeName() const {406    switch (KernelEnvironment.Configuration.ExecMode) {407    case OMP_TGT_EXEC_MODE_BARE:408      return "BARE";409    case OMP_TGT_EXEC_MODE_SPMD:410      return "SPMD";411    case OMP_TGT_EXEC_MODE_GENERIC:412      return "Generic";413    case OMP_TGT_EXEC_MODE_GENERIC_SPMD:414      return "Generic-SPMD";415    case OMP_TGT_EXEC_MODE_SPMD_NO_LOOP:416      return "SPMD-No-Loop";417    }418    llvm_unreachable("Unknown execution mode!");419  }420 421  /// Prints generic kernel launch information.422  Error printLaunchInfo(GenericDeviceTy &GenericDevice,423                        KernelArgsTy &KernelArgs, uint32_t NumThreads[3],424                        uint32_t NumBlocks[3]) const;425 426  /// Prints plugin-specific kernel launch information after generic kernel427  /// launch information428  virtual Error printLaunchInfoDetails(GenericDeviceTy &GenericDevice,429                                       KernelArgsTy &KernelArgs,430                                       uint32_t NumThreads[3],431                                       uint32_t NumBlocks[3]) const;432 433private:434  /// Prepare the arguments before launching the kernel.435  KernelLaunchParamsTy436  prepareArgs(GenericDeviceTy &GenericDevice, void **ArgPtrs,437              ptrdiff_t *ArgOffsets, uint32_t &NumArgs,438              llvm::SmallVectorImpl<void *> &Args,439              llvm::SmallVectorImpl<void *> &Ptrs,440              KernelLaunchEnvironmentTy *KernelLaunchEnvironment) const;441 442  /// Get the number of threads and blocks for the kernel based on the443  /// user-defined threads and block clauses.444  uint32_t getNumThreads(GenericDeviceTy &GenericDevice,445                         uint32_t ThreadLimitClause[3]) const;446 447  /// The number of threads \p NumThreads can be adjusted by this method.448  /// \p IsNumThreadsFromUser is true is \p NumThreads is defined by user via449  /// thread_limit clause.450  uint32_t getNumBlocks(GenericDeviceTy &GenericDevice,451                        uint32_t BlockLimitClause[3], uint64_t LoopTripCount,452                        uint32_t &NumThreads, bool IsNumThreadsFromUser) const;453 454  /// Indicate if the kernel works in Generic SPMD, Generic, No-Loop455  /// or SPMD mode.456  bool isGenericSPMDMode() const {457    return KernelEnvironment.Configuration.ExecMode ==458           OMP_TGT_EXEC_MODE_GENERIC_SPMD;459  }460  bool isGenericMode() const {461    return KernelEnvironment.Configuration.ExecMode ==462           OMP_TGT_EXEC_MODE_GENERIC;463  }464  bool isSPMDMode() const {465    return KernelEnvironment.Configuration.ExecMode == OMP_TGT_EXEC_MODE_SPMD;466  }467  bool isBareMode() const {468    return KernelEnvironment.Configuration.ExecMode == OMP_TGT_EXEC_MODE_BARE;469  }470  bool isNoLoopMode() const {471    return KernelEnvironment.Configuration.ExecMode ==472           OMP_TGT_EXEC_MODE_SPMD_NO_LOOP;473  }474 475  /// The kernel name.476  std::string Name;477 478  /// The image that contains this kernel.479  DeviceImageTy *ImagePtr = nullptr;480 481protected:482  /// The preferred number of threads to run the kernel.483  uint32_t PreferredNumThreads;484 485  /// The maximum number of threads which the kernel could leverage.486  uint32_t MaxNumThreads;487 488  /// The kernel environment, including execution flags.489  KernelEnvironmentTy KernelEnvironment;490 491  /// The prototype kernel launch environment.492  KernelLaunchEnvironmentTy KernelLaunchEnvironment;493};494 495/// Information about an allocation, when it has been allocated, and when/if it496/// has been deallocated, for error reporting purposes.497struct AllocationTraceInfoTy {498 499  /// The stack trace of the allocation itself.500  std::string AllocationTrace;501 502  /// The stack trace of the deallocation, or empty.503  std::string DeallocationTrace;504 505  /// The allocated device pointer.506  void *DevicePtr = nullptr;507 508  /// The corresponding host pointer (can be null).509  void *HostPtr = nullptr;510 511  /// The size of the allocation.512  uint64_t Size = 0;513 514  /// The kind of the allocation.515  TargetAllocTy Kind = TargetAllocTy::TARGET_ALLOC_DEFAULT;516 517  /// Information about the last allocation at this address, if any.518  AllocationTraceInfoTy *LastAllocationInfo = nullptr;519 520  /// Lock to keep accesses race free.521  std::mutex Lock;522};523 524/// Information about an allocation, when it has been allocated, and when/if it525/// has been deallocated, for error reporting purposes.526struct KernelTraceInfoTy {527 528  /// The launched kernel.529  GenericKernelTy *Kernel;530 531  /// The stack trace of the launch itself.532  std::string LaunchTrace;533 534  /// The async info the kernel was launched in.535  __tgt_async_info *AsyncInfo;536};537 538struct KernelTraceInfoRecordTy {539  KernelTraceInfoRecordTy() { KTIs.fill({}); }540 541  /// Return the (maximal) record size.542  auto size() const { return KTIs.size(); }543 544  /// Create a new kernel trace info and add it into the record.545  void emplace(GenericKernelTy *Kernel, const std::string &&StackTrace,546               __tgt_async_info *AsyncInfo) {547    KTIs[Idx] = {Kernel, std::move(StackTrace), AsyncInfo};548    Idx = (Idx + 1) % size();549  }550 551  /// Return the \p I'th last kernel trace info.552  auto getKernelTraceInfo(int32_t I) const {553    // Note that kernel trace infos "grow forward", so lookup is backwards.554    return KTIs[(Idx - I - 1 + size()) % size()];555  }556 557private:558  std::array<KernelTraceInfoTy, 8> KTIs;559  unsigned Idx = 0;560};561 562/// Class representing a map of host pinned allocations. We track these pinned563/// allocations, so memory transfers involving these buffers can be optimized.564class PinnedAllocationMapTy {565 566  /// Struct representing a map entry.567  struct EntryTy {568    /// The host pointer of the pinned allocation.569    void *HstPtr;570 571    /// The pointer that devices' driver should use to transfer data from/to the572    /// pinned allocation. In most plugins, this pointer will be the same as the573    /// host pointer above.574    void *DevAccessiblePtr;575 576    /// The size of the pinned allocation.577    size_t Size;578 579    /// Indicate whether the allocation was locked from outside the plugin, for580    /// instance, from the application. The externally locked allocations are581    /// not unlocked by the plugin when unregistering the last user.582    bool ExternallyLocked;583 584    /// The number of references to the pinned allocation. The allocation should585    /// remain pinned and registered to the map until the number of references586    /// becomes zero.587    mutable size_t References;588 589    /// Create an entry with the host and device accessible pointers, the buffer590    /// size, and a boolean indicating whether the buffer was locked externally.591    EntryTy(void *HstPtr, void *DevAccessiblePtr, size_t Size,592            bool ExternallyLocked)593        : HstPtr(HstPtr), DevAccessiblePtr(DevAccessiblePtr), Size(Size),594          ExternallyLocked(ExternallyLocked), References(1) {}595 596    /// Utility constructor used for std::set searches.597    EntryTy(void *HstPtr)598        : HstPtr(HstPtr), DevAccessiblePtr(nullptr), Size(0),599          ExternallyLocked(false), References(0) {}600  };601 602  /// Comparator of mep entries. Use the host pointer to enforce an order603  /// between entries.604  struct EntryCmpTy {605    bool operator()(const EntryTy &Left, const EntryTy &Right) const {606      return Left.HstPtr < Right.HstPtr;607    }608  };609 610  typedef std::set<EntryTy, EntryCmpTy> PinnedAllocSetTy;611 612  /// The map of host pinned allocations.613  PinnedAllocSetTy Allocs;614 615  /// The mutex to protect accesses to the map.616  mutable std::shared_mutex Mutex;617 618  /// Reference to the corresponding device.619  GenericDeviceTy &Device;620 621  /// Indicate whether mapped host buffers should be locked automatically.622  bool LockMappedBuffers;623 624  /// Indicate whether failures when locking mapped buffers should be ignored.625  bool IgnoreLockMappedFailures;626 627  /// Find an allocation that intersects with \p HstPtr pointer. Assume the628  /// map's mutex is acquired.629  const EntryTy *findIntersecting(const void *HstPtr) const {630    if (Allocs.empty())631      return nullptr;632 633    // Search the first allocation with starting address that is not less than634    // the buffer address.635    auto It = Allocs.lower_bound({const_cast<void *>(HstPtr)});636 637    // Direct match of starting addresses.638    if (It != Allocs.end() && It->HstPtr == HstPtr)639      return &(*It);640 641    // Not direct match but may be a previous pinned allocation in the map which642    // contains the buffer. Return false if there is no such a previous643    // allocation.644    if (It == Allocs.begin())645      return nullptr;646 647    // Move to the previous pinned allocation.648    --It;649 650    // The buffer is not contained in the pinned allocation.651    if (utils::advancePtr(It->HstPtr, It->Size) > HstPtr)652      return &(*It);653 654    // None found.655    return nullptr;656  }657 658  /// Insert an entry to the map representing a locked buffer. The number of659  /// references is set to one.660  Error insertEntry(void *HstPtr, void *DevAccessiblePtr, size_t Size,661                    bool ExternallyLocked = false);662 663  /// Erase an existing entry from the map.664  Error eraseEntry(const EntryTy &Entry);665 666  /// Register a new user into an entry that represents a locked buffer. Check667  /// also that the registered buffer with \p HstPtr address and \p Size is668  /// actually contained into the entry.669  Error registerEntryUse(const EntryTy &Entry, void *HstPtr, size_t Size);670 671  /// Unregister a user from the entry and return whether it is the last user.672  /// If it is the last user, the entry will have to be removed from the map673  /// and unlock the entry's host buffer (if necessary).674  Expected<bool> unregisterEntryUse(const EntryTy &Entry);675 676  /// Indicate whether the first range A fully contains the second range B.677  static bool contains(void *PtrA, size_t SizeA, void *PtrB, size_t SizeB) {678    void *EndA = utils::advancePtr(PtrA, SizeA);679    void *EndB = utils::advancePtr(PtrB, SizeB);680    return (PtrB >= PtrA && EndB <= EndA);681  }682 683  /// Indicate whether the first range A intersects with the second range B.684  static bool intersects(void *PtrA, size_t SizeA, void *PtrB, size_t SizeB) {685    void *EndA = utils::advancePtr(PtrA, SizeA);686    void *EndB = utils::advancePtr(PtrB, SizeB);687    return (PtrA < EndB && PtrB < EndA);688  }689 690public:691  /// Create the map of pinned allocations corresponding to a specific device.692  PinnedAllocationMapTy(GenericDeviceTy &Device) : Device(Device) {693 694    // Envar that indicates whether mapped host buffers should be locked695    // automatically. The possible values are boolean (on/off) and a special:696    //   off:       Mapped host buffers are not locked.697    //   on:        Mapped host buffers are locked in a best-effort approach.698    //              Failure to lock the buffers are silent.699    //   mandatory: Mapped host buffers are always locked and failures to lock700    //              a buffer results in a fatal error.701    StringEnvar OMPX_LockMappedBuffers("LIBOMPTARGET_LOCK_MAPPED_HOST_BUFFERS",702                                       "off");703 704    bool Enabled;705    if (StringParser::parse(OMPX_LockMappedBuffers.get().data(), Enabled)) {706      // Parsed as a boolean value. Enable the feature if necessary.707      LockMappedBuffers = Enabled;708      IgnoreLockMappedFailures = true;709    } else if (OMPX_LockMappedBuffers.get() == "mandatory") {710      // Enable the feature and failures are fatal.711      LockMappedBuffers = true;712      IgnoreLockMappedFailures = false;713    } else {714      // Disable by default.715      DP("Invalid value LIBOMPTARGET_LOCK_MAPPED_HOST_BUFFERS=%s\n",716         OMPX_LockMappedBuffers.get().data());717      LockMappedBuffers = false;718    }719  }720 721  /// Register a buffer that was recently allocated as a locked host buffer.722  /// None of the already registered pinned allocations should intersect with723  /// this new one. The registration requires the host pointer in \p HstPtr,724  /// the device accessible pointer in \p DevAccessiblePtr, and the size of the725  /// allocation in \p Size. The allocation must be unregistered using the726  /// unregisterHostBuffer function.727  Error registerHostBuffer(void *HstPtr, void *DevAccessiblePtr, size_t Size);728 729  /// Unregister a host pinned allocation passing the host pointer which was730  /// previously registered using the registerHostBuffer function. When calling731  /// this function, the pinned allocation cannot have any other user and will732  /// not be unlocked by this function.733  Error unregisterHostBuffer(void *HstPtr);734 735  /// Lock the host buffer at \p HstPtr or register a new user if it intersects736  /// with an already existing one. A partial overlapping with extension is not737  /// allowed. The function returns the device accessible pointer of the pinned738  /// buffer. The buffer must be unlocked using the unlockHostBuffer function.739  Expected<void *> lockHostBuffer(void *HstPtr, size_t Size);740 741  /// Unlock the host buffer at \p HstPtr or unregister a user if other users742  /// are still using the pinned allocation. If this was the last user, the743  /// pinned allocation is removed from the map and the memory is unlocked.744  Error unlockHostBuffer(void *HstPtr);745 746  /// Lock or register a host buffer that was recently mapped by libomptarget.747  /// This behavior is applied if LIBOMPTARGET_LOCK_MAPPED_HOST_BUFFERS is748  /// enabled. Even if not enabled, externally locked buffers are registered749  /// in order to optimize their transfers.750  Error lockMappedHostBuffer(void *HstPtr, size_t Size);751 752  /// Unlock or unregister a host buffer that was unmapped by libomptarget.753  Error unlockUnmappedHostBuffer(void *HstPtr);754 755  /// Return the device accessible pointer associated to the host pinned756  /// allocation which the \p HstPtr belongs, if any. Return null in case the757  /// \p HstPtr does not belong to any host pinned allocation. The device758  /// accessible pointer is the one that devices should use for data transfers759  /// that involve a host pinned buffer.760  void *getDeviceAccessiblePtrFromPinnedBuffer(const void *HstPtr) const {761    std::shared_lock<std::shared_mutex> Lock(Mutex);762 763    // Find the intersecting allocation if any.764    const EntryTy *Entry = findIntersecting(HstPtr);765    if (!Entry)766      return nullptr;767 768    return utils::advancePtr(Entry->DevAccessiblePtr,769                             utils::getPtrDiff(HstPtr, Entry->HstPtr));770  }771 772  /// Check whether a buffer belongs to a registered host pinned allocation.773  bool isHostPinnedBuffer(const void *HstPtr) const {774    std::shared_lock<std::shared_mutex> Lock(Mutex);775 776    // Return whether there is an intersecting allocation.777    return (findIntersecting(const_cast<void *>(HstPtr)) != nullptr);778  }779};780 781/// Class implementing common functionalities of offload devices. Each plugin782/// should define the specific device class, derive from this generic one, and783/// implement the necessary virtual function members.784struct GenericDeviceTy : public DeviceAllocatorTy {785  /// Construct a device with its device id within the plugin, the number of786  /// devices in the plugin and the grid values for that kind of device.787  GenericDeviceTy(GenericPluginTy &Plugin, int32_t DeviceId, int32_t NumDevices,788                  const llvm::omp::GV &GridValues);789 790  /// Get the device identifier within the corresponding plugin. Notice that791  /// this id is not unique between different plugins; they may overlap.792  int32_t getDeviceId() const { return DeviceId; }793 794  /// Get the unique identifier of the device.795  const char *getDeviceUid() const { return DeviceUid.c_str(); }796 797  /// Get the total shared memory per block (in bytes) that can be used in any798  /// kernel.799  size_t getMaxBlockSharedMemSize() const { return MaxBlockSharedMemSize; }800 801  /// Set the context of the device if needed, before calling device-specific802  /// functions. Plugins may implement this function as a no-op if not needed.803  virtual Error setContext() = 0;804 805  /// Initialize the device. After this call, the device should be already806  /// working and ready to accept queries or modifications.807  Error init(GenericPluginTy &Plugin);808  virtual Error initImpl(GenericPluginTy &Plugin) = 0;809 810  /// Deinitialize the device and free all its resources. After this call, the811  /// device is no longer considered ready, so no queries or modifications are812  /// allowed.813  Error deinit(GenericPluginTy &Plugin);814  virtual Error deinitImpl() = 0;815 816  /// Load the binary image into the device and return the target table.817  Expected<DeviceImageTy *> loadBinary(GenericPluginTy &Plugin,818                                       StringRef TgtImage);819  virtual Expected<DeviceImageTy *>820  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId) = 0;821 822  /// Unload a previously loaded Image from the device823  Error unloadBinary(DeviceImageTy *Image);824  virtual Error unloadBinaryImpl(DeviceImageTy *Image) = 0;825 826  // Setup the RPC server for this device if needed. This may not run on some827  // plugins like the CPU targets. By default, it will not be executed so it is828  // up to the target to override this using the shouldSetupRPCServer function.829  Error setupRPCServer(GenericPluginTy &Plugin, DeviceImageTy &Image);830 831  /// Synchronize the current thread with the pending operations on the832  /// __tgt_async_info structure. If ReleaseQueue is false, then the833  // underlying queue will not be released. In this case, additional834  // work may be submitted to the queue whilst a synchronize is running.835  Error synchronize(__tgt_async_info *AsyncInfo, bool ReleaseQueue = true);836  virtual Error synchronizeImpl(__tgt_async_info &AsyncInfo,837                                bool ReleaseQueue) = 0;838 839  /// Invokes any global constructors on the device if present and is required840  /// by the target.841  virtual Error callGlobalConstructors(GenericPluginTy &Plugin,842                                       DeviceImageTy &Image) {843    return Error::success();844  }845 846  /// Invokes any global destructors on the device if present and is required847  /// by the target.848  virtual Error callGlobalDestructors(GenericPluginTy &Plugin,849                                      DeviceImageTy &Image) {850    return Error::success();851  }852 853  /// Query for the completion of the pending operations on the __tgt_async_info854  /// structure in a non-blocking manner.855  Error queryAsync(__tgt_async_info *AsyncInfo);856  virtual Error queryAsyncImpl(__tgt_async_info &AsyncInfo) = 0;857 858  /// Check whether the architecture supports VA management859  virtual bool supportVAManagement() const { return false; }860 861  /// Get the total device memory size862  virtual Error getDeviceMemorySize(uint64_t &DSize);863 864  /// Allocates \p RSize bytes (rounded up to page size) and hints the driver to865  /// map it to \p VAddr. The obtained address is stored in \p Addr. At return866  /// \p RSize contains the actual size which can be equal or larger than the867  /// requested size.868  virtual Error memoryVAMap(void **Addr, void *VAddr, size_t *RSize);869 870  /// De-allocates device memory and unmaps the virtual address \p VAddr871  virtual Error memoryVAUnMap(void *VAddr, size_t Size);872 873  /// Allocate data on the device or involving the device.874  Expected<void *> dataAlloc(int64_t Size, void *HostPtr, TargetAllocTy Kind);875 876  /// Deallocate data from the device or involving the device.877  Error dataDelete(void *TgtPtr, TargetAllocTy Kind);878 879  /// Pin host memory to optimize transfers and return the device accessible880  /// pointer that devices should use for memory transfers involving the host881  /// pinned allocation.882  Expected<void *> dataLock(void *HstPtr, int64_t Size) {883    return PinnedAllocs.lockHostBuffer(HstPtr, Size);884  }885 886  /// Unpin a host memory buffer that was previously pinned.887  Error dataUnlock(void *HstPtr) {888    return PinnedAllocs.unlockHostBuffer(HstPtr);889  }890 891  /// Lock the host buffer \p HstPtr with \p Size bytes with the vendor-specific892  /// API and return the device accessible pointer.893  virtual Expected<void *> dataLockImpl(void *HstPtr, int64_t Size) = 0;894 895  /// Unlock a previously locked host buffer starting at \p HstPtr.896  virtual Error dataUnlockImpl(void *HstPtr) = 0;897 898  /// Mark the host buffer with address \p HstPtr and \p Size bytes as a mapped899  /// buffer. This means that libomptarget created a new mapping of that host900  /// buffer (e.g., because a user OpenMP target map) and the buffer may be used901  /// as source/destination of memory transfers. We can use this information to902  /// lock the host buffer and optimize its memory transfers.903  Error notifyDataMapped(void *HstPtr, int64_t Size) {904    return PinnedAllocs.lockMappedHostBuffer(HstPtr, Size);905  }906 907  /// Mark the host buffer with address \p HstPtr as unmapped. This means that908  /// libomptarget removed an existing mapping. If the plugin locked the buffer909  /// in notifyDataMapped, this function should unlock it.910  Error notifyDataUnmapped(void *HstPtr) {911    return PinnedAllocs.unlockUnmappedHostBuffer(HstPtr);912  }913 914  /// Check whether the host buffer with address \p HstPtr is pinned by the915  /// underlying vendor-specific runtime (if any). Retrieve the host pointer,916  /// the device accessible pointer and the size of the original pinned buffer.917  virtual Expected<bool> isPinnedPtrImpl(void *HstPtr, void *&BaseHstPtr,918                                         void *&BaseDevAccessiblePtr,919                                         size_t &BaseSize) const = 0;920 921  /// Submit data to the device (host to device transfer).922  Error dataSubmit(void *TgtPtr, const void *HstPtr, int64_t Size,923                   __tgt_async_info *AsyncInfo);924  virtual Error dataSubmitImpl(void *TgtPtr, const void *HstPtr, int64_t Size,925                               AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;926 927  /// Retrieve data from the device (device to host transfer).928  Error dataRetrieve(void *HstPtr, const void *TgtPtr, int64_t Size,929                     __tgt_async_info *AsyncInfo);930  virtual Error dataRetrieveImpl(void *HstPtr, const void *TgtPtr, int64_t Size,931                                 AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;932 933  /// Instert a data fence between previous data operations and the following934  /// operations if necessary for the device935  virtual Error dataFence(__tgt_async_info *AsyncInfo) = 0;936 937  /// Exchange data between devices (device to device transfer). Calling this938  /// function is only valid if GenericPlugin::isDataExchangable() passing the939  /// two devices returns true.940  Error dataExchange(const void *SrcPtr, GenericDeviceTy &DstDev, void *DstPtr,941                     int64_t Size, __tgt_async_info *AsyncInfo);942  virtual Error dataExchangeImpl(const void *SrcPtr, GenericDeviceTy &DstDev,943                                 void *DstPtr, int64_t Size,944                                 AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;945 946  /// Fill data on the device with a pattern from the host947  Error dataFill(void *TgtPtr, const void *PatternPtr, int64_t PatternSize,948                 int64_t Size, __tgt_async_info *AsyncInfo);949  virtual Error dataFillImpl(void *TgtPtr, const void *PatternPtr,950                             int64_t PatternSize, int64_t Size,951                             AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;952 953  /// Run the kernel associated with \p EntryPtr954  Error launchKernel(void *EntryPtr, void **ArgPtrs, ptrdiff_t *ArgOffsets,955                     KernelArgsTy &KernelArgs, __tgt_async_info *AsyncInfo);956 957  /// Initialize a __tgt_async_info structure.958  Error initAsyncInfo(__tgt_async_info **AsyncInfoPtr);959  virtual Error initAsyncInfoImpl(AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;960 961  /// Enqueue a host call to AsyncInfo962  Error enqueueHostCall(void (*Callback)(void *), void *UserData,963                        __tgt_async_info *AsyncInfo);964  virtual Error enqueueHostCallImpl(void (*Callback)(void *), void *UserData,965                                    AsyncInfoWrapperTy &AsyncInfo) = 0;966 967  /// Create an event.968  Error createEvent(void **EventPtrStorage);969  virtual Error createEventImpl(void **EventPtrStorage) = 0;970 971  /// Destroy an event.972  Error destroyEvent(void *Event);973  virtual Error destroyEventImpl(void *EventPtr) = 0;974 975  /// Start the recording of the event.976  Error recordEvent(void *Event, __tgt_async_info *AsyncInfo);977  virtual Error recordEventImpl(void *EventPtr,978                                AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;979 980  /// Wait for an event to finish. Notice this wait is asynchronous if the981  /// __tgt_async_info is not nullptr.982  Error waitEvent(void *Event, __tgt_async_info *AsyncInfo);983  virtual Error waitEventImpl(void *EventPtr,984                              AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;985 986  /// Check if the event enqueued to AsyncInfo is complete987  Expected<bool> isEventComplete(void *Event, __tgt_async_info *AsyncInfo);988  virtual Expected<bool>989  isEventCompleteImpl(void *EventPtr, AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;990 991  /// Synchronize the current thread with the event.992  Error syncEvent(void *EventPtr);993  virtual Error syncEventImpl(void *EventPtr) = 0;994 995  /// Obtain information about the device.996  Expected<InfoTreeNode> obtainInfo();997  virtual Expected<InfoTreeNode> obtainInfoImpl() = 0;998 999  /// Print information about the device.1000  Error printInfo();1001 1002  /// Return true if the device has work that is either queued or currently1003  /// running1004  ///1005  /// Devices which cannot report this information should always return true1006  Expected<bool> hasPendingWork(__tgt_async_info *AsyncInfo);1007  virtual Expected<bool>1008  hasPendingWorkImpl(AsyncInfoWrapperTy &AsyncInfoWrapper) = 0;1009 1010  /// Getters of the grid values.1011  uint32_t getWarpSize() const { return GridValues.GV_Warp_Size; }1012  uint32_t getThreadLimit() const { return GridValues.GV_Max_WG_Size; }1013  uint32_t getBlockLimit() const { return GridValues.GV_Max_Teams; }1014  uint32_t getDefaultNumThreads() const {1015    return GridValues.GV_Default_WG_Size;1016  }1017  uint32_t getDefaultNumBlocks() const {1018    return GridValues.GV_Default_Num_Teams;1019  }1020  uint32_t getDebugKind() const { return OMPX_DebugKind; }1021  uint32_t getDynamicMemorySize() const { return OMPX_SharedMemorySize; }1022  virtual uint64_t getClockFrequency() const { return CLOCKS_PER_SEC; }1023 1024  /// Get target compute unit kind (e.g., sm_80, or gfx908).1025  virtual std::string getComputeUnitKind() const { return "unknown"; }1026 1027  /// Post processing after jit backend. The ownership of \p MB will be taken.1028  virtual Expected<std::unique_ptr<MemoryBuffer>>1029  doJITPostProcessing(std::unique_ptr<MemoryBuffer> MB) const {1030    return std::move(MB);1031  }1032 1033  /// The minimum number of threads we use for a low-trip count combined loop.1034  /// Instead of using more threads we increase the outer (block/team)1035  /// parallelism.1036  /// @see OMPX_MinThreadsForLowTripCount1037  virtual uint32_t getMinThreadsForLowTripCountLoop() {1038    return OMPX_MinThreadsForLowTripCount;1039  }1040 1041  /// Whether or not to reuse blocks for high trip count loops.1042  /// @see OMPX_ReuseBlocksForHighTripCount1043  bool getReuseBlocksForHighTripCount() {1044    return OMPX_ReuseBlocksForHighTripCount;1045  }1046 1047  /// Get the total amount of hardware parallelism supported by the target1048  /// device. This is the total amount of warps or wavefronts that can be1049  /// resident on the device simultaneously.1050  virtual uint64_t getHardwareParallelism() const { return 0; }1051 1052  /// Get the RPC server running on this device.1053  RPCServerTy *getRPCServer() const { return RPCServer; }1054 1055  /// The number of parallel RPC ports to use on the device. In general, this1056  /// should be roughly equivalent to the amount of hardware parallelism the1057  /// device can support. This is because GPUs in general do not have forward1058  /// progress guarantees, so we minimize thread level dependencies by1059  /// allocating enough space such that each device thread can have a port. This1060  /// is likely overly pessimistic in the average case, but guarantees no1061  /// deadlocks at the cost of memory. This must be overloaded by targets1062  /// expecting to use the RPC server.1063  virtual uint64_t requestedRPCPortCount() const {1064    assert(!shouldSetupRPCServer() && "Default implementation cannot be used");1065    return 0;1066  }1067 1068  virtual Error getDeviceStackSize(uint64_t &V) = 0;1069 1070  virtual bool hasDeviceHeapSize() { return false; }1071  virtual Error getDeviceHeapSize(uint64_t &V) {1072    return Plugin::error(error::ErrorCode::UNSUPPORTED,1073                         "%s not supported by platform", __func__);1074  }1075  virtual Error setDeviceHeapSize(uint64_t V) {1076    return Plugin::error(error::ErrorCode::UNSUPPORTED,1077                         "%s not supported by platform", __func__);1078  }1079 1080  /// Returns true if current plugin architecture is an APU1081  /// and unified_shared_memory was not requested by the program.1082  bool useAutoZeroCopy();1083  virtual bool useAutoZeroCopyImpl() { return false; }1084 1085  /// Returns true if the plugin can guarantee that the associated1086  /// storage is accessible1087  Expected<bool> isAccessiblePtr(const void *Ptr, size_t Size);1088 1089  virtual Expected<omp_interop_val_t *>1090  createInterop(int32_t InteropType, interop_spec_t &InteropSpec) {1091    return nullptr;1092  }1093 1094  virtual Error releaseInterop(omp_interop_val_t *Interop) {1095    return Plugin::success();1096  }1097 1098  virtual interop_spec_t selectInteropPreference(int32_t InteropType,1099                                                 int32_t NumPrefers,1100                                                 interop_spec_t *Prefers) {1101    return interop_spec_t{tgt_fr_none, {false, 0}, 0};1102  }1103 1104  /// Allocate and construct a kernel object.1105  virtual Expected<GenericKernelTy &> constructKernel(const char *Name) = 0;1106 1107  /// Reference to the underlying plugin that created this device.1108  GenericPluginTy &Plugin;1109 1110  /// Map to record when allocations have been performed, and when they have1111  /// been deallocated, both for error reporting purposes.1112  ProtectedObj<DenseMap<void *, AllocationTraceInfoTy *>> AllocationTraces;1113 1114  /// Return the allocation trace info for a device pointer, that is the1115  /// allocation into which this device pointer points to (or pointed into).1116  AllocationTraceInfoTy *getAllocationTraceInfoForAddr(void *DevicePtr) {1117    auto AllocationTraceMap = AllocationTraces.getExclusiveAccessor();1118    for (auto &It : *AllocationTraceMap) {1119      if (It.first <= DevicePtr &&1120          utils::advancePtr(It.first, It.second->Size) > DevicePtr)1121        return It.second;1122    }1123    return nullptr;1124  }1125 1126  /// Return the allocation trace info for a device pointer, that is the1127  /// allocation into which this device pointer points to (or pointed into).1128  AllocationTraceInfoTy *1129  getClosestAllocationTraceInfoForAddr(void *DevicePtr, uintptr_t &Distance) {1130    Distance = 0;1131    if (auto *ATI = getAllocationTraceInfoForAddr(DevicePtr)) {1132      return ATI;1133    }1134 1135    AllocationTraceInfoTy *ATI = nullptr;1136    uintptr_t DevicePtrI = uintptr_t(DevicePtr);1137    auto AllocationTraceMap = AllocationTraces.getExclusiveAccessor();1138    for (auto &It : *AllocationTraceMap) {1139      uintptr_t Begin = uintptr_t(It.second->DevicePtr);1140      uintptr_t End = Begin + It.second->Size - 1;1141      uintptr_t ItDistance = std::min(Begin - DevicePtrI, DevicePtrI - End);1142      if (ATI && ItDistance > Distance)1143        continue;1144      ATI = It.second;1145      Distance = ItDistance;1146    }1147    return ATI;1148  }1149 1150  /// Map to record kernel have been launchedl, for error reporting purposes.1151  ProtectedObj<KernelTraceInfoRecordTy> KernelLaunchTraces;1152 1153  /// Environment variable to determine if stack traces for kernel launches are1154  /// tracked.1155  UInt32Envar OMPX_TrackNumKernelLaunches =1156      UInt32Envar("OFFLOAD_TRACK_NUM_KERNEL_LAUNCH_TRACES", 0);1157 1158  /// Environment variable to determine if stack traces for allocations and1159  /// deallocations are tracked.1160  BoolEnvar OMPX_TrackAllocationTraces =1161      BoolEnvar("OFFLOAD_TRACK_ALLOCATION_TRACES", false);1162 1163  /// Array of images loaded into the device. Images are automatically1164  /// deallocated by the allocator.1165  llvm::SmallVector<DeviceImageTy *> LoadedImages;1166 1167private:1168  /// Get and set the stack size and heap size for the device. If not used, the1169  /// plugin can implement the setters as no-op and setting the output1170  /// value to zero for the getters.1171  virtual Error setDeviceStackSize(uint64_t V) = 0;1172 1173  /// Indicate whether or not the device should setup the RPC server. This is1174  /// only necessary for unhosted targets like the GPU.1175  virtual bool shouldSetupRPCServer() const { return false; }1176 1177  /// Pointer to the memory manager or nullptr if not available.1178  MemoryManagerTy *MemoryManager;1179 1180  /// Per device setting of MemoryManager's Threshold1181  virtual size_t getMemoryManagerSizeThreshold() { return 0; }1182 1183  virtual Expected<bool> isAccessiblePtrImpl(const void *Ptr, size_t Size) {1184    return false;1185  }1186 1187  /// Environment variables defined by the OpenMP standard.1188  Int32Envar OMP_TeamLimit;1189  Int32Envar OMP_NumTeams;1190  Int32Envar OMP_TeamsThreadLimit;1191 1192  /// Environment variables defined by the LLVM OpenMP implementation.1193  Int32Envar OMPX_DebugKind;1194  UInt32Envar OMPX_SharedMemorySize;1195  UInt64Envar OMPX_TargetStackSize;1196  UInt64Envar OMPX_TargetHeapSize;1197 1198  /// Environment flag to set the minimum number of threads we use for a1199  /// low-trip count combined loop. Instead of using more threads we increase1200  /// the outer (block/team) parallelism.1201  UInt32Envar OMPX_MinThreadsForLowTripCount =1202      UInt32Envar("LIBOMPTARGET_MIN_THREADS_FOR_LOW_TRIP_COUNT", 32);1203 1204  BoolEnvar OMPX_ReuseBlocksForHighTripCount =1205      BoolEnvar("LIBOMPTARGET_REUSE_BLOCKS_FOR_HIGH_TRIP_COUNT", true);1206 1207protected:1208  /// Environment variables defined by the LLVM OpenMP implementation1209  /// regarding the initial number of streams and events.1210  UInt32Envar OMPX_InitialNumStreams;1211  UInt32Envar OMPX_InitialNumEvents;1212 1213  /// The identifier of the device within the plugin. Notice this is not a1214  /// global device id and is not the device id visible to the OpenMP user.1215  const int32_t DeviceId;1216 1217  /// The unique identifier of the device.1218  /// Per default, the unique identifier of the device is set to the device id,1219  /// combined with the plugin name, since the offload device id may overlap1220  /// between different plugins.1221  std::string DeviceUid;1222  /// Construct the device UID from the vendor (U)UID.1223  void setDeviceUidFromVendorUid(StringRef VendorUid);1224 1225  /// The default grid values used for this device.1226  llvm::omp::GV GridValues;1227 1228  /// Enumeration used for representing the current state between two devices1229  /// two devices (both under the same plugin) for the peer access between them.1230  /// The states can be a) PENDING when the state has not been queried and needs1231  /// to be queried, b) AVAILABLE when the peer access is available to be used,1232  /// and c) UNAVAILABLE if the system does not allow it.1233  enum class PeerAccessState : uint8_t { AVAILABLE, UNAVAILABLE, PENDING };1234 1235  /// Array of peer access states with the rest of devices. This means that if1236  /// the device I has a matrix PeerAccesses with PeerAccesses == AVAILABLE,1237  /// the device I can access device J's memory directly. However, notice this1238  /// does not mean that device J can access device I's memory directly.1239  llvm::SmallVector<PeerAccessState> PeerAccesses;1240  std::mutex PeerAccessesLock;1241 1242  /// Map of host pinned allocations used for optimize device transfers.1243  PinnedAllocationMapTy PinnedAllocs;1244 1245  /// A pointer to an RPC server instance attached to this device if present.1246  /// This is used to run the RPC server during task synchronization.1247  RPCServerTy *RPCServer;1248 1249#ifdef OMPT_SUPPORT1250  /// OMPT callback functions1251#define defineOmptCallback(Name, Type, Code) Name##_t Name##_fn = nullptr;1252  FOREACH_OMPT_DEVICE_EVENT(defineOmptCallback)1253#undef defineOmptCallback1254 1255  /// Internal representation for OMPT device (initialize & finalize)1256  std::atomic<bool> OmptInitialized;1257#endif1258 1259  /// The total per-block native shared memory that a kernel may use.1260  size_t MaxBlockSharedMemSize = 0;1261};1262 1263/// Class implementing common functionalities of offload plugins. Each plugin1264/// should define the specific plugin class, derive from this generic one, and1265/// implement the necessary virtual function members.1266struct GenericPluginTy {1267 1268  /// Construct a plugin instance.1269  GenericPluginTy(Triple::ArchType TA)1270      : GlobalHandler(nullptr), JIT(TA), RPCServer(nullptr),1271        RecordReplay(nullptr) {}1272 1273  virtual ~GenericPluginTy() {}1274 1275  /// Initialize the plugin.1276  Error init();1277 1278  /// Initialize the plugin and return the number of available devices.1279  virtual Expected<int32_t> initImpl() = 0;1280 1281  /// Deinitialize the plugin and release the resources.1282  Error deinit();1283  virtual Error deinitImpl() = 0;1284 1285  /// Create a new device for the underlying plugin.1286  virtual GenericDeviceTy *createDevice(GenericPluginTy &Plugin,1287                                        int32_t DeviceID,1288                                        int32_t NumDevices) = 0;1289 1290  /// Create a new global handler for the underlying plugin.1291  virtual GenericGlobalHandlerTy *createGlobalHandler() = 0;1292 1293  /// Get the reference to the device with a certain device id.1294  GenericDeviceTy &getDevice(int32_t DeviceId) {1295    assert(isValidDeviceId(DeviceId) && "Invalid device id");1296    assert(Devices[DeviceId] && "Device is uninitialized");1297 1298    return *Devices[DeviceId];1299  }1300 1301  /// Get the number of active devices.1302  int32_t getNumDevices() const { return NumDevices; }1303 1304  /// Get the plugin-specific device identifier.1305  int32_t getUserId(int32_t DeviceId) const {1306    assert(UserDeviceIds.contains(DeviceId) && "No user-id registered");1307    return UserDeviceIds.at(DeviceId);1308  }1309 1310  /// Get the UID for the host device.1311  static constexpr const char *getHostDeviceUid() { return "HOST"; }1312 1313  /// Get the ELF code to recognize the binary image of this plugin.1314  virtual uint16_t getMagicElfBits() const = 0;1315 1316  /// Get the target triple of this plugin.1317  virtual Triple::ArchType getTripleArch() const = 0;1318 1319  /// Get the constant name identifier for this plugin.1320  virtual const char *getName() const = 0;1321 1322  /// Allocate a structure using the internal allocator.1323  template <typename Ty> Ty *allocate() {1324    return reinterpret_cast<Ty *>(Allocator.Allocate(sizeof(Ty), alignof(Ty)));1325  }1326 1327  template <typename Ty> void free(Ty *Mem) { Allocator.Deallocate(Mem); }1328 1329  /// Get the reference to the global handler of this plugin.1330  GenericGlobalHandlerTy &getGlobalHandler() {1331    assert(GlobalHandler && "Global handler not initialized");1332    return *GlobalHandler;1333  }1334 1335  /// Get the reference to the JIT used for all devices connected to this1336  /// plugin.1337  JITEngine &getJIT() { return JIT; }1338 1339  /// Get a reference to the RPC server used to provide host services.1340  RPCServerTy &getRPCServer() {1341    assert(RPCServer && "RPC server not initialized");1342    return *RPCServer;1343  }1344 1345  /// Get a reference to the record and replay interface for the plugin.1346  RecordReplayTy &getRecordReplay() {1347    assert(RecordReplay && "RR interface not initialized");1348    return *RecordReplay;1349  }1350 1351  /// Initialize a device within the plugin.1352  Error initDevice(int32_t DeviceId);1353 1354  /// Deinitialize a device within the plugin and release its resources.1355  Error deinitDevice(int32_t DeviceId);1356 1357  /// Indicate whether data can be exchanged directly between two devices under1358  /// this same plugin. If this function returns true, it's safe to call the1359  /// GenericDeviceTy::exchangeData() function on the source device.1360  virtual bool isDataExchangable(int32_t SrcDeviceId, int32_t DstDeviceId) {1361    return isValidDeviceId(SrcDeviceId) && isValidDeviceId(DstDeviceId);1362  }1363 1364  /// Top level interface to verify if a given ELF image can be executed on a1365  /// given target. Returns true if the \p Image is compatible with the plugin.1366  Expected<bool> checkELFImage(StringRef Image) const;1367 1368  /// Return true if the \p Image can be compiled to run on the platform's1369  /// target architecture.1370  Expected<bool> checkBitcodeImage(StringRef Image) const;1371 1372  /// Indicate if an image is compatible with the plugin devices. Notice that1373  /// this function may be called before actually initializing the devices. So1374  /// we could not move this function into GenericDeviceTy.1375  virtual Expected<bool> isELFCompatible(uint32_t DeviceID,1376                                         StringRef Image) const = 0;1377 1378  virtual Error flushQueueImpl(omp_interop_val_t *Interop) {1379    return Plugin::success();1380  }1381 1382  virtual Error syncBarrierImpl(omp_interop_val_t *Interop) {1383    return Plugin::error(error::ErrorCode::UNSUPPORTED,1384                         "sync_barrier not supported");1385  }1386 1387  virtual Error asyncBarrierImpl(omp_interop_val_t *Interop) {1388    return Plugin::error(error::ErrorCode::UNSUPPORTED,1389                         "async_barrier not supported");1390  }1391 1392protected:1393  /// Indicate whether a device id is valid.1394  bool isValidDeviceId(int32_t DeviceId) const {1395    return (DeviceId >= 0 && DeviceId < getNumDevices());1396  }1397 1398public:1399  // TODO: This plugin interface needs to be cleaned up.1400 1401  /// Returns non-zero if the plugin runtime has been initialized.1402  int32_t is_initialized() const;1403 1404  /// Returns non-zero if the \p Image is compatible with the plugin. This1405  /// function does not require the plugin to be initialized before use.1406  int32_t isPluginCompatible(StringRef Image);1407 1408  /// Returns non-zero if the \p Image is compatible with the device.1409  int32_t isDeviceCompatible(int32_t DeviceId, StringRef Image);1410 1411  /// Returns non-zero if the plugin device has been initialized.1412  int32_t is_device_initialized(int32_t DeviceId) const;1413 1414  /// Initialize the device inside of the plugin.1415  int32_t init_device(int32_t DeviceId);1416 1417  /// Return the number of devices this plugin can support.1418  int32_t number_of_devices();1419 1420  /// Returns non-zero if the data can be exchanged between the two devices.1421  int32_t is_data_exchangable(int32_t SrcDeviceId, int32_t DstDeviceId);1422 1423  /// Initializes the record and replay mechanism inside the plugin.1424  int32_t initialize_record_replay(int32_t DeviceId, int64_t MemorySize,1425                                   void *VAddr, bool isRecord, bool SaveOutput,1426                                   uint64_t &ReqPtrArgOffset);1427 1428  /// Loads the associated binary into the plugin and returns a handle to it.1429  int32_t load_binary(int32_t DeviceId, __tgt_device_image *TgtImage,1430                      __tgt_device_binary *Binary);1431 1432  /// Allocates memory that is accessively to the given device.1433  void *data_alloc(int32_t DeviceId, int64_t Size, void *HostPtr, int32_t Kind);1434 1435  /// Deallocates memory on the given device.1436  int32_t data_delete(int32_t DeviceId, void *TgtPtr, int32_t Kind);1437 1438  /// Locks / pins host memory using the plugin runtime.1439  int32_t data_lock(int32_t DeviceId, void *Ptr, int64_t Size,1440                    void **LockedPtr);1441 1442  /// Unlocks / unpins host memory using the plugin runtime.1443  int32_t data_unlock(int32_t DeviceId, void *Ptr);1444 1445  /// Notify the runtime about a new mapping that has been created outside.1446  int32_t data_notify_mapped(int32_t DeviceId, void *HstPtr, int64_t Size);1447 1448  /// Notify t he runtime about a mapping that has been deleted.1449  int32_t data_notify_unmapped(int32_t DeviceId, void *HstPtr);1450 1451  /// Copy data to the given device.1452  int32_t data_submit(int32_t DeviceId, void *TgtPtr, void *HstPtr,1453                      int64_t Size);1454 1455  /// Copy data to the given device asynchronously.1456  int32_t data_submit_async(int32_t DeviceId, void *TgtPtr, void *HstPtr,1457                            int64_t Size, __tgt_async_info *AsyncInfoPtr);1458 1459  /// Copy data from the given device.1460  int32_t data_retrieve(int32_t DeviceId, void *HstPtr, void *TgtPtr,1461                        int64_t Size);1462 1463  /// Copy data from the given device asynchronously.1464  int32_t data_retrieve_async(int32_t DeviceId, void *HstPtr, void *TgtPtr,1465                              int64_t Size, __tgt_async_info *AsyncInfoPtr);1466 1467  /// Exchange memory addresses between two devices.1468  int32_t data_exchange(int32_t SrcDeviceId, void *SrcPtr, int32_t DstDeviceId,1469                        void *DstPtr, int64_t Size);1470 1471  /// Exchange memory addresses between two devices asynchronously.1472  int32_t data_exchange_async(int32_t SrcDeviceId, void *SrcPtr,1473                              int DstDeviceId, void *DstPtr, int64_t Size,1474                              __tgt_async_info *AsyncInfo);1475 1476  /// Places a fence between previous data movements and following data1477  /// movements if necessary on the device1478  int32_t data_fence(int32_t DeviceId, __tgt_async_info *AsyncInfo);1479 1480  /// Begin executing a kernel on the given device.1481  int32_t launch_kernel(int32_t DeviceId, void *TgtEntryPtr, void **TgtArgs,1482                        ptrdiff_t *TgtOffsets, KernelArgsTy *KernelArgs,1483                        __tgt_async_info *AsyncInfoPtr);1484 1485  /// Synchronize an asyncrhonous queue with the plugin runtime.1486  int32_t synchronize(int32_t DeviceId, __tgt_async_info *AsyncInfoPtr);1487 1488  /// Query the current state of an asynchronous queue.1489  int32_t query_async(int32_t DeviceId, __tgt_async_info *AsyncInfoPtr);1490 1491  /// Prints information about the given devices supported by the plugin.1492  void print_device_info(int32_t DeviceId);1493 1494  /// Creates an event in the given plugin if supported.1495  int32_t create_event(int32_t DeviceId, void **EventPtr);1496 1497  /// Records an event that has occurred.1498  int32_t record_event(int32_t DeviceId, void *EventPtr,1499                       __tgt_async_info *AsyncInfoPtr);1500 1501  /// Wait until an event has occurred.1502  int32_t wait_event(int32_t DeviceId, void *EventPtr,1503                     __tgt_async_info *AsyncInfoPtr);1504 1505  /// Synchronize execution until an event is done.1506  int32_t sync_event(int32_t DeviceId, void *EventPtr);1507 1508  /// Remove the event from the plugin.1509  int32_t destroy_event(int32_t DeviceId, void *EventPtr);1510 1511  /// Remove the event from the plugin.1512  void set_info_flag(uint32_t NewInfoLevel);1513 1514  /// Creates an asynchronous queue for the given plugin.1515  int32_t init_async_info(int32_t DeviceId, __tgt_async_info **AsyncInfoPtr);1516 1517  /// Sets the offset into the devices for use by OMPT.1518  int32_t set_device_identifier(int32_t UserId, int32_t DeviceId);1519 1520  /// Returns if the plugin can support automatic copy.1521  int32_t use_auto_zero_copy(int32_t DeviceId);1522 1523  /// Returns if the associated storage is accessible for a given device.1524  int32_t is_accessible_ptr(int32_t DeviceId, const void *Ptr, size_t Size);1525 1526  /// Look up a global symbol in the given binary.1527  int32_t get_global(__tgt_device_binary Binary, uint64_t Size,1528                     const char *Name, void **DevicePtr);1529 1530  /// Look up a kernel function in the given binary.1531  int32_t get_function(__tgt_device_binary Binary, const char *Name,1532                       void **KernelPtr);1533 1534  /// Return the interop specification that the plugin supports1535  /// It might not be one of the user specified ones.1536  interop_spec_t select_interop_preference(int32_t ID, int32_t InteropType,1537                                           int32_t NumPrefers,1538                                           interop_spec_t *Prefers) {1539    auto &Device = getDevice(ID);1540    return Device.selectInteropPreference(InteropType, NumPrefers, Prefers);1541  }1542 1543  /// Create OpenMP interop with the given interop context1544  omp_interop_val_t *create_interop(int32_t ID, int32_t InteropContext,1545                                    interop_spec_t *InteropSpec);1546 1547  /// Release OpenMP interop object1548  int32_t release_interop(int32_t ID, omp_interop_val_t *Interop);1549 1550  /// Flush the queue associated with the interop object if necessary1551  int32_t flush_queue(omp_interop_val_t *Interop);1552 1553  /// Perform a host synchronization with the queue associated with the interop1554  /// object and wait for it to complete.1555  int32_t sync_barrier(omp_interop_val_t *Interop);1556 1557  /// Queue an asynchronous barrier in the queue associated with the interop1558  /// object and return immediately.1559  int32_t async_barrier(omp_interop_val_t *Interop);1560 1561private:1562  /// Indicates if the platform runtime has been fully initialized.1563  bool Initialized = false;1564 1565  /// Number of devices available for the plugin.1566  int32_t NumDevices = 0;1567 1568  /// Map of plugin device identifiers to the user device identifier.1569  llvm::DenseMap<int32_t, int32_t> UserDeviceIds;1570 1571  /// Array of pointers to the devices. Initially, they are all set to nullptr.1572  /// Once a device is initialized, the pointer is stored in the position given1573  /// by its device id. A position with nullptr means that the corresponding1574  /// device was not initialized yet.1575  llvm::SmallVector<GenericDeviceTy *> Devices;1576 1577  /// Pointer to the global handler for this plugin.1578  GenericGlobalHandlerTy *GlobalHandler;1579 1580  /// Internal allocator for different structures.1581  BumpPtrAllocator Allocator;1582 1583  /// The JIT engine shared by all devices connected to this plugin.1584  JITEngine JIT;1585 1586  /// The interface between the plugin and the GPU for host services.1587  RPCServerTy *RPCServer;1588 1589  /// The interface between the plugin and the GPU for host services.1590  RecordReplayTy *RecordReplay;1591};1592 1593/// Auxiliary interface class for GenericDeviceResourceManagerTy. This class1594/// acts as a reference to a device resource, such as a stream, and requires1595/// some basic functions to be implemented. The derived class should define an1596/// empty constructor that creates an empty and invalid resource reference. Do1597/// not create a new resource on the ctor, but on the create() function instead.1598///1599/// The derived class should also define the type HandleTy as the underlying1600/// resource handle type. For instance, in a CUDA stream it would be:1601///   using HandleTy = CUstream;1602struct GenericDeviceResourceRef {1603  /// Create a new resource and stores a reference.1604  virtual Error create(GenericDeviceTy &Device) = 0;1605 1606  /// Destroy and release the resources pointed by the reference.1607  virtual Error destroy(GenericDeviceTy &Device) = 0;1608 1609protected:1610  ~GenericDeviceResourceRef() = default;1611};1612 1613/// Class that implements a resource pool belonging to a device. This class1614/// operates with references to the actual resources. These reference must1615/// derive from the GenericDeviceResourceRef class and implement the create1616/// and destroy virtual functions.1617template <typename ResourceRef> class GenericDeviceResourceManagerTy {1618  using ResourcePoolTy = GenericDeviceResourceManagerTy<ResourceRef>;1619  using ResourceHandleTy = typename ResourceRef::HandleTy;1620 1621public:1622  /// Create an empty resource pool for a specific device.1623  GenericDeviceResourceManagerTy(GenericDeviceTy &Device)1624      : Device(Device), NextAvailable(0) {}1625 1626  /// Destroy the resource pool. At this point, the deinit() function should1627  /// already have been executed so the resource pool should be empty.1628  virtual ~GenericDeviceResourceManagerTy() {1629    assert(ResourcePool.empty() && "Resource pool not empty");1630  }1631 1632  /// Initialize the resource pool.1633  Error init(uint32_t InitialSize) {1634    assert(ResourcePool.empty() && "Resource pool already initialized");1635    return ResourcePoolTy::resizeResourcePool(InitialSize);1636  }1637 1638  /// Deinitialize the resource pool and delete all resources. This function1639  /// must be called before the destructor.1640  virtual Error deinit() {1641    if (NextAvailable)1642      DP("Missing %d resources to be returned\n", NextAvailable);1643 1644    // TODO: This prevents a bug on libomptarget to make the plugins fail. There1645    // may be some resources not returned. Do not destroy these ones.1646    if (auto Err = ResourcePoolTy::resizeResourcePool(NextAvailable))1647      return Err;1648 1649    ResourcePool.clear();1650 1651    return Plugin::success();1652  }1653 1654  /// Get a resource from the pool or create new ones. If the function1655  /// succeeds, the handle to the resource is saved in \p Handle.1656  virtual Error getResource(ResourceHandleTy &Handle) {1657    // Get a resource with an empty resource processor.1658    return getResourcesImpl(1, &Handle,1659                            [](ResourceHandleTy) { return Plugin::success(); });1660  }1661 1662  /// Get multiple resources from the pool or create new ones. If the function1663  /// succeeds, the handles to the resources are saved in \p Handles.1664  virtual Error getResources(uint32_t Num, ResourceHandleTy *Handles) {1665    // Get resources with an empty resource processor.1666    return getResourcesImpl(Num, Handles,1667                            [](ResourceHandleTy) { return Plugin::success(); });1668  }1669 1670  /// Return resource to the pool.1671  virtual Error returnResource(ResourceHandleTy Handle) {1672    // Return a resource with an empty resource processor.1673    return returnResourceImpl(1674        Handle, [](ResourceHandleTy) { return Plugin::success(); });1675  }1676 1677protected:1678  /// Get multiple resources from the pool or create new ones. If the function1679  /// succeeds, the handles to the resources are saved in \p Handles. Also1680  /// process each of the obtained resources with \p Processor.1681  template <typename FuncTy>1682  Error getResourcesImpl(uint32_t Num, ResourceHandleTy *Handles,1683                         FuncTy Processor) {1684    const std::lock_guard<std::mutex> Lock(Mutex);1685 1686    assert(NextAvailable <= ResourcePool.size() &&1687           "Resource pool is corrupted");1688 1689    if (NextAvailable + Num > ResourcePool.size())1690      // Double the resource pool or resize it to provide the requested ones.1691      if (auto Err = ResourcePoolTy::resizeResourcePool(1692              std::max(NextAvailable * 2, NextAvailable + Num)))1693        return Err;1694 1695    // Save the handles in the output array parameter.1696    for (uint32_t r = 0; r < Num; ++r)1697      Handles[r] = ResourcePool[NextAvailable + r];1698 1699    // Process all obtained resources.1700    for (uint32_t r = 0; r < Num; ++r)1701      if (auto Err = Processor(Handles[r]))1702        return Err;1703 1704    NextAvailable += Num;1705 1706    return Plugin::success();1707  }1708 1709  /// Return resource to the pool and process the resource with \p Processor.1710  template <typename FuncTy>1711  Error returnResourceImpl(ResourceHandleTy Handle, FuncTy Processor) {1712    const std::lock_guard<std::mutex> Lock(Mutex);1713 1714    // Process the returned resource.1715    if (auto Err = Processor(Handle))1716      return Err;1717 1718    assert(NextAvailable > 0 && "Resource pool is corrupted");1719    ResourcePool[--NextAvailable] = Handle;1720 1721    return Plugin::success();1722  }1723 1724protected:1725  /// The resources between \p OldSize and \p NewSize need to be created or1726  /// destroyed. The mutex is locked when this function is called.1727  Error resizeResourcePoolImpl(uint32_t OldSize, uint32_t NewSize) {1728    assert(OldSize != NewSize && "Resizing to the same size");1729 1730    if (auto Err = Device.setContext())1731      return Err;1732 1733    if (OldSize < NewSize) {1734      // Create new resources.1735      for (uint32_t I = OldSize; I < NewSize; ++I) {1736        if (auto Err = ResourcePool[I].create(Device))1737          return Err;1738      }1739    } else {1740      // Destroy the obsolete resources.1741      for (uint32_t I = NewSize; I < OldSize; ++I) {1742        if (auto Err = ResourcePool[I].destroy(Device))1743          return Err;1744      }1745    }1746    return Plugin::success();1747  }1748 1749  /// Increase or decrease the number of resources. This function should1750  /// be called with the mutex acquired.1751  Error resizeResourcePool(uint32_t NewSize) {1752    uint32_t OldSize = ResourcePool.size();1753 1754    // Nothing to do.1755    if (OldSize == NewSize)1756      return Plugin::success();1757 1758    if (OldSize < NewSize) {1759      // Increase the number of resources.1760      ResourcePool.resize(NewSize);1761      return ResourcePoolTy::resizeResourcePoolImpl(OldSize, NewSize);1762    }1763 1764    // Decrease the number of resources otherwise.1765    auto Err = ResourcePoolTy::resizeResourcePoolImpl(OldSize, NewSize);1766    ResourcePool.resize(NewSize);1767 1768    return Err;1769  }1770 1771  /// The device to which the resources belong1772  GenericDeviceTy &Device;1773 1774  /// Mutex for the resource pool.1775  std::mutex Mutex;1776 1777  /// The next available resource in the pool.1778  uint32_t NextAvailable;1779 1780  /// The actual resource pool.1781  std::deque<ResourceRef> ResourcePool;1782};1783 1784} // namespace plugin1785} // namespace target1786} // namespace omp1787} // namespace llvm1788 1789#endif // OPENMP_LIBOMPTARGET_PLUGINS_COMMON_PLUGININTERFACE_H1790