[Openmp-commits] [llvm] [openmp] [offload] Use HSA SVM for AMDGPU shared memory (PR #215801)

Joseph Huber via Openmp-commits openmp-commits at lists.llvm.org
Wed Aug 12 07:09:06 PDT 2026


================
@@ -450,6 +455,157 @@ struct AMDGPUMemoryPoolTy {
   size_t PoolAllocationAlignment;
 };
 
+/// Class that implements shared (managed) allocations on top of the HSA shared
+/// virtual memory (SVM) interface.
+///
+/// SVM allocations are backed by ordinary system memory that is made
+/// accessible to all the kernel agents. With XNACK enabled, the driver
+/// migrates the pages between the host and the devices on demand; otherwise
+/// they remain resident in system memory.
+struct AMDGPUSVMManagerTy {
+  /// Determine whether the SVM interface can be used for shared allocations.
+  void init() {
+    if (!OMPX_UseSVM)
+      return;
+
+    bool SVMSupported = false;
+    if (hsa_system_get_info(HSA_AMD_SYSTEM_INFO_SVM_SUPPORTED, &SVMSupported) !=
+        HSA_STATUS_SUCCESS)
+      return;
+
+    Supported = SVMSupported;
+    PageSize = llvm::sys::Process::getPageSizeEstimate();
+  }
+
+  /// Release the allocations that are still live.
+  Error deinit() {
+    std::lock_guard<std::mutex> Lock(Mutex);
+
+    Error Err = Plugin::success();
+    for (auto &Allocation : Allocations)
+      if (std::error_code EC =
+              llvm::sys::Memory::releaseMappedMemory(Allocation.second))
+        Err = joinErrors(std::move(Err),
+                         Plugin::error(ErrorCode::UNKNOWN,
+                                       "error releasing SVM memory: %s",
+                                       EC.message().c_str()));
+
+    Allocations.clear();
+    return Err;
+  }
+
+  /// Whether new SVM allocations can be created.
+  bool isSupported() const { return Supported; }
+
+  /// Allocate system memory and make it accessible to all the \p Agents.
+  /// Returns a null pointer if the request cannot be served, either because
+  /// the requested alignment is too large or because the driver rejected the
+  /// allocation.
+  Expected<void *> allocate(size_t Size, size_t Alignment,
+                            ArrayRef<hsa_agent_t> Agents) {
+    // A concurrent allocation may have disabled the SVM interface.
+    if (!Supported)
+      return nullptr;
+
+    // Mapped memory is only page aligned.
+    if (Alignment > PageSize)
+      return nullptr;
+
+    std::error_code EC;
+    llvm::sys::MemoryBlock Block = llvm::sys::Memory::allocateMappedMemory(
+        Size, /*NearBlock=*/nullptr,
+        llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE, EC);
+    if (EC)
+      return Plugin::error(ErrorCode::OUT_OF_RESOURCES,
+                           "error allocating SVM memory: %s",
+                           EC.message().c_str());
+
+    // Give all the kernel agents access to the allocation. Accesses may incur
+    // a page fault and the migration of the memory to the accessing agent.
+    llvm::SmallVector<hsa_amd_svm_attribute_pair_t> Attrs;
+    for (hsa_agent_t Agent : Agents)
+      Attrs.push_back({HSA_AMD_SVM_ATTRIB_AGENT_ACCESSIBLE, Agent.handle});
+
+    hsa_status_t Status = hsa_amd_svm_attributes_set(
+        Block.base(), Block.allocatedSize(), Attrs.data(), Attrs.size());
+    if (auto Err =
+            Plugin::check(Status, "error in hsa_amd_svm_attributes_set: %s")) {
+      // The driver rejected the allocation, e.g., because one of the agents
+      // does not support SVM. Stop creating SVM allocations, reporting the
+      // rejection once.
+      if (Supported.exchange(false))
+        REPORT() << "Serving shared allocations from host memory: "
+                 << toString(std::move(Err));
+      else
+        consumeError(std::move(Err));
+
+      // Ignore any error from undoing the allocation.
+      consumeError(llvm::errorCodeToError(
+          llvm::sys::Memory::releaseMappedMemory(Block)));
+      return nullptr;
+    }
+
+    std::lock_guard<std::mutex> Lock(Mutex);
+    Allocations[reinterpret_cast<uintptr_t>(Block.base())] = Block;
+    return Block.base();
+  }
+
+  /// Release the SVM allocation starting at \p Ptr. Returns whether \p Ptr is
+  /// an SVM allocation.
+  Expected<bool> deallocate(void *Ptr) {
+    llvm::sys::MemoryBlock Block;
+    {
+      std::lock_guard<std::mutex> Lock(Mutex);
+      auto It = Allocations.find(reinterpret_cast<uintptr_t>(Ptr));
+      if (It == Allocations.end())
+        return false;
+      Block = It->second;
+      Allocations.erase(It);
+    }
+
+    if (std::error_code EC = llvm::sys::Memory::releaseMappedMemory(Block))
+      return Plugin::error(ErrorCode::UNKNOWN, "error releasing SVM memory: %s",
+                           EC.message().c_str());
+    return true;
+  }
+
+  /// Whether the \p Size bytes starting at \p Ptr are within an SVM
+  /// allocation. Allocations stay live after the SVM interface is disabled, so
+  /// this must not check Supported.
+  bool contains(const void *Ptr, size_t Size = 1) const {
----------------
jhuber6 wrote:

Do we need a full region check? The user shouldn't corrupt the pointers returned by the allocator. I'd only expect to need this if we were caching pages. i.e. several allocations share a 4KiB page and before freeing it all others need to be freed.

https://github.com/llvm/llvm-project/pull/215801


More information about the Openmp-commits mailing list