Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
025d36e
[OpenMP] Adds omp_target_is_accessible routine
nicebert May 2, 2025
b33b27e
Update offload/libomptarget/OpenMP/API.cpp
nicebert Jul 28, 2025
bf01578
Fix comment spelling
nicebert Jul 28, 2025
d20f4d5
[OpenMP] Adds omp_target_is_accessible routine
nicebert May 2, 2025
cb87242
[OpenMP] Adds omp_target_is_accessible routine
nicebert May 2, 2025
03d45cc
Merge branch 'feat/omp_target_is_accessible' of github.com:nicebert/l…
nicebert Jul 31, 2025
21b1d6a
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Jul 31, 2025
95ab6fe
[OpenMP] Reverts omp_get_initial_device changes
nicebert Aug 1, 2025
2436211
Merge branch 'feat/omp_target_is_accessible' of github.com:nicebert/l…
nicebert Aug 1, 2025
c92c94f
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Aug 1, 2025
eeb7604
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Aug 6, 2025
9d97424
[OpenMP] Adds omp_target_is_accessible routine
nicebert May 2, 2025
34acf27
Update offload/libomptarget/OpenMP/API.cpp
nicebert Jul 28, 2025
d4ecaf6
Fix comment spelling
nicebert Jul 28, 2025
2792290
[OpenMP] Adds omp_target_is_accessible routine
nicebert May 2, 2025
712bdd1
[OpenMP] Reverts omp_get_initial_device changes
nicebert Aug 1, 2025
dd15747
[OpenMP] Rework implementation to be conform to OpenMP 6.0
nicebert Sep 15, 2025
108e4b9
Merge branch 'feat/omp_target_is_accessible' of github.com:nicebert/l…
nicebert Sep 15, 2025
e9dccd6
Applies git-clang-format
nicebert Sep 17, 2025
4345232
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Sep 17, 2025
4b51745
Fixes formatting and comment issues.
nicebert Sep 19, 2025
0ef2e79
Merge branch 'feat/omp_target_is_accessible' of github.com:nicebert/l…
nicebert Sep 19, 2025
454df9e
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Sep 19, 2025
79dd36f
[OpenMP] Fixes check for host device number
nicebert Sep 19, 2025
0ccae1d
Merge branch 'feat/omp_target_is_accessible' of github.com:nicebert/l…
nicebert Sep 19, 2025
588c394
Fixes formatting
nicebert Sep 19, 2025
b751e75
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Sep 19, 2025
584553e
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Sep 19, 2025
8961e19
Review nit add braces to for loop body
nicebert Oct 14, 2025
357ccd9
[OpenMP] Changes error handling for omp_target_is_accessible
nicebert Oct 16, 2025
f271422
Merge branch 'feat/omp_target_is_accessible' of github.com:nicebert/l…
nicebert Oct 16, 2025
4a223a4
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Oct 16, 2025
464235c
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Oct 22, 2025
d84d07b
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Oct 22, 2025
ed8cf2d
Merge branch 'main' into feat/omp_target_is_accessible
nicebert Oct 22, 2025
af149ce
[OpenMP] Update OpenMP support document
nicebert Oct 22, 2025
e8838d6
Merge branch 'feat/omp_target_is_accessible' of github.com:nicebert/l…
nicebert Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions offload/include/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ struct DeviceTy {
/// Ask the device whether the runtime should use auto zero-copy.
bool useAutoZeroCopy();

/// Ask the device whether the storage is accessible.
bool isAccessiblePtr(const void *Ptr, size_t Size);

/// Check if there are pending images for this device.
bool hasPendingImages() const { return HasPendingImages; }

Expand Down
1 change: 1 addition & 0 deletions offload/include/omptarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ int omp_get_initial_device(void);
void *omp_target_alloc(size_t Size, int DeviceNum);
void omp_target_free(void *DevicePtr, int DeviceNum);
int omp_target_is_present(const void *Ptr, int DeviceNum);
int omp_target_is_accessible(const void *Ptr, size_t Size, int DeviceNum);
int omp_target_memcpy(void *Dst, const void *Src, size_t Length,
size_t DstOffset, size_t SrcOffset, int DstDevice,
int SrcDevice);
Expand Down
28 changes: 28 additions & 0 deletions offload/libomptarget/OpenMP/API.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,34 @@ EXTERN int omp_target_is_present(const void *Ptr, int DeviceNum) {
return Rc;
}

/// Check whether a pointer is accessible from a device.
/// Returns true when accessibility is guaranteed otherwise returns false.
EXTERN int omp_target_is_accessible(const void *Ptr, size_t Size,
int DeviceNum) {
TIMESCOPE();
OMPT_IF_BUILT(ReturnAddressSetterRAII RA(__builtin_return_address(0)));
DP("Call to omp_target_is_accessible for device %d, address " DPxMOD
", size %zu\n",
DeviceNum, DPxPTR(Ptr), Size);

if (!Ptr) {
DP("Call to omp_target_is_accessible with NULL ptr returning false\n");
return false;
}

if (DeviceNum == omp_get_initial_device() || DeviceNum == -1) {
DP("Call to omp_target_is_accessible on host, returning true\n");
return true;
}

// The device number must refer to a valid device
auto DeviceOrErr = PM->getDevice(DeviceNum);
if (!DeviceOrErr)
FATAL_MESSAGE(DeviceNum, "%s", toString(DeviceOrErr.takeError()).c_str());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether this is a fatal message or simple just return false? What does the spec say when the device number is invalid?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @shiltian : let's return false if the device doesn't exist. A warning is also in order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make this change but then we probably need to reconsider how we handle invalid devices in all cases in the runtime since in the API.cpp file alone it's handled in this way for:
is_present
memcpy
(dis)associate_ptr
get_mapped_ptr

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a question to spec. If spec doesn't say anything, my $.02 is to return false. We probably don't want a program to crash immediately with invalid input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't change the fact that we have those other functions that don't state that the program is to crash immediately with invalid input. Do we then need to adjust those functions as well to return false and emit a warning? I don't think it's sensible to have one function act in one way and one in another if the device is invalid.

Copy link
Contributor

@shiltian shiltian Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes indeed. We definitely don't want inconsistent behavior. We need to update them all (in a separate PR for others which may also server as an RFC).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think crashing is the better behavior for several reasons.

  1. It is in line with what the other routines do and also how kernel invocations fail when the device number does not exist.

  2. While returning would be OK, because passing an incorrect device number does constitute unspecified behavior and the implementation is allowed to not check for this to happen, it may make debugging much harder. The failing code will return a seemingly useful result and thus introduce some inconsistent state in the program. I would expect that shortly after the code would re-use incorrect device number to invoke a kernel and thus fail there. So, we can also fail early.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is different. For a target region, yes, crash is okay, since the spec doesn't seem to say anything about how to deal with invalid input in clauses, based on my understanding (might be out of date though). APIs, on the other hand, is different. I think it'd not be a good idea someone calls some functions with invalid input and then their program crashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a discussion we need to have in another setting than this issue as it does not just affect this API routine but existing routines (of which I stated a couple of examples above). My implementation follows the way this is currently handled so I think if you want to change the handling the discussion needs to be had in a different setting with a bigger audience.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. CC @jhuber6 Maybe a topic for the next Wed meeting. :-)


return DeviceOrErr->isAccessiblePtr(Ptr, Size);
}

