From c7824750af75aadc93913740ba01bf0eff6e4d28 Mon Sep 17 00:00:00 2001 From: Akash Banerjee Date: Thu, 27 Nov 2025 19:41:59 +0000 Subject: [PATCH 1/2] [MLIR][OpenMP] Add OpenMPToLLVMIRTranslation support for is_device_ptr This PR adds support for the OpenMP is_device_ptr clause in the MLIR to LLVM IR translation for target regions. The is_device_ptr clause allows device pointers (allocated via OpenMP runtime APIs) to be used directly in target regions without implicit mapping. --- .../OpenMP/map-types-and-sizes.f90 | 9 ++++ .../OpenMP/OpenMPToLLVMIRTranslation.cpp | 16 ++++--- mlir/test/Target/LLVMIR/omptarget-llvm.mlir | 17 +++++++ mlir/test/Target/LLVMIR/openmp-todo.mlir | 11 ----- .../fortran/target-is-device-ptr.f90 | 46 +++++++++++++++++++ 5 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 offload/test/offloading/fortran/target-is-device-ptr.f90 diff --git a/flang/test/Integration/OpenMP/map-types-and-sizes.f90 b/flang/test/Integration/OpenMP/map-types-and-sizes.f90 index 44a049f5ac510..85434460bbea6 100644 --- a/flang/test/Integration/OpenMP/map-types-and-sizes.f90 +++ b/flang/test/Integration/OpenMP/map-types-and-sizes.f90 @@ -33,6 +33,15 @@ subroutine mapType_array !$omp end target end subroutine mapType_array +!CHECK: @.offload_sizes{{.*}} = private unnamed_addr constant [1 x i64] [i64 8] +!CHECK: @.offload_maptypes{{.*}} = private unnamed_addr constant [1 x i64] [i64 288] +subroutine mapType_is_device_ptr + use iso_c_binding, only : c_ptr + type(c_ptr) :: p + !$omp target is_device_ptr(p) + !$omp end target +end subroutine mapType_is_device_ptr + !CHECK: @.offload_sizes{{.*}} = private unnamed_addr constant [4 x i64] [i64 0, i64 24, i64 8, i64 0] !CHECK: @.offload_maptypes{{.*}} = private unnamed_addr constant [4 x i64] [i64 32, i64 281474976711169, i64 281474976711171, i64 281474976711187] subroutine mapType_ptr diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp index f28454075f1d3..abd582e6a4e63 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -332,10 +332,6 @@ static LogicalResult checkImplementationStatus(Operation &op) { op.getInReductionSyms()) result = todo("in_reduction"); }; - auto checkIsDevicePtr = [&todo](auto op, LogicalResult &result) { - if (!op.getIsDevicePtrVars().empty()) - result = todo("is_device_ptr"); - }; auto checkLinear = [&todo](auto op, LogicalResult &result) { if (!op.getLinearVars().empty() || !op.getLinearStepVars().empty()) result = todo("linear"); @@ -444,7 +440,6 @@ static LogicalResult checkImplementationStatus(Operation &op) { checkBare(op, result); checkDevice(op, result); checkInReduction(op, result); - checkIsDevicePtr(op, result); }) .Default([](Operation &) { // Assume all clauses for an operation can be translated unless they are @@ -3875,6 +3870,11 @@ convertClauseMapFlags(omp::ClauseMapFlags mlirFlags) { if (mapTypeToBool(omp::ClauseMapFlags::attach)) mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ATTACH; + if (mapTypeToBool(omp::ClauseMapFlags::is_device_ptr)) { + mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TARGET_PARAM; + mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL; + } + return mapType; } @@ -3996,6 +3996,9 @@ static void collectMapDataFromMapOperands( llvm::Value *origValue = moduleTranslation.lookupValue(offloadPtr); auto mapType = convertClauseMapFlags(mapOp.getMapType()); auto mapTypeAlways = llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS; + bool isDevicePtr = + (mapOp.getMapType() & omp::ClauseMapFlags::is_device_ptr) != + omp::ClauseMapFlags::none; mapData.OriginalValue.push_back(origValue); mapData.BasePointers.push_back(origValue); @@ -4029,7 +4032,8 @@ static void collectMapDataFromMapOperands( mapData.Names.push_back(LLVM::createMappingInformation( mapOp.getLoc(), *moduleTranslation.getOpenMPBuilder())); mapData.DevicePointers.push_back( - llvm::OpenMPIRBuilder::DeviceInfoTy::Address); + isDevicePtr ? llvm::OpenMPIRBuilder::DeviceInfoTy::Pointer + : llvm::OpenMPIRBuilder::DeviceInfoTy::Address); mapData.IsAMapping.push_back(false); mapData.IsAMember.push_back(checkIsAMember(hasDevAddrOperands, mapOp)); } diff --git a/mlir/test/Target/LLVMIR/omptarget-llvm.mlir b/mlir/test/Target/LLVMIR/omptarget-llvm.mlir index e6ea3aaeec656..e289d5d013eaa 100644 --- a/mlir/test/Target/LLVMIR/omptarget-llvm.mlir +++ b/mlir/test/Target/LLVMIR/omptarget-llvm.mlir @@ -622,3 +622,20 @@ module attributes {omp.target_triples = ["amdgcn-amd-amdhsa"]} { // CHECK: br label %[[VAL_40]] // CHECK: omp.done: ; preds = %[[VAL_68]], %[[VAL_63]], %[[VAL_32]] // CHECK: ret void + +// ----- + +module attributes {omp.target_triples = ["amdgcn-amd-amdhsa"]} { + llvm.func @_QPomp_target_is_device_ptr(%arg0 : !llvm.ptr) { + %map = omp.map.info var_ptr(%arg0 : !llvm.ptr, !llvm.ptr) + map_clauses(is_device_ptr) capture(ByRef) -> !llvm.ptr {name = ""} + omp.target map_entries(%map -> %ptr_arg : !llvm.ptr) { + omp.terminator + } + llvm.return + } +} + +// CHECK: @.offload_sizes = private unnamed_addr constant [1 x i64] [i64 8] +// CHECK: @.offload_maptypes = private unnamed_addr constant [1 x i64] [i64 288] +// CHECK-LABEL: define void @_QPomp_target_is_device_ptr diff --git a/mlir/test/Target/LLVMIR/openmp-todo.mlir b/mlir/test/Target/LLVMIR/openmp-todo.mlir index af6d254cfd3c3..0704008aa7135 100644 --- a/mlir/test/Target/LLVMIR/openmp-todo.mlir +++ b/mlir/test/Target/LLVMIR/openmp-todo.mlir @@ -238,17 +238,6 @@ llvm.func @target_in_reduction(%x : !llvm.ptr) { // ----- -llvm.func @target_is_device_ptr(%x : !llvm.ptr) { - // expected-error@below {{not yet implemented: Unhandled clause is_device_ptr in omp.target operation}} - // expected-error@below {{LLVM Translation failed for operation: omp.target}} - omp.target is_device_ptr(%x : !llvm.ptr) { - omp.terminator - } - llvm.return -} - -// ----- - llvm.func @target_enter_data_depend(%x: !llvm.ptr) { // expected-error@below {{not yet implemented: Unhandled clause depend in omp.target_enter_data operation}} // expected-error@below {{LLVM Translation failed for operation: omp.target_enter_data}} diff --git a/offload/test/offloading/fortran/target-is-device-ptr.f90 b/offload/test/offloading/fortran/target-is-device-ptr.f90 new file mode 100644 index 0000000000000..b2da1ad23d093 --- /dev/null +++ b/offload/test/offloading/fortran/target-is-device-ptr.f90 @@ -0,0 +1,46 @@ +! Validate that a device pointer obtained via omp_get_mapped_ptr can be used +! inside a TARGET region with the is_device_ptr clause. +! REQUIRES: flang, amdgcn-amd-amdhsa + +! RUN: %libomptarget-compile-fortran-run-and-check-generic + +program is_device_ptr_target + use iso_c_binding, only : c_ptr, c_loc + implicit none + + interface + function omp_get_mapped_ptr(host_ptr, device_num) & + bind(C, name="omp_get_mapped_ptr") + use iso_c_binding, only : c_ptr, c_int + type(c_ptr) :: omp_get_mapped_ptr + type(c_ptr), value :: host_ptr + integer(c_int), value :: device_num + end function omp_get_mapped_ptr + end interface + + integer, parameter :: n = 4 + integer, parameter :: dev = 0 + integer, target :: a(n) + type(c_ptr) :: dptr + integer :: flag + + a = [2, 4, 6, 8] + flag = 0 + + !$omp target data map(tofrom: a, flag) + dptr = omp_get_mapped_ptr(c_loc(a), dev) + + !$omp target is_device_ptr(dptr) map(tofrom: flag) + flag = flag + 1 + !$omp end target + !$omp end target data + + if (flag .eq. 1 .and. all(a == [2, 4, 6, 8])) then + print *, "PASS" + else + print *, "FAIL", a + end if + +end program is_device_ptr_target + +!CHECK: PASS From 31d0e473409ea3183d1909f915bb2e89768f8786 Mon Sep 17 00:00:00 2001 From: Akash Banerjee Date: Mon, 1 Dec 2025 18:59:52 +0000 Subject: [PATCH 2/2] Update test. --- flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 5 +- .../OpenMP/map-types-and-sizes.f90 | 2 +- .../OpenMP/OpenMPToLLVMIRTranslation.cpp | 11 +++- .../fortran/target-is-device-ptr.f90 | 62 ++++++++++++------- 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index 8c15395c938c0..474d5725e4b82 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -1145,8 +1145,11 @@ bool ClauseProcessor::processIsDevicePtr( [&](const omp::clause::IsDevicePtr &clause, const parser::CharBlock &source) { mlir::Location location = converter.genLocation(source); + // Force a map so the descriptor is materialized on the device with the + // device address inside. mlir::omp::ClauseMapFlags mapTypeBits = - mlir::omp::ClauseMapFlags::is_device_ptr; + mlir::omp::ClauseMapFlags::is_device_ptr | + mlir::omp::ClauseMapFlags::to; processMapObjects(stmtCtx, location, clause.v, mapTypeBits, parentMemberIndices, result.isDevicePtrVars, isDeviceSyms); diff --git a/flang/test/Integration/OpenMP/map-types-and-sizes.f90 b/flang/test/Integration/OpenMP/map-types-and-sizes.f90 index 85434460bbea6..133b1664668d7 100644 --- a/flang/test/Integration/OpenMP/map-types-and-sizes.f90 +++ b/flang/test/Integration/OpenMP/map-types-and-sizes.f90 @@ -34,7 +34,7 @@ subroutine mapType_array end subroutine mapType_array !CHECK: @.offload_sizes{{.*}} = private unnamed_addr constant [1 x i64] [i64 8] -!CHECK: @.offload_maptypes{{.*}} = private unnamed_addr constant [1 x i64] [i64 288] +!CHECK: @.offload_maptypes{{.*}} = private unnamed_addr constant [1 x i64] [i64 33] subroutine mapType_is_device_ptr use iso_c_binding, only : c_ptr type(c_ptr) :: p diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp index abd582e6a4e63..a1adbef23180f 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -3830,6 +3830,9 @@ convertClauseMapFlags(omp::ClauseMapFlags mlirFlags) { auto mapTypeToBool = [&mlirFlags](omp::ClauseMapFlags flag) { return (mlirFlags & flag) == flag; }; + const bool hasExplicitMap = + (mlirFlags & ~omp::ClauseMapFlags::is_device_ptr) != + omp::ClauseMapFlags::none; llvm::omp::OpenMPOffloadMappingFlags mapType = llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE; @@ -3872,7 +3875,8 @@ convertClauseMapFlags(omp::ClauseMapFlags mlirFlags) { if (mapTypeToBool(omp::ClauseMapFlags::is_device_ptr)) { mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TARGET_PARAM; - mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL; + if (!hasExplicitMap) + mapType |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL; } return mapType; @@ -4025,8 +4029,11 @@ static void collectMapDataFromMapOperands( mapData.Mappers.push_back(nullptr); } } else { + // For is_device_ptr we need the map type to propagate so the runtime + // can materialize the device-side copy of the pointer container. mapData.Types.push_back( - llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL); + isDevicePtr ? mapType + : llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_LITERAL); mapData.Mappers.push_back(nullptr); } mapData.Names.push_back(LLVM::createMappingInformation( diff --git a/offload/test/offloading/fortran/target-is-device-ptr.f90 b/offload/test/offloading/fortran/target-is-device-ptr.f90 index b2da1ad23d093..d6d8c02f50d6a 100644 --- a/offload/test/offloading/fortran/target-is-device-ptr.f90 +++ b/offload/test/offloading/fortran/target-is-device-ptr.f90 @@ -4,41 +4,55 @@ ! RUN: %libomptarget-compile-fortran-run-and-check-generic -program is_device_ptr_target - use iso_c_binding, only : c_ptr, c_loc +module mod implicit none + integer, parameter :: n = 4 +contains + subroutine kernel(dptr) + use iso_c_binding, only : c_ptr, c_f_pointer + implicit none + + type(c_ptr) :: dptr + integer, dimension(:), pointer :: b + integer :: i + + b => null() + + !$omp target is_device_ptr(dptr) + call c_f_pointer(dptr, b, [n]) + do i = 1, n + b(i) = b(i) + 1 + end do + !$omp end target + end subroutine kernel +end module mod - interface - function omp_get_mapped_ptr(host_ptr, device_num) & - bind(C, name="omp_get_mapped_ptr") - use iso_c_binding, only : c_ptr, c_int - type(c_ptr) :: omp_get_mapped_ptr - type(c_ptr), value :: host_ptr - integer(c_int), value :: device_num - end function omp_get_mapped_ptr - end interface +program is_device_ptr_target + use iso_c_binding, only : c_ptr, c_loc, c_f_pointer + use omp_lib, only: omp_get_default_device, omp_get_mapped_ptr + use mod, only: kernel, n + implicit none - integer, parameter :: n = 4 - integer, parameter :: dev = 0 - integer, target :: a(n) + integer, dimension(n), target :: a + integer :: dev type(c_ptr) :: dptr - integer :: flag a = [2, 4, 6, 8] - flag = 0 + print '("BEFORE:", I3)', a - !$omp target data map(tofrom: a, flag) - dptr = omp_get_mapped_ptr(c_loc(a), dev) + dev = omp_get_default_device() - !$omp target is_device_ptr(dptr) map(tofrom: flag) - flag = flag + 1 - !$omp end target + !$omp target data map(tofrom: a) + dptr = omp_get_mapped_ptr(c_loc(a), dev) + call kernel(dptr) !$omp end target data - if (flag .eq. 1 .and. all(a == [2, 4, 6, 8])) then - print *, "PASS" + print '("AFTER: ", I3)', a + + if (all(a == [3, 5, 7, 9])) then + print '("PASS")' else - print *, "FAIL", a + print '("FAIL ", I3)', a end if end program is_device_ptr_target