diff --git a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp index 57be863cfa1b8..4e19fac5569e9 100644 --- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp +++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp @@ -75,6 +75,44 @@ class MapInfoFinalizationPass /// | | std::map localBoxAllocas; + // List of deferrable descriptors to process at the end of + // the pass. + llvm::SmallVector deferrableDesc; + + // Check if the declaration operation we have refers to a dummy + // function argument. + bool isDummyArgument(mlir::Value mappedValue) { + if (auto declareOp = mlir::dyn_cast_if_present( + mappedValue.getDefiningOp())) + if (auto dummyScope = declareOp.getDummyScope()) + return true; + return false; + } + + // Relevant for OpenMP < 5.2, where attach semantics and rules don't exist. + // As descriptors were an unspoken implementation detail in these versions + // there's certain cases where the user (and the compiler implementation) + // can create data mapping errors by having temporary descriptors stuck + // in memory. The main example is calling an 'target enter data map' + // without a corresponding exit on an assumed shape or size dummy + // argument, a local stack descriptor is generated, gets mapped and + // is then left on device. A user doesn't realize what they've done as + // the OpenMP specification isn't explicit on descriptor handling in + // earlier versions and as far as Fortran is concerned this si something + // hidden from a user. To avoid this we can defer the descriptor mapping + // in these cases until target or target data regions, when we can be + // sure they have a clear limited scope on device. + bool canDeferDescriptorMapping(mlir::Value descriptor) { + if (fir::isAllocatableType(descriptor.getType()) || + fir::isPointerType(descriptor.getType())) + return false; + if (isDummyArgument(descriptor) && + (fir::isAssumedType(descriptor.getType()) || + fir::isAssumedShape(descriptor.getType()))) + return true; + return false; + } + /// getMemberUserList gathers all users of a particular MapInfoOp that are /// other MapInfoOp's and places them into the mapMemberUsers list, which /// records the map that the current argument MapInfoOp "op" is part of @@ -126,13 +164,16 @@ class MapInfoFinalizationPass /// fir::BoxOffsetOp we utilise to access the descriptor datas /// base address can be utilised. mlir::Value getDescriptorFromBoxMap(mlir::omp::MapInfoOp boxMap, - fir::FirOpBuilder &builder) { + fir::FirOpBuilder &builder, + bool &canDescBeDeferred) { mlir::Value descriptor = boxMap.getVarPtr(); if (!fir::isTypeWithDescriptor(boxMap.getVarType())) if (auto addrOp = mlir::dyn_cast_if_present( boxMap.getVarPtr().getDefiningOp())) descriptor = addrOp.getVal(); + canDescBeDeferred = canDeferDescriptorMapping(descriptor); + if (!mlir::isa(descriptor.getType()) && !fir::factory::isOptionalArgument(descriptor.getDefiningOp())) return descriptor; @@ -283,8 +324,7 @@ class MapInfoFinalizationPass /// Check if the mapOp is present in the HasDeviceAddr clause on /// the userOp. Only applies to TargetOp. - bool isHasDeviceAddr(mlir::omp::MapInfoOp mapOp, mlir::Operation *userOp) { - assert(userOp && "Expecting non-null argument"); + bool isHasDeviceAddr(mlir::omp::MapInfoOp mapOp, mlir::Operation &userOp) { if (auto targetOp = llvm::dyn_cast(userOp)) { for (mlir::Value hda : targetOp.getHasDeviceAddrVars()) { if (hda.getDefiningOp() == mapOp) @@ -294,6 +334,26 @@ class MapInfoFinalizationPass return false; } + bool isUseDeviceAddr(mlir::omp::MapInfoOp mapOp, mlir::Operation &userOp) { + if (auto targetDataOp = llvm::dyn_cast(userOp)) { + for (mlir::Value uda : targetDataOp.getUseDeviceAddrVars()) { + if (uda.getDefiningOp() == mapOp) + return true; + } + } + return false; + } + + bool isUseDevicePtr(mlir::omp::MapInfoOp mapOp, mlir::Operation &userOp) { + if (auto targetDataOp = llvm::dyn_cast(userOp)) { + for (mlir::Value udp : targetDataOp.getUseDevicePtrVars()) { + if (udp.getDefiningOp() == mapOp) + return true; + } + } + return false; + } + mlir::omp::MapInfoOp genBoxcharMemberMap(mlir::omp::MapInfoOp op, fir::FirOpBuilder &builder) { if (!op.getMembers().empty()) @@ -358,12 +418,14 @@ class MapInfoFinalizationPass // TODO: map the addendum segment of the descriptor, similarly to the // base address/data pointer member. - mlir::Value descriptor = getDescriptorFromBoxMap(op, builder); + bool descCanBeDeferred = false; + mlir::Value descriptor = + getDescriptorFromBoxMap(op, builder, descCanBeDeferred); mlir::ArrayAttr newMembersAttr; mlir::SmallVector newMembers; llvm::SmallVector> memberIndices; - bool IsHasDeviceAddr = isHasDeviceAddr(op, target); + bool IsHasDeviceAddr = isHasDeviceAddr(op, *target); if (!mapMemberUsers.empty() || !op.getMembers().empty()) getMemberIndicesAsVectors( @@ -445,6 +507,10 @@ class MapInfoFinalizationPass /*partial_map=*/builder.getBoolAttr(false)); op.replaceAllUsesWith(newDescParentMapOp.getResult()); op->erase(); + + if (descCanBeDeferred) + deferrableDesc.push_back(newDescParentMapOp); + return newDescParentMapOp; } @@ -593,6 +659,124 @@ class MapInfoFinalizationPass return nullptr; } + void addImplicitDescriptorMapToTargetDataOp(mlir::omp::MapInfoOp op, + fir::FirOpBuilder &builder, + mlir::Operation &target) { + // Checks if the map is present as an explicit map already on the target + // data directive, and not just present on a use_device_addr/ptr, as if + // that's the case, we should not need to add an implicit map for the + // descriptor. + auto explicitMappingPresent = [](mlir::omp::MapInfoOp op, + mlir::omp::TargetDataOp tarData) { + // Verify top-level descriptor mapping is at least equal with same + // varPtr, the map type should always be To for a descriptor, which is + // all we really care about for this mapping as we aim to make sure the + // descriptor is always present on device if we're expecting to access + // the underlying data. + if (tarData.getMapVars().empty()) + return false; + + for (mlir::Value mapVar : tarData.getMapVars()) { + auto mapOp = llvm::cast(mapVar.getDefiningOp()); + if (mapOp.getVarPtr() == op.getVarPtr() && + mapOp.getVarPtrPtr() == op.getVarPtrPtr()) { + return true; + } + } + + return false; + }; + + // if we're not a top level descriptor with members (e.g. member of a + // derived type), we do not want to perform this step. + if (!llvm::isa(target) || op.getMembers().empty()) + return; + + if (!isUseDeviceAddr(op, target) && !isUseDevicePtr(op, target)) + return; + + auto targetDataOp = llvm::cast(target); + if (explicitMappingPresent(op, targetDataOp)) + return; + + mlir::omp::MapInfoOp newDescParentMapOp = + builder.create( + op->getLoc(), op.getResult().getType(), op.getVarPtr(), + op.getVarTypeAttr(), + builder.getIntegerAttr( + builder.getIntegerType(64, false), + llvm::to_underlying( + llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | + llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS)), + op.getMapCaptureTypeAttr(), /*varPtrPtr=*/mlir::Value{}, + mlir::SmallVector{}, mlir::ArrayAttr{}, + /*bounds=*/mlir::SmallVector{}, + /*mapperId*/ mlir::FlatSymbolRefAttr(), op.getNameAttr(), + /*partial_map=*/builder.getBoolAttr(false)); + + targetDataOp.getMapVarsMutable().append({newDescParentMapOp}); + } + + void removeTopLevelDescriptor(mlir::omp::MapInfoOp op, + fir::FirOpBuilder &builder, + mlir::Operation *target) { + if (llvm::isa(target)) + return; + + // if we're not a top level descriptor with members (e.g. member of a + // derived type), we do not want to perform this step. + if (op.getMembers().empty()) + return; + + mlir::SmallVector members = op.getMembers(); + mlir::omp::MapInfoOp baseAddr = + mlir::dyn_cast_or_null( + members.front().getDefiningOp()); + assert(baseAddr && "Expected member to be MapInfoOp"); + members.erase(members.begin()); + + llvm::SmallVector> memberIndices; + getMemberIndicesAsVectors(op, memberIndices); + + // Can skip the extra processing if there's only 1 member as it'd + // be the base addresses, which we're promoting to the parent. + mlir::ArrayAttr membersAttr; + if (memberIndices.size() > 1) { + memberIndices.erase(memberIndices.begin()); + membersAttr = builder.create2DI64ArrayAttr(memberIndices); + } + + // VarPtrPtr is tied to detecting if something is a pointer in the later + // lowering currently, this at the moment comes tied with + // OMP_MAP_PTR_AND_OBJ being applied which breaks the problem this tries to + // solve by emitting a 8-byte mapping tied to the descriptor address (even + // if we only emit a single map). So we circumvent this by removing the + // varPtrPtr mapping, however, a side affect of this is we lose the + // additional load from the backend tied to this which is required for + // correctness and getting the correct address of the data to perform our + // mapping. So we do our load at this stage. + // TODO/FIXME: Tidy up the OMP_MAP_PTR_AND_OBJ and varPtrPtr being tied to + // if something is a pointer to try and tidy up the implementation a bit. + // This is an unfortunate complexity from push-back from upstream. We + // could also emit a load at this level for all base addresses as well, + // which in turn will simplify the later lowering a bit as well. But first + // need to see how well this alteration works. + auto loadBaseAddr = + builder.loadIfRef(op->getLoc(), baseAddr.getVarPtrPtr()); + mlir::omp::MapInfoOp newBaseAddrMapOp = + builder.create( + op->getLoc(), loadBaseAddr.getType(), loadBaseAddr, + baseAddr.getVarTypeAttr(), baseAddr.getMapTypeAttr(), + baseAddr.getMapCaptureTypeAttr(), mlir::Value{}, members, + membersAttr, baseAddr.getBounds(), + /*mapperId*/ mlir::FlatSymbolRefAttr(), op.getNameAttr(), + /*partial_map=*/builder.getBoolAttr(false)); + op.replaceAllUsesWith(newBaseAddrMapOp.getResult()); + op->erase(); + baseAddr.erase(); + } + // This pass executes on omp::MapInfoOp's containing descriptor based types // (allocatables, pointers, assumed shape etc.) and expanding them into // multiple omp::MapInfoOp's for each pointer member contained within the @@ -622,6 +806,7 @@ class MapInfoFinalizationPass // clear all local allocations we made for any boxes in any prior // iterations from previous function scopes. localBoxAllocas.clear(); + deferrableDesc.clear(); // First, walk `omp.map.info` ops to see if any of them have varPtrs // with an underlying type of fir.char, i.e a character @@ -864,6 +1049,36 @@ class MapInfoFinalizationPass } }); + // Now that we've expanded all of our boxes into a descriptor and base + // address map where necessary, we check if the map owner is an + // enter/exit/target data directive, and if they are we drop the initial + // descriptor (top-level parent) and replace it with the + // base_address/data. + // + // This circumvents issues with stack allocated descriptors bound to + // device colliding which in Flang is rather trivial for a user to do by + // accident due to the rather pervasive local intermediate descriptor + // generation that occurs whenever you pass boxes around different scopes. + // In OpenMP 6+ mapping these would be a user error as the tools required + // to circumvent these issues are provided by the spec (ref_ptr/ptee map + // types), but in prior specifications these tools are not available and + // it becomes an implementation issue for us to solve. + // + // We do this by dropping the top-level descriptor which will be the stack + // descriptor when we perform enter/exit maps, as we don't want these to + // be bound until necessary which is when we utilise the descriptor type + // within a target region. At which point we map the relevant descriptor + // data and the runtime should correctly associate the data with the + // descriptor and bind together and allow clean mapping and execution. + for (auto *op : deferrableDesc) { + auto mapOp = llvm::dyn_cast(op); + mlir::Operation *targetUser = getFirstTargetUser(mapOp); + assert(targetUser && "expected user of map operation was not found"); + builder.setInsertionPoint(mapOp); + removeTopLevelDescriptor(mapOp, builder, targetUser); + addImplicitDescriptorMapToTargetDataOp(mapOp, builder, *targetUser); + } + // Wait until after we have generated all of our maps to add them onto // the target's block arguments, simplifying the process as there would be // no need to avoid accidental duplicate additions. diff --git a/flang/test/Lower/OpenMP/map-descriptor-deferral.f90 b/flang/test/Lower/OpenMP/map-descriptor-deferral.f90 new file mode 100644 index 0000000000000..daea2f321414f --- /dev/null +++ b/flang/test/Lower/OpenMP/map-descriptor-deferral.f90 @@ -0,0 +1,96 @@ +!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s + +! This test checks that the descriptor deferral behaviour of the +! MapInfoFinalization pass is preserved. Descriptor deferral is the +! act of removing the mapping of the descriptor in certain cases when +! a descriptor carrying type is mapped. This only applies in certain +! cases and to assumed shape and size dummy arguments that are not +! allocatable or pointers. + +subroutine assume_map_target_enter_exit(assumed_arr) + integer :: assumed_arr(:) + !$omp target enter data map(to: assumed_arr) + !$omp target + assumed_arr(1) = 10 + !$omp end target + !$omp target exit data map(from: assumed_arr) +end subroutine + +!CHECK-LABEL: func.func @_QPassume_map_target_enter_exit( +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +!CHECK: %[[LOAD_BOX:.*]] = fir.load %[[BOX_ADDR]] : !fir.llvm_ptr>> +!CHECK: %[[MAP_ADDR:.*]] = omp.map.info var_ptr(%[[LOAD_BOX]] : !fir.ref>, i32) map_clauses(to) capture(ByRef) bounds(%{{.*}}) -> !fir.ref> {name = "assumed_arr"} +!CHECK: omp.target_enter_data map_entries(%[[MAP_ADDR]] : !fir.ref>) +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +!CHECK: %[[MAP_ADDR:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>, i32) map_clauses(implicit, tofrom) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[MAP_BOX:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>, !fir.box>) map_clauses(implicit, to) capture(ByRef) members(%{{.*}} : [0] : !fir.llvm_ptr>>) -> !fir.ref> {name = "assumed_arr"} +!CHECK: omp.target map_entries(%[[MAP_BOX]] -> %{{.*}}, %[[MAP_ADDR]] -> %{{.*}} : !fir.ref>, !fir.llvm_ptr>>) { +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +!CHECK: %[[LOAD_BOX:.*]] = fir.load %[[BOX_ADDR]] : !fir.llvm_ptr>> +!CHECK: %[[MAP_ADDR:.*]] = omp.map.info var_ptr(%[[LOAD_BOX]] : !fir.ref>, i32) map_clauses(from) capture(ByRef) bounds(%{{.*}}) -> !fir.ref> {name = "assumed_arr"} +!CHECK: omp.target_exit_data map_entries(%[[MAP_ADDR]] : !fir.ref>) + +subroutine assume_alloca_map_target_enter_exit(assumed_arr) + integer, allocatable :: assumed_arr(:) + !$omp target enter data map(to: assumed_arr) + !$omp target + assumed_arr(1) = 10 + !$omp end target + !$omp target exit data map(from: assumed_arr) +end subroutine + +!CHECK-LABEL: func.func @_QPassume_alloca_map_target_enter_exit( +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>>) -> !fir.llvm_ptr>> +!CHECK: %[[BOX_ADDR_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, i32) map_clauses(to) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[DESC_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, !fir.box>>) map_clauses(to) capture(ByRef) members(%[[BOX_ADDR_MAP]] : [0] : !fir.llvm_ptr>>) -> !fir.ref>>> {name = "assumed_arr"} +!CHECK: omp.target_enter_data map_entries(%[[DESC_MAP]], %[[BOX_ADDR_MAP]] : !fir.ref>>>, !fir.llvm_ptr>>) +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>>) -> !fir.llvm_ptr>> +!CHECK: %[[BOX_ADDR_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, i32) map_clauses(implicit, tofrom) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[DESC_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, !fir.box>>) map_clauses(implicit, to) capture(ByRef) members(%[[BOX_ADDR_MAP]] : [0] : !fir.llvm_ptr>>) -> !fir.ref>>> {name = "assumed_arr"} +!CHECK: omp.target map_entries(%[[DESC_MAP]] -> %[[VAL_28:.*]], %[[BOX_ADDR_MAP]] -> %[[VAL_29:.*]] : !fir.ref>>>, !fir.llvm_ptr>>) { +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>>) -> !fir.llvm_ptr>> +!CHECK: %[[BOX_ADDR_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, i32) map_clauses(from) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[DESC_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, !fir.box>>) map_clauses(from) capture(ByRef) members(%[[BOX_ADDR_MAP]] : [0] : !fir.llvm_ptr>>) -> !fir.ref>>> {name = "assumed_arr"} +!CHECK: omp.target_exit_data map_entries(%[[DESC_MAP]], %[[BOX_ADDR_MAP]] : !fir.ref>>>, !fir.llvm_ptr>>) + +subroutine assume_pointer_map_target_enter_exit(assumed_arr) + integer, pointer :: assumed_arr(:) + !$omp target enter data map(to: assumed_arr) + !$omp target + assumed_arr(1) = 10 + !$omp end target + !$omp target exit data map(from: assumed_arr) +end subroutine + +!CHECK-LABEL: func.func @_QPassume_pointer_map_target_enter_exit( +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>>) -> !fir.llvm_ptr>> +!CHECK: %[[BOX_ADDR_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, i32) map_clauses(to) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[DESC_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, !fir.box>>) map_clauses(to) capture(ByRef) members(%[[BOX_ADDR_MAP]] : [0] : !fir.llvm_ptr>>) -> !fir.ref>>> {name = "assumed_arr"} +!CHECK: omp.target_enter_data map_entries(%[[DESC_MAP]], %[[BOX_ADDR_MAP]] : !fir.ref>>>, !fir.llvm_ptr>>) +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>>) -> !fir.llvm_ptr>> +!CHECK: %[[BOX_ADDR_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, i32) map_clauses(implicit, tofrom) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[DESC_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, !fir.box>>) map_clauses(implicit, to) capture(ByRef) members(%[[BOX_ADDR_MAP]] : [0] : !fir.llvm_ptr>>) -> !fir.ref>>> {name = "assumed_arr"} +!CHECK: omp.target map_entries(%[[DESC_MAP]] -> %[[VAL_28:.*]], %[[BOX_ADDR_MAP]] -> %[[VAL_29:.*]] : !fir.ref>>>, !fir.llvm_ptr>>) { +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>>) -> !fir.llvm_ptr>> +!CHECK: %[[BOX_ADDR_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, i32) map_clauses(from) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[DESC_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>>, !fir.box>>) map_clauses(from) capture(ByRef) members(%[[BOX_ADDR_MAP]] : [0] : !fir.llvm_ptr>>) -> !fir.ref>>> {name = "assumed_arr"} +!CHECK: omp.target_exit_data map_entries(%[[DESC_MAP]], %[[BOX_ADDR_MAP]] : !fir.ref>>>, !fir.llvm_ptr>>) + +subroutine assume_map_target_data(assumed_arr) + integer :: assumed_arr(:) + !$omp target data map(to: assumed_arr) + !$omp target + assumed_arr(1) = 10 + !$omp end target + !$omp end target data +end subroutine + +!CHECK-LABEL: func.func @_QPassume_map_target_data( +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +!CHECK: %[[MAP_ADDR:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>, i32) map_clauses(to) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[MAP_BOX:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>, !fir.box>) map_clauses(to) capture(ByRef) members(%[[MAP_ADDR]] : [0] : !fir.llvm_ptr>>) -> !fir.ref> {name = "assumed_arr"} +!CHECK: omp.target_data map_entries(%[[MAP_BOX]], %[[MAP_ADDR]] : !fir.ref>, !fir.llvm_ptr>>) { +!CHECK: %[[BOX_ADDR:.*]] = fir.box_offset %{{.*}} base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +!CHECK: %[[MAP_ADDR:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>, i32) map_clauses(implicit, tofrom) capture(ByRef) var_ptr_ptr(%[[BOX_ADDR]] : !fir.llvm_ptr>>) bounds(%{{.*}}) -> !fir.llvm_ptr>> {name = ""} +!CHECK: %[[MAP_BOX:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref>>, !fir.box>) map_clauses(implicit, to) capture(ByRef) members(%[[MAP_ADDR]] : [0] : !fir.llvm_ptr>>) -> !fir.ref> {name = "assumed_arr"} +!CHECK: omp.target map_entries(%[[MAP_BOX]] -> %{{.*}}, %[[MAP_ADDR]] -> %{{.*}} : !fir.ref>, !fir.llvm_ptr>>) { diff --git a/flang/test/Transforms/omp-map-info-finalization.fir b/flang/test/Transforms/omp-map-info-finalization.fir index ed814cdcfc728..7bc0ae4a72b05 100644 --- a/flang/test/Transforms/omp-map-info-finalization.fir +++ b/flang/test/Transforms/omp-map-info-finalization.fir @@ -326,15 +326,15 @@ func.func @_QPreuse_alloca(%arg0: !fir.box> {fir.bindc_name = // CHECK: %{{[0-9]+}} = omp.map.info var_ptr(%[[ALLOCA]] // CHECK: %{{[0-9]+}} = omp.map.info var_ptr(%[[ALLOCA]] // CHECK: omp.target_data map_entries -// CHECK: %{{[0-9]+}} = omp.map.info var_ptr(%[[ALLOCA]] -// CHECK: %{{[0-9]+}} = omp.map.info var_ptr(%[[ALLOCA]] +// CHECK: %[[BOX_OFFSET:.*]] = fir.box_offset %[[ALLOCA]] +// CHECK: %[[LOAD_OFFSET:.*]] = fir.load %[[BOX_OFFSET]] : !fir.llvm_ptr>> +// CHECK: %{{[0-9]+}} = omp.map.info var_ptr(%[[LOAD_OFFSET]] // CHECK: omp.target_update map_entries // CHECK: omp.terminator // CHECK: } // CHECK: return - omp.private {type = firstprivate} @boxchar.privatizer : !fir.boxchar<1> copy { ^bb0(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>): omp.yield(%arg0 : !fir.boxchar<1>) diff --git a/offload/test/offloading/fortran/descriptor-stack-jam-regression.f90 b/offload/test/offloading/fortran/descriptor-stack-jam-regression.f90 new file mode 100644 index 0000000000000..45a18b7f38ed3 --- /dev/null +++ b/offload/test/offloading/fortran/descriptor-stack-jam-regression.f90 @@ -0,0 +1,101 @@ +! This test doesn't expect any results, the pass condition is running to completion +! without any memory access errors on device or mapping issues from descriptor +! collisions due to local descriptors being placed on device and not being unampped +! before a subsequent local descriptor residing at the same address is mapped to +! device. +! REQUIRES: flang, amdgpu + +! RUN: %libomptarget-compile-fortran-run-and-check-generic +module test +contains + subroutine kernel_1d(array) + implicit none + real, dimension(:) :: array + integer :: i + + !$omp target enter data map(alloc:array) + !$omp target teams distribute parallel do + do i=1, ubound(array, 1) + array(i) = 42.0 + end do + !$omp target update from(array) + end subroutine + + subroutine kernel_2d(array) + implicit none + real, dimension(:,:) :: array + integer :: i, j + + !$omp target enter data map(alloc:array) + !$omp target teams distribute parallel do collapse(2) + do j=1, ubound(array, 2) + do i=1, ubound(array, 1) + array(i,j) = 42.0 + end do + end do + !$omp target update from(array) + end subroutine + + subroutine kernel_3d(array) + implicit none + real, dimension(:,:,:) :: array + integer :: i, j, k + + !$omp target enter data map(alloc:array) + !$omp target teams distribute parallel do collapse(3) + do k=1, ubound(array, 3) + do j=1, ubound(array, 2) + do i=1, ubound(array, 1) + array(i,j,k) = 42.0 + end do + end do + end do + !$omp target update from(array) + end subroutine + + subroutine kernel_4d(array) + implicit none + real, dimension(:,:,:,:) :: array + integer :: i, j, k, l + + !$omp target enter data map(alloc:array) + !$omp target teams distribute parallel do collapse(4) + do l=1, ubound(array, 4) + do k=1, ubound(array, 3) + do j=1, ubound(array, 2) + do i=1, ubound(array, 1) + array(i,j,k,l) = 42.0 + end do + end do + end do + enddo + !$omp target update from(array) + end subroutine +end module + +program main + use test + implicit none + integer, parameter :: n = 2 + real :: array1(n) + real :: array2(n,n) + real :: array3(n,n,n) + real :: array4(n,n,n,n) + + call kernel_1d(array1) + call kernel_2d(array2) + call kernel_3d(array3) + call kernel_4d(array4) + + print *, array1 + print *, array2 + print *, array3 + print *, array4 + print *, "PASS" +end program + +! CHECK: 42. 42. +! CHECK: 42. 42. 42. 42. +! CHECK: 42. 42. 42. 42. 42. 42. 42. 42. +! CHECK: 42. 42. 42. 42. 42. 42. 42. 42. 42. 42. 42. 42. 42. 42. 42. 42. +! CHECK: PASS