brintos

brintos / llvm-project-archived public Read only

0
0
Text · 146.3 KiB · 5196509 Raw
3981 lines · cpp
1//===----RTLs/amdgpu/src/rtl.cpp - Target RTLs Implementation ----- C++ -*-===//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// RTL NextGen for AMDGPU machine10//11//===----------------------------------------------------------------------===//12 13#include <atomic>14#include <cassert>15#include <cstddef>16#include <cstdint>17#include <deque>18#include <functional>19#include <mutex>20#include <string>21#include <system_error>22#include <unistd.h>23#include <unordered_map>24 25#include "ErrorReporting.h"26#include "Shared/APITypes.h"27#include "Shared/Debug.h"28#include "Shared/Environment.h"29#include "Shared/RefCnt.h"30#include "Shared/Utils.h"31#include "Utils/ELF.h"32 33#include "GlobalHandler.h"34#include "OpenMP/OMPT/Callback.h"35#include "PluginInterface.h"36#include "UtilitiesRTL.h"37#include "omptarget.h"38 39#include "llvm/ADT/SmallString.h"40#include "llvm/ADT/SmallVector.h"41#include "llvm/ADT/StringRef.h"42#include "llvm/BinaryFormat/ELF.h"43#include "llvm/Frontend/OpenMP/OMPConstants.h"44#include "llvm/Frontend/OpenMP/OMPGridValues.h"45#include "llvm/Support/Error.h"46#include "llvm/Support/FileOutputBuffer.h"47#include "llvm/Support/FileSystem.h"48#include "llvm/Support/MemoryBuffer.h"49#include "llvm/Support/Program.h"50#include "llvm/Support/Signals.h"51#include "llvm/Support/raw_ostream.h"52 53#if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) ||           \54    !defined(__ORDER_BIG_ENDIAN__)55#error "Missing preprocessor definitions for endianness detection."56#endif57 58// The HSA headers require these definitions.59#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)60#define LITTLEENDIAN_CPU61#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)62#define BIGENDIAN_CPU63#endif64 65#if defined(__has_include)66#if __has_include("hsa.h")67#include "hsa.h"68#include "hsa_ext_amd.h"69#elif __has_include("hsa/hsa.h")70#include "hsa/hsa.h"71#include "hsa/hsa_ext_amd.h"72#endif73#else74#include "hsa/hsa.h"75#include "hsa/hsa_ext_amd.h"76#endif77 78using namespace error;79 80namespace llvm {81namespace omp {82namespace target {83namespace plugin {84 85/// Forward declarations for all specialized data structures.86struct AMDGPUKernelTy;87struct AMDGPUDeviceTy;88struct AMDGPUPluginTy;89struct AMDGPUStreamTy;90struct AMDGPUEventTy;91struct AMDGPUStreamManagerTy;92struct AMDGPUEventManagerTy;93struct AMDGPUDeviceImageTy;94struct AMDGPUMemoryManagerTy;95struct AMDGPUMemoryPoolTy;96 97namespace hsa_utils {98 99/// Iterate elements using an HSA iterate function. Do not use this function100/// directly but the specialized ones below instead.101template <typename ElemTy, typename IterFuncTy, typename CallbackTy>102static hsa_status_t iterate(IterFuncTy Func, CallbackTy Cb) {103  auto L = [](ElemTy Elem, void *Data) -> hsa_status_t {104    CallbackTy *Unwrapped = static_cast<CallbackTy *>(Data);105    return (*Unwrapped)(Elem);106  };107  return Func(L, static_cast<void *>(&Cb));108}109 110/// Iterate elements using an HSA iterate function passing a parameter. Do not111/// use this function directly but the specialized ones below instead.112template <typename ElemTy, typename IterFuncTy, typename IterFuncArgTy,113          typename CallbackTy>114static hsa_status_t iterate(IterFuncTy Func, IterFuncArgTy FuncArg,115                            CallbackTy Cb) {116  auto L = [](ElemTy Elem, void *Data) -> hsa_status_t {117    CallbackTy *Unwrapped = static_cast<CallbackTy *>(Data);118    return (*Unwrapped)(Elem);119  };120  return Func(FuncArg, L, static_cast<void *>(&Cb));121}122 123/// Iterate elements using an HSA iterate function passing a parameter. Do not124/// use this function directly but the specialized ones below instead.125template <typename Elem1Ty, typename Elem2Ty, typename IterFuncTy,126          typename IterFuncArgTy, typename CallbackTy>127static hsa_status_t iterate(IterFuncTy Func, IterFuncArgTy FuncArg,128                            CallbackTy Cb) {129  auto L = [](Elem1Ty Elem1, Elem2Ty Elem2, void *Data) -> hsa_status_t {130    CallbackTy *Unwrapped = static_cast<CallbackTy *>(Data);131    return (*Unwrapped)(Elem1, Elem2);132  };133  return Func(FuncArg, L, static_cast<void *>(&Cb));134}135 136/// Iterate agents.137template <typename CallbackTy> static Error iterateAgents(CallbackTy Callback) {138  hsa_status_t Status = iterate<hsa_agent_t>(hsa_iterate_agents, Callback);139  return Plugin::check(Status, "error in hsa_iterate_agents: %s");140}141 142/// Iterate ISAs of an agent.143template <typename CallbackTy>144static Error iterateAgentISAs(hsa_agent_t Agent, CallbackTy Cb) {145  hsa_status_t Status = iterate<hsa_isa_t>(hsa_agent_iterate_isas, Agent, Cb);146  return Plugin::check(Status, "error in hsa_agent_iterate_isas: %s");147}148 149/// Iterate memory pools of an agent.150template <typename CallbackTy>151static Error iterateAgentMemoryPools(hsa_agent_t Agent, CallbackTy Cb) {152  hsa_status_t Status = iterate<hsa_amd_memory_pool_t>(153      hsa_amd_agent_iterate_memory_pools, Agent, Cb);154  return Plugin::check(Status,155                       "error in hsa_amd_agent_iterate_memory_pools: %s");156}157 158/// Dispatches an asynchronous memory copy.159/// Enables different SDMA engines for the dispatch in a round-robin fashion.160static Error asyncMemCopy(bool UseMultipleSdmaEngines, void *Dst,161                          hsa_agent_t DstAgent, const void *Src,162                          hsa_agent_t SrcAgent, size_t Size,163                          uint32_t NumDepSignals,164                          const hsa_signal_t *DepSignals,165                          hsa_signal_t CompletionSignal) {166  if (!UseMultipleSdmaEngines) {167    hsa_status_t S =168        hsa_amd_memory_async_copy(Dst, DstAgent, Src, SrcAgent, Size,169                                  NumDepSignals, DepSignals, CompletionSignal);170    return Plugin::check(S, "error in hsa_amd_memory_async_copy: %s");171  }172 173// This solution is probably not the best174#if !(HSA_AMD_INTERFACE_VERSION_MAJOR >= 1 &&                                  \175      HSA_AMD_INTERFACE_VERSION_MINOR >= 2)176  return Plugin::error(ErrorCode::UNSUPPORTED,177                       "async copy on selected SDMA requires ROCm 5.7");178#else179  static std::atomic<int> SdmaEngine{1};180 181  // This atomics solution is probably not the best, but should be sufficient182  // for now.183  // In a worst case scenario, in which threads read the same value, they will184  // dispatch to the same SDMA engine. This may result in sub-optimal185  // performance. However, I think the possibility to be fairly low.186  int LocalSdmaEngine = SdmaEngine.load(std::memory_order_acquire);187  // This call is only avail in ROCm >= 5.7188  hsa_status_t S = hsa_amd_memory_async_copy_on_engine(189      Dst, DstAgent, Src, SrcAgent, Size, NumDepSignals, DepSignals,190      CompletionSignal, (hsa_amd_sdma_engine_id_t)LocalSdmaEngine,191      /*force_copy_on_sdma=*/true);192  // Increment to use one of two SDMA engines: 0x1, 0x2193  LocalSdmaEngine = (LocalSdmaEngine << 1) % 3;194  SdmaEngine.store(LocalSdmaEngine, std::memory_order_relaxed);195 196  return Plugin::check(S, "error in hsa_amd_memory_async_copy_on_engine: %s");197#endif198}199 200static Error getTargetTripleAndFeatures(hsa_agent_t Agent,201                                        SmallVector<SmallString<32>> &Targets) {202  auto Err = hsa_utils::iterateAgentISAs(Agent, [&](hsa_isa_t ISA) {203    uint32_t Length;204    hsa_status_t Status;205    Status = hsa_isa_get_info_alt(ISA, HSA_ISA_INFO_NAME_LENGTH, &Length);206    if (Status != HSA_STATUS_SUCCESS)207      return Status;208 209    llvm::SmallVector<char> ISAName(Length);210    Status = hsa_isa_get_info_alt(ISA, HSA_ISA_INFO_NAME, ISAName.begin());211    if (Status != HSA_STATUS_SUCCESS)212      return Status;213 214    llvm::StringRef TripleTarget(ISAName.begin(), Length);215    if (TripleTarget.consume_front("amdgcn-amd-amdhsa")) {216      auto Target = TripleTarget.ltrim('-').rtrim('\0');217      Targets.push_back(Target);218    }219    return HSA_STATUS_SUCCESS;220  });221  return Err;222}223} // namespace hsa_utils224 225/// Utility class representing generic resource references to AMDGPU resources.226template <typename ResourceTy>227struct AMDGPUResourceRef : public GenericDeviceResourceRef {228  /// The underlying handle type for resources.229  using HandleTy = ResourceTy *;230 231  /// Create an empty reference to an invalid resource.232  AMDGPUResourceRef() : Resource(nullptr) {}233 234  /// Create a reference to an existing resource.235  AMDGPUResourceRef(HandleTy Resource) : Resource(Resource) {}236 237  virtual ~AMDGPUResourceRef() {}238 239  /// Create a new resource and save the reference. The reference must be empty240  /// before calling to this function.241  Error create(GenericDeviceTy &Device) override;242 243  /// Destroy the referenced resource and invalidate the reference. The244  /// reference must be to a valid resource before calling to this function.245  Error destroy(GenericDeviceTy &Device) override {246    if (!Resource)247      return Plugin::error(ErrorCode::INVALID_ARGUMENT,248                           "destroying an invalid resource");249 250    if (auto Err = Resource->deinit())251      return Err;252 253    delete Resource;254 255    Resource = nullptr;256    return Plugin::success();257  }258 259  /// Get the underlying resource handle.260  operator HandleTy() const { return Resource; }261 262private:263  /// The handle to the actual resource.264  HandleTy Resource;265};266 267/// Class holding an HSA memory pool.268struct AMDGPUMemoryPoolTy {269  /// Create a memory pool from an HSA memory pool.270  AMDGPUMemoryPoolTy(hsa_amd_memory_pool_t MemoryPool)271      : MemoryPool(MemoryPool), GlobalFlags(0) {}272 273  /// Initialize the memory pool retrieving its properties.274  Error init() {275    if (auto Err = getAttr(HSA_AMD_MEMORY_POOL_INFO_SEGMENT, Segment))276      return Err;277 278    if (auto Err = getAttr(HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS, GlobalFlags))279      return Err;280 281    return Plugin::success();282  }283 284  /// Getter of the HSA memory pool.285  hsa_amd_memory_pool_t get() const { return MemoryPool; }286 287  /// Indicate the segment which belongs to.288  bool isGlobal() const { return (Segment == HSA_AMD_SEGMENT_GLOBAL); }289  bool isReadOnly() const { return (Segment == HSA_AMD_SEGMENT_READONLY); }290  bool isPrivate() const { return (Segment == HSA_AMD_SEGMENT_PRIVATE); }291  bool isGroup() const { return (Segment == HSA_AMD_SEGMENT_GROUP); }292 293  /// Indicate if it is fine-grained memory. Valid only for global.294  bool isFineGrained() const {295    assert(isGlobal() && "Not global memory");296    return (GlobalFlags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_FINE_GRAINED);297  }298 299  /// Indicate if it is coarse-grained memory. Valid only for global.300  bool isCoarseGrained() const {301    assert(isGlobal() && "Not global memory");302    return (GlobalFlags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_COARSE_GRAINED);303  }304 305  /// Indicate if it supports storing kernel arguments. Valid only for global.306  bool supportsKernelArgs() const {307    assert(isGlobal() && "Not global memory");308    return (GlobalFlags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_KERNARG_INIT);309  }310 311  /// Allocate memory on the memory pool.312  Error allocate(size_t Size, void **PtrStorage) {313    hsa_status_t Status =314        hsa_amd_memory_pool_allocate(MemoryPool, Size, 0, PtrStorage);315    return Plugin::check(Status, "error in hsa_amd_memory_pool_allocate: %s");316  }317 318  /// Return memory to the memory pool.319  Error deallocate(void *Ptr) {320    hsa_status_t Status = hsa_amd_memory_pool_free(Ptr);321    return Plugin::check(Status, "error in hsa_amd_memory_pool_free: %s");322  }323 324  /// Returns if the \p Agent can access the memory pool.325  bool canAccess(hsa_agent_t Agent) {326    hsa_amd_memory_pool_access_t Access;327    if (hsa_amd_agent_memory_pool_get_info(328            Agent, MemoryPool, HSA_AMD_AGENT_MEMORY_POOL_INFO_ACCESS, &Access))329      return false;330    return Access != HSA_AMD_MEMORY_POOL_ACCESS_NEVER_ALLOWED;331  }332 333  /// Allow the device to access a specific allocation.334  Error enableAccess(void *Ptr, int64_t Size,335                     const llvm::SmallVector<hsa_agent_t> &Agents) const {336#ifdef OMPTARGET_DEBUG337    for (hsa_agent_t Agent : Agents) {338      hsa_amd_memory_pool_access_t Access;339      if (auto Err =340              getAttr(Agent, HSA_AMD_AGENT_MEMORY_POOL_INFO_ACCESS, Access))341        return Err;342 343      // The agent is not allowed to access the memory pool in any case. Do not344      // continue because otherwise it result in undefined behavior.345      if (Access == HSA_AMD_MEMORY_POOL_ACCESS_NEVER_ALLOWED)346        return Plugin::error(ErrorCode::INVALID_VALUE,347                             "an agent is not allowed to access a memory pool");348    }349#endif350 351    // We can access but it is disabled by default. Enable the access then.352    hsa_status_t Status =353        hsa_amd_agents_allow_access(Agents.size(), Agents.data(), nullptr, Ptr);354    return Plugin::check(Status, "error in hsa_amd_agents_allow_access: %s");355  }356 357  /// Get attribute from the memory pool.358  template <typename Ty>359  Error getAttr(hsa_amd_memory_pool_info_t Kind, Ty &Value) const {360    hsa_status_t Status;361    Status = hsa_amd_memory_pool_get_info(MemoryPool, Kind, &Value);362    return Plugin::check(Status, "error in hsa_amd_memory_pool_get_info: %s");363  }364 365  template <typename Ty>366  hsa_status_t getAttrRaw(hsa_amd_memory_pool_info_t Kind, Ty &Value) const {367    return hsa_amd_memory_pool_get_info(MemoryPool, Kind, &Value);368  }369 370  /// Get attribute from the memory pool relating to an agent.371  template <typename Ty>372  Error getAttr(hsa_agent_t Agent, hsa_amd_agent_memory_pool_info_t Kind,373                Ty &Value) const {374    hsa_status_t Status;375    Status =376        hsa_amd_agent_memory_pool_get_info(Agent, MemoryPool, Kind, &Value);377    return Plugin::check(Status,378                         "error in hsa_amd_agent_memory_pool_get_info: %s");379  }380 381private:382  /// The HSA memory pool.383  hsa_amd_memory_pool_t MemoryPool;384 385  /// The segment where the memory pool belongs to.386  hsa_amd_segment_t Segment;387 388  /// The global flags of memory pool. Only valid if the memory pool belongs to389  /// the global segment.390  uint32_t GlobalFlags;391};392 393/// Class that implements a memory manager that gets memory from a specific394/// memory pool.395struct AMDGPUMemoryManagerTy : public DeviceAllocatorTy {396 397  /// Create an empty memory manager.398  AMDGPUMemoryManagerTy(AMDGPUPluginTy &Plugin)399      : Plugin(Plugin), MemoryPool(nullptr), MemoryManager(nullptr) {}400 401  /// Initialize the memory manager from a memory pool.402  Error init(AMDGPUMemoryPoolTy &MemoryPool) {403    const uint32_t Threshold = 1 << 30;404    this->MemoryManager = new MemoryManagerTy(*this, Threshold);405    this->MemoryPool = &MemoryPool;406    return Plugin::success();407  }408 409  /// Deinitialize the memory manager and free its allocations.410  Error deinit() {411    assert(MemoryManager && "Invalid memory manager");412 413    // Delete and invalidate the memory manager. At this point, the memory414    // manager will deallocate all its allocations.415    delete MemoryManager;416    MemoryManager = nullptr;417 418    return Plugin::success();419  }420 421  /// Reuse or allocate memory through the memory manager.422  Error allocate(size_t Size, void **PtrStorage) {423    assert(MemoryManager && "Invalid memory manager");424    assert(PtrStorage && "Invalid pointer storage");425 426    auto PtrStorageOrErr = MemoryManager->allocate(Size, nullptr);427    if (!PtrStorageOrErr)428      return PtrStorageOrErr.takeError();429 430    *PtrStorage = *PtrStorageOrErr;431    if (Size && *PtrStorage == nullptr)432      return Plugin::error(ErrorCode::OUT_OF_RESOURCES,433                           "failure to allocate from AMDGPU memory manager");434 435    return Plugin::success();436  }437 438  /// Release an allocation to be reused.439  Error deallocate(void *Ptr) {440    if (MemoryManager->free(Ptr))441      return Plugin::error(ErrorCode::UNKNOWN,442                           "failure to deallocate from AMDGPU memory manager");443 444    return Plugin::success();445  }446 447private:448  /// Allocation callback that will be called once the memory manager does not449  /// have more previously allocated buffers.450  Expected<void *> allocate(size_t Size, void *HstPtr,451                            TargetAllocTy Kind) override;452 453  /// Deallocation callback that will be called by the memory manager.454  Error free(void *TgtPtr, TargetAllocTy Kind) override {455    return MemoryPool->deallocate(TgtPtr);456  }457 458  /// The underlying plugin that owns this memory manager.459  AMDGPUPluginTy &Plugin;460 461  /// The memory pool used to allocate memory.462  AMDGPUMemoryPoolTy *MemoryPool;463 464  /// Reference to the actual memory manager.465  MemoryManagerTy *MemoryManager;466};467 468/// Class implementing the AMDGPU device images' properties.469struct AMDGPUDeviceImageTy : public DeviceImageTy {470  /// Create the AMDGPU image with the id and the target image pointer.471  AMDGPUDeviceImageTy(int32_t ImageId, GenericDeviceTy &Device,472                      std::unique_ptr<MemoryBuffer> &&TgtImage)473      : DeviceImageTy(ImageId, Device, std::move(TgtImage)) {}474 475  /// Prepare and load the executable corresponding to the image.476  Error loadExecutable(const AMDGPUDeviceTy &Device);477 478  /// Unload the executable.479  Error unloadExecutable() {480    hsa_status_t Status = hsa_executable_destroy(Executable);481    return Plugin::check(Status, "error in hsa_executable_destroy: %s");482  }483 484  /// Get the executable.485  hsa_executable_t getExecutable() const { return Executable; }486 487  /// Get to Code Object Version of the ELF488  uint16_t getELFABIVersion() const { return ELFABIVersion; }489 490  /// Find an HSA device symbol by its name on the executable.491  Expected<hsa_executable_symbol_t>492  findDeviceSymbol(GenericDeviceTy &Device, StringRef SymbolName) const;493 494  /// Get additional info for kernel, e.g., register spill counts495  std::optional<offloading::amdgpu::AMDGPUKernelMetaData>496  getKernelInfo(StringRef Identifier) const {497    auto It = KernelInfoMap.find(Identifier);498 499    if (It == KernelInfoMap.end())500      return {};501 502    return It->second;503  }504 505private:506  /// The executable loaded on the agent.507  hsa_executable_t Executable;508  StringMap<offloading::amdgpu::AMDGPUKernelMetaData> KernelInfoMap;509  uint16_t ELFABIVersion;510};511 512/// Class implementing the AMDGPU kernel functionalities which derives from the513/// generic kernel class.514struct AMDGPUKernelTy : public GenericKernelTy {515  /// Create an AMDGPU kernel with a name and an execution mode.516  AMDGPUKernelTy(const char *Name) : GenericKernelTy(Name) {}517 518  /// Initialize the AMDGPU kernel.519  Error initImpl(GenericDeviceTy &Device, DeviceImageTy &Image) override {520    AMDGPUDeviceImageTy &AMDImage = static_cast<AMDGPUDeviceImageTy &>(Image);521 522    // Kernel symbols have a ".kd" suffix.523    std::string KernelName(getName());524    KernelName += ".kd";525 526    // Find the symbol on the device executable.527    auto SymbolOrErr = AMDImage.findDeviceSymbol(Device, KernelName);528    if (!SymbolOrErr)529      return SymbolOrErr.takeError();530 531    hsa_executable_symbol_t Symbol = *SymbolOrErr;532    hsa_symbol_kind_t SymbolType;533    hsa_status_t Status;534 535    // Retrieve different properties of the kernel symbol.536    std::pair<hsa_executable_symbol_info_t, void *> RequiredInfos[] = {537        {HSA_EXECUTABLE_SYMBOL_INFO_TYPE, &SymbolType},538        {HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_OBJECT, &KernelObject},539        {HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_KERNARG_SEGMENT_SIZE, &ArgsSize},540        {HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_GROUP_SEGMENT_SIZE, &GroupSize},541        {HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_DYNAMIC_CALLSTACK, &DynamicStack},542        {HSA_EXECUTABLE_SYMBOL_INFO_KERNEL_PRIVATE_SEGMENT_SIZE, &PrivateSize}};543 544    for (auto &Info : RequiredInfos) {545      Status = hsa_executable_symbol_get_info(Symbol, Info.first, Info.second);546      if (auto Err = Plugin::check(547              Status, "error in hsa_executable_symbol_get_info: %s"))548        return Err;549    }550 551    // Make sure it is a kernel symbol.552    if (SymbolType != HSA_SYMBOL_KIND_KERNEL)553      return Plugin::error(ErrorCode::INVALID_BINARY,554                           "symbol %s is not a kernel function");555 556    // TODO: Read the kernel descriptor for the max threads per block. May be557    // read from the image.558 559    ImplicitArgsSize =560        hsa_utils::getImplicitArgsSize(AMDImage.getELFABIVersion());561    DP("ELFABIVersion: %d\n", AMDImage.getELFABIVersion());562 563    // Get additional kernel info read from image564    KernelInfo = AMDImage.getKernelInfo(getName());565    if (!KernelInfo.has_value())566      INFO(OMP_INFOTYPE_PLUGIN_KERNEL, Device.getDeviceId(),567           "Could not read extra information for kernel %s.", getName());568 569    return Plugin::success();570  }571 572  /// Launch the AMDGPU kernel function.573  Error launchImpl(GenericDeviceTy &GenericDevice, uint32_t NumThreads[3],574                   uint32_t NumBlocks[3], KernelArgsTy &KernelArgs,575                   KernelLaunchParamsTy LaunchParams,576                   AsyncInfoWrapperTy &AsyncInfoWrapper) const override;577 578  /// Return maximum block size for maximum occupancy579  ///580  /// TODO: This needs to be implemented for amdgpu581  Expected<uint64_t> maxGroupSize(GenericDeviceTy &GenericDevice,582                                  uint64_t DynamicMemSize) const override {583    return Plugin::error(584        ErrorCode::UNSUPPORTED,585        "occupancy calculations for AMDGPU are not yet implemented");586  }587 588  /// Print more elaborate kernel launch info for AMDGPU589  Error printLaunchInfoDetails(GenericDeviceTy &GenericDevice,590                               KernelArgsTy &KernelArgs, uint32_t NumThreads[3],591                               uint32_t NumBlocks[3]) const override;592 593  /// Get group and private segment kernel size.594  uint32_t getGroupSize() const { return GroupSize; }595  uint32_t getPrivateSize() const { return PrivateSize; }596 597  /// Get the HSA kernel object representing the kernel function.598  uint64_t getKernelObject() const { return KernelObject; }599 600  /// Get the size of implicitargs based on the code object version.601  uint32_t getImplicitArgsSize() const { return ImplicitArgsSize; }602 603  /// Indicates whether or not we need to set up our own private segment size.604  bool usesDynamicStack() const { return DynamicStack; }605 606private:607  /// The kernel object to execute.608  uint64_t KernelObject;609 610  /// The args, group and private segments sizes required by a kernel instance.611  uint32_t ArgsSize;612  uint32_t GroupSize;613  uint32_t PrivateSize;614  bool DynamicStack;615 616  /// The size of implicit kernel arguments.617  uint32_t ImplicitArgsSize;618 619  /// Additional Info for the AMD GPU Kernel620  std::optional<offloading::amdgpu::AMDGPUKernelMetaData> KernelInfo;621};622 623/// Class representing an HSA signal. Signals are used to define dependencies624/// between asynchronous operations: kernel launches and memory transfers.625struct AMDGPUSignalTy {626  /// Create an empty signal.627  AMDGPUSignalTy() : HSASignal({0}), UseCount() {}628  AMDGPUSignalTy(AMDGPUDeviceTy &Device) : HSASignal({0}), UseCount() {}629 630  /// Initialize the signal with an initial value.631  Error init(uint32_t InitialValue = 1) {632    hsa_status_t Status =633        hsa_amd_signal_create(InitialValue, 0, nullptr, 0, &HSASignal);634    return Plugin::check(Status, "error in hsa_signal_create: %s");635  }636 637  /// Deinitialize the signal.638  Error deinit() {639    hsa_status_t Status = hsa_signal_destroy(HSASignal);640    return Plugin::check(Status, "error in hsa_signal_destroy: %s");641  }642 643  /// Wait until the signal gets a zero value.644  Error wait(const uint64_t ActiveTimeout = 0,645             GenericDeviceTy *Device = nullptr) const {646    if (ActiveTimeout) {647      hsa_signal_value_t Got = 1;648      Got = hsa_signal_wait_scacquire(HSASignal, HSA_SIGNAL_CONDITION_EQ, 0,649                                      ActiveTimeout, HSA_WAIT_STATE_ACTIVE);650      if (Got == 0)651        return Plugin::success();652    }653 654    // If there is an RPC device attached to this stream we run it as a server.655    uint64_t Timeout = UINT64_MAX;656    auto WaitState = HSA_WAIT_STATE_BLOCKED;657    while (hsa_signal_wait_scacquire(HSASignal, HSA_SIGNAL_CONDITION_EQ, 0,658                                     Timeout, WaitState) != 0)659      ;660    return Plugin::success();661  }662 663  /// Load the value on the signal.664  hsa_signal_value_t load() const {665    return hsa_signal_load_scacquire(HSASignal);666  }667 668  /// Signal decrementing by one.669  void signal() {670    assert(load() > 0 && "Invalid signal value");671    hsa_signal_subtract_screlease(HSASignal, 1);672  }673 674  /// Reset the signal value before reusing the signal. Do not call this675  /// function if the signal is being currently used by any watcher, such as a676  /// plugin thread or the HSA runtime.677  void reset() { hsa_signal_store_screlease(HSASignal, 1); }678 679  /// Increase the number of concurrent uses.680  void increaseUseCount() { UseCount.increase(); }681 682  /// Decrease the number of concurrent uses and return whether was the last.683  bool decreaseUseCount() { return UseCount.decrease(); }684 685  hsa_signal_t get() const { return HSASignal; }686 687private:688  /// The underlying HSA signal.689  hsa_signal_t HSASignal;690 691  /// Reference counter for tracking the concurrent use count. This is mainly692  /// used for knowing how many streams are using the signal.693  RefCountTy<> UseCount;694};695 696/// Classes for holding AMDGPU signals and managing signals.697using AMDGPUSignalRef = AMDGPUResourceRef<AMDGPUSignalTy>;698using AMDGPUSignalManagerTy = GenericDeviceResourceManagerTy<AMDGPUSignalRef>;699 700/// Class holding an HSA queue to submit kernel and barrier packets.701struct AMDGPUQueueTy {702  /// Create an empty queue.703  AMDGPUQueueTy() : Queue(nullptr), Mutex(), NumUsers(0) {}704 705  /// Lazily initialize a new queue belonging to a specific agent.706  Error init(GenericDeviceTy &Device, hsa_agent_t Agent, int32_t QueueSize) {707    if (Queue)708      return Plugin::success();709    hsa_status_t Status =710        hsa_queue_create(Agent, QueueSize, HSA_QUEUE_TYPE_MULTI, callbackError,711                         &Device, UINT32_MAX, UINT32_MAX, &Queue);712    return Plugin::check(Status, "error in hsa_queue_create: %s");713  }714 715  /// Deinitialize the queue and destroy its resources.716  Error deinit() {717    std::lock_guard<std::mutex> Lock(Mutex);718    if (!Queue)719      return Plugin::success();720    hsa_status_t Status = hsa_queue_destroy(Queue);721    return Plugin::check(Status, "error in hsa_queue_destroy: %s");722  }723 724  /// Returns the number of streams, this queue is currently assigned to.725  bool getUserCount() const { return NumUsers; }726 727  /// Returns if the underlying HSA queue is initialized.728  bool isInitialized() { return Queue != nullptr; }729 730  /// Decrement user count of the queue object.731  void removeUser() { --NumUsers; }732 733  /// Increase user count of the queue object.734  void addUser() { ++NumUsers; }735 736  /// Push a kernel launch to the queue. The kernel launch requires an output737  /// signal and can define an optional input signal (nullptr if none).738  Error pushKernelLaunch(const AMDGPUKernelTy &Kernel, void *KernelArgs,739                         uint32_t NumThreads[3], uint32_t NumBlocks[3],740                         uint32_t GroupSize, uint64_t StackSize,741                         AMDGPUSignalTy *OutputSignal,742                         AMDGPUSignalTy *InputSignal) {743    assert(OutputSignal && "Invalid kernel output signal");744 745    // Lock the queue during the packet publishing process. Notice this blocks746    // the addition of other packets to the queue. The following piece of code747    // should be lightweight; do not block the thread, allocate memory, etc.748    std::lock_guard<std::mutex> Lock(Mutex);749    assert(Queue && "Interacted with a non-initialized queue!");750 751    // Add a barrier packet before the kernel packet in case there is a pending752    // preceding operation. The barrier packet will delay the processing of753    // subsequent queue's packets until the barrier input signal are satisfied.754    // No need output signal needed because the dependency is already guaranteed755    // by the queue barrier itself.756    if (InputSignal && InputSignal->load())757      if (auto Err = pushBarrierImpl(nullptr, InputSignal))758        return Err;759 760    // Now prepare the kernel packet.761    uint64_t PacketId;762    hsa_kernel_dispatch_packet_t *Packet = acquirePacket(PacketId);763    assert(Packet && "Invalid packet");764 765    // The first 32 bits of the packet are written after the other fields766    uint16_t Dims = NumBlocks[2] * NumThreads[2] > 1767                        ? 3768                        : 1 + (NumBlocks[1] * NumThreads[1] != 1);769    uint16_t Setup = UINT16_C(Dims)770                     << HSA_KERNEL_DISPATCH_PACKET_SETUP_DIMENSIONS;771    Packet->workgroup_size_x = NumThreads[0];772    Packet->workgroup_size_y = NumThreads[1];773    Packet->workgroup_size_z = NumThreads[2];774    Packet->reserved0 = 0;775    Packet->grid_size_x = NumBlocks[0] * NumThreads[0];776    Packet->grid_size_y = NumBlocks[1] * NumThreads[1];777    Packet->grid_size_z = NumBlocks[2] * NumThreads[2];778    Packet->private_segment_size =779        Kernel.usesDynamicStack() ? StackSize : Kernel.getPrivateSize();780    Packet->group_segment_size = GroupSize;781    Packet->kernel_object = Kernel.getKernelObject();782    Packet->kernarg_address = KernelArgs;783    Packet->reserved2 = 0;784    Packet->completion_signal = OutputSignal->get();785 786    // Publish the packet. Do not modify the packet after this point.787    publishKernelPacket(PacketId, Setup, Packet);788 789    return Plugin::success();790  }791 792  /// Push a barrier packet that will wait up to two input signals. All signals793  /// are optional (nullptr if none).794  Error pushBarrier(AMDGPUSignalTy *OutputSignal,795                    const AMDGPUSignalTy *InputSignal1,796                    const AMDGPUSignalTy *InputSignal2) {797    // Lock the queue during the packet publishing process.798    std::lock_guard<std::mutex> Lock(Mutex);799    assert(Queue && "Interacted with a non-initialized queue!");800 801    // Push the barrier with the lock acquired.802    return pushBarrierImpl(OutputSignal, InputSignal1, InputSignal2);803  }804 805private:806  /// Push a barrier packet that will wait up to two input signals. Assumes the807  /// the queue lock is acquired.808  Error pushBarrierImpl(AMDGPUSignalTy *OutputSignal,809                        const AMDGPUSignalTy *InputSignal1,810                        const AMDGPUSignalTy *InputSignal2 = nullptr) {811    // Add a queue barrier waiting on both the other stream's operation and the812    // last operation on the current stream (if any).813    uint64_t PacketId;814    hsa_barrier_and_packet_t *Packet =815        (hsa_barrier_and_packet_t *)acquirePacket(PacketId);816    assert(Packet && "Invalid packet");817 818    Packet->reserved0 = 0;819    Packet->reserved1 = 0;820    Packet->dep_signal[0] = {0};821    Packet->dep_signal[1] = {0};822    Packet->dep_signal[2] = {0};823    Packet->dep_signal[3] = {0};824    Packet->dep_signal[4] = {0};825    Packet->reserved2 = 0;826    Packet->completion_signal = {0};827 828    // Set input and output dependencies if needed.829    if (OutputSignal)830      Packet->completion_signal = OutputSignal->get();831    if (InputSignal1)832      Packet->dep_signal[0] = InputSignal1->get();833    if (InputSignal2)834      Packet->dep_signal[1] = InputSignal2->get();835 836    // Publish the packet. Do not modify the packet after this point.837    publishBarrierPacket(PacketId, Packet);838 839    return Plugin::success();840  }841 842  /// Acquire a packet from the queue. This call may block the thread if there843  /// is no space in the underlying HSA queue. It may need to wait until the HSA844  /// runtime processes some packets. Assumes the queue lock is acquired.845  hsa_kernel_dispatch_packet_t *acquirePacket(uint64_t &PacketId) {846    // Increase the queue index with relaxed memory order. Notice this will need847    // another subsequent atomic operation with acquire order.848    PacketId = hsa_queue_add_write_index_relaxed(Queue, 1);849 850    // Wait for the package to be available. Notice the atomic operation uses851    // the acquire memory order.852    while (PacketId - hsa_queue_load_read_index_scacquire(Queue) >= Queue->size)853      ;854 855    // Return the packet reference.856    const uint32_t Mask = Queue->size - 1; // The size is a power of 2.857    return (hsa_kernel_dispatch_packet_t *)Queue->base_address +858           (PacketId & Mask);859  }860 861  /// Publish the kernel packet so that the HSA runtime can start processing862  /// the kernel launch. Do not modify the packet once this function is called.863  /// Assumes the queue lock is acquired.864  void publishKernelPacket(uint64_t PacketId, uint16_t Setup,865                           hsa_kernel_dispatch_packet_t *Packet) {866    uint32_t *PacketPtr = reinterpret_cast<uint32_t *>(Packet);867 868    uint16_t Header = HSA_PACKET_TYPE_KERNEL_DISPATCH << HSA_PACKET_HEADER_TYPE;869    Header |= HSA_FENCE_SCOPE_SYSTEM << HSA_PACKET_HEADER_ACQUIRE_FENCE_SCOPE;870    Header |= HSA_FENCE_SCOPE_SYSTEM << HSA_PACKET_HEADER_RELEASE_FENCE_SCOPE;871 872    // Publish the packet. Do not modify the package after this point.873    uint32_t HeaderWord = Header | (Setup << 16u);874    __atomic_store_n(PacketPtr, HeaderWord, __ATOMIC_RELEASE);875 876    // Signal the doorbell about the published packet.877    hsa_signal_store_relaxed(Queue->doorbell_signal, PacketId);878  }879 880  /// Publish the barrier packet so that the HSA runtime can start processing881  /// the barrier. Next packets in the queue will not be processed until all882  /// barrier dependencies (signals) are satisfied. Assumes the queue is locked883  void publishBarrierPacket(uint64_t PacketId,884                            hsa_barrier_and_packet_t *Packet) {885    uint32_t *PacketPtr = reinterpret_cast<uint32_t *>(Packet);886    uint16_t Setup = 0;887    uint16_t Header = HSA_PACKET_TYPE_BARRIER_AND << HSA_PACKET_HEADER_TYPE;888    Header |= HSA_FENCE_SCOPE_SYSTEM << HSA_PACKET_HEADER_ACQUIRE_FENCE_SCOPE;889    Header |= HSA_FENCE_SCOPE_SYSTEM << HSA_PACKET_HEADER_RELEASE_FENCE_SCOPE;890 891    // Publish the packet. Do not modify the package after this point.892    uint32_t HeaderWord = Header | (Setup << 16u);893    __atomic_store_n(PacketPtr, HeaderWord, __ATOMIC_RELEASE);894 895    // Signal the doorbell about the published packet.896    hsa_signal_store_relaxed(Queue->doorbell_signal, PacketId);897  }898 899  /// Callback that will be called when an error is detected on the HSA queue.900  static void callbackError(hsa_status_t Status, hsa_queue_t *Source,901                            void *Data);902 903  /// The HSA queue.904  hsa_queue_t *Queue;905 906  /// Mutex to protect the acquiring and publishing of packets. For the moment,907  /// we need this mutex to prevent publishing packets that are not ready to be908  /// published in a multi-thread scenario. Without a queue lock, a thread T1909  /// could acquire packet P and thread T2 acquire packet P+1. Thread T2 could910  /// publish its packet P+1 (signaling the queue's doorbell) before packet P911  /// from T1 is ready to be processed. That scenario should be invalid. Thus,912  /// we use the following mutex to make packet acquiring and publishing atomic.913  /// TODO: There are other more advanced approaches to avoid this mutex using914  /// atomic operations. We can further investigate it if this is a bottleneck.915  std::mutex Mutex;916 917  /// The number of streams, this queue is currently assigned to. A queue is918  /// considered idle when this is zero, otherwise: busy.919  uint32_t NumUsers;920};921 922/// Struct that implements a stream of asynchronous operations for AMDGPU923/// devices. This class relies on signals to implement streams and define the924/// dependencies between asynchronous operations.925struct AMDGPUStreamTy {926public:927  /// Function pointer type for `pushHostCallback`928  using HostFnType = void (*)(void *);929 930private:931  /// Utility struct holding arguments for async H2H memory copies.932  struct MemcpyArgsTy {933    void *Dst;934    const void *Src;935    size_t Size;936    size_t NumTimes;937  };938 939  /// Utility struct holding arguments for freeing buffers to memory managers.940  struct ReleaseBufferArgsTy {941    void *Buffer;942    AMDGPUMemoryManagerTy *MemoryManager;943  };944 945  /// Utility struct holding arguments for releasing signals to signal managers.946  struct ReleaseSignalArgsTy {947    AMDGPUSignalTy *Signal;948    AMDGPUSignalManagerTy *SignalManager;949  };950 951  using AMDGPUStreamCallbackTy = Error(void *Data);952 953  /// The stream is composed of N stream's slots. The struct below represents954  /// the fields of each slot. Each slot has a signal and an optional action955  /// function. When appending an HSA asynchronous operation to the stream, one956  /// slot is consumed and used to store the operation's information. The957  /// operation's output signal is set to the consumed slot's signal. If there958  /// is a previous asynchronous operation on the previous slot, the HSA async959  /// operation's input signal is set to the signal of the previous slot. This960  /// way, we obtain a chain of dependent async operations. The action is a961  /// function that will be executed eventually after the operation is962  /// completed, e.g., for releasing a buffer.963  struct StreamSlotTy {964    /// The output signal of the stream operation. May be used by the subsequent965    /// operation as input signal.966    AMDGPUSignalTy *Signal;967 968    /// The actions that must be performed after the operation's completion. Set969    /// to nullptr when there is no action to perform.970    llvm::SmallVector<AMDGPUStreamCallbackTy *> Callbacks;971 972    /// Space for the action's arguments. A pointer to these arguments is passed973    /// to the action function. Notice the space of arguments is limited.974    union ActionArgsTy {975      MemcpyArgsTy MemcpyArgs;976      ReleaseBufferArgsTy ReleaseBufferArgs;977      ReleaseSignalArgsTy ReleaseSignalArgs;978      void *CallbackArgs;979    };980 981    llvm::SmallVector<ActionArgsTy> ActionArgs;982 983    /// Create an empty slot.984    StreamSlotTy() : Signal(nullptr), Callbacks({}), ActionArgs({}) {}985 986    /// Schedule a host memory copy action on the slot.987    ///988    /// Num times will repeat the copy that many times, sequentually in the dest989    /// buffer.990    Error schedHostMemoryCopy(void *Dst, const void *Src, size_t Size,991                              size_t NumTimes = 1) {992      Callbacks.emplace_back(memcpyAction);993      ActionArgs.emplace_back().MemcpyArgs =994          MemcpyArgsTy{Dst, Src, Size, NumTimes};995      return Plugin::success();996    }997 998    /// Schedule a release buffer action on the slot.999    Error schedReleaseBuffer(void *Buffer, AMDGPUMemoryManagerTy &Manager) {1000      Callbacks.emplace_back(releaseBufferAction);1001      ActionArgs.emplace_back().ReleaseBufferArgs =1002          ReleaseBufferArgsTy{Buffer, &Manager};1003      return Plugin::success();1004    }1005 1006    /// Schedule a signal release action on the slot.1007    Error schedReleaseSignal(AMDGPUSignalTy *SignalToRelease,1008                             AMDGPUSignalManagerTy *SignalManager) {1009      Callbacks.emplace_back(releaseSignalAction);1010      ActionArgs.emplace_back().ReleaseSignalArgs =1011          ReleaseSignalArgsTy{SignalToRelease, SignalManager};1012      return Plugin::success();1013    }1014 1015    /// Register a callback to be called on compleition1016    Error schedCallback(AMDGPUStreamCallbackTy *Func, void *Data) {1017      Callbacks.emplace_back(Func);1018      ActionArgs.emplace_back().CallbackArgs = Data;1019 1020      return Plugin::success();1021    }1022 1023    // Perform the action if needed.1024    Error performAction() {1025      if (Callbacks.empty())1026        return Plugin::success();1027 1028      assert(Callbacks.size() == ActionArgs.size() && "Size mismatch");1029      for (auto [Callback, ActionArg] : llvm::zip(Callbacks, ActionArgs)) {1030        // Perform the action.1031        if (Callback == memcpyAction) {1032          if (auto Err = memcpyAction(&ActionArg))1033            return Err;1034        } else if (Callback == releaseBufferAction) {1035          if (auto Err = releaseBufferAction(&ActionArg))1036            return Err;1037        } else if (Callback == releaseSignalAction) {1038          if (auto Err = releaseSignalAction(&ActionArg))1039            return Err;1040        } else if (Callback) {1041          if (auto Err = Callback(ActionArg.CallbackArgs))1042            return Err;1043        }1044      }1045 1046      // Invalidate the action.1047      Callbacks.clear();1048      ActionArgs.clear();1049 1050      return Plugin::success();1051    }1052  };1053 1054  /// The device agent where the stream was created.1055  hsa_agent_t Agent;1056 1057  /// The queue that the stream uses to launch kernels.1058  AMDGPUQueueTy *Queue;1059 1060  /// The manager of signals to reuse signals.1061  AMDGPUSignalManagerTy &SignalManager;1062 1063  /// A reference to the associated device.1064  GenericDeviceTy &Device;1065 1066  /// Array of stream slots. Use std::deque because it can dynamically grow1067  /// without invalidating the already inserted elements. For instance, the1068  /// std::vector may invalidate the elements by reallocating the internal1069  /// array if there is not enough space on new insertions.1070  std::deque<StreamSlotTy> Slots;1071 1072  /// The next available slot on the queue. This is reset to zero each time the1073  /// stream is synchronized. It also indicates the current number of consumed1074  /// slots at a given time.1075  uint32_t NextSlot;1076 1077  /// The synchronization id. This number is increased each time the stream is1078  /// synchronized. It is useful to detect if an AMDGPUEventTy points to an1079  /// operation that was already finalized in a previous stream sycnhronize.1080  uint32_t SyncCycle;1081 1082  /// Mutex to protect stream's management.1083  mutable std::mutex Mutex;1084 1085  /// Timeout hint for HSA actively waiting for signal value to change1086  const uint64_t StreamBusyWaitMicroseconds;1087 1088  /// Indicate to spread data transfers across all available SDMAs1089  bool UseMultipleSdmaEngines;1090 1091  struct CallbackDataType {1092    HostFnType UserFn;1093    void *UserData;1094    AMDGPUSignalTy *OutputSignal;1095  };1096  /// Wrapper function for implementing host callbacks1097  static bool callbackWrapper([[maybe_unused]] hsa_signal_value_t Signal,1098                              void *UserData) {1099    auto CallbackData = reinterpret_cast<CallbackDataType *>(UserData);1100    CallbackData->UserFn(CallbackData->UserData);1101    CallbackData->OutputSignal->signal();1102    delete CallbackData;1103    return false;1104  }1105 1106  /// Return the current number of asynchronous operations on the stream.1107  uint32_t size() const { return NextSlot; }1108 1109  /// Return the last valid slot on the stream.1110  uint32_t last() const { return size() - 1; }1111 1112  /// Consume one slot from the stream. Since the stream uses signals on demand1113  /// and releases them once the slot is no longer used, the function requires1114  /// an idle signal for the new consumed slot.1115  std::pair<uint32_t, AMDGPUSignalTy *> consume(AMDGPUSignalTy *OutputSignal) {1116    // Double the stream size if needed. Since we use std::deque, this operation1117    // does not invalidate the already added slots.1118    if (Slots.size() == NextSlot)1119      Slots.resize(Slots.size() * 2);1120 1121    // Update the next available slot and the stream size.1122    uint32_t Curr = NextSlot++;1123 1124    // Retrieve the input signal, if any, of the current operation.1125    AMDGPUSignalTy *InputSignal = (Curr > 0) ? Slots[Curr - 1].Signal : nullptr;1126 1127    // Set the output signal of the current slot.1128    Slots[Curr].Signal = OutputSignal;1129 1130    return std::make_pair(Curr, InputSignal);1131  }1132 1133  /// Complete all pending post actions and reset the stream after synchronizing1134  /// or positively querying the stream.1135  Error complete() {1136    for (uint32_t Slot = 0; Slot < NextSlot; ++Slot) {1137      // Take the post action of the operation if any.1138      if (auto Err = Slots[Slot].performAction())1139        return Err;1140 1141      // Release the slot's signal if possible. Otherwise, another user will.1142      if (Slots[Slot].Signal->decreaseUseCount())1143        if (auto Err = SignalManager.returnResource(Slots[Slot].Signal))1144          return Err;1145 1146      Slots[Slot].Signal = nullptr;1147    }1148 1149    // Reset the stream slots to zero.1150    NextSlot = 0;1151 1152    // Increase the synchronization id since the stream completed a sync cycle.1153    SyncCycle += 1;1154 1155    return Plugin::success();1156  }1157 1158  /// Complete pending post actions until and including the event in target1159  /// slot.1160  Error completeUntil(uint32_t TargetSlot) {1161    for (uint32_t Slot = 0; Slot <= TargetSlot; ++Slot) {1162      // Take the post action of the operation if any.1163      if (auto Err = Slots[Slot].performAction())1164        return Err;1165    }1166 1167    return Plugin::success();1168  }1169 1170  /// Make the current stream wait on a specific operation of another stream.1171  /// The idea is to make the current stream waiting on two signals: 1) the last1172  /// signal of the current stream, and 2) the last signal of the other stream.1173  /// Use a barrier packet with two input signals.1174  Error waitOnStreamOperation(AMDGPUStreamTy &OtherStream, uint32_t Slot) {1175    if (Queue == nullptr)1176      return Plugin::error(ErrorCode::INVALID_NULL_POINTER,1177                           "target queue was nullptr");1178 1179    /// The signal that we must wait from the other stream.1180    AMDGPUSignalTy *OtherSignal = OtherStream.Slots[Slot].Signal;1181 1182    // Prevent the release of the other stream's signal.1183    OtherSignal->increaseUseCount();1184 1185    // Retrieve an available signal for the operation's output.1186    AMDGPUSignalTy *OutputSignal = nullptr;1187    if (auto Err = SignalManager.getResource(OutputSignal))1188      return Err;1189    OutputSignal->reset();1190    OutputSignal->increaseUseCount();1191 1192    // Consume stream slot and compute dependencies.1193    auto [Curr, InputSignal] = consume(OutputSignal);1194 1195    // Setup the post action to release the signal.1196    if (auto Err = Slots[Curr].schedReleaseSignal(OtherSignal, &SignalManager))1197      return Err;1198 1199    // Push a barrier into the queue with both input signals.1200    return Queue->pushBarrier(OutputSignal, InputSignal, OtherSignal);1201  }1202 1203  /// Callback for running a specific asynchronous operation. This callback is1204  /// used for hsa_amd_signal_async_handler. The argument is the operation that1205  /// should be executed. Notice we use the post action mechanism to codify the1206  /// asynchronous operation.1207  static bool asyncActionCallback(hsa_signal_value_t Value, void *Args) {1208    StreamSlotTy *Slot = reinterpret_cast<StreamSlotTy *>(Args);1209    assert(Slot && "Invalid slot");1210    assert(Slot->Signal && "Invalid signal");1211 1212    // This thread is outside the stream mutex. Make sure the thread sees the1213    // changes on the slot.1214    std::atomic_thread_fence(std::memory_order_acquire);1215 1216    // Perform the operation.1217    if (auto Err = Slot->performAction())1218      FATAL_MESSAGE(1, "Error performing post action: %s",1219                    toString(std::move(Err)).data());1220 1221    // Signal the output signal to notify the asynchronous operation finalized.1222    Slot->Signal->signal();1223 1224    // Unregister callback.1225    return false;1226  }1227 1228  // Callback for host-to-host memory copies. This is an asynchronous action.1229  static Error memcpyAction(void *Data) {1230    MemcpyArgsTy *Args = reinterpret_cast<MemcpyArgsTy *>(Data);1231    assert(Args && "Invalid arguments");1232    assert(Args->Dst && "Invalid destination buffer");1233    assert(Args->Src && "Invalid source buffer");1234 1235    auto *BasePtr = Args->Dst;1236    for (size_t I = 0; I < Args->NumTimes; I++) {1237      std::memcpy(BasePtr, Args->Src, Args->Size);1238      BasePtr = reinterpret_cast<uint8_t *>(BasePtr) + Args->Size;1239    }1240 1241    return Plugin::success();1242  }1243 1244  /// Releasing a memory buffer to a memory manager. This is a post completion1245  /// action. There are two kinds of memory buffers:1246  ///   1. For kernel arguments. This buffer can be freed after receiving the1247  ///   kernel completion signal.1248  ///   2. For H2D transfers that need pinned memory space for staging. This1249  ///   buffer can be freed after receiving the transfer completion signal.1250  ///   3. For D2H transfers that need pinned memory space for staging. This1251  ///   buffer cannot be freed after receiving the transfer completion signal1252  ///   because of the following asynchronous H2H callback.1253  ///      For this reason, This action can only be taken at1254  ///      AMDGPUStreamTy::complete()1255  /// Because of the case 3, all releaseBufferActions are taken at1256  /// AMDGPUStreamTy::complete() in the current implementation.1257  static Error releaseBufferAction(void *Data) {1258    ReleaseBufferArgsTy *Args = reinterpret_cast<ReleaseBufferArgsTy *>(Data);1259    assert(Args && "Invalid arguments");1260    assert(Args->MemoryManager && "Invalid memory manager");1261 1262    // Release the allocation to the memory manager.1263    return Args->MemoryManager->deallocate(Args->Buffer);1264  }1265 1266  /// Releasing a signal object back to SignalManager. This is a post completion1267  /// action. This action can only be taken at AMDGPUStreamTy::complete()1268  static Error releaseSignalAction(void *Data) {1269    ReleaseSignalArgsTy *Args = reinterpret_cast<ReleaseSignalArgsTy *>(Data);1270    assert(Args && "Invalid arguments");1271    assert(Args->Signal && "Invalid signal");1272    assert(Args->SignalManager && "Invalid signal manager");1273 1274    // Release the signal if needed.1275    if (Args->Signal->decreaseUseCount())1276      if (auto Err = Args->SignalManager->returnResource(Args->Signal))1277        return Err;1278 1279    return Plugin::success();1280  }1281 1282public:1283  /// Create an empty stream associated with a specific device.1284  AMDGPUStreamTy(AMDGPUDeviceTy &Device);1285 1286  /// Initialize the stream's signals.1287  Error init() { return Plugin::success(); }1288 1289  /// Deinitialize the stream's signals.1290  Error deinit() { return Plugin::success(); }1291 1292  /// Push a asynchronous kernel to the stream. The kernel arguments must be1293  /// placed in a special allocation for kernel args and must keep alive until1294  /// the kernel finalizes. Once the kernel is finished, the stream will release1295  /// the kernel args buffer to the specified memory manager.1296  Error pushKernelLaunch(const AMDGPUKernelTy &Kernel, void *KernelArgs,1297                         uint32_t NumThreads[3], uint32_t NumBlocks[3],1298                         uint32_t GroupSize, uint64_t StackSize,1299                         AMDGPUMemoryManagerTy &MemoryManager) {1300    if (Queue == nullptr)1301      return Plugin::error(ErrorCode::INVALID_NULL_POINTER,1302                           "target queue was nullptr");1303 1304    // Retrieve an available signal for the operation's output.1305    AMDGPUSignalTy *OutputSignal = nullptr;1306    if (auto Err = SignalManager.getResource(OutputSignal))1307      return Err;1308    OutputSignal->reset();1309    OutputSignal->increaseUseCount();1310 1311    std::lock_guard<std::mutex> StreamLock(Mutex);1312 1313    // Consume stream slot and compute dependencies.1314    auto [Curr, InputSignal] = consume(OutputSignal);1315 1316    // Setup the post action to release the kernel args buffer.1317    if (auto Err = Slots[Curr].schedReleaseBuffer(KernelArgs, MemoryManager))1318      return Err;1319 1320    // If we are running an RPC server we want to wake up the server thread1321    // whenever there is a kernel running and let it sleep otherwise.1322    if (Device.getRPCServer())1323      Device.Plugin.getRPCServer().Thread->notify();1324 1325    // Push the kernel with the output signal and an input signal (optional)1326    if (auto Err = Queue->pushKernelLaunch(Kernel, KernelArgs, NumThreads,1327                                           NumBlocks, GroupSize, StackSize,1328                                           OutputSignal, InputSignal))1329      return Err;1330 1331    // Register a callback to indicate when the kernel is complete.1332    if (Device.getRPCServer()) {1333      if (auto Err = Slots[Curr].schedCallback(1334              [](void *Data) -> llvm::Error {1335                GenericPluginTy &Plugin =1336                    *reinterpret_cast<GenericPluginTy *>(Data);1337                Plugin.getRPCServer().Thread->finish();1338                return Error::success();1339              },1340              &Device.Plugin))1341        return Err;1342    }1343    return Plugin::success();1344  }1345 1346  /// Push an asynchronous memory copy between pinned memory buffers.1347  Error pushPinnedMemoryCopyAsync(void *Dst, const void *Src,1348                                  uint64_t CopySize) {1349    // Retrieve an available signal for the operation's output.1350    AMDGPUSignalTy *OutputSignal = nullptr;1351    if (auto Err = SignalManager.getResource(OutputSignal))1352      return Err;1353    OutputSignal->reset();1354    OutputSignal->increaseUseCount();1355 1356    std::lock_guard<std::mutex> Lock(Mutex);1357 1358    // Consume stream slot and compute dependencies.1359    auto [Curr, InputSignal] = consume(OutputSignal);1360 1361    // Issue the async memory copy.1362    if (InputSignal && InputSignal->load()) {1363      hsa_signal_t InputSignalRaw = InputSignal->get();1364      return hsa_utils::asyncMemCopy(UseMultipleSdmaEngines, Dst, Agent, Src,1365                                     Agent, CopySize, 1, &InputSignalRaw,1366                                     OutputSignal->get());1367    }1368 1369    return hsa_utils::asyncMemCopy(UseMultipleSdmaEngines, Dst, Agent, Src,1370                                   Agent, CopySize, 0, nullptr,1371                                   OutputSignal->get());1372  }1373 1374  /// Push an asynchronous memory copy device-to-host involving an unpinned1375  /// memory buffer. The operation consists of a two-step copy from the1376  /// device buffer to an intermediate pinned host buffer, and then, to a1377  /// unpinned host buffer. Both operations are asynchronous and dependent.1378  /// The intermediate pinned buffer will be released to the specified memory1379  /// manager once the operation completes.1380  Error pushMemoryCopyD2HAsync(void *Dst, const void *Src, void *Inter,1381                               uint64_t CopySize,1382                               AMDGPUMemoryManagerTy &MemoryManager) {1383    // Retrieve available signals for the operation's outputs.1384    AMDGPUSignalTy *OutputSignals[2] = {};1385    if (auto Err = SignalManager.getResources(/*Num=*/2, OutputSignals))1386      return Err;1387    for (auto *Signal : OutputSignals) {1388      Signal->reset();1389      Signal->increaseUseCount();1390    }1391 1392    std::lock_guard<std::mutex> Lock(Mutex);1393 1394    // Consume stream slot and compute dependencies.1395    auto [Curr, InputSignal] = consume(OutputSignals[0]);1396 1397    // Setup the post action for releasing the intermediate buffer.1398    if (auto Err = Slots[Curr].schedReleaseBuffer(Inter, MemoryManager))1399      return Err;1400 1401    // Issue the first step: device to host transfer. Avoid defining the input1402    // dependency if already satisfied.1403    if (InputSignal && InputSignal->load()) {1404      hsa_signal_t InputSignalRaw = InputSignal->get();1405      if (auto Err = hsa_utils::asyncMemCopy(1406              UseMultipleSdmaEngines, Inter, Agent, Src, Agent, CopySize, 1,1407              &InputSignalRaw, OutputSignals[0]->get()))1408        return Err;1409    } else {1410      if (auto Err = hsa_utils::asyncMemCopy(UseMultipleSdmaEngines, Inter,1411                                             Agent, Src, Agent, CopySize, 0,1412                                             nullptr, OutputSignals[0]->get()))1413        return Err;1414    }1415 1416    // Consume another stream slot and compute dependencies.1417    std::tie(Curr, InputSignal) = consume(OutputSignals[1]);1418    assert(InputSignal && "Invalid input signal");1419 1420    // The std::memcpy is done asynchronously using an async handler. We store1421    // the function's information in the action but it's not actually an action.1422    if (auto Err = Slots[Curr].schedHostMemoryCopy(Dst, Inter, CopySize))1423      return Err;1424 1425    // Make changes on this slot visible to the async handler's thread.1426    std::atomic_thread_fence(std::memory_order_release);1427 1428    // Issue the second step: host to host transfer.1429    hsa_status_t Status = hsa_amd_signal_async_handler(1430        InputSignal->get(), HSA_SIGNAL_CONDITION_EQ, 0, asyncActionCallback,1431        (void *)&Slots[Curr]);1432 1433    return Plugin::check(Status, "error in hsa_amd_signal_async_handler: %s");1434  }1435 1436  /// Push an asynchronous memory copy host-to-device involving an unpinned1437  /// memory buffer. The operation consists of a two-step copy from the1438  /// unpinned host buffer to an intermediate pinned host buffer, and then, to1439  /// the pinned host buffer. Both operations are asynchronous and dependent.1440  /// The intermediate pinned buffer will be released to the specified memory1441  /// manager once the operation completes.1442  Error pushMemoryCopyH2DAsync(void *Dst, const void *Src, void *Inter,1443                               uint64_t CopySize,1444                               AMDGPUMemoryManagerTy &MemoryManager,1445                               size_t NumTimes = 1) {1446    // Retrieve available signals for the operation's outputs.1447    AMDGPUSignalTy *OutputSignals[2] = {};1448    if (auto Err = SignalManager.getResources(/*Num=*/2, OutputSignals))1449      return Err;1450    for (auto *Signal : OutputSignals) {1451      Signal->reset();1452      Signal->increaseUseCount();1453    }1454 1455    AMDGPUSignalTy *OutputSignal = OutputSignals[0];1456 1457    std::lock_guard<std::mutex> Lock(Mutex);1458 1459    // Consume stream slot and compute dependencies.1460    auto [Curr, InputSignal] = consume(OutputSignal);1461 1462    // Issue the first step: host to host transfer.1463    if (InputSignal && InputSignal->load()) {1464      // The std::memcpy is done asynchronously using an async handler. We store1465      // the function's information in the action but it is not actually a1466      // post action.1467      if (auto Err =1468              Slots[Curr].schedHostMemoryCopy(Inter, Src, CopySize, NumTimes))1469        return Err;1470 1471      // Make changes on this slot visible to the async handler's thread.1472      std::atomic_thread_fence(std::memory_order_release);1473 1474      hsa_status_t Status = hsa_amd_signal_async_handler(1475          InputSignal->get(), HSA_SIGNAL_CONDITION_EQ, 0, asyncActionCallback,1476          (void *)&Slots[Curr]);1477 1478      if (auto Err = Plugin::check(Status,1479                                   "error in hsa_amd_signal_async_handler: %s"))1480        return Err;1481 1482      // Let's use now the second output signal.1483      OutputSignal = OutputSignals[1];1484 1485      // Consume another stream slot and compute dependencies.1486      std::tie(Curr, InputSignal) = consume(OutputSignal);1487    } else {1488      // All preceding operations completed, copy the memory synchronously.1489      auto *InterPtr = Inter;1490      for (size_t I = 0; I < NumTimes; I++) {1491        std::memcpy(InterPtr, Src, CopySize);1492        InterPtr = reinterpret_cast<uint8_t *>(InterPtr) + CopySize;1493      }1494 1495      // Return the second signal because it will not be used.1496      OutputSignals[1]->decreaseUseCount();1497      if (auto Err = SignalManager.returnResource(OutputSignals[1]))1498        return Err;1499    }1500 1501    // Setup the post action to release the intermediate pinned buffer.1502    if (auto Err = Slots[Curr].schedReleaseBuffer(Inter, MemoryManager))1503      return Err;1504 1505    // Issue the second step: host to device transfer. Avoid defining the input1506    // dependency if already satisfied.1507    if (InputSignal && InputSignal->load()) {1508      hsa_signal_t InputSignalRaw = InputSignal->get();1509      return hsa_utils::asyncMemCopy(UseMultipleSdmaEngines, Dst, Agent, Inter,1510                                     Agent, CopySize * NumTimes, 1,1511                                     &InputSignalRaw, OutputSignal->get());1512    }1513    return hsa_utils::asyncMemCopy(UseMultipleSdmaEngines, Dst, Agent, Inter,1514                                   Agent, CopySize * NumTimes, 0, nullptr,1515                                   OutputSignal->get());1516  }1517 1518  // AMDGPUDeviceTy is incomplete here, passing the underlying agent instead1519  Error pushMemoryCopyD2DAsync(void *Dst, hsa_agent_t DstAgent, const void *Src,1520                               hsa_agent_t SrcAgent, uint64_t CopySize) {1521    AMDGPUSignalTy *OutputSignal;1522    if (auto Err = SignalManager.getResources(/*Num=*/1, &OutputSignal))1523      return Err;1524    OutputSignal->reset();1525    OutputSignal->increaseUseCount();1526 1527    std::lock_guard<std::mutex> Lock(Mutex);1528 1529    // Consume stream slot and compute dependencies.1530    auto [Curr, InputSignal] = consume(OutputSignal);1531 1532    // The agents need to have access to the corresponding memory1533    // This is presently only true if the pointers were originally1534    // allocated by this runtime or the caller made the appropriate1535    // access calls.1536 1537    if (InputSignal && InputSignal->load()) {1538      hsa_signal_t InputSignalRaw = InputSignal->get();1539      return hsa_utils::asyncMemCopy(UseMultipleSdmaEngines, Dst, DstAgent, Src,1540                                     SrcAgent, CopySize, 1, &InputSignalRaw,1541                                     OutputSignal->get());1542    }1543    return hsa_utils::asyncMemCopy(UseMultipleSdmaEngines, Dst, DstAgent, Src,1544                                   SrcAgent, CopySize, 0, nullptr,1545                                   OutputSignal->get());1546  }1547 1548  Error pushHostCallback(HostFnType Callback, void *UserData) {1549    // Retrieve an available signal for the operation's output.1550    AMDGPUSignalTy *OutputSignal = nullptr;1551    if (auto Err = SignalManager.getResource(OutputSignal))1552      return Err;1553    OutputSignal->reset();1554    OutputSignal->increaseUseCount();1555 1556    AMDGPUSignalTy *InputSignal;1557    {1558      std::lock_guard<std::mutex> Lock(Mutex);1559 1560      // Consume stream slot and compute dependencies.1561      InputSignal = consume(OutputSignal).second;1562    }1563 1564    auto *CallbackData = new CallbackDataType{Callback, UserData, OutputSignal};1565    if (InputSignal && InputSignal->load()) {1566      hsa_status_t Status = hsa_amd_signal_async_handler(1567          InputSignal->get(), HSA_SIGNAL_CONDITION_EQ, 0, callbackWrapper,1568          CallbackData);1569 1570      return Plugin::check(Status, "error in hsa_amd_signal_async_handler: %s");1571    }1572 1573    // No dependencies - schedule it now.1574    // Using a seperate thread because this function should run asynchronously1575    // and not block the main thread.1576    std::thread([](void *CallbackData) { callbackWrapper(0, CallbackData); },1577                CallbackData)1578        .detach();1579    return Plugin::success();1580  }1581 1582  /// Synchronize with the stream. The current thread waits until all operations1583  /// are finalized and it performs the pending post actions (i.e., releasing1584  /// intermediate buffers).1585  Error synchronize() {1586    std::lock_guard<std::mutex> Lock(Mutex);1587 1588    // No need to synchronize anything.1589    if (size() == 0)1590      return Plugin::success();1591 1592    // Wait until all previous operations on the stream have completed.1593    if (auto Err =1594            Slots[last()].Signal->wait(StreamBusyWaitMicroseconds, &Device))1595      return Err;1596 1597    // Reset the stream and perform all pending post actions.1598    return complete();1599  }1600 1601  /// Synchronize the stream until the given event. The current thread waits1602  /// until the provided event is finalized, and it performs the pending post1603  /// actions for that and prior events.1604  Error synchronizeOn(AMDGPUEventTy &Event);1605 1606  /// Return true if the event from this queue is complete1607  Expected<bool> isEventComplete(const AMDGPUEventTy &Event);1608 1609  /// Query the stream and complete pending post actions if operations finished.1610  /// Return whether all the operations completed. This operation does not block1611  /// the calling thread.1612  Expected<bool> query() {1613    std::lock_guard<std::mutex> Lock(Mutex);1614 1615    // No need to query anything.1616    if (size() == 0)1617      return true;1618 1619    // The last operation did not complete yet. Return directly.1620    if (Slots[last()].Signal->load())1621      return false;1622 1623    // Reset the stream and perform all pending post actions.1624    if (auto Err = complete())1625      return std::move(Err);1626 1627    return true;1628  }1629 1630  const AMDGPUQueueTy *getQueue() const { return Queue; }1631 1632  /// Record the state of the stream on an event.1633  Error recordEvent(AMDGPUEventTy &Event) const;1634 1635  /// Make the stream wait on an event.1636  Error waitEvent(const AMDGPUEventTy &Event);1637 1638  friend struct AMDGPUStreamManagerTy;1639};1640 1641/// Class representing an event on AMDGPU. The event basically stores some1642/// information regarding the state of the recorded stream.1643struct AMDGPUEventTy {1644  /// Create an empty event.1645  AMDGPUEventTy(AMDGPUDeviceTy &Device)1646      : RecordedStream(nullptr), RecordedSlot(-1), RecordedSyncCycle(-1) {}1647 1648  /// Initialize and deinitialize.1649  Error init() { return Plugin::success(); }1650  Error deinit() { return Plugin::success(); }1651 1652  /// Record the state of a stream on the event.1653  Error record(AMDGPUStreamTy &Stream) {1654    std::lock_guard<std::mutex> Lock(Mutex);1655 1656    // Ignore the last recorded stream.1657    RecordedStream = &Stream;1658 1659    return Stream.recordEvent(*this);1660  }1661 1662  /// Make a stream wait on the current event.1663  Error wait(AMDGPUStreamTy &Stream) {1664    std::lock_guard<std::mutex> Lock(Mutex);1665 1666    if (!RecordedStream)1667      return Plugin::error(ErrorCode::INVALID_ARGUMENT,1668                           "event does not have any recorded stream");1669 1670    // Synchronizing the same stream. Do nothing.1671    if (RecordedStream == &Stream)1672      return Plugin::success();1673 1674    // No need to wait anything, the recorded stream already finished the1675    // corresponding operation.1676    if (RecordedSlot < 0)1677      return Plugin::success();1678 1679    return Stream.waitEvent(*this);1680  }1681 1682  Error sync() {1683    std::lock_guard<std::mutex> Lock(Mutex);1684 1685    if (!RecordedStream)1686      return Plugin::error(ErrorCode::INVALID_ARGUMENT,1687                           "event does not have any recorded stream");1688 1689    // No need to wait on anything, the recorded stream already finished the1690    // corresponding operation.1691    if (RecordedSlot < 0)1692      return Plugin::success();1693 1694    return RecordedStream->synchronizeOn(*this);1695  }1696 1697protected:1698  /// The stream registered in this event.1699  AMDGPUStreamTy *RecordedStream;1700 1701  /// The recordered operation on the recorded stream.1702  int64_t RecordedSlot;1703 1704  /// The sync cycle when the stream was recorded. Used to detect stale events.1705  int64_t RecordedSyncCycle;1706 1707  /// Mutex to safely access event fields.1708  mutable std::mutex Mutex;1709 1710  friend struct AMDGPUStreamTy;1711};1712 1713Error AMDGPUStreamTy::recordEvent(AMDGPUEventTy &Event) const {1714  std::lock_guard<std::mutex> Lock(Mutex);1715 1716  if (size() > 0) {1717    // Record the synchronize identifier (to detect stale recordings) and1718    // the last valid stream's operation.1719    Event.RecordedSyncCycle = SyncCycle;1720    Event.RecordedSlot = last();1721 1722    assert(Event.RecordedSyncCycle >= 0 && "Invalid recorded sync cycle");1723    assert(Event.RecordedSlot >= 0 && "Invalid recorded slot");1724  } else {1725    // The stream is empty, everything already completed, record nothing.1726    Event.RecordedSyncCycle = -1;1727    Event.RecordedSlot = -1;1728  }1729  return Plugin::success();1730}1731 1732Error AMDGPUStreamTy::waitEvent(const AMDGPUEventTy &Event) {1733  // Retrieve the recorded stream on the event.1734  AMDGPUStreamTy &RecordedStream = *Event.RecordedStream;1735 1736  std::scoped_lock<std::mutex, std::mutex> Lock(Mutex, RecordedStream.Mutex);1737 1738  // The recorded stream already completed the operation because the synchronize1739  // identifier is already outdated.1740  if (RecordedStream.SyncCycle != (uint32_t)Event.RecordedSyncCycle)1741    return Plugin::success();1742 1743  // Again, the recorded stream already completed the operation, the last1744  // operation's output signal is satisfied.1745  if (!RecordedStream.Slots[Event.RecordedSlot].Signal->load())1746    return Plugin::success();1747 1748  // Otherwise, make the current stream wait on the other stream's operation.1749  return waitOnStreamOperation(RecordedStream, Event.RecordedSlot);1750}1751 1752Error AMDGPUStreamTy::synchronizeOn(AMDGPUEventTy &Event) {1753  std::lock_guard<std::mutex> Lock(Mutex);1754 1755  // If this event was for an older sync cycle, it has already been finalized1756  if (Event.RecordedSyncCycle < SyncCycle)1757    return Plugin::success();1758  assert(Event.RecordedSyncCycle == SyncCycle && "event is from the future?");1759 1760  // Wait until the requested slot has completed1761  if (auto Err = Slots[Event.RecordedSlot].Signal->wait(1762          StreamBusyWaitMicroseconds, &Device))1763    return Err;1764 1765  // If the event is the last one in the stream, just do a full finalize1766  if (Event.RecordedSlot == last())1767    return complete();1768 1769  // Otherwise, only finalize until the appropriate event1770  return completeUntil(Event.RecordedSlot);1771}1772 1773Expected<bool> AMDGPUStreamTy::isEventComplete(const AMDGPUEventTy &Event) {1774  std::lock_guard<std::mutex> Lock(Mutex);1775  assert(Event.RecordedStream == this && "event is for a different stream");1776 1777  if (Event.RecordedSyncCycle < SyncCycle) {1778    return true;1779  }1780  assert(Event.RecordedSyncCycle == SyncCycle && "event is from the future?");1781 1782  return !Slots[Event.RecordedSlot].Signal->load();1783}1784 1785struct AMDGPUStreamManagerTy final1786    : GenericDeviceResourceManagerTy<AMDGPUResourceRef<AMDGPUStreamTy>> {1787  using ResourceRef = AMDGPUResourceRef<AMDGPUStreamTy>;1788  using ResourcePoolTy = GenericDeviceResourceManagerTy<ResourceRef>;1789 1790  AMDGPUStreamManagerTy(GenericDeviceTy &Device, hsa_agent_t HSAAgent)1791      : GenericDeviceResourceManagerTy(Device), Device(Device),1792        OMPX_QueueTracking("LIBOMPTARGET_AMDGPU_HSA_QUEUE_BUSY_TRACKING", true),1793        NextQueue(0), Agent(HSAAgent) {}1794 1795  Error init(uint32_t InitialSize, int NumHSAQueues, int HSAQueueSize) {1796    Queues = std::vector<AMDGPUQueueTy>(NumHSAQueues);1797    QueueSize = HSAQueueSize;1798    MaxNumQueues = NumHSAQueues;1799    // Initialize one queue eagerly1800    if (auto Err = Queues.front().init(Device, Agent, QueueSize))1801      return Err;1802 1803    return GenericDeviceResourceManagerTy::init(InitialSize);1804  }1805 1806  /// Deinitialize the resource pool and delete all resources. This function1807  /// must be called before the destructor.1808  Error deinit() override {1809    // De-init all queues1810    for (AMDGPUQueueTy &Queue : Queues) {1811      if (auto Err = Queue.deinit())1812        return Err;1813    }1814 1815    return GenericDeviceResourceManagerTy::deinit();1816  }1817 1818  /// Get a single stream from the pool or create new resources.1819  virtual Error getResource(AMDGPUStreamTy *&StreamHandle) override {1820    return getResourcesImpl(1, &StreamHandle, [this](AMDGPUStreamTy *&Handle) {1821      return assignNextQueue(Handle);1822    });1823  }1824 1825  /// Return stream to the pool.1826  virtual Error returnResource(AMDGPUStreamTy *StreamHandle) override {1827    return returnResourceImpl(StreamHandle, [](AMDGPUStreamTy *Handle) {1828      Handle->Queue->removeUser();1829      return Plugin::success();1830    });1831  }1832 1833private:1834  /// Search for and assign an preferably idle queue to the given Stream. If1835  /// there is no queue without current users, choose the queue with the lowest1836  /// user count. If utilization is ignored: use round robin selection.1837  inline Error assignNextQueue(AMDGPUStreamTy *Stream) {1838    // Start from zero when tracking utilization, otherwise: round robin policy.1839    uint32_t Index = OMPX_QueueTracking ? 0 : NextQueue++ % MaxNumQueues;1840 1841    if (OMPX_QueueTracking) {1842      // Find the least used queue.1843      for (uint32_t I = 0; I < MaxNumQueues; ++I) {1844        // Early exit when an initialized queue is idle.1845        if (Queues[I].isInitialized() && Queues[I].getUserCount() == 0) {1846          Index = I;1847          break;1848        }1849 1850        // Update the least used queue.1851        if (Queues[Index].getUserCount() > Queues[I].getUserCount())1852          Index = I;1853      }1854    }1855 1856    // Make sure the queue is initialized, then add user & assign.1857    if (auto Err = Queues[Index].init(Device, Agent, QueueSize))1858      return Err;1859    Queues[Index].addUser();1860    Stream->Queue = &Queues[Index];1861 1862    return Plugin::success();1863  }1864 1865  /// The device associated with this stream.1866  GenericDeviceTy &Device;1867 1868  /// Envar for controlling the tracking of busy HSA queues.1869  BoolEnvar OMPX_QueueTracking;1870 1871  /// The next queue index to use for round robin selection.1872  uint32_t NextQueue;1873 1874  /// The queues which are assigned to requested streams.1875  std::vector<AMDGPUQueueTy> Queues;1876 1877  /// The corresponding device as HSA agent.1878  hsa_agent_t Agent;1879 1880  /// The maximum number of queues.1881  uint32_t MaxNumQueues;1882 1883  /// The size of created queues.1884  uint32_t QueueSize;1885};1886 1887/// Abstract class that holds the common members of the actual kernel devices1888/// and the host device. Both types should inherit from this class.1889struct AMDGenericDeviceTy {1890  AMDGenericDeviceTy() {}1891 1892  virtual ~AMDGenericDeviceTy() {}1893 1894  /// Create all memory pools which the device has access to and classify them.1895  Error initMemoryPools() {1896    // Retrieve all memory pools from the device agent(s).1897    Error Err = retrieveAllMemoryPools();1898    if (Err)1899      return Err;1900 1901    for (AMDGPUMemoryPoolTy *MemoryPool : AllMemoryPools) {1902      // Initialize the memory pool and retrieve some basic info.1903      Error Err = MemoryPool->init();1904      if (Err)1905        return Err;1906 1907      if (!MemoryPool->isGlobal())1908        continue;1909 1910      // Classify the memory pools depending on their properties.1911      if (MemoryPool->isFineGrained()) {1912        FineGrainedMemoryPools.push_back(MemoryPool);1913        if (MemoryPool->supportsKernelArgs())1914          ArgsMemoryPools.push_back(MemoryPool);1915      } else if (MemoryPool->isCoarseGrained()) {1916        CoarseGrainedMemoryPools.push_back(MemoryPool);1917      }1918    }1919    return Plugin::success();1920  }1921 1922  /// Destroy all memory pools.1923  Error deinitMemoryPools() {1924    for (AMDGPUMemoryPoolTy *Pool : AllMemoryPools)1925      delete Pool;1926 1927    AllMemoryPools.clear();1928    FineGrainedMemoryPools.clear();1929    CoarseGrainedMemoryPools.clear();1930    ArgsMemoryPools.clear();1931 1932    return Plugin::success();1933  }1934 1935  /// Retrieve and construct all memory pools from the device agent(s).1936  virtual Error retrieveAllMemoryPools() = 0;1937 1938  /// Get the device agent.1939  virtual hsa_agent_t getAgent() const = 0;1940 1941protected:1942  /// Array of all memory pools available to the host agents.1943  llvm::SmallVector<AMDGPUMemoryPoolTy *> AllMemoryPools;1944 1945  /// Array of fine-grained memory pools available to the host agents.1946  llvm::SmallVector<AMDGPUMemoryPoolTy *> FineGrainedMemoryPools;1947 1948  /// Array of coarse-grained memory pools available to the host agents.1949  llvm::SmallVector<AMDGPUMemoryPoolTy *> CoarseGrainedMemoryPools;1950 1951  /// Array of kernel args memory pools available to the host agents.1952  llvm::SmallVector<AMDGPUMemoryPoolTy *> ArgsMemoryPools;1953};1954 1955/// Class representing the host device. This host device may have more than one1956/// HSA host agent. We aggregate all its resources into the same instance.1957struct AMDHostDeviceTy : public AMDGenericDeviceTy {1958  /// Create a host device from an array of host agents.1959  AMDHostDeviceTy(AMDGPUPluginTy &Plugin,1960                  const llvm::SmallVector<hsa_agent_t> &HostAgents)1961      : AMDGenericDeviceTy(), Agents(HostAgents), ArgsMemoryManager(Plugin),1962        PinnedMemoryManager(Plugin) {1963    assert(HostAgents.size() && "No host agent found");1964  }1965 1966  /// Initialize the host device memory pools and the memory managers for1967  /// kernel args and host pinned memory allocations.1968  Error init() {1969    if (auto Err = initMemoryPools())1970      return Err;1971 1972    if (auto Err = ArgsMemoryManager.init(getArgsMemoryPool()))1973      return Err;1974 1975    if (auto Err = PinnedMemoryManager.init(getFineGrainedMemoryPool()))1976      return Err;1977 1978    return Plugin::success();1979  }1980 1981  /// Deinitialize memory pools and managers.1982  Error deinit() {1983    if (auto Err = deinitMemoryPools())1984      return Err;1985 1986    if (auto Err = ArgsMemoryManager.deinit())1987      return Err;1988 1989    if (auto Err = PinnedMemoryManager.deinit())1990      return Err;1991 1992    return Plugin::success();1993  }1994 1995  /// Retrieve and construct all memory pools from the host agents.1996  Error retrieveAllMemoryPools() override {1997    // Iterate through the available pools across the host agents.1998    for (hsa_agent_t Agent : Agents) {1999      Error Err = hsa_utils::iterateAgentMemoryPools(2000          Agent, [&](hsa_amd_memory_pool_t HSAMemoryPool) {2001            AMDGPUMemoryPoolTy *MemoryPool =2002                new AMDGPUMemoryPoolTy(HSAMemoryPool);2003            AllMemoryPools.push_back(MemoryPool);2004            return HSA_STATUS_SUCCESS;2005          });2006      if (Err)2007        return Err;2008    }2009    return Plugin::success();2010  }2011 2012  /// Get one of the host agents. Return always the first agent.2013  hsa_agent_t getAgent() const override { return Agents[0]; }2014 2015  /// Get a memory pool for fine-grained allocations.2016  AMDGPUMemoryPoolTy &getFineGrainedMemoryPool() {2017    assert(!FineGrainedMemoryPools.empty() && "No fine-grained mempool");2018    // Retrieve any memory pool.2019    return *FineGrainedMemoryPools[0];2020  }2021 2022  AMDGPUMemoryPoolTy &getCoarseGrainedMemoryPool() {2023    assert(!CoarseGrainedMemoryPools.empty() && "No coarse-grained mempool");2024    // Retrieve any memory pool.2025    return *CoarseGrainedMemoryPools[0];2026  }2027 2028  /// Get a memory pool for kernel args allocations.2029  AMDGPUMemoryPoolTy &getArgsMemoryPool() {2030    assert(!ArgsMemoryPools.empty() && "No kernelargs mempool");2031    // Retrieve any memory pool.2032    return *ArgsMemoryPools[0];2033  }2034 2035  /// Getters for kernel args and host pinned memory managers.2036  AMDGPUMemoryManagerTy &getArgsMemoryManager() { return ArgsMemoryManager; }2037  AMDGPUMemoryManagerTy &getPinnedMemoryManager() {2038    return PinnedMemoryManager;2039  }2040 2041private:2042  /// Array of agents on the host side.2043  const llvm::SmallVector<hsa_agent_t> Agents;2044 2045  // Memory manager for kernel arguments.2046  AMDGPUMemoryManagerTy ArgsMemoryManager;2047 2048  // Memory manager for pinned memory.2049  AMDGPUMemoryManagerTy PinnedMemoryManager;2050};2051 2052/// Class implementing the AMDGPU device functionalities which derives from the2053/// generic device class.2054struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {2055  // Create an AMDGPU device with a device id and default AMDGPU grid values.2056  AMDGPUDeviceTy(GenericPluginTy &Plugin, int32_t DeviceId, int32_t NumDevices,2057                 AMDHostDeviceTy &HostDevice, hsa_agent_t Agent)2058      : GenericDeviceTy(Plugin, DeviceId, NumDevices, {}), AMDGenericDeviceTy(),2059        OMPX_NumQueues("LIBOMPTARGET_AMDGPU_NUM_HSA_QUEUES", 4),2060        OMPX_QueueSize("LIBOMPTARGET_AMDGPU_HSA_QUEUE_SIZE", 512),2061        OMPX_DefaultTeamsPerCU("LIBOMPTARGET_AMDGPU_TEAMS_PER_CU", 4),2062        OMPX_MaxAsyncCopyBytes("LIBOMPTARGET_AMDGPU_MAX_ASYNC_COPY_BYTES",2063                               1 * 1024 * 1024), // 1MB2064        OMPX_InitialNumSignals("LIBOMPTARGET_AMDGPU_NUM_INITIAL_HSA_SIGNALS",2065                               64),2066        OMPX_StreamBusyWait("LIBOMPTARGET_AMDGPU_STREAM_BUSYWAIT", 2000000),2067        OMPX_UseMultipleSdmaEngines(2068            "LIBOMPTARGET_AMDGPU_USE_MULTIPLE_SDMA_ENGINES", false),2069        OMPX_ApuMaps("OMPX_APU_MAPS", false), AMDGPUStreamManager(*this, Agent),2070        AMDGPUEventManager(*this), AMDGPUSignalManager(*this), Agent(Agent),2071        HostDevice(HostDevice) {}2072 2073  ~AMDGPUDeviceTy() {}2074 2075  /// Initialize the device, its resources and get its properties.2076  Error initImpl(GenericPluginTy &Plugin) override {2077    // First setup all the memory pools.2078    if (auto Err = initMemoryPools())2079      return Err;2080 2081    char GPUName[64];2082    if (auto Err = getDeviceAttr(HSA_AGENT_INFO_NAME, GPUName))2083      return Err;2084    ComputeUnitKind = GPUName;2085 2086    // From the ROCm HSA documentation:2087    // Query the UUID of the agent. The value is an Ascii string with a maximum2088    // of 21 chars including NUL. The string value consists of two parts: header2089    // and body. The header identifies the device type (GPU, CPU, DSP) while the2090    // body encodes the UUID as a 16 digit hex string.2091    //2092    // Agents that do not support UUID will return the string "GPU-XX" or2093    // "CPU-XX" or "DSP-XX" depending on their device type.2094    char UUID[24] = {0};2095    if (auto Err = getDeviceAttr(HSA_AMD_AGENT_INFO_UUID, UUID))2096      return Err;2097    if (!StringRef(UUID).ends_with("-XX"))2098      setDeviceUidFromVendorUid(UUID);2099 2100    // Get the wavefront size.2101    uint32_t WavefrontSize = 0;2102    if (auto Err = getDeviceAttr(HSA_AGENT_INFO_WAVEFRONT_SIZE, WavefrontSize))2103      return Err;2104    GridValues.GV_Warp_Size = WavefrontSize;2105 2106    // Get the frequency of the steady clock. If the attribute is missing2107    // assume running on an older libhsa and default to 0, omp_get_wtime2108    // will be inaccurate but otherwise programs can still run.2109    if (getDeviceAttrRaw(HSA_AMD_AGENT_INFO_TIMESTAMP_FREQUENCY,2110                         ClockFrequency) != HSA_STATUS_SUCCESS)2111      ClockFrequency = 0;2112 2113    // Load the grid values depending on the wavefront.2114    if (WavefrontSize == 32)2115      GridValues = getAMDGPUGridValues<32>();2116    else if (WavefrontSize == 64)2117      GridValues = getAMDGPUGridValues<64>();2118    else2119      return Plugin::error(ErrorCode::UNSUPPORTED,2120                           "unexpected AMDGPU wavefront %d", WavefrontSize);2121 2122    // Get maximum number of workitems per workgroup.2123    uint16_t WorkgroupMaxDim[3];2124    if (auto Err =2125            getDeviceAttr(HSA_AGENT_INFO_WORKGROUP_MAX_DIM, WorkgroupMaxDim))2126      return Err;2127    GridValues.GV_Max_WG_Size = WorkgroupMaxDim[0];2128 2129    // Get maximum number of workgroups.2130    hsa_dim3_t GridMaxDim;2131    if (auto Err = getDeviceAttr(HSA_AGENT_INFO_GRID_MAX_DIM, GridMaxDim))2132      return Err;2133 2134    GridValues.GV_Max_Teams = GridMaxDim.x / GridValues.GV_Max_WG_Size;2135    if (GridValues.GV_Max_Teams == 0)2136      return Plugin::error(ErrorCode::INVALID_ARGUMENT,2137                           "maximum number of teams cannot be zero");2138 2139    // Compute the default number of teams.2140    uint32_t ComputeUnits = 0;2141    if (auto Err =2142            getDeviceAttr(HSA_AMD_AGENT_INFO_COMPUTE_UNIT_COUNT, ComputeUnits))2143      return Err;2144    GridValues.GV_Default_Num_Teams = ComputeUnits * OMPX_DefaultTeamsPerCU;2145 2146    uint32_t WavesPerCU = 0;2147    if (auto Err =2148            getDeviceAttr(HSA_AMD_AGENT_INFO_MAX_WAVES_PER_CU, WavesPerCU))2149      return Err;2150    HardwareParallelism = ComputeUnits * WavesPerCU;2151 2152    // Get maximum size of any device queues and maximum number of queues.2153    uint32_t MaxQueueSize;2154    if (auto Err = getDeviceAttr(HSA_AGENT_INFO_QUEUE_MAX_SIZE, MaxQueueSize))2155      return Err;2156 2157    uint32_t MaxQueues;2158    if (auto Err = getDeviceAttr(HSA_AGENT_INFO_QUEUES_MAX, MaxQueues))2159      return Err;2160 2161    // Compute the number of queues and their size.2162    OMPX_NumQueues = std::max(1U, std::min(OMPX_NumQueues.get(), MaxQueues));2163    OMPX_QueueSize = std::min(OMPX_QueueSize.get(), MaxQueueSize);2164 2165    // Initialize stream pool.2166    if (auto Err = AMDGPUStreamManager.init(OMPX_InitialNumStreams,2167                                            OMPX_NumQueues, OMPX_QueueSize))2168      return Err;2169 2170    // Initialize event pool.2171    if (auto Err = AMDGPUEventManager.init(OMPX_InitialNumEvents))2172      return Err;2173 2174    // Initialize signal pool.2175    if (auto Err = AMDGPUSignalManager.init(OMPX_InitialNumSignals))2176      return Err;2177 2178    // Detect if XNACK is enabled2179    SmallVector<SmallString<32>> Targets;2180    if (auto Err = hsa_utils::getTargetTripleAndFeatures(Agent, Targets))2181      return Err;2182    if (!Targets.empty() && Targets[0].str().contains("xnack+"))2183      IsXnackEnabled = true;2184 2185    // detect if device is an APU.2186    if (auto Err = checkIfAPU())2187      return Err;2188 2189    // Retrieve the size of the group memory.2190    for (const auto *Pool : AllMemoryPools) {2191      if (Pool->isGroup()) {2192        if (auto Err = Pool->getAttr(HSA_AMD_MEMORY_POOL_INFO_SIZE,2193                                     MaxBlockSharedMemSize))2194          return Err;2195        break;2196      }2197    }2198 2199    return Plugin::success();2200  }2201 2202  Error unloadBinaryImpl(DeviceImageTy *Image) override {2203    AMDGPUDeviceImageTy &AMDImage = static_cast<AMDGPUDeviceImageTy &>(*Image);2204 2205    // Unload the executable of the image.2206    if (auto Err = AMDImage.unloadExecutable())2207      return Err;2208 2209    // Destroy the associated memory and invalidate the object.2210    Plugin.free(Image);2211    return Error::success();2212  }2213 2214  /// Deinitialize the device and release its resources.2215  Error deinitImpl() override {2216    // Deinitialize the stream and event pools.2217    if (auto Err = AMDGPUStreamManager.deinit())2218      return Err;2219 2220    if (auto Err = AMDGPUEventManager.deinit())2221      return Err;2222 2223    if (auto Err = AMDGPUSignalManager.deinit())2224      return Err;2225 2226    // Invalidate agent reference.2227    Agent = {0};2228 2229    return Plugin::success();2230  }2231 2232  virtual Error callGlobalConstructors(GenericPluginTy &Plugin,2233                                       DeviceImageTy &Image) override {2234    return callGlobalCtorDtorCommon(Plugin, Image, /*IsCtor=*/true);2235  }2236 2237  virtual Error callGlobalDestructors(GenericPluginTy &Plugin,2238                                      DeviceImageTy &Image) override {2239    return callGlobalCtorDtorCommon(Plugin, Image, /*IsCtor=*/false);2240  }2241 2242  uint64_t getStreamBusyWaitMicroseconds() const { return OMPX_StreamBusyWait; }2243 2244  Expected<std::unique_ptr<MemoryBuffer>>2245  doJITPostProcessing(std::unique_ptr<MemoryBuffer> MB) const override {2246 2247    // TODO: We should try to avoid materialization but there seems to be no2248    // good linker interface w/o file i/o.2249    SmallString<128> LinkerInputFilePath;2250    std::error_code EC = sys::fs::createTemporaryFile("amdgpu-pre-link-jit",2251                                                      "o", LinkerInputFilePath);2252    if (EC)2253      return Plugin::error(ErrorCode::HOST_IO,2254                           "failed to create temporary file for linker");2255 2256    // Write the file's contents to the output file.2257    Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =2258        FileOutputBuffer::create(LinkerInputFilePath, MB->getBuffer().size());2259    if (!OutputOrErr)2260      return OutputOrErr.takeError();2261    std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);2262    llvm::copy(MB->getBuffer(), Output->getBufferStart());2263    if (Error E = Output->commit())2264      return std::move(E);2265 2266    SmallString<128> LinkerOutputFilePath;2267    EC = sys::fs::createTemporaryFile("amdgpu-pre-link-jit", "so",2268                                      LinkerOutputFilePath);2269    if (EC)2270      return Plugin::error(ErrorCode::HOST_IO,2271                           "failed to create temporary file for linker");2272 2273    const auto &ErrorOrPath = sys::findProgramByName("lld");2274    if (!ErrorOrPath)2275      return createStringError(ErrorCode::HOST_TOOL_NOT_FOUND,2276                               "failed to find `lld` on the PATH.");2277 2278    std::string LLDPath = ErrorOrPath.get();2279    INFO(OMP_INFOTYPE_PLUGIN_KERNEL, getDeviceId(),2280         "Using `%s` to link JITed amdgcn output.", LLDPath.c_str());2281 2282    std::string MCPU = "-plugin-opt=mcpu=" + getComputeUnitKind();2283    StringRef Args[] = {LLDPath,2284                        "-flavor",2285                        "gnu",2286                        "--no-undefined",2287                        "-shared",2288                        MCPU,2289                        "-o",2290                        LinkerOutputFilePath.data(),2291                        LinkerInputFilePath.data()};2292 2293    std::string Error;2294    int RC = sys::ExecuteAndWait(LLDPath, Args, std::nullopt, {}, 0, 0, &Error);2295    if (RC)2296      return Plugin::error(ErrorCode::LINK_FAILURE,2297                           "linking optimized bitcode failed: %s",2298                           Error.c_str());2299 2300    auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(LinkerOutputFilePath);2301    if (!BufferOrErr)2302      return Plugin::error(ErrorCode::HOST_IO,2303                           "failed to open temporary file for lld");2304 2305    // Clean up the temporary files afterwards.2306    if (sys::fs::remove(LinkerOutputFilePath))2307      return Plugin::error(ErrorCode::HOST_IO,2308                           "failed to remove temporary output file for lld");2309    if (sys::fs::remove(LinkerInputFilePath))2310      return Plugin::error(ErrorCode::HOST_IO,2311                           "failed to remove temporary input file for lld");2312 2313    return std::move(*BufferOrErr);2314  }2315 2316  /// See GenericDeviceTy::getComputeUnitKind().2317  std::string getComputeUnitKind() const override { return ComputeUnitKind; }2318 2319  /// Returns the clock frequency for the given AMDGPU device.2320  uint64_t getClockFrequency() const override { return ClockFrequency; }2321 2322  /// Allocate and construct an AMDGPU kernel.2323  Expected<GenericKernelTy &> constructKernel(const char *Name) override {2324    // Allocate and construct the AMDGPU kernel.2325    AMDGPUKernelTy *AMDGPUKernel = Plugin.allocate<AMDGPUKernelTy>();2326    if (!AMDGPUKernel)2327      return Plugin::error(ErrorCode::OUT_OF_RESOURCES,2328                           "failed to allocate memory for AMDGPU kernel");2329 2330    new (AMDGPUKernel) AMDGPUKernelTy(Name);2331 2332    return *AMDGPUKernel;2333  }2334 2335  /// Set the current context to this device's context. Do nothing since the2336  /// AMDGPU devices do not have the concept of contexts.2337  Error setContext() override { return Plugin::success(); }2338 2339  /// AMDGPU returns the product of the number of compute units and the waves2340  /// per compute unit.2341  uint64_t getHardwareParallelism() const override {2342    return HardwareParallelism;2343  }2344 2345  /// We want to set up the RPC server for host services to the GPU if it is2346  /// available.2347  bool shouldSetupRPCServer() const override { return true; }2348 2349  /// The RPC interface should have enough space for all available parallelism.2350  uint64_t requestedRPCPortCount() const override {2351    return getHardwareParallelism();2352  }2353 2354  /// Get the stream of the asynchronous info structure or get a new one.2355  Error getStream(AsyncInfoWrapperTy &AsyncInfoWrapper,2356                  AMDGPUStreamTy *&Stream) {2357    auto WrapperStream =2358        AsyncInfoWrapper.getOrInitQueue<AMDGPUStreamTy *>(AMDGPUStreamManager);2359    if (!WrapperStream)2360      return WrapperStream.takeError();2361    Stream = *WrapperStream;2362    return Plugin::success();2363  }2364 2365  /// Load the binary image into the device and allocate an image object.2366  Expected<DeviceImageTy *>2367  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,2368                 int32_t ImageId) override {2369    // Allocate and initialize the image object.2370    AMDGPUDeviceImageTy *AMDImage = Plugin.allocate<AMDGPUDeviceImageTy>();2371    new (AMDImage) AMDGPUDeviceImageTy(ImageId, *this, std::move(TgtImage));2372 2373    // Load the HSA executable.2374    if (Error Err = AMDImage->loadExecutable(*this))2375      return std::move(Err);2376 2377    return AMDImage;2378  }2379 2380  /// Allocate memory on the device or related to the device.2381  Expected<void *> allocate(size_t Size, void *, TargetAllocTy Kind) override;2382 2383  /// Deallocate memory on the device or related to the device.2384  Error free(void *TgtPtr, TargetAllocTy Kind) override {2385    if (TgtPtr == nullptr)2386      return Plugin::success();2387 2388    AMDGPUMemoryPoolTy *MemoryPool = nullptr;2389    switch (Kind) {2390    case TARGET_ALLOC_DEFAULT:2391    case TARGET_ALLOC_DEVICE:2392      MemoryPool = CoarseGrainedMemoryPools[0];2393      break;2394    case TARGET_ALLOC_HOST:2395      MemoryPool = &HostDevice.getFineGrainedMemoryPool();2396      break;2397    case TARGET_ALLOC_SHARED:2398      MemoryPool = &HostDevice.getFineGrainedMemoryPool();2399      break;2400    }2401 2402    if (!MemoryPool)2403      return Plugin::error(ErrorCode::OUT_OF_RESOURCES,2404                           "no memory pool for the specified allocation kind");2405 2406    if (auto Err = MemoryPool->deallocate(TgtPtr))2407      return Err;2408 2409    return Plugin::success();2410  }2411 2412  /// Synchronize current thread with the pending operations on the async info.2413  Error synchronizeImpl(__tgt_async_info &AsyncInfo,2414                        bool ReleaseQueue) override {2415    AMDGPUStreamTy *Stream =2416        reinterpret_cast<AMDGPUStreamTy *>(AsyncInfo.Queue);2417    assert(Stream && "Invalid stream");2418 2419    if (auto Err = Stream->synchronize())2420      return Err;2421 2422    // Once the stream is synchronized, return it to stream pool and reset2423    // AsyncInfo. This is to make sure the synchronization only works for its2424    // own tasks.2425    if (ReleaseQueue) {2426      AsyncInfo.Queue = nullptr;2427      return AMDGPUStreamManager.returnResource(Stream);2428    }2429    return Plugin::success();2430  }2431 2432  /// Query for the completion of the pending operations on the async info.2433  Error queryAsyncImpl(__tgt_async_info &AsyncInfo) override {2434    AMDGPUStreamTy *Stream =2435        reinterpret_cast<AMDGPUStreamTy *>(AsyncInfo.Queue);2436    assert(Stream && "Invalid stream");2437 2438    auto CompletedOrErr = Stream->query();2439    if (!CompletedOrErr)2440      return CompletedOrErr.takeError();2441 2442    // Return if it the stream did not complete yet.2443    if (!(*CompletedOrErr))2444      return Plugin::success();2445 2446    // Once the stream is completed, return it to stream pool and reset2447    // AsyncInfo. This is to make sure the synchronization only works for its2448    // own tasks.2449    AsyncInfo.Queue = nullptr;2450    return AMDGPUStreamManager.returnResource(Stream);2451  }2452 2453  /// Pin the host buffer and return the device pointer that should be used for2454  /// device transfers.2455  Expected<void *> dataLockImpl(void *HstPtr, int64_t Size) override {2456    void *PinnedPtr = nullptr;2457 2458    hsa_status_t Status =2459        hsa_amd_memory_lock(HstPtr, Size, nullptr, 0, &PinnedPtr);2460    if (auto Err = Plugin::check(Status, "error in hsa_amd_memory_lock: %s\n"))2461      return std::move(Err);2462 2463    return PinnedPtr;2464  }2465 2466  /// Unpin the host buffer.2467  Error dataUnlockImpl(void *HstPtr) override {2468    hsa_status_t Status = hsa_amd_memory_unlock(HstPtr);2469    return Plugin::check(Status, "error in hsa_amd_memory_unlock: %s\n");2470  }2471 2472  /// Check through the HSA runtime whether the \p HstPtr buffer is pinned.2473  Expected<bool> isPinnedPtrImpl(void *HstPtr, void *&BaseHstPtr,2474                                 void *&BaseDevAccessiblePtr,2475                                 size_t &BaseSize) const override {2476    hsa_amd_pointer_info_t Info;2477    Info.size = sizeof(hsa_amd_pointer_info_t);2478 2479    hsa_status_t Status = hsa_amd_pointer_info(2480        HstPtr, &Info, /*Allocator=*/nullptr, /*num_agents_accessible=*/nullptr,2481        /*accessible=*/nullptr);2482    if (auto Err = Plugin::check(Status, "error in hsa_amd_pointer_info: %s"))2483      return std::move(Err);2484 2485    // The buffer may be locked or allocated through HSA allocators. Assume that2486    // the buffer is host pinned if the runtime reports a HSA type.2487    if (Info.type != HSA_EXT_POINTER_TYPE_LOCKED &&2488        Info.type != HSA_EXT_POINTER_TYPE_HSA)2489      return false;2490 2491    assert(Info.hostBaseAddress && "Invalid host pinned address");2492    assert(Info.agentBaseAddress && "Invalid agent pinned address");2493    assert(Info.sizeInBytes > 0 && "Invalid pinned allocation size");2494 2495    // Save the allocation info in the output parameters.2496    BaseHstPtr = Info.hostBaseAddress;2497    BaseDevAccessiblePtr = Info.agentBaseAddress;2498    BaseSize = Info.sizeInBytes;2499 2500    return true;2501  }2502 2503  /// Submit data to the device (host to device transfer).2504  Error dataSubmitImpl(void *TgtPtr, const void *HstPtr, int64_t Size,2505                       AsyncInfoWrapperTy &AsyncInfoWrapper) override {2506    AMDGPUStreamTy *Stream = nullptr;2507    void *PinnedPtr = nullptr;2508 2509    // Use one-step asynchronous operation when host memory is already pinned.2510    if (void *PinnedPtr =2511            PinnedAllocs.getDeviceAccessiblePtrFromPinnedBuffer(HstPtr)) {2512      if (auto Err = getStream(AsyncInfoWrapper, Stream))2513        return Err;2514      return Stream->pushPinnedMemoryCopyAsync(TgtPtr, PinnedPtr, Size);2515    }2516 2517    // For large transfers use synchronous behavior.2518    if (Size >= OMPX_MaxAsyncCopyBytes) {2519      if (AsyncInfoWrapper.hasQueue())2520        if (auto Err = synchronize(AsyncInfoWrapper))2521          return Err;2522 2523      hsa_status_t Status;2524      Status = hsa_amd_memory_lock(const_cast<void *>(HstPtr), Size, nullptr, 0,2525                                   &PinnedPtr);2526      if (auto Err =2527              Plugin::check(Status, "error in hsa_amd_memory_lock: %s\n"))2528        return Err;2529 2530      AMDGPUSignalTy Signal;2531      if (auto Err = Signal.init())2532        return Err;2533 2534      if (auto Err = hsa_utils::asyncMemCopy(useMultipleSdmaEngines(), TgtPtr,2535                                             Agent, PinnedPtr, Agent, Size, 0,2536                                             nullptr, Signal.get()))2537        return Err;2538 2539      if (auto Err = Signal.wait(getStreamBusyWaitMicroseconds()))2540        return Err;2541 2542      if (auto Err = Signal.deinit())2543        return Err;2544 2545      Status = hsa_amd_memory_unlock(const_cast<void *>(HstPtr));2546      return Plugin::check(Status, "error in hsa_amd_memory_unlock: %s\n");2547    }2548 2549    // Otherwise, use two-step copy with an intermediate pinned host buffer.2550    AMDGPUMemoryManagerTy &PinnedMemoryManager =2551        HostDevice.getPinnedMemoryManager();2552    if (auto Err = PinnedMemoryManager.allocate(Size, &PinnedPtr))2553      return Err;2554 2555    if (auto Err = getStream(AsyncInfoWrapper, Stream))2556      return Err;2557 2558    return Stream->pushMemoryCopyH2DAsync(TgtPtr, HstPtr, PinnedPtr, Size,2559                                          PinnedMemoryManager);2560  }2561 2562  /// Retrieve data from the device (device to host transfer).2563  Error dataRetrieveImpl(void *HstPtr, const void *TgtPtr, int64_t Size,2564                         AsyncInfoWrapperTy &AsyncInfoWrapper) override {2565    AMDGPUStreamTy *Stream = nullptr;2566    void *PinnedPtr = nullptr;2567 2568    // Use one-step asynchronous operation when host memory is already pinned.2569    if (void *PinnedPtr =2570            PinnedAllocs.getDeviceAccessiblePtrFromPinnedBuffer(HstPtr)) {2571      if (auto Err = getStream(AsyncInfoWrapper, Stream))2572        return Err;2573 2574      return Stream->pushPinnedMemoryCopyAsync(PinnedPtr, TgtPtr, Size);2575    }2576 2577    // For large transfers use synchronous behavior.2578    if (Size >= OMPX_MaxAsyncCopyBytes) {2579      if (AsyncInfoWrapper.hasQueue())2580        if (auto Err = synchronize(AsyncInfoWrapper))2581          return Err;2582 2583      hsa_status_t Status;2584      Status = hsa_amd_memory_lock(const_cast<void *>(HstPtr), Size, nullptr, 0,2585                                   &PinnedPtr);2586      if (auto Err =2587              Plugin::check(Status, "error in hsa_amd_memory_lock: %s\n"))2588        return Err;2589 2590      AMDGPUSignalTy Signal;2591      if (auto Err = Signal.init())2592        return Err;2593 2594      if (auto Err = hsa_utils::asyncMemCopy(useMultipleSdmaEngines(),2595                                             PinnedPtr, Agent, TgtPtr, Agent,2596                                             Size, 0, nullptr, Signal.get()))2597        return Err;2598 2599      if (auto Err = Signal.wait(getStreamBusyWaitMicroseconds()))2600        return Err;2601 2602      if (auto Err = Signal.deinit())2603        return Err;2604 2605      Status = hsa_amd_memory_unlock(const_cast<void *>(HstPtr));2606      return Plugin::check(Status, "error in hsa_amd_memory_unlock: %s\n");2607    }2608 2609    // Otherwise, use two-step copy with an intermediate pinned host buffer.2610    AMDGPUMemoryManagerTy &PinnedMemoryManager =2611        HostDevice.getPinnedMemoryManager();2612    if (auto Err = PinnedMemoryManager.allocate(Size, &PinnedPtr))2613      return Err;2614 2615    if (auto Err = getStream(AsyncInfoWrapper, Stream))2616      return Err;2617 2618    return Stream->pushMemoryCopyD2HAsync(HstPtr, TgtPtr, PinnedPtr, Size,2619                                          PinnedMemoryManager);2620  }2621 2622  /// Exchange data between two devices within the plugin.2623  Error dataExchangeImpl(const void *SrcPtr, GenericDeviceTy &DstGenericDevice,2624                         void *DstPtr, int64_t Size,2625                         AsyncInfoWrapperTy &AsyncInfoWrapper) override {2626    AMDGPUDeviceTy &DstDevice = static_cast<AMDGPUDeviceTy &>(DstGenericDevice);2627 2628    // For large transfers use synchronous behavior.2629    if (Size >= OMPX_MaxAsyncCopyBytes) {2630      if (AsyncInfoWrapper.hasQueue())2631        if (auto Err = synchronize(AsyncInfoWrapper))2632          return Err;2633 2634      AMDGPUSignalTy Signal;2635      if (auto Err = Signal.init())2636        return Err;2637 2638      if (auto Err = hsa_utils::asyncMemCopy(2639              useMultipleSdmaEngines(), DstPtr, DstDevice.getAgent(), SrcPtr,2640              getAgent(), (uint64_t)Size, 0, nullptr, Signal.get()))2641        return Err;2642 2643      if (auto Err = Signal.wait(getStreamBusyWaitMicroseconds()))2644        return Err;2645 2646      return Signal.deinit();2647    }2648 2649    AMDGPUStreamTy *Stream = nullptr;2650    if (auto Err = getStream(AsyncInfoWrapper, Stream))2651      return Err;2652    if (Size <= 0)2653      return Plugin::success();2654 2655    return Stream->pushMemoryCopyD2DAsync(DstPtr, DstDevice.getAgent(), SrcPtr,2656                                          getAgent(), (uint64_t)Size);2657  }2658 2659  /// Insert a data fence between previous data operations and the following2660  /// operations. This is a no-op for AMDGPU devices as operations inserted into2661  /// a queue are in-order.2662  Error dataFence(__tgt_async_info *Async) override {2663    return Plugin::success();2664  }2665 2666  Error dataFillImpl(void *TgtPtr, const void *PatternPtr, int64_t PatternSize,2667                     int64_t Size,2668                     AsyncInfoWrapperTy &AsyncInfoWrapper) override {2669    // Fast case, where we can use the 4 byte hsa_amd_memory_fill2670    if (Size % 4 == 0 &&2671        (PatternSize == 4 || PatternSize == 2 || PatternSize == 1)) {2672      uint32_t Pattern;2673      if (PatternSize == 1) {2674        auto *Byte = reinterpret_cast<const uint8_t *>(PatternPtr);2675        Pattern = *Byte | *Byte << 8 | *Byte << 16 | *Byte << 24;2676      } else if (PatternSize == 2) {2677        auto *Word = reinterpret_cast<const uint16_t *>(PatternPtr);2678        Pattern = *Word | (*Word << 16);2679      } else if (PatternSize == 4) {2680        Pattern = *reinterpret_cast<const uint32_t *>(PatternPtr);2681      } else {2682        // Shouldn't be here if the pattern size is outwith those values2683        llvm_unreachable("Invalid pattern size");2684      }2685 2686      if (hasPendingWorkImpl(AsyncInfoWrapper)) {2687        AMDGPUStreamTy *Stream = nullptr;2688        if (auto Err = getStream(AsyncInfoWrapper, Stream))2689          return Err;2690 2691        struct MemFillArgsTy {2692          void *Dst;2693          uint32_t Pattern;2694          int64_t Size;2695        };2696        auto *Args = new MemFillArgsTy{TgtPtr, Pattern, Size / 4};2697        auto Fill = [](void *Data) {2698          MemFillArgsTy *Args = reinterpret_cast<MemFillArgsTy *>(Data);2699          assert(Args && "Invalid arguments");2700 2701          auto Status =2702              hsa_amd_memory_fill(Args->Dst, Args->Pattern, Args->Size);2703          delete Args;2704          auto Err =2705              Plugin::check(Status, "error in hsa_amd_memory_fill: %s\n");2706          if (Err) {2707            FATAL_MESSAGE(1, "error performing async fill: %s",2708                          toString(std::move(Err)).data());2709          }2710        };2711 2712        // hsa_amd_memory_fill doesn't signal completion using a signal, so use2713        // the existing host callback logic to handle that instead2714        return Stream->pushHostCallback(Fill, Args);2715      }2716      // If there is no pending work, do the fill synchronously2717      auto Status = hsa_amd_memory_fill(TgtPtr, Pattern, Size / 4);2718      return Plugin::check(Status, "error in hsa_amd_memory_fill: %s\n");2719    }2720 2721    // Slow case; allocate an appropriate memory size and enqueue copies2722    void *PinnedPtr = nullptr;2723    AMDGPUMemoryManagerTy &PinnedMemoryManager =2724        HostDevice.getPinnedMemoryManager();2725    if (auto Err = PinnedMemoryManager.allocate(Size, &PinnedPtr))2726      return Err;2727 2728    AMDGPUStreamTy *Stream = nullptr;2729    if (auto Err = getStream(AsyncInfoWrapper, Stream))2730      return Err;2731 2732    return Stream->pushMemoryCopyH2DAsync(TgtPtr, PatternPtr, PinnedPtr,2733                                          PatternSize, PinnedMemoryManager,2734                                          Size / PatternSize);2735  }2736 2737  /// Initialize the async info2738  Error initAsyncInfoImpl(AsyncInfoWrapperTy &AsyncInfoWrapper) override {2739    // TODO: Implement this function.2740    return Plugin::success();2741  }2742 2743  interop_spec_t selectInteropPreference(int32_t InteropType,2744                                         int32_t NumPrefers,2745                                         interop_spec_t *Prefers) override {2746    // TODO: update once targetsync is supported2747    if (InteropType == kmp_interop_type_target)2748      return interop_spec_t{tgt_fr_hsa, {false, 0}, 0};2749    return interop_spec_t{tgt_fr_none, {false, 0}, 0};2750  }2751 2752  Expected<omp_interop_val_t *>2753  createInterop(int32_t InteropType, interop_spec_t &InteropSpec) override {2754    auto *Ret = new omp_interop_val_t(2755        DeviceId, static_cast<kmp_interop_type_t>(InteropType));2756    Ret->fr_id = tgt_fr_hsa;2757    Ret->vendor_id = omp_vendor_amd;2758 2759    // TODO: implement targetsync support2760 2761    Ret->device_info.Platform = nullptr;2762    Ret->device_info.Device = reinterpret_cast<void *>(Agent.handle);2763    Ret->device_info.Context = nullptr;2764 2765    return Ret;2766  }2767 2768  Error releaseInterop(omp_interop_val_t *Interop) override {2769    if (Interop)2770      delete Interop;2771    return Plugin::success();2772  }2773 2774  Error enqueueHostCallImpl(AMDGPUStreamTy::HostFnType Callback, void *UserData,2775                            AsyncInfoWrapperTy &AsyncInfo) override {2776    AMDGPUStreamTy *Stream = nullptr;2777    if (auto Err = getStream(AsyncInfo, Stream))2778      return Err;2779 2780    return Stream->pushHostCallback(Callback, UserData);2781  };2782 2783  /// Create an event.2784  Error createEventImpl(void **EventPtrStorage) override {2785    AMDGPUEventTy **Event = reinterpret_cast<AMDGPUEventTy **>(EventPtrStorage);2786    return AMDGPUEventManager.getResource(*Event);2787  }2788 2789  /// Destroy a previously created event.2790  Error destroyEventImpl(void *EventPtr) override {2791    AMDGPUEventTy *Event = reinterpret_cast<AMDGPUEventTy *>(EventPtr);2792    return AMDGPUEventManager.returnResource(Event);2793  }2794 2795  /// Record the event.2796  Error recordEventImpl(void *EventPtr,2797                        AsyncInfoWrapperTy &AsyncInfoWrapper) override {2798    AMDGPUEventTy *Event = reinterpret_cast<AMDGPUEventTy *>(EventPtr);2799    assert(Event && "Invalid event");2800 2801    AMDGPUStreamTy *Stream = nullptr;2802    if (auto Err = getStream(AsyncInfoWrapper, Stream))2803      return Err;2804 2805    return Event->record(*Stream);2806  }2807 2808  /// Make the stream wait on the event.2809  Error waitEventImpl(void *EventPtr,2810                      AsyncInfoWrapperTy &AsyncInfoWrapper) override {2811    AMDGPUEventTy *Event = reinterpret_cast<AMDGPUEventTy *>(EventPtr);2812 2813    AMDGPUStreamTy *Stream = nullptr;2814    if (auto Err = getStream(AsyncInfoWrapper, Stream))2815      return Err;2816 2817    return Event->wait(*Stream);2818  }2819 2820  Expected<bool> hasPendingWorkImpl(AsyncInfoWrapperTy &AsyncInfo) override {2821    auto *Stream = AsyncInfo.getQueueAs<AMDGPUStreamTy *>();2822    if (!Stream)2823      return false;2824 2825    auto Query = Stream->query();2826    if (Query)2827      return !*Query;2828    return Query.takeError();2829  }2830 2831  Expected<bool> isEventCompleteImpl(void *EventPtr,2832                                     AsyncInfoWrapperTy &AsyncInfo) override {2833    AMDGPUEventTy *Event = reinterpret_cast<AMDGPUEventTy *>(EventPtr);2834    auto *Stream = AsyncInfo.getQueueAs<AMDGPUStreamTy *>();2835    return Stream && Stream->isEventComplete(*Event);2836  }2837 2838  /// Synchronize the current thread with the event.2839  Error syncEventImpl(void *EventPtr) override {2840    AMDGPUEventTy *Event = reinterpret_cast<AMDGPUEventTy *>(EventPtr);2841    return Event->sync();2842  }2843 2844  /// Print information about the device.2845  Expected<InfoTreeNode> obtainInfoImpl() override {2846    char TmpChar[1000];2847    const char *TmpCharPtr = "Unknown";2848    uint16_t Major, Minor;2849    uint32_t TmpUInt, TmpUInt2;2850    uint32_t CacheSize[4];2851    size_t TmpSt;2852    bool TmpBool;2853    uint16_t WorkgrpMaxDim[3];2854    hsa_dim3_t GridMaxDim;2855    hsa_status_t Status, Status2;2856    InfoTreeNode Info;2857 2858    Status = hsa_system_get_info(HSA_SYSTEM_INFO_VERSION_MAJOR, &Major);2859    Status2 = hsa_system_get_info(HSA_SYSTEM_INFO_VERSION_MINOR, &Minor);2860    if (Status == HSA_STATUS_SUCCESS && Status2 == HSA_STATUS_SUCCESS)2861      Info.add("HSA Runtime Version",2862               std::to_string(Major) + "." + std::to_string(Minor), "",2863               DeviceInfo::DRIVER_VERSION);2864 2865    Info.add("HSA OpenMP Device Number", DeviceId);2866 2867    Status = getDeviceAttrRaw(HSA_AMD_AGENT_INFO_PRODUCT_NAME, TmpChar);2868    if (Status == HSA_STATUS_SUCCESS)2869      Info.add("Product Name", TmpChar, "", DeviceInfo::PRODUCT_NAME);2870 2871    Status = getDeviceAttrRaw(HSA_AGENT_INFO_NAME, TmpChar);2872    if (Status == HSA_STATUS_SUCCESS)2873      Info.add("Device Name", TmpChar, "", DeviceInfo::NAME);2874 2875    Status = getDeviceAttrRaw(HSA_AGENT_INFO_VENDOR_NAME, TmpChar);2876    if (Status == HSA_STATUS_SUCCESS)2877      Info.add("Vendor Name", TmpChar, "", DeviceInfo::VENDOR);2878 2879    Info.add("Vendor ID", uint64_t{4130}, "", DeviceInfo::VENDOR_ID);2880 2881    hsa_machine_model_t MachineModel;2882    Status = getDeviceAttrRaw(HSA_AGENT_INFO_MACHINE_MODEL, MachineModel);2883    if (Status == HSA_STATUS_SUCCESS)2884      Info.add("Memory Address Size",2885               uint64_t{MachineModel == HSA_MACHINE_MODEL_SMALL ? 32u : 64u},2886               "bits", DeviceInfo::ADDRESS_BITS);2887 2888    hsa_device_type_t DevType;2889    Status = getDeviceAttrRaw(HSA_AGENT_INFO_DEVICE, DevType);2890    if (Status == HSA_STATUS_SUCCESS) {2891      switch (static_cast<int>(DevType)) {2892      case HSA_DEVICE_TYPE_CPU:2893        TmpCharPtr = "CPU";2894        break;2895      case HSA_DEVICE_TYPE_GPU:2896        TmpCharPtr = "GPU";2897        break;2898      case HSA_DEVICE_TYPE_DSP:2899        TmpCharPtr = "DSP";2900        break;2901      default:2902        TmpCharPtr = "Unknown";2903        break;2904      }2905      Info.add("Device Type", TmpCharPtr);2906    }2907 2908    Status = getDeviceAttrRaw(HSA_AGENT_INFO_QUEUES_MAX, TmpUInt);2909    if (Status == HSA_STATUS_SUCCESS)2910      Info.add("Max Queues", TmpUInt);2911 2912    Status = getDeviceAttrRaw(HSA_AGENT_INFO_QUEUE_MIN_SIZE, TmpUInt);2913    if (Status == HSA_STATUS_SUCCESS)2914      Info.add("Queue Min Size", TmpUInt);2915 2916    Status = getDeviceAttrRaw(HSA_AGENT_INFO_QUEUE_MAX_SIZE, TmpUInt);2917    if (Status == HSA_STATUS_SUCCESS)2918      Info.add("Queue Max Size", TmpUInt);2919 2920    // FIXME: This is deprecated according to HSA documentation. But using2921    // hsa_agent_iterate_caches and hsa_cache_get_info breaks execution during2922    // runtime.2923    Status = getDeviceAttrRaw(HSA_AGENT_INFO_CACHE_SIZE, CacheSize);2924    if (Status == HSA_STATUS_SUCCESS) {2925      auto &Cache = *Info.add("Cache");2926 2927      for (int I = 0; I < 4; I++)2928        if (CacheSize[I])2929          Cache.add("L" + std::to_string(I), CacheSize[I]);2930    }2931 2932    Status = getDeviceAttrRaw(HSA_AMD_AGENT_INFO_CACHELINE_SIZE, TmpUInt);2933    if (Status == HSA_STATUS_SUCCESS)2934      Info.add("Cacheline Size", TmpUInt);2935 2936    Info.add("Max Shared Memory per Work Group", MaxBlockSharedMemSize, "bytes",2937             DeviceInfo::WORK_GROUP_LOCAL_MEM_SIZE);2938 2939    Status = getDeviceAttrRaw(HSA_AMD_AGENT_INFO_MAX_CLOCK_FREQUENCY, TmpUInt);2940    if (Status == HSA_STATUS_SUCCESS)2941      Info.add("Max Clock Freq", TmpUInt, "MHz",2942               DeviceInfo::MAX_CLOCK_FREQUENCY);2943 2944    Status = getDeviceAttrRaw(HSA_AMD_AGENT_INFO_MEMORY_MAX_FREQUENCY, TmpUInt);2945    if (Status == HSA_STATUS_SUCCESS)2946      Info.add("Max Memory Clock Freq", TmpUInt, "MHz",2947               DeviceInfo::MEMORY_CLOCK_RATE);2948 2949    Status = getDeviceAttrRaw(HSA_AMD_AGENT_INFO_COMPUTE_UNIT_COUNT, TmpUInt);2950    if (Status == HSA_STATUS_SUCCESS)2951      Info.add("Compute Units", TmpUInt, "", DeviceInfo::NUM_COMPUTE_UNITS);2952 2953    Status = getDeviceAttrRaw(HSA_AMD_AGENT_INFO_NUM_SIMDS_PER_CU, TmpUInt);2954    if (Status == HSA_STATUS_SUCCESS)2955      Info.add("SIMD per CU", TmpUInt);2956 2957    Status = getDeviceAttrRaw(HSA_AGENT_INFO_FAST_F16_OPERATION, TmpBool);2958    if (Status == HSA_STATUS_SUCCESS)2959      Info.add("Fast F16 Operation", TmpBool);2960 2961    Status = getDeviceAttrRaw(HSA_AGENT_INFO_WAVEFRONT_SIZE, TmpUInt2);2962    if (Status == HSA_STATUS_SUCCESS)2963      Info.add("Wavefront Size", TmpUInt2);2964 2965    Status = getDeviceAttrRaw(HSA_AGENT_INFO_WORKGROUP_MAX_SIZE, TmpUInt);2966    if (Status == HSA_STATUS_SUCCESS)2967      Info.add("Workgroup Max Size", TmpUInt, "",2968               DeviceInfo::MAX_WORK_GROUP_SIZE);2969 2970    Status = getDeviceAttrRaw(HSA_AGENT_INFO_WORKGROUP_MAX_DIM, WorkgrpMaxDim);2971    if (Status == HSA_STATUS_SUCCESS) {2972      auto &MaxSize =2973          *Info.add("Workgroup Max Size per Dimension", std::monostate{}, "",2974                    DeviceInfo::MAX_WORK_GROUP_SIZE_PER_DIMENSION);2975      MaxSize.add("x", WorkgrpMaxDim[0]);2976      MaxSize.add("y", WorkgrpMaxDim[1]);2977      MaxSize.add("z", WorkgrpMaxDim[2]);2978    }2979 2980    Status = getDeviceAttrRaw(2981        (hsa_agent_info_t)HSA_AMD_AGENT_INFO_MAX_WAVES_PER_CU, TmpUInt);2982    if (Status == HSA_STATUS_SUCCESS) {2983      Info.add("Max Waves Per CU", TmpUInt);2984      Info.add("Max Work-item Per CU", TmpUInt * TmpUInt2);2985    }2986 2987    Status = getDeviceAttrRaw(HSA_AGENT_INFO_GRID_MAX_SIZE, TmpUInt);2988    if (Status == HSA_STATUS_SUCCESS)2989      Info.add("Grid Max Size", TmpUInt, "", DeviceInfo::MAX_WORK_SIZE);2990 2991    Status = getDeviceAttrRaw(HSA_AGENT_INFO_GRID_MAX_DIM, GridMaxDim);2992    if (Status == HSA_STATUS_SUCCESS) {2993      auto &MaxDim = *Info.add("Grid Max Size per Dimension", std::monostate{},2994                               "", DeviceInfo::MAX_WORK_SIZE_PER_DIMENSION);2995      MaxDim.add("x", GridMaxDim.x);2996      MaxDim.add("y", GridMaxDim.y);2997      MaxDim.add("z", GridMaxDim.z);2998    }2999 3000    Status = getDeviceAttrRaw(HSA_AGENT_INFO_FBARRIER_MAX_SIZE, TmpUInt);3001    if (Status == HSA_STATUS_SUCCESS)3002      Info.add("Max fbarriers/Workgrp", TmpUInt);3003 3004    auto &RootPool = *Info.add("Memory Pools");3005    for (AMDGPUMemoryPoolTy *Pool : AllMemoryPools) {3006      std::string TmpStr, TmpStr2;3007 3008      if (Pool->isGlobal())3009        TmpStr = "Global";3010      else if (Pool->isReadOnly())3011        TmpStr = "ReadOnly";3012      else if (Pool->isPrivate())3013        TmpStr = "Private";3014      else if (Pool->isGroup())3015        TmpStr = "Group";3016      else3017        TmpStr = "Unknown";3018 3019      auto &PoolNode = *RootPool.add(std::string("Pool ") + TmpStr);3020 3021      if (Pool->isGlobal()) {3022        if (Pool->isFineGrained())3023          TmpStr2 += "Fine Grained ";3024        if (Pool->isCoarseGrained())3025          TmpStr2 += "Coarse Grained ";3026        if (Pool->supportsKernelArgs())3027          TmpStr2 += "Kernarg ";3028 3029        PoolNode.add("Flags", TmpStr2);3030      }3031 3032      Status = Pool->getAttrRaw(HSA_AMD_MEMORY_POOL_INFO_SIZE, TmpSt);3033      if (Status == HSA_STATUS_SUCCESS)3034        PoolNode.add(3035            "Size", TmpSt, "bytes",3036            (Pool->isGlobal() && Pool->isCoarseGrained())3037                ? std::optional<DeviceInfo>{DeviceInfo::GLOBAL_MEM_SIZE}3038                : std::nullopt);3039 3040      Status = Pool->getAttrRaw(HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED,3041                                TmpBool);3042      if (Status == HSA_STATUS_SUCCESS)3043        PoolNode.add("Allocatable", TmpBool);3044 3045      Status = Pool->getAttrRaw(HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_GRANULE,3046                                TmpSt);3047      if (Status == HSA_STATUS_SUCCESS)3048        PoolNode.add("Runtime Alloc Granule", TmpSt, "bytes");3049 3050      Status = Pool->getAttrRaw(3051          HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALIGNMENT, TmpSt);3052      if (Status == HSA_STATUS_SUCCESS)3053        PoolNode.add("Runtime Alloc Alignment", TmpSt, "bytes");3054 3055      Status =3056          Pool->getAttrRaw(HSA_AMD_MEMORY_POOL_INFO_ACCESSIBLE_BY_ALL, TmpBool);3057      if (Status == HSA_STATUS_SUCCESS)3058        PoolNode.add("Accessible by all", TmpBool);3059    }3060 3061    auto &ISAs = *Info.add("ISAs");3062    auto Err = hsa_utils::iterateAgentISAs(getAgent(), [&](hsa_isa_t ISA) {3063      Status = hsa_isa_get_info_alt(ISA, HSA_ISA_INFO_NAME, TmpChar);3064      if (Status == HSA_STATUS_SUCCESS)3065        ISAs.add("Name", TmpChar);3066 3067      return Status;3068    });3069 3070    // Silently consume the error.3071    if (Err)3072      consumeError(std::move(Err));3073 3074    return Info;3075  }3076 3077  /// Returns true if auto zero-copy the best configuration for the current3078  /// arch.3079  /// On AMDGPUs, automatic zero-copy is turned on3080  /// when running on an APU with XNACK (unified memory) support3081  /// enabled. On discrete GPUs, automatic zero-copy is triggered3082  /// if the user sets the environment variable OMPX_APU_MAPS=13083  /// and if XNACK is enabled. The rationale is that zero-copy3084  /// is the best configuration (performance, memory footprint) on APUs,3085  /// while it is often not the best on discrete GPUs.3086  /// XNACK can be enabled with a kernel boot parameter or with3087  /// the HSA_XNACK environment variable.3088  bool useAutoZeroCopyImpl() override {3089    return ((IsAPU || OMPX_ApuMaps) && IsXnackEnabled);3090  }3091 3092  Expected<bool> isAccessiblePtrImpl(const void *Ptr, size_t Size) override {3093    hsa_amd_pointer_info_t Info;3094    Info.size = sizeof(hsa_amd_pointer_info_t);3095 3096    hsa_agent_t *Agents = nullptr;3097    uint32_t Count = 0;3098    hsa_status_t Status =3099        hsa_amd_pointer_info(Ptr, &Info, malloc, &Count, &Agents);3100 3101    if (auto Err = Plugin::check(Status, "error in hsa_amd_pointer_info: %s"))3102      return std::move(Err);3103 3104    // Checks if the pointer is known by HSA and accessible by the device3105    for (uint32_t i = 0; i < Count; i++) {3106      if (Agents[i].handle == getAgent().handle)3107        return Info.sizeInBytes >= Size;3108    }3109 3110    // If the pointer is unknown to HSA it's assumed a host pointer3111    // in that case the device can access it on unified memory support is3112    // enabled3113    return IsXnackEnabled;3114  }3115 3116  /// Getters and setters for stack and heap sizes.3117  Error getDeviceStackSize(uint64_t &Value) override {3118    Value = StackSize;3119    return Plugin::success();3120  }3121  Error setDeviceStackSize(uint64_t Value) override {3122    StackSize = Value;3123    return Plugin::success();3124  }3125  Error getDeviceMemorySize(uint64_t &Value) override {3126    for (AMDGPUMemoryPoolTy *Pool : AllMemoryPools) {3127      if (Pool->isGlobal()) {3128        hsa_status_t Status =3129            Pool->getAttrRaw(HSA_AMD_MEMORY_POOL_INFO_SIZE, Value);3130        return Plugin::check(Status, "error in getting device memory size: %s");3131      }3132    }3133    return Plugin::error(ErrorCode::UNSUPPORTED,3134                         "getDeviceMemorySize:: no global pool");3135  }3136 3137  /// AMDGPU-specific function to get device attributes.3138  template <typename Ty> Error getDeviceAttr(uint32_t Kind, Ty &Value) {3139    hsa_status_t Status =3140        hsa_agent_get_info(Agent, (hsa_agent_info_t)Kind, &Value);3141    return Plugin::check(Status, "Error in hsa_agent_get_info: %s");3142  }3143 3144  template <typename Ty>3145  hsa_status_t getDeviceAttrRaw(uint32_t Kind, Ty &Value) {3146    return hsa_agent_get_info(Agent, (hsa_agent_info_t)Kind, &Value);3147  }3148 3149  /// Get the device agent.3150  hsa_agent_t getAgent() const override { return Agent; }3151 3152  /// Get the signal manager.3153  AMDGPUSignalManagerTy &getSignalManager() { return AMDGPUSignalManager; }3154 3155  /// Retrieve and construct all memory pools of the device agent.3156  Error retrieveAllMemoryPools() override {3157    // Iterate through the available pools of the device agent.3158    return hsa_utils::iterateAgentMemoryPools(3159        Agent, [&](hsa_amd_memory_pool_t HSAMemoryPool) {3160          AMDGPUMemoryPoolTy *MemoryPool =3161              Plugin.allocate<AMDGPUMemoryPoolTy>();3162          new (MemoryPool) AMDGPUMemoryPoolTy(HSAMemoryPool);3163          AllMemoryPools.push_back(MemoryPool);3164          return HSA_STATUS_SUCCESS;3165        });3166  }3167 3168  bool useMultipleSdmaEngines() const { return OMPX_UseMultipleSdmaEngines; }3169 3170private:3171  using AMDGPUEventRef = AMDGPUResourceRef<AMDGPUEventTy>;3172  using AMDGPUEventManagerTy = GenericDeviceResourceManagerTy<AMDGPUEventRef>;3173 3174  /// Common method to invoke a single threaded constructor or destructor3175  /// kernel by name.3176  Error callGlobalCtorDtorCommon(GenericPluginTy &Plugin, DeviceImageTy &Image,3177                                 bool IsCtor) {3178    const char *KernelName =3179        IsCtor ? "amdgcn.device.init" : "amdgcn.device.fini";3180    // Perform a quick check for the named kernel in the image. The kernel3181    // should be created by the 'amdgpu-lower-ctor-dtor' pass.3182    GenericGlobalHandlerTy &Handler = Plugin.getGlobalHandler();3183    if (!Handler.isSymbolInImage(*this, Image, KernelName))3184      return Plugin::success();3185 3186    // Allocate and construct the AMDGPU kernel.3187    AMDGPUKernelTy AMDGPUKernel(KernelName);3188    if (auto Err = AMDGPUKernel.init(*this, Image))3189      return Err;3190 3191    AsyncInfoWrapperTy AsyncInfoWrapper(*this, nullptr);3192 3193    KernelArgsTy KernelArgs = {};3194    uint32_t NumBlocksAndThreads[3] = {1u, 1u, 1u};3195    if (auto Err = AMDGPUKernel.launchImpl(3196            *this, NumBlocksAndThreads, NumBlocksAndThreads, KernelArgs,3197            KernelLaunchParamsTy{}, AsyncInfoWrapper))3198      return Err;3199 3200    Error Err = Plugin::success();3201    AsyncInfoWrapper.finalize(Err);3202 3203    return Err;3204  }3205 3206  /// Detect if current architecture is an APU.3207  Error checkIfAPU() {3208    // TODO: replace with ROCr API once it becomes available.3209    llvm::StringRef StrGfxName(ComputeUnitKind);3210    bool MayBeAPU = llvm::StringSwitch<bool>(StrGfxName)3211                        .Case("gfx942", true)3212                        .Default(false);3213    if (!MayBeAPU)3214      return Plugin::success();3215 3216    // can be MI300A or MI300X3217    uint32_t ChipID = 0;3218    if (auto Err = getDeviceAttr(HSA_AMD_AGENT_INFO_CHIP_ID, ChipID))3219      return Err;3220 3221    if (!(ChipID & 0x1)) {3222      IsAPU = true;3223      return Plugin::success();3224    }3225    return Plugin::success();3226  }3227 3228  bool checkIfCoarseGrainMemoryNearOrAbove64GB() {3229    for (AMDGPUMemoryPoolTy *Pool : AllMemoryPools) {3230      if (!Pool->isGlobal() || !Pool->isCoarseGrained())3231        continue;3232      uint64_t Value;3233      hsa_status_t Status =3234          Pool->getAttrRaw(HSA_AMD_MEMORY_POOL_INFO_SIZE, Value);3235      if (Status != HSA_STATUS_SUCCESS)3236        continue;3237      constexpr uint64_t Almost64Gig = 0xFF0000000;3238      if (Value >= Almost64Gig)3239        return true;3240    }3241    return false; // CoarseGrain pool w/ 64GB or more capacity not found3242  }3243 3244  size_t getMemoryManagerSizeThreshold() override {3245    // Targeting high memory capacity GPUs such as3246    // data center GPUs.3247    if (checkIfCoarseGrainMemoryNearOrAbove64GB()) {3248      // Set GenericDeviceTy::MemoryManager's Threshold to 3GiB,3249      // if threshold is not already set by ENV var3250      // LIBOMPTARGET_MEMORY_MANAGER_THRESHOLD.3251      // This MemoryManager is used for omp_target_alloc(), OpenMP3252      // (non-usm) map clause, etc.3253      //3254      // Ideally, this kind of pooling is best performed at3255      // a common level (e.g, user side of HSA) between OpenMP and HIP3256      // but that feature does not exist (yet).3257      return 3ul * 1024 * 1024 * 1024 /* 3 GiB */;3258    }3259    return 0;3260  }3261 3262  /// Envar for controlling the number of HSA queues per device. High number of3263  /// queues may degrade performance.3264  UInt32Envar OMPX_NumQueues;3265 3266  /// Envar for controlling the size of each HSA queue. The size is the number3267  /// of HSA packets a queue is expected to hold. It is also the number of HSA3268  /// packets that can be pushed into each queue without waiting the driver to3269  /// process them.3270  UInt32Envar OMPX_QueueSize;3271 3272  /// Envar for controlling the default number of teams relative to the number3273  /// of compute units (CUs) the device has:3274  ///   #default_teams = OMPX_DefaultTeamsPerCU * #CUs.3275  UInt32Envar OMPX_DefaultTeamsPerCU;3276 3277  /// Envar specifying the maximum size in bytes where the memory copies are3278  /// asynchronous operations. Up to this transfer size, the memory copies are3279  /// asynchronous operations pushed to the corresponding stream. For larger3280  /// transfers, they are synchronous transfers.3281  UInt32Envar OMPX_MaxAsyncCopyBytes;3282 3283  /// Envar controlling the initial number of HSA signals per device. There is3284  /// one manager of signals per device managing several pre-allocated signals.3285  /// These signals are mainly used by AMDGPU streams. If needed, more signals3286  /// will be created.3287  UInt32Envar OMPX_InitialNumSignals;3288 3289  /// Environment variables to set the time to wait in active state before3290  /// switching to blocked state. The default 2000000 busywaits for 2 seconds3291  /// before going into a blocking HSA wait state. The unit for these variables3292  /// are microseconds.3293  UInt32Envar OMPX_StreamBusyWait;3294 3295  /// Use ROCm 5.7 interface for multiple SDMA engines3296  BoolEnvar OMPX_UseMultipleSdmaEngines;3297 3298  /// Value of OMPX_APU_MAPS env var used to force3299  /// automatic zero-copy behavior on non-APU GPUs.3300  BoolEnvar OMPX_ApuMaps;3301 3302  /// Stream manager for AMDGPU streams.3303  AMDGPUStreamManagerTy AMDGPUStreamManager;3304 3305  /// Event manager for AMDGPU events.3306  AMDGPUEventManagerTy AMDGPUEventManager;3307 3308  /// Signal manager for AMDGPU signals.3309  AMDGPUSignalManagerTy AMDGPUSignalManager;3310 3311  /// The agent handler corresponding to the device.3312  hsa_agent_t Agent;3313 3314  /// The GPU architecture.3315  std::string ComputeUnitKind;3316 3317  /// The frequency of the steady clock inside the device.3318  uint64_t ClockFrequency;3319 3320  /// The total number of concurrent work items that can be running on the GPU.3321  uint64_t HardwareParallelism;3322 3323  /// Reference to the host device.3324  AMDHostDeviceTy &HostDevice;3325 3326  /// The current size of the stack that will be used in cases where it could3327  /// not be statically determined.3328  uint64_t StackSize = 16 * 1024 /* 16 KB */;3329 3330  /// Is the plugin associated with an APU?3331  bool IsAPU = false;3332 3333  /// True is the system is configured with XNACK-Enabled.3334  /// False otherwise.3335  bool IsXnackEnabled = false;3336};3337 3338Error AMDGPUDeviceImageTy::loadExecutable(const AMDGPUDeviceTy &Device) {3339  hsa_code_object_reader_t Reader;3340  hsa_status_t Status =3341      hsa_code_object_reader_create_from_memory(getStart(), getSize(), &Reader);3342  if (auto Err = Plugin::check(3343          Status, "error in hsa_code_object_reader_create_from_memory: %s"))3344    return Err;3345 3346  Status = hsa_executable_create_alt(3347      HSA_PROFILE_FULL, HSA_DEFAULT_FLOAT_ROUNDING_MODE_ZERO, "", &Executable);3348  if (auto Err =3349          Plugin::check(Status, "error in hsa_executable_create_alt: %s"))3350    return Err;3351 3352  hsa_loaded_code_object_t Object;3353  Status = hsa_executable_load_agent_code_object(Executable, Device.getAgent(),3354                                                 Reader, "", &Object);3355  if (auto Err = Plugin::check(3356          Status, "error in hsa_executable_load_agent_code_object: %s"))3357    return Err;3358 3359  Status = hsa_executable_freeze(Executable, "");3360  if (auto Err = Plugin::check(Status, "error in hsa_executable_freeze: %s"))3361    return Err;3362 3363  uint32_t Result;3364  Status = hsa_executable_validate(Executable, &Result);3365  if (auto Err = Plugin::check(Status, "error in hsa_executable_validate: %s"))3366    return Err;3367 3368  if (Result)3369    return Plugin::error(ErrorCode::INVALID_BINARY,3370                         "loaded HSA executable does not validate");3371 3372  Status = hsa_code_object_reader_destroy(Reader);3373  if (auto Err =3374          Plugin::check(Status, "error in hsa_code_object_reader_destroy: %s"))3375    return Err;3376 3377  if (auto Err = hsa_utils::readAMDGPUMetaDataFromImage(3378          getMemoryBuffer(), KernelInfoMap, ELFABIVersion))3379    return Err;3380 3381  return Plugin::success();3382}3383 3384Expected<hsa_executable_symbol_t>3385AMDGPUDeviceImageTy::findDeviceSymbol(GenericDeviceTy &Device,3386                                      StringRef SymbolName) const {3387 3388  AMDGPUDeviceTy &AMDGPUDevice = static_cast<AMDGPUDeviceTy &>(Device);3389  hsa_agent_t Agent = AMDGPUDevice.getAgent();3390 3391  hsa_executable_symbol_t Symbol;3392  hsa_status_t Status = hsa_executable_get_symbol_by_name(3393      Executable, SymbolName.data(), &Agent, &Symbol);3394  if (auto Err = Plugin::check(3395          Status, "error in hsa_executable_get_symbol_by_name(%s): %s",3396          SymbolName.data()))3397    return std::move(Err);3398 3399  return Symbol;3400}3401 3402template <typename ResourceTy>3403Error AMDGPUResourceRef<ResourceTy>::create(GenericDeviceTy &Device) {3404  if (Resource)3405    return Plugin::error(ErrorCode::INVALID_ARGUMENT,3406                         "creating an existing resource");3407 3408  AMDGPUDeviceTy &AMDGPUDevice = static_cast<AMDGPUDeviceTy &>(Device);3409 3410  Resource = new ResourceTy(AMDGPUDevice);3411 3412  return Resource->init();3413}3414 3415AMDGPUStreamTy::AMDGPUStreamTy(AMDGPUDeviceTy &Device)3416    : Agent(Device.getAgent()), Queue(nullptr),3417      SignalManager(Device.getSignalManager()), Device(Device),3418      // Initialize the std::deque with some empty positions.3419      Slots(32), NextSlot(0), SyncCycle(0),3420      StreamBusyWaitMicroseconds(Device.getStreamBusyWaitMicroseconds()),3421      UseMultipleSdmaEngines(Device.useMultipleSdmaEngines()) {}3422 3423/// Class implementing the AMDGPU-specific functionalities of the global3424/// handler.3425struct AMDGPUGlobalHandlerTy final : public GenericGlobalHandlerTy {3426  /// Get the metadata of a global from the device. The name and size of the3427  /// global is read from DeviceGlobal and the address of the global is written3428  /// to DeviceGlobal.3429  Error getGlobalMetadataFromDevice(GenericDeviceTy &Device,3430                                    DeviceImageTy &Image,3431                                    GlobalTy &DeviceGlobal) override {3432    AMDGPUDeviceImageTy &AMDImage = static_cast<AMDGPUDeviceImageTy &>(Image);3433 3434    // Find the symbol on the device executable.3435    auto SymbolOrErr =3436        AMDImage.findDeviceSymbol(Device, DeviceGlobal.getName());3437    if (!SymbolOrErr)3438      return SymbolOrErr.takeError();3439 3440    hsa_executable_symbol_t Symbol = *SymbolOrErr;3441    hsa_symbol_kind_t SymbolType;3442    hsa_status_t Status;3443    uint64_t SymbolAddr;3444    uint32_t SymbolSize;3445 3446    // Retrieve the type, address and size of the symbol.3447    std::pair<hsa_executable_symbol_info_t, void *> RequiredInfos[] = {3448        {HSA_EXECUTABLE_SYMBOL_INFO_TYPE, &SymbolType},3449        {HSA_EXECUTABLE_SYMBOL_INFO_VARIABLE_ADDRESS, &SymbolAddr},3450        {HSA_EXECUTABLE_SYMBOL_INFO_VARIABLE_SIZE, &SymbolSize}};3451 3452    for (auto &Info : RequiredInfos) {3453      Status = hsa_executable_symbol_get_info(Symbol, Info.first, Info.second);3454      if (auto Err = Plugin::check(3455              Status, "error in hsa_executable_symbol_get_info: %s"))3456        return Err;3457    }3458 3459    // Check the size of the symbol.3460    if (DeviceGlobal.getSize() && SymbolSize != DeviceGlobal.getSize())3461      return Plugin::error(3462          ErrorCode::INVALID_BINARY,3463          "failed to load global '%s' due to size mismatch (%zu != %zu)",3464          DeviceGlobal.getName().data(), SymbolSize,3465          (size_t)DeviceGlobal.getSize());3466 3467    // Store the symbol address and size on the device global metadata.3468    DeviceGlobal.setPtr(reinterpret_cast<void *>(SymbolAddr));3469    DeviceGlobal.setSize(SymbolSize);3470 3471    return Plugin::success();3472  }3473};3474 3475/// Class implementing the AMDGPU-specific functionalities of the plugin.3476struct AMDGPUPluginTy final : public GenericPluginTy {3477  /// Create an AMDGPU plugin and initialize the AMDGPU driver.3478  AMDGPUPluginTy()3479      : GenericPluginTy(getTripleArch()), Initialized(false),3480        HostDevice(nullptr) {}3481 3482  /// This class should not be copied.3483  AMDGPUPluginTy(const AMDGPUPluginTy &) = delete;3484  AMDGPUPluginTy(AMDGPUPluginTy &&) = delete;3485 3486  /// Initialize the plugin and return the number of devices.3487  Expected<int32_t> initImpl() override {3488    hsa_status_t Status = hsa_init();3489    if (Status != HSA_STATUS_SUCCESS) {3490      // Cannot call hsa_success_string.3491      DP("Failed to initialize AMDGPU's HSA library\n");3492      return 0;3493    }3494 3495    // The initialization of HSA was successful. It should be safe to call3496    // HSA functions from now on, e.g., hsa_shut_down.3497    Initialized = true;3498 3499    // Register event handler to detect memory errors on the devices.3500    Status = hsa_amd_register_system_event_handler(eventHandler, this);3501    if (auto Err = Plugin::check(3502            Status, "error in hsa_amd_register_system_event_handler: %s"))3503      return std::move(Err);3504 3505    // List of host (CPU) agents.3506    llvm::SmallVector<hsa_agent_t> HostAgents;3507 3508    // Count the number of available agents.3509    auto Err = hsa_utils::iterateAgents([&](hsa_agent_t Agent) {3510      // Get the device type of the agent.3511      hsa_device_type_t DeviceType;3512      hsa_status_t Status =3513          hsa_agent_get_info(Agent, HSA_AGENT_INFO_DEVICE, &DeviceType);3514      if (Status != HSA_STATUS_SUCCESS)3515        return Status;3516 3517      // Classify the agents into kernel (GPU) and host (CPU) kernels.3518      if (DeviceType == HSA_DEVICE_TYPE_GPU) {3519        // Ensure that the GPU agent supports kernel dispatch packets.3520        hsa_agent_feature_t Features;3521        Status = hsa_agent_get_info(Agent, HSA_AGENT_INFO_FEATURE, &Features);3522        if (Features & HSA_AGENT_FEATURE_KERNEL_DISPATCH)3523          KernelAgents.push_back(Agent);3524      } else if (DeviceType == HSA_DEVICE_TYPE_CPU) {3525        HostAgents.push_back(Agent);3526      }3527      return HSA_STATUS_SUCCESS;3528    });3529 3530    if (Err)3531      return std::move(Err);3532 3533    int32_t NumDevices = KernelAgents.size();3534    if (NumDevices == 0) {3535      // Do not initialize if there are no devices.3536      DP("There are no devices supporting AMDGPU.\n");3537      return 0;3538    }3539 3540    // There are kernel agents but there is no host agent. That should be3541    // treated as an error.3542    if (HostAgents.empty())3543      return Plugin::error(ErrorCode::BACKEND_FAILURE, "no AMDGPU host agents");3544 3545    // Initialize the host device using host agents.3546    HostDevice = allocate<AMDHostDeviceTy>();3547    new (HostDevice) AMDHostDeviceTy(*this, HostAgents);3548 3549    // Setup the memory pools of available for the host.3550    if (auto Err = HostDevice->init())3551      return std::move(Err);3552 3553    return NumDevices;3554  }3555 3556  /// Deinitialize the plugin.3557  Error deinitImpl() override {3558    // The HSA runtime was not initialized, so nothing from the plugin was3559    // actually initialized.3560    if (!Initialized)3561      return Plugin::success();3562 3563    if (HostDevice)3564      if (auto Err = HostDevice->deinit())3565        return Err;3566 3567    // Finalize the HSA runtime.3568    hsa_status_t Status = hsa_shut_down();3569    return Plugin::check(Status, "error in hsa_shut_down: %s");3570  }3571 3572  /// Creates an AMDGPU device.3573  GenericDeviceTy *createDevice(GenericPluginTy &Plugin, int32_t DeviceId,3574                                int32_t NumDevices) override {3575    return new AMDGPUDeviceTy(Plugin, DeviceId, NumDevices, getHostDevice(),3576                              getKernelAgent(DeviceId));3577  }3578 3579  /// Creates an AMDGPU global handler.3580  GenericGlobalHandlerTy *createGlobalHandler() override {3581    return new AMDGPUGlobalHandlerTy();3582  }3583 3584  Triple::ArchType getTripleArch() const override { return Triple::amdgcn; }3585 3586  const char *getName() const override { return GETNAME(TARGET_NAME); }3587 3588  /// Get the ELF code for recognizing the compatible image binary.3589  uint16_t getMagicElfBits() const override { return ELF::EM_AMDGPU; }3590 3591  /// Check whether the image is compatible with an AMDGPU device.3592  Expected<bool> isELFCompatible(uint32_t DeviceId,3593                                 StringRef Image) const override {3594    // Get the associated architecture and flags from the ELF.3595    auto ElfOrErr = ELF64LEObjectFile::create(3596        MemoryBufferRef(Image, /*Identifier=*/""), /*InitContent=*/false);3597    if (!ElfOrErr)3598      return ElfOrErr.takeError();3599    std::optional<StringRef> Processor = ElfOrErr->tryGetCPUName();3600    if (!Processor)3601      return false;3602 3603    SmallVector<SmallString<32>> Targets;3604    if (auto Err = hsa_utils::getTargetTripleAndFeatures(3605            getKernelAgent(DeviceId), Targets))3606      return Err;3607    for (auto &Target : Targets)3608      if (offloading::amdgpu::isImageCompatibleWithEnv(3609              Processor ? *Processor : "", ElfOrErr->getPlatformFlags(),3610              Target.str()))3611        return true;3612    return false;3613  }3614 3615  bool isDataExchangable(int32_t SrcDeviceId, int32_t DstDeviceId) override {3616    return true;3617  }3618 3619  /// Get the host device instance.3620  AMDHostDeviceTy &getHostDevice() {3621    assert(HostDevice && "Host device not initialized");3622    return *HostDevice;3623  }3624 3625  /// Get the kernel agent with the corresponding agent id.3626  hsa_agent_t getKernelAgent(int32_t AgentId) const {3627    assert((uint32_t)AgentId < KernelAgents.size() && "Invalid agent id");3628    return KernelAgents[AgentId];3629  }3630 3631  /// Get the list of the available kernel agents.3632  const llvm::SmallVector<hsa_agent_t> &getKernelAgents() const {3633    return KernelAgents;3634  }3635 3636private:3637  /// Event handler that will be called by ROCr if an event is detected.3638  static hsa_status_t eventHandler(const hsa_amd_event_t *Event,3639                                   void *PluginPtr) {3640    if (Event->event_type != HSA_AMD_GPU_MEMORY_FAULT_EVENT)3641      return HSA_STATUS_SUCCESS;3642 3643    SmallVector<std::string> Reasons;3644    uint32_t ReasonsMask = Event->memory_fault.fault_reason_mask;3645    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_PAGE_NOT_PRESENT)3646      Reasons.emplace_back("Page not present or supervisor privilege");3647    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_READ_ONLY)3648      Reasons.emplace_back("Write access to a read-only page");3649    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_NX)3650      Reasons.emplace_back("Execute access to a page marked NX");3651    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_HOST_ONLY)3652      Reasons.emplace_back("GPU attempted access to a host only page");3653    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_DRAMECC)3654      Reasons.emplace_back("DRAM ECC failure");3655    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_IMPRECISE)3656      Reasons.emplace_back("Can't determine the exact fault address");3657    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_SRAMECC)3658      Reasons.emplace_back("SRAM ECC failure (ie registers, no fault address)");3659    if (ReasonsMask & HSA_AMD_MEMORY_FAULT_HANG)3660      Reasons.emplace_back("GPU reset following unspecified hang");3661 3662    // If we do not know the reason, say so, otherwise remove the trailing comma3663    // and space.3664    if (Reasons.empty())3665      Reasons.emplace_back("Unknown (" + std::to_string(ReasonsMask) + ")");3666 3667    uint32_t Node = -1;3668    hsa_agent_get_info(Event->memory_fault.agent, HSA_AGENT_INFO_NODE, &Node);3669 3670    AMDGPUPluginTy &Plugin = *reinterpret_cast<AMDGPUPluginTy *>(PluginPtr);3671    for (uint32_t I = 0, E = Plugin.getNumDevices();3672         Node != uint32_t(-1) && I < E; ++I) {3673      AMDGPUDeviceTy &AMDGPUDevice =3674          reinterpret_cast<AMDGPUDeviceTy &>(Plugin.getDevice(I));3675      auto KernelTraceInfoRecord =3676          AMDGPUDevice.KernelLaunchTraces.getExclusiveAccessor();3677 3678      uint32_t DeviceNode = -1;3679      if (auto Err =3680              AMDGPUDevice.getDeviceAttr(HSA_AGENT_INFO_NODE, DeviceNode)) {3681        consumeError(std::move(Err));3682        continue;3683      }3684      if (DeviceNode != Node)3685        continue;3686      void *DevicePtr = (void *)Event->memory_fault.virtual_address;3687      std::string S;3688      llvm::raw_string_ostream OS(S);3689      OS << llvm::format("memory access fault by GPU %" PRIu323690                         " (agent 0x%" PRIx643691                         ") at virtual address %p. Reasons: %s",3692                         Node, Event->memory_fault.agent.handle,3693                         (void *)Event->memory_fault.virtual_address,3694                         llvm::join(Reasons, ", ").c_str());3695      ErrorReporter::reportKernelTraces(AMDGPUDevice, *KernelTraceInfoRecord);3696      ErrorReporter::reportMemoryAccessError(AMDGPUDevice, DevicePtr, S,3697                                             /*Abort*/ true);3698    }3699 3700    // Abort the execution since we do not recover from this error.3701    FATAL_MESSAGE(1,3702                  "memory access fault by GPU %" PRIu32 " (agent 0x%" PRIx643703                  ") at virtual address %p. Reasons: %s",3704                  Node, Event->memory_fault.agent.handle,3705                  (void *)Event->memory_fault.virtual_address,3706                  llvm::join(Reasons, ", ").c_str());3707 3708    return HSA_STATUS_ERROR;3709  }3710 3711  /// Indicate whether the HSA runtime was correctly initialized. Even if there3712  /// is no available devices this boolean will be true. It indicates whether3713  /// we can safely call HSA functions (e.g., hsa_shut_down).3714  bool Initialized;3715 3716  /// Arrays of the available GPU and CPU agents. These arrays of handles should3717  /// not be here but in the AMDGPUDeviceTy structures directly. However, the3718  /// HSA standard does not provide API functions to retirve agents directly,3719  /// only iterating functions. We cache the agents here for convenience.3720  llvm::SmallVector<hsa_agent_t> KernelAgents;3721 3722  /// The device representing all HSA host agents.3723  AMDHostDeviceTy *HostDevice;3724};3725 3726Error AMDGPUKernelTy::launchImpl(GenericDeviceTy &GenericDevice,3727                                 uint32_t NumThreads[3], uint32_t NumBlocks[3],3728                                 KernelArgsTy &KernelArgs,3729                                 KernelLaunchParamsTy LaunchParams,3730                                 AsyncInfoWrapperTy &AsyncInfoWrapper) const {3731  AMDGPUPluginTy &AMDGPUPlugin =3732      static_cast<AMDGPUPluginTy &>(GenericDevice.Plugin);3733  AMDHostDeviceTy &HostDevice = AMDGPUPlugin.getHostDevice();3734  AMDGPUMemoryManagerTy &ArgsMemoryManager = HostDevice.getArgsMemoryManager();3735 3736  void *AllArgs = nullptr;3737  if (auto Err = ArgsMemoryManager.allocate(ArgsSize, &AllArgs))3738    return Err;3739 3740  // Account for user requested dynamic shared memory.3741  uint32_t GroupSize = getGroupSize();3742  if (uint32_t MaxDynCGroupMem = std::max(3743          KernelArgs.DynCGroupMem, GenericDevice.getDynamicMemorySize())) {3744    GroupSize += MaxDynCGroupMem;3745  }3746 3747  uint64_t StackSize;3748  if (auto Err = GenericDevice.getDeviceStackSize(StackSize))3749    return Err;3750 3751  // Copy the explicit arguments.3752  // TODO: We should expose the args memory manager alloc to the common part as3753  // 	   alternative to copying them twice.3754  if (LaunchParams.Size)3755    std::memcpy(AllArgs, LaunchParams.Data, LaunchParams.Size);3756 3757  AMDGPUDeviceTy &AMDGPUDevice = static_cast<AMDGPUDeviceTy &>(GenericDevice);3758 3759  AMDGPUStreamTy *Stream = nullptr;3760  if (auto Err = AMDGPUDevice.getStream(AsyncInfoWrapper, Stream))3761    return Err;3762 3763  uint64_t ImplArgsOffset = utils::roundUp(3764      LaunchParams.Size, alignof(hsa_utils::AMDGPUImplicitArgsTy));3765  if (ArgsSize > ImplArgsOffset) {3766    hsa_utils::AMDGPUImplicitArgsTy *ImplArgs =3767        reinterpret_cast<hsa_utils::AMDGPUImplicitArgsTy *>(3768            utils::advancePtr(AllArgs, ImplArgsOffset));3769 3770    // Set the COV5+ implicit arguments to the appropriate values if present.3771    uint64_t ImplArgsSize = ArgsSize - ImplArgsOffset;3772    std::memset(ImplArgs, 0, ImplArgsSize);3773 3774    using ImplArgsTy = hsa_utils::AMDGPUImplicitArgsTy;3775    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::BlockCountX, ImplArgsSize,3776                           NumBlocks[0]);3777    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::BlockCountY, ImplArgsSize,3778                           NumBlocks[1]);3779    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::BlockCountZ, ImplArgsSize,3780                           NumBlocks[2]);3781 3782    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::GroupSizeX, ImplArgsSize,3783                           NumThreads[0]);3784    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::GroupSizeY, ImplArgsSize,3785                           NumThreads[1]);3786    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::GroupSizeZ, ImplArgsSize,3787                           NumThreads[2]);3788 3789    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::GridDims, ImplArgsSize,3790                           NumBlocks[2] * NumThreads[2] > 13791                               ? 33792                               : 1 + (NumBlocks[1] * NumThreads[1] != 1));3793 3794    hsa_utils::initImplArg(ImplArgs, &ImplArgsTy::DynamicLdsSize, ImplArgsSize,3795                           KernelArgs.DynCGroupMem);3796  }3797 3798  // Push the kernel launch into the stream.3799  return Stream->pushKernelLaunch(*this, AllArgs, NumThreads, NumBlocks,3800                                  GroupSize, StackSize, ArgsMemoryManager);3801}3802 3803Error AMDGPUKernelTy::printLaunchInfoDetails(GenericDeviceTy &GenericDevice,3804                                             KernelArgsTy &KernelArgs,3805                                             uint32_t NumThreads[3],3806                                             uint32_t NumBlocks[3]) const {3807  // Only do all this when the output is requested3808  if (!(getInfoLevel() & OMP_INFOTYPE_PLUGIN_KERNEL))3809    return Plugin::success();3810 3811  // We don't have data to print additional info, but no hard error3812  if (!KernelInfo.has_value())3813    return Plugin::success();3814 3815  // General Info3816  auto *NumGroups = NumBlocks;3817  auto *ThreadsPerGroup = NumThreads;3818 3819  // Kernel Arguments Info3820  auto ArgNum = KernelArgs.NumArgs;3821  auto LoopTripCount = KernelArgs.Tripcount;3822 3823  // Details for AMDGPU kernels (read from image)3824  // https://www.llvm.org/docs/AMDGPUUsage.html#code-object-v4-metadata3825  auto GroupSegmentSize = (*KernelInfo).GroupSegmentList;3826  auto SGPRCount = (*KernelInfo).SGPRCount;3827  auto VGPRCount = (*KernelInfo).VGPRCount;3828  auto SGPRSpillCount = (*KernelInfo).SGPRSpillCount;3829  auto VGPRSpillCount = (*KernelInfo).VGPRSpillCount;3830  auto MaxFlatWorkgroupSize = (*KernelInfo).MaxFlatWorkgroupSize;3831 3832  // Prints additional launch info that contains the following.3833  // Num Args: The number of kernel arguments3834  // Teams x Thrds: The number of teams and the number of threads actually3835  // running.3836  // MaxFlatWorkgroupSize: Maximum flat work-group size supported by the3837  // kernel in work-items3838  // LDS Usage: Amount of bytes used in LDS storage3839  // S/VGPR Count: the number of S/V GPRs occupied by the kernel3840  // S/VGPR Spill Count: how many S/VGPRs are spilled by the kernel3841  // Tripcount: loop tripcount for the kernel3842  INFO(OMP_INFOTYPE_PLUGIN_KERNEL, GenericDevice.getDeviceId(),3843       "#Args: %d Teams x Thrds: %4ux%4u (MaxFlatWorkGroupSize: %u) LDS "3844       "Usage: %uB #SGPRs/VGPRs: %u/%u #SGPR/VGPR Spills: %u/%u Tripcount: "3845       "%lu\n",3846       ArgNum, NumGroups[0] * NumGroups[1] * NumGroups[2],3847       ThreadsPerGroup[0] * ThreadsPerGroup[1] * ThreadsPerGroup[2],3848       MaxFlatWorkgroupSize, GroupSegmentSize, SGPRCount, VGPRCount,3849       SGPRSpillCount, VGPRSpillCount, LoopTripCount);3850 3851  return Plugin::success();3852}3853 3854template <typename... ArgsTy>3855static Error Plugin::check(int32_t Code, const char *ErrFmt, ArgsTy... Args) {3856  hsa_status_t ResultCode = static_cast<hsa_status_t>(Code);3857  if (ResultCode == HSA_STATUS_SUCCESS || ResultCode == HSA_STATUS_INFO_BREAK)3858    return Plugin::success();3859 3860  const char *Desc = "unknown error";3861  hsa_status_t Ret = hsa_status_string(ResultCode, &Desc);3862  if (Ret != HSA_STATUS_SUCCESS)3863    REPORT("Unrecognized " GETNAME(TARGET_NAME) " error code %d\n", Code);3864 3865  // TODO: Add more entries to this switch3866  ErrorCode OffloadErrCode;3867  switch (ResultCode) {3868  case HSA_STATUS_ERROR_INVALID_SYMBOL_NAME:3869    OffloadErrCode = ErrorCode::NOT_FOUND;3870    break;3871  case HSA_STATUS_ERROR_INVALID_CODE_OBJECT:3872    OffloadErrCode = ErrorCode::INVALID_BINARY;3873    break;3874  default:3875    OffloadErrCode = ErrorCode::UNKNOWN;3876  }3877 3878  return Plugin::error(OffloadErrCode, ErrFmt, Args..., Desc);3879}3880 3881Expected<void *> AMDGPUMemoryManagerTy::allocate(size_t Size, void *HstPtr,3882                                                 TargetAllocTy Kind) {3883  // Allocate memory from the pool.3884  void *Ptr = nullptr;3885  if (auto Err = MemoryPool->allocate(Size, &Ptr))3886    return std::move(Err);3887 3888  assert(Ptr && "Invalid pointer");3889 3890  // Get a list of agents that can access this memory pool.3891  llvm::SmallVector<hsa_agent_t> Agents;3892  llvm::copy_if(3893      Plugin.getKernelAgents(), std::back_inserter(Agents),3894      [&](hsa_agent_t Agent) { return MemoryPool->canAccess(Agent); });3895 3896  // Allow all valid kernel agents to access the allocation.3897  if (auto Err = MemoryPool->enableAccess(Ptr, Size, Agents))3898    return std::move(Err);3899  return Ptr;3900}3901 3902Expected<void *> AMDGPUDeviceTy::allocate(size_t Size, void *,3903                                          TargetAllocTy Kind) {3904  if (Size == 0)3905    return nullptr;3906 3907  // Find the correct memory pool.3908  AMDGPUMemoryPoolTy *MemoryPool = nullptr;3909  switch (Kind) {3910  case TARGET_ALLOC_DEFAULT:3911  case TARGET_ALLOC_DEVICE:3912    MemoryPool = CoarseGrainedMemoryPools[0];3913    break;3914  case TARGET_ALLOC_HOST:3915    MemoryPool = &HostDevice.getFineGrainedMemoryPool();3916    break;3917  case TARGET_ALLOC_SHARED:3918    MemoryPool = &HostDevice.getFineGrainedMemoryPool();3919    break;3920  }3921 3922  if (!MemoryPool)3923    return Plugin::error(ErrorCode::UNSUPPORTED,3924                         "no memory pool for the specified allocation kind");3925 3926  // Allocate from the corresponding memory pool.3927  void *Alloc = nullptr;3928  if (auto Err = MemoryPool->allocate(Size, &Alloc))3929    return std::move(Err);3930 3931  if (Alloc) {3932    // Get a list of agents that can access this memory pool. Inherently3933    // necessary for host or shared allocations Also enabled for device memory3934    // to allow device to device memcpy3935    llvm::SmallVector<hsa_agent_t> Agents;3936    llvm::copy_if(static_cast<AMDGPUPluginTy &>(Plugin).getKernelAgents(),3937                  std::back_inserter(Agents), [&](hsa_agent_t Agent) {3938                    return MemoryPool->canAccess(Agent);3939                  });3940 3941    // Enable all valid kernel agents to access the buffer.3942    if (auto Err = MemoryPool->enableAccess(Alloc, Size, Agents))3943      return std::move(Err);3944  }3945 3946  return Alloc;3947}3948 3949void AMDGPUQueueTy::callbackError(hsa_status_t Status, hsa_queue_t *Source,3950                                  void *Data) {3951  auto &AMDGPUDevice = *reinterpret_cast<AMDGPUDeviceTy *>(Data);3952 3953  if (Status == HSA_STATUS_ERROR_EXCEPTION) {3954    auto KernelTraceInfoRecord =3955        AMDGPUDevice.KernelLaunchTraces.getExclusiveAccessor();3956    std::function<bool(__tgt_async_info &)> AsyncInfoWrapperMatcher =3957        [=](__tgt_async_info &AsyncInfo) {3958          auto *Stream = reinterpret_cast<AMDGPUStreamTy *>(AsyncInfo.Queue);3959          if (!Stream || !Stream->getQueue())3960            return false;3961          return Stream->getQueue()->Queue == Source;3962        };3963    ErrorReporter::reportTrapInKernel(AMDGPUDevice, *KernelTraceInfoRecord,3964                                      AsyncInfoWrapperMatcher);3965  }3966 3967  auto Err = Plugin::check(Status, "received error in queue %p: %s", Source);3968  FATAL_MESSAGE(1, "%s", toString(std::move(Err)).data());3969}3970 3971} // namespace plugin3972} // namespace target3973} // namespace omp3974} // namespace llvm3975 3976extern "C" {3977llvm::omp::target::plugin::GenericPluginTy *createPlugin_amdgpu() {3978  return new llvm::omp::target::plugin::AMDGPUPluginTy();3979}3980}3981