[llvm] [OpenMP][Offload] Handle `present/to/from` when a different entry did `alloc/delete`. (PR #165494)
Robert Imschweiler via llvm-commits
llvm-commits at lists.llvm.org
Fri Feb 13 01:51:12 PST 2026
================
@@ -1193,10 +1243,128 @@ int targetDataEnd(ident_t *Loc, DeviceTy &Device, int32_t ArgNum,
// copy-back was issued but before it completed. Since the reuse might
// also copy-back a value we would race.
if (TPR.Flags.IsLast) {
- if (TPR.getEntry()->addEventIfNecessary(Device, AsyncInfo) !=
- OFFLOAD_SUCCESS)
+ if (Entry->addEventIfNecessary(Device, AsyncInfo) != OFFLOAD_SUCCESS)
return OFFLOAD_FAIL;
}
+
+ // Track this transfer to avoid duplicate transfers later on.
+ StateInfo->addTransferredFromEntry(HstPtr, Size);
+
+ return OFFLOAD_SUCCESS;
+ };
+
+ // Lambda to check if this pointer was previously released.
+ //
+ // This is needed to handle cases like the following:
+ // p1 = p2 = &x;
+ // ... map(delete: p1[:]) map(from: p2[0:1])
+ // The ref-count becomes zero before encountering the FROM entry, but we
+ // still need to do a transfer, if it went from non-zero to zero.
+ //
+ // OpenMP 6.0, sec. 7.9.6 "map Clause", p. 284 L24-26:
+ // If the reference count of the corresponding list item is one or if
+ // the always-modifier or delete-modifier is specified, and if the map
+ // type is from, the original list item is updated as if the list item
+ // appeared in a from clause on a target_update directive.
+ auto WasPreviouslyReleased = [&]() -> bool {
+ auto ReleasedEntry = StateInfo->wasPreviouslyReleased(HstPtrBegin);
+ if (!ReleasedEntry)
+ return false;
+
+ void *ReleasedPtr = ReleasedEntry->first;
+ int64_t ReleasedSize = ReleasedEntry->second;
+ ODBG(ODT_Mapping) << "Pointer HstPtr=" << HstPtrBegin
+ << " falls within a range previously released ["
+ << ReleasedPtr << ", "
+ << static_cast<void *>(
+ static_cast<char *>(ReleasedPtr) + ReleasedSize)
+ << ") with size=" << ReleasedSize;
+ return true;
+ };
+
+ bool IsMapFromOnNonHostNonZeroData =
+ HasFrom && !TPR.Flags.IsHostPointer && DataSize != 0;
+
+ auto IsLastOrHasAlwaysOrWasReleased = [&]() {
+ return TPR.Flags.IsLast || HasAlways || WasPreviouslyReleased();
+ };
+
+ if (IsMapFromOnNonHostNonZeroData && IsLastOrHasAlwaysOrWasReleased()) {
+ Ret = PerformFromRetrieval(HstPtrBegin, TgtPtrBegin, DataSize,
+ TPR.getEntry());
+ if (Ret != OFFLOAD_SUCCESS)
+ return OFFLOAD_FAIL;
+ } else if (IsMapFromOnNonHostNonZeroData) {
+ // We can have cases like the following:
+ // p1 = p2 = &x;
+ // ... map(storage: p1[:]) map(from: p2[1:1])
+ //
+ // where it's possible that when the FROM entry is processed, the
+ // ref count is not zero, so no data transfer happens for it. But
+ // the ref-count can go down to zero once all maps have been processed
+ // for the current construct, in which case a transfer should happen.
+ //
+ // So, we keep track of any skipped FROM data-transfers, in case
+ // the ref-count goes down to zero later on.
+ //
+ // This cannot be handled in the compiler for all cases because the
+ // list-items may look very different, as shown in the example above,
+ // which is allowed with OpenMP 6.0:
+ //
+ // OpenMP 6.0, sec. 7.9.6 "map Clause", p. 286 L18-21:
+ // Two list items of the map clauses on the same construct must not share
+ // original storage unless one of the following is true: they are the same
+ // list item, one is the containing structure of the other, at least one
+ // is an assumed-size array, or at least one is implicitly mapped due to
+ // the list item also appearing in a use_device_addr clause.
+ StateInfo->addSkippedFromEntry(HstPtrBegin, DataSize);
+ ODBG(ODT_Mapping) << "Skipping FROM map transfer for HstPtr="
+ << HstPtrBegin << " size=" << DataSize
+ << " (IsLast=" << TPR.Flags.IsLast << ", TotalRefCount="
+ << TPR.getEntry()->getTotalRefCount() << ")";
+ }
+
+ // If the ref-count went to zero (IsLast=true), check if any previously
+ // skipped FROM entries fall within this released entry's range.
+ if (TPR.Flags.IsLast && !StateInfo->SkippedFromEntries.empty()) {
+ uintptr_t ReleasedBeginPtrInt = TPR.getEntry()->HstPtrBegin;
+ uintptr_t ReleasedEndPtrInt = TPR.getEntry()->HstPtrEnd;
+ SmallVector<void *, 32> ToRemove;
+
+ for (auto &SkippedFromEntry : StateInfo->SkippedFromEntries) {
+ void *FromBeginPtr = SkippedFromEntry.first;
+ int64_t FromDataSize = SkippedFromEntry.second;
+ uintptr_t FromBeginPtrInt = reinterpret_cast<uintptr_t>(FromBeginPtr);
+
+ // Check if this skipped FROM entry's starting pointer falls within this
+ // released entry
+ if (FromBeginPtrInt >= ReleasedBeginPtrInt &&
+ FromBeginPtrInt < ReleasedEndPtrInt) {
----------------
ro-i wrote:
why do we only check if the starting pointer is within the released entry? What if `FromDataSize` would lead to an out-of-bounds read below when fetching that size from `FromTgtBeginPtr`?
https://github.com/llvm/llvm-project/pull/165494
More information about the llvm-commits
mailing list