EXTERN int omp_target_memcpy(void *Dst, const void *Src, size_t Length,
size_t DstOffset, size_t SrcOffset, int DstDevice,
int SrcDevice) {
Expand Down
4 changes: 4 additions & 0 deletions offload/libomptarget/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,7 @@ bool DeviceTy::useAutoZeroCopy() {
return false;
return RTL->use_auto_zero_copy(RTLDeviceID);
}

bool DeviceTy::isAccessiblePtr(const void *Ptr, size_t Size) {
return RTL->is_accessible_ptr(RTLDeviceID, Ptr, Size);
}
1 change: 1 addition & 0 deletions offload/libomptarget/exports
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ VERS1.0 {
omp_get_initial_device;
omp_target_alloc;
omp_target_free;
omp_target_is_accessible;
omp_target_is_present;
omp_target_memcpy;
omp_target_memcpy_rect;
Expand Down
24 changes: 24 additions & 0 deletions offload/plugins-nextgen/amdgpu/src/rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3062,6 +3062,30 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {
return ((IsAPU || OMPX_ApuMaps) && IsXnackEnabled);
}

Expected<bool> isAccessiblePtrImpl(const void *Ptr, size_t Size) override {
hsa_amd_pointer_info_t Info;
Info.size = sizeof(hsa_amd_pointer_info_t);

hsa_agent_t *Agents = nullptr;
uint32_t Count = 0;
hsa_status_t Status =
hsa_amd_pointer_info(Ptr, &Info, malloc, &Count, &Agents);

if (auto Err = Plugin::check(Status, "error in hsa_amd_pointer_info: %s"))
return std::move(Err);

// Checks if the pointer is known by HSA and accessible by the device
for (uint32_t i = 0; i < Count; i++) {
if (Agents[i].handle == getAgent().handle)
return Info.sizeInBytes >= Size;
}

// If the pointer is unknown to HSA it's assumed a host pointer
// in that case the device can access it on unified memory support is
// enabled
return IsXnackEnabled;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit: there are GPUs that do not have xnack, but still have the ability to access host memory. This behavior is fine for now, but we will have to revisit based on GPU (later, not in this PR).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it should be documented.

}

/// Getters and setters for stack and heap sizes.
Error getDeviceStackSize(uint64_t &Value) override {
Value = StackSize;
Expand Down
11 changes: 11 additions & 0 deletions offload/plugins-nextgen/common/include/PluginInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,10 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
bool useAutoZeroCopy();
virtual bool useAutoZeroCopyImpl() { return false; }

/// Returns true if the plugin can guarantee that the associated
/// storage is accessible
Expected<bool> isAccessiblePtr(const void *Ptr, size_t Size);

virtual Expected<omp_interop_val_t *>
createInterop(int32_t InteropType, interop_spec_t &InteropSpec) {
return nullptr;
Expand Down Expand Up @@ -1166,6 +1170,10 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
/// Per device setting of MemoryManager's Threshold
virtual size_t getMemoryManagerSizeThreshold() { return 0; }

virtual Expected<bool> isAccessiblePtrImpl(const void *Ptr, size_t Size) {
return false;
}

/// Environment variables defined by the OpenMP standard.
Int32Envar OMP_TeamLimit;
Int32Envar OMP_NumTeams;
Expand Down Expand Up @@ -1492,6 +1500,9 @@ struct GenericPluginTy {
/// Returns if the plugin can support automatic copy.
int32_t use_auto_zero_copy(int32_t DeviceId);

/// Returns if the associated storage is accessible for a given device.
int32_t is_accessible_ptr(int32_t DeviceId, const void *Ptr, size_t Size);

/// Look up a global symbol in the given binary.
int32_t get_global(__tgt_device_binary Binary, uint64_t Size,
const char *Name, void **DevicePtr);
Expand Down
20 changes: 20 additions & 0 deletions offload/plugins-nextgen/common/src/PluginInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1599,6 +1599,10 @@ Error GenericDeviceTy::syncEvent(void *EventPtr) {

bool GenericDeviceTy::useAutoZeroCopy() { return useAutoZeroCopyImpl(); }

Expected<bool> GenericDeviceTy::isAccessiblePtr(const void *Ptr, size_t Size) {
return isAccessiblePtrImpl(Ptr, Size);
}

Error GenericPluginTy::init() {
if (Initialized)
return Plugin::success();
Expand Down Expand Up @@ -2133,6 +2137,22 @@ int32_t GenericPluginTy::use_auto_zero_copy(int32_t DeviceId) {
return getDevice(DeviceId).useAutoZeroCopy();
}

int32_t GenericPluginTy::is_accessible_ptr(int32_t DeviceId, const void *Ptr,
size_t Size) {
auto HandleError = [&](Error Err) -> bool {
[[maybe_unused]] std::string ErrStr = toString(std::move(Err));
DP("Failure while checking accessibility of pointer %p for device %d: %s",
Ptr, DeviceId, ErrStr.c_str());
return false;
};

auto AccessibleOrErr = getDevice(DeviceId).isAccessiblePtr(Ptr, Size);
if (Error Err = AccessibleOrErr.takeError())
return HandleError(std::move(Err));

return *AccessibleOrErr;
}

int32_t GenericPluginTy::get_global(__tgt_device_binary Binary, uint64_t Size,
const char *Name, void **DevicePtr) {
assert(Binary.handle && "Invalid device binary handle");
Expand Down
40 changes: 40 additions & 0 deletions offload/test/mapping/is_accessible.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: %libomptarget-compilexx-generic
// RUN: env HSA_XNACK=1 %libomptarget-run-generic 2>&1 \
// RUN: | %fcheck-generic

// RUN: %libomptarget-compilexx-generic
// RUN: env HSA_XNACK=0 %libomptarget-run-generic 2>&1 \
// RUN: | %fcheck-generic -check-prefix=NO_USM

// REQUIRES: unified_shared_memory
// XFAIL: nvptx

// CHECK: SUCCESS
// NO_USM: Not accessible

#include <assert.h>
#include <iostream>
#include <omp.h>
#include <stdio.h>

int main() {
int n = 10000;
int *a = new int[n];
int err = 0;

// program must be executed with HSA_XNACK=1
if (!omp_target_is_accessible(a, n * sizeof(int), /*device_num=*/0))
printf("Not accessible\n");
else {
#pragma omp target teams distribute parallel for
for (int i = 0; i < n; i++)
a[i] = i;

for (int i = 0; i < n; i++)
if (a[i] != i)
err++;
}

printf("%s\n", err == 0 ? "SUCCESS" : "FAIL");
return err;
}
Loading