diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 7503d33c8df38..c03b5d410f2e6 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -4300,6 +4300,168 @@ static mlir::omp::TargetDataOp genTargetDataOp( return targetDataOp; } +struct TargetUpdateKernelEntry { + mlir::omp::MapInfoOp mapInfo; + mlir::Value hostPtr; + mlir::Type componentType; +}; + +static std::optional +getTargetUpdateKernelEntry(mlir::Value mapVar) { + auto mapInfo = mapVar.getDefiningOp(); + if (!mapInfo) + return std::nullopt; + + // Keep the fast path to plain synchronous H2D motion. In particular, do not + // silently weaken `present` motion modifiers. + if (mapInfo.getMapType() != mlir::omp::ClauseMapFlags::to || + mapInfo.getVarPtrPtr() || !mapInfo.getMembers().empty() || + !mapInfo.getBounds().empty() || mapInfo.getMapperId()) + return std::nullopt; + + mlir::Value hostPtr = mapInfo.getVarPtr(); + auto designate = hostPtr.getDefiningOp(); + if (!designate || !designate.getComponent() || + designate.getComponentShape() || !designate.getIndices().empty() || + !designate.getSubstring().empty() || designate.getComplexPart() || + designate.getShape() || !designate.getTypeparams().empty()) + return std::nullopt; + + mlir::Type baseType = fir::unwrapRefType(designate.getMemref().getType()); + auto recordType = mlir::dyn_cast(baseType); + if (!recordType || recordType.getNumLenParams() != 0) + return std::nullopt; + + llvm::StringRef component = designate.getComponent()->getValue(); + mlir::Type componentType = recordType.getType(component); + if (!componentType || !fir::isa_trivial(componentType)) + return std::nullopt; + + return TargetUpdateKernelEntry{mapInfo, hostPtr, componentType}; +} + +/// Replace several scalar H2D updates with one packed transfer and a target +/// region that scatters the values to their original device addresses. The +/// source tuple has one `to` map, while each destination uses a `storage` map +/// so it resolves an existing device association without copying host data. +static mlir::omp::TargetOp +genTargetUpdateKernel(lower::AbstractConverter &converter, mlir::Location loc, + llvm::ArrayRef entries) { + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + mlir::omp::TargetExtOperands targetClauseOps; + targetClauseOps.kernelType = mlir::omp::TargetExecModeAttr::get( + builder.getContext(), mlir::omp::TargetExecMode::generic); + + llvm::SmallVector destinationMaps; + destinationMaps.reserve(entries.size()); + + llvm::SmallVector sourceTypes; + llvm::transform( + entries, std::back_inserter(sourceTypes), + [](const TargetUpdateKernelEntry &entry) { return entry.componentType; }); + mlir::TupleType sourceType = + mlir::TupleType::get(builder.getContext(), sourceTypes); + mlir::Value sourcePack = builder.createTemporary(loc, sourceType); + + for (auto [i, entry] : llvm::enumerate(entries)) { + mlir::Value sourceValue = fir::LoadOp::create(builder, loc, entry.hostPtr); + mlir::Value index = + builder.createIntegerConstant(loc, builder.getI32Type(), i); + mlir::Value sourceAddr = fir::CoordinateOp::create( + builder, loc, builder.getRefType(entry.componentType), sourcePack, + index); + fir::StoreOp::create(builder, loc, sourceValue, sourceAddr); + + mlir::Value destinationMap = createMapInfoOp( + builder, loc, entry.hostPtr, /*varPtrPtr=*/mlir::Value{}, + /*name=*/"", /*bounds=*/{}, /*members=*/{}, + /*membersIndex=*/mlir::ArrayAttr{}, mlir::omp::ClauseMapFlags::storage, + mlir::omp::VariableCaptureKind::ByRef, entry.hostPtr.getType()); + destinationMaps.push_back(destinationMap); + } + + mlir::Value sourceMap = createMapInfoOp( + builder, loc, sourcePack, /*varPtrPtr=*/mlir::Value{}, + ".omp.target.update.source", /*bounds=*/{}, /*members=*/{}, + /*membersIndex=*/mlir::ArrayAttr{}, mlir::omp::ClauseMapFlags::to, + mlir::omp::VariableCaptureKind::ByRef, sourcePack.getType()); + targetClauseOps.mapVars.push_back(sourceMap); + targetClauseOps.mapVars.append(destinationMaps); + + auto targetOp = mlir::omp::TargetOp::create(builder, loc, targetClauseOps); + llvm::SmallVector mapBaseValues; + extractMappedBaseValues(targetClauseOps.mapVars, mapBaseValues); + ObjectEntryBlockArgs args; + args.map.vars = mapBaseValues; + genEntryBlock(builder, args.asEntryBlockArgs(), targetOp.getRegion()); + + auto argIface = llvm::cast(*targetOp); + llvm::ArrayRef mapBlockArgs = argIface.getMapBlockArgs(); + assert(mapBlockArgs.size() == entries.size() + 1 && + "expected source and destination map arguments"); + builder.setInsertionPointToEnd(&targetOp.getRegion().front()); + for (auto [i, entry] : llvm::enumerate(entries)) { + mlir::Value index = + builder.createIntegerConstant(loc, builder.getI32Type(), i); + mlir::Value sourceAddr = fir::CoordinateOp::create( + builder, loc, builder.getRefType(entry.componentType), + mapBlockArgs.front(), index); + mlir::Value sourceValue = fir::LoadOp::create(builder, loc, sourceAddr); + fir::StoreOp::create(builder, loc, sourceValue, mapBlockArgs[i + 1]); + } + mlir::omp::TerminatorOp::create(builder, loc); + builder.setInsertionPointAfter(targetOp); + return targetOp; +} + +static mlir::Operation *tryGenTargetUpdateKernel( + lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx, + mlir::Location loc, + mlir::omp::TargetEnterExitUpdateDataOperands &clauseOps) { + // Updating several small, discontiguous fields issues one device transfer + // for every map entry. Pack their host values and use one target region so + // that the runtime performs one H2D transfer followed by the scalar stores. + // This addresses the AMDGPU runtime transfer cost and is only enabled when + // an AMDGPU image will actually be emitted. + mlir::ModuleOp module = converter.getModuleOp(); + if (!hasOnlyAMDGCNTargets(module) || + requiresUnifiedSharedMemory(module, semaCtx) || + clauseOps.mapVars.size() < 2 || !clauseOps.dependVars.empty() || + !clauseOps.dependIterated.empty() || !clauseOps.mapIterated.empty() || + clauseOps.nowait || clauseOps.device) + return nullptr; + + llvm::SmallVector entries; + entries.reserve(clauseOps.mapVars.size()); + for (mlir::Value mapVar : clauseOps.mapVars) { + std::optional entry = + getTargetUpdateKernelEntry(mapVar); + if (!entry) + return nullptr; + entries.push_back(*entry); + } + + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + mlir::Operation *firstGenerated = nullptr; + + if (mlir::Value ifExpr = clauseOps.ifExpr) { + auto ifOp = fir::IfOp::create(builder, loc, ifExpr, + /*withElseRegion=*/false); + firstGenerated = ifOp; + builder.setInsertionPoint(ifOp.getThenRegion().front().getTerminator()); + genTargetUpdateKernel(converter, loc, entries); + builder.setInsertionPointAfter(ifOp); + } else { + firstGenerated = genTargetUpdateKernel(converter, loc, entries); + } + + for (TargetUpdateKernelEntry &entry : entries) + if (entry.mapInfo->use_empty()) + entry.mapInfo.erase(); + + return firstGenerated; +} + template static OpTy genTargetEnterExitUpdateDataOp( lower::AbstractConverter &converter, lower::SymMap &symTable, @@ -4327,6 +4489,25 @@ static OpTy genTargetEnterExitUpdateDataOp( return OpTy::create(firOpBuilder, loc, clauseOps); } +static mlir::Operation * +genTargetUpdateDataOp(lower::AbstractConverter &converter, + lower::SymMap &symTable, lower::StatementContext &stmtCtx, + semantics::SemanticsContext &semaCtx, mlir::Location loc, + const ConstructQueue &queue, + ConstructQueue::const_iterator item) { + fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); + mlir::omp::TargetEnterExitUpdateDataOperands clauseOps; + genTargetEnterExitUpdateDataClauses( + converter, semaCtx, symTable, stmtCtx, item->clauses, loc, + llvm::omp::Directive::OMPD_target_update, clauseOps); + + if (mlir::Operation *op = + tryGenTargetUpdateKernel(converter, semaCtx, loc, clauseOps)) + return op; + + return mlir::omp::TargetUpdateOp::create(firOpBuilder, loc, clauseOps); +} + static mlir::omp::TaskOp genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable, lower::StatementContext &stmtCtx, @@ -5501,8 +5682,8 @@ static void genOMPDispatch(lower::AbstractConverter &converter, converter, symTable, stmtCtx, semaCtx, loc, queue, item); break; case llvm::omp::Directive::OMPD_target_update: - newOp = genTargetEnterExitUpdateDataOp( - converter, symTable, stmtCtx, semaCtx, loc, queue, item); + newOp = genTargetUpdateDataOp(converter, symTable, stmtCtx, semaCtx, loc, + queue, item); break; case llvm::omp::Directive::OMPD_task: newOp = genTaskOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp index 4e7cf5ca34cc3..e28117c759973 100644 --- a/flang/lib/Lower/OpenMP/Utils.cpp +++ b/flang/lib/Lower/OpenMP/Utils.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -1383,6 +1384,48 @@ static llvm::Triple getOffloadTargetTriple(mlir::ModuleOp module) { return llvm::Triple(); } +bool hasOnlyAMDGCNTargets(mlir::ModuleOp module) { + auto offloadModule = + llvm::cast(module.getOperation()); + if (offloadModule.getIsTargetDevice()) + return fir::getTargetTriple(module).isAMDGCN(); + llvm::ArrayRef targetTriples = + offloadModule.getTargetTriples(); + return !targetTriples.empty() && + llvm::all_of(targetTriples, [](mlir::Attribute attr) { + auto tripleAttr = llvm::dyn_cast(attr); + return tripleAttr && llvm::Triple(tripleAttr.getValue()).isAMDGCN(); + }); +} + +static bool scopeRequiresUnifiedSharedMemory(const semantics::Scope &scope) { + if (const semantics::Symbol *symbol = scope.symbol()) { + bool requiresUSM = common::visit( + [](const auto &details) { + using Details = std::decay_t; + if constexpr (std::is_base_of_v) + return details.ompRequires().test( + llvm::omp::Clause::OMPC_unified_shared_memory); + return false; + }, + symbol->details()); + if (requiresUSM) + return true; + } + + return llvm::any_of(scope.children(), scopeRequiresUnifiedSharedMemory); +} + +bool requiresUnifiedSharedMemory(mlir::ModuleOp module, + semantics::SemanticsContext &semaCtx) { + auto offloadModule = llvm::cast(*module); + return mlir::omp::bitEnumContainsAny( + offloadModule.getRequires(), + mlir::omp::ClauseRequires::unified_shared_memory) || + scopeRequiresUnifiedSharedMemory(semaCtx.globalScope()); +} + semantics::omp::OmpVariantMatchContext makeVariantMatchContext( mlir::ModuleOp module, llvm::ArrayRef constructTraits) { diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h index 94f85c43f7033..42c70a478f304 100644 --- a/flang/lib/Lower/OpenMP/Utils.h +++ b/flang/lib/Lower/OpenMP/Utils.h @@ -29,6 +29,7 @@ class RecordType; namespace Fortran { namespace semantics { +class SemanticsContext; class Symbol; namespace omp { class OmpVariantMatchContext; @@ -267,6 +268,15 @@ void collectEnclosingConstructTraits( mlir::Operation *op, llvm::SmallVectorImpl &constructTraits); +/// Return true when \p module is being compiled for an AMDGPU device or all of +/// its offload targets are AMDGPU devices. +bool hasOnlyAMDGCNTargets(mlir::ModuleOp module); + +/// Return true when unified shared memory is required by either the OpenMP +/// module attributes or a source-level `requires` directive. +bool requiresUnifiedSharedMemory(mlir::ModuleOp module, + semantics::SemanticsContext &semaCtx); + /// Build the OpenMP variant-matching context for \p module. The device flag, /// host triple, offload triple, and target features are read from the module; /// \p constructTraits seeds the enclosing-construct traits. diff --git a/flang/test/Lower/OpenMP/target-update-derived-type-usm.f90 b/flang/test/Lower/OpenMP/target-update-derived-type-usm.f90 new file mode 100644 index 0000000000000..7271a10a3ad64 --- /dev/null +++ b/flang/test/Lower/OpenMP/target-update-derived-type-usm.f90 @@ -0,0 +1,27 @@ +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa %s -o - | FileCheck %s + +! Verify that unified shared memory keeps the regular target update because a +! packed transfer and target region would add overhead to directly accessible +! storage. + +module target_update_derived_type_usm + !$omp requires unified_shared_memory + type :: aggregate + real(8) :: first + real(8) :: gap + integer :: last + end type +contains + +! CHECK-LABEL: func.func @_QMtarget_update_derived_type_usmPupdate( +subroutine update(value) + type(aggregate) :: value + + ! CHECK: %[[FIRST_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK: %[[LAST_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK-NOT: omp.target kernel_type(generic) + ! CHECK: omp.target_update map_entries(%[[FIRST_MAP]], %[[LAST_MAP]] + !$omp target update to(value%first, value%last) +end subroutine + +end module diff --git a/flang/test/Lower/OpenMP/target-update-derived-type.f90 b/flang/test/Lower/OpenMP/target-update-derived-type.f90 new file mode 100644 index 0000000000000..cdf81aa477330 --- /dev/null +++ b/flang/test/Lower/OpenMP/target-update-derived-type.f90 @@ -0,0 +1,174 @@ +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa %s -o - | FileCheck %s +! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s --check-prefix=HOST +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-targets=nvptx64-nvidia-cuda %s -o - | FileCheck %s --check-prefix=NONAMD +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa,nvptx64-nvidia-cuda %s -o - | FileCheck %s --check-prefix=MIXED +! RUN: %flang_fc1 -triple amdgcn-amd-amdhsa -emit-hlfir -fopenmp -fopenmp-is-target-device %s -o - | FileCheck %s --check-prefix=DEVICE + +module target_update_derived_type + type :: wavefun + real(8) :: ferwe + real(8) :: aux + complex(8) :: celen + integer :: pad1 + integer :: nb + integer :: pad2 + integer :: isp + integer :: pad3 + logical :: ldo + integer, pointer :: ptr + end type +contains + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_with_if( +! DEVICE-LABEL: func.func @_QMtarget_update_derived_typePupdate_with_if( +! DEVICE: omp.target kernel_type(generic) +! DEVICE-NOT: omp.target_update +! HOST-LABEL: func.func @_QMtarget_update_derived_typePupdate_with_if( +! HOST-NOT: omp.target kernel_type(generic) +! HOST: omp.target_update +! HOST-NOT: omp.target kernel_type(generic) +! HOST-LABEL: func.func @_QMtarget_update_derived_typePupdate_without_if( +! NONAMD-LABEL: func.func @_QMtarget_update_derived_typePupdate_with_if( +! NONAMD-NOT: omp.target kernel_type(generic) +! NONAMD: omp.target_update +! NONAMD-NOT: omp.target kernel_type(generic) +! NONAMD-LABEL: func.func @_QMtarget_update_derived_typePupdate_without_if( +! MIXED-LABEL: func.func @_QMtarget_update_derived_typePupdate_with_if( +! MIXED-NOT: omp.target kernel_type(generic) +! MIXED: omp.target_update +! MIXED-NOT: omp.target kernel_type(generic) +! MIXED-LABEL: func.func @_QMtarget_update_derived_typePupdate_without_if( +subroutine update_with_if(w, enabled) + type(wavefun) :: w + logical :: enabled + + ! CHECK: %[[SOURCE:.*]] = fir.alloca tuple, i32, i32, !fir.logical<4>> + ! CHECK: %[[COND:.*]] = fir.convert %{{.*}} : (!fir.logical<4>) -> i1 + ! CHECK: %[[FERWE:.*]] = hlfir.designate %{{.*}}{"ferwe"} + ! CHECK: %[[CELEN:.*]] = hlfir.designate %{{.*}}{"celen"} + ! CHECK: %[[NB:.*]] = hlfir.designate %{{.*}}{"nb"} + ! CHECK: %[[ISP:.*]] = hlfir.designate %{{.*}}{"isp"} + ! CHECK: %[[LDO:.*]] = hlfir.designate %{{.*}}{"ldo"} + ! CHECK: fir.if %[[COND]] { + ! CHECK: %[[FERWE_HOST:.*]] = fir.load %[[FERWE]] : !fir.ref + ! CHECK: %[[PACK0:.*]] = fir.coordinate_of %[[SOURCE]], {{.*}} -> !fir.ref + ! CHECK: fir.store %[[FERWE_HOST]] to %[[PACK0]] : !fir.ref + ! CHECK: %[[FERWE_MAP:.*]] = omp.map.info var_ptr(%[[FERWE]] : !fir.ref, f64) map_clauses(storage) capture(ByRef) + ! CHECK: %[[CELEN_HOST:.*]] = fir.load %[[CELEN]] : !fir.ref> + ! CHECK: %[[PACK1:.*]] = fir.coordinate_of %[[SOURCE]], {{.*}} -> !fir.ref> + ! CHECK: fir.store %[[CELEN_HOST]] to %[[PACK1]] : !fir.ref> + ! CHECK: %[[CELEN_MAP:.*]] = omp.map.info var_ptr(%[[CELEN]] : !fir.ref>, complex) map_clauses(storage) capture(ByRef) + ! CHECK: %[[NB_HOST:.*]] = fir.load %[[NB]] : !fir.ref + ! CHECK: %[[PACK2:.*]] = fir.coordinate_of %[[SOURCE]], {{.*}} -> !fir.ref + ! CHECK: fir.store %[[NB_HOST]] to %[[PACK2]] : !fir.ref + ! CHECK: %[[NB_MAP:.*]] = omp.map.info var_ptr(%[[NB]] : !fir.ref, i32) map_clauses(storage) capture(ByRef) + ! CHECK: %[[ISP_HOST:.*]] = fir.load %[[ISP]] : !fir.ref + ! CHECK: %[[PACK3:.*]] = fir.coordinate_of %[[SOURCE]], {{.*}} -> !fir.ref + ! CHECK: fir.store %[[ISP_HOST]] to %[[PACK3]] : !fir.ref + ! CHECK: %[[ISP_MAP:.*]] = omp.map.info var_ptr(%[[ISP]] : !fir.ref, i32) map_clauses(storage) capture(ByRef) + ! CHECK: %[[LDO_HOST:.*]] = fir.load %[[LDO]] : !fir.ref> + ! CHECK: %[[PACK4:.*]] = fir.coordinate_of %[[SOURCE]], {{.*}} -> !fir.ref> + ! CHECK: fir.store %[[LDO_HOST]] to %[[PACK4]] : !fir.ref> + ! CHECK: %[[LDO_MAP:.*]] = omp.map.info var_ptr(%[[LDO]] : !fir.ref>, !fir.logical<4>) map_clauses(storage) capture(ByRef) + ! CHECK: %[[SOURCE_MAP:.*]] = omp.map.info var_ptr(%[[SOURCE]] {{.*}}) map_clauses(to) capture(ByRef) name(".omp.target.update.source") + ! CHECK: omp.target kernel_type(generic) map_entries(%[[SOURCE_MAP]] -> [[SOURCE_ARG:%[^, ]+]], %[[FERWE_MAP]] -> [[FERWE_ARG:%[^, ]+]], %[[CELEN_MAP]] -> [[CELEN_ARG:%[^, ]+]], %[[NB_MAP]] -> [[NB_ARG:%[^, ]+]], %[[ISP_MAP]] -> [[ISP_ARG:%[^, ]+]], %[[LDO_MAP]] -> [[LDO_ARG:%[^, ]+]] + ! CHECK: %[[FERWE_SOURCE:.*]] = fir.coordinate_of [[SOURCE_ARG]], {{.*}} -> !fir.ref + ! CHECK: %[[FERWE_VALUE:.*]] = fir.load %[[FERWE_SOURCE]] : !fir.ref + ! CHECK: fir.store %[[FERWE_VALUE]] to [[FERWE_ARG]] : !fir.ref + ! CHECK: %[[CELEN_SOURCE:.*]] = fir.coordinate_of [[SOURCE_ARG]], {{.*}} -> !fir.ref> + ! CHECK: %[[CELEN_VALUE:.*]] = fir.load %[[CELEN_SOURCE]] : !fir.ref> + ! CHECK: fir.store %[[CELEN_VALUE]] to [[CELEN_ARG]] : !fir.ref> + ! CHECK: %[[NB_SOURCE:.*]] = fir.coordinate_of [[SOURCE_ARG]], {{.*}} -> !fir.ref + ! CHECK: %[[NB_VALUE:.*]] = fir.load %[[NB_SOURCE]] : !fir.ref + ! CHECK: fir.store %[[NB_VALUE]] to [[NB_ARG]] : !fir.ref + ! CHECK: %[[ISP_SOURCE:.*]] = fir.coordinate_of [[SOURCE_ARG]], {{.*}} -> !fir.ref + ! CHECK: %[[ISP_VALUE:.*]] = fir.load %[[ISP_SOURCE]] : !fir.ref + ! CHECK: fir.store %[[ISP_VALUE]] to [[ISP_ARG]] : !fir.ref + ! CHECK: %[[LDO_SOURCE:.*]] = fir.coordinate_of [[SOURCE_ARG]], {{.*}} -> !fir.ref> + ! CHECK: %[[LDO_VALUE:.*]] = fir.load %[[LDO_SOURCE]] : !fir.ref> + ! CHECK: fir.store %[[LDO_VALUE]] to [[LDO_ARG]] : !fir.ref> + ! CHECK-NEXT: omp.terminator + ! CHECK-NOT: omp.target_update + ! CHECK: return + !$omp target update to(w%ferwe, w%celen, w%nb, w%isp, w%ldo) if(enabled) +end subroutine + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_without_if( +! DEVICE-LABEL: func.func @_QMtarget_update_derived_typePupdate_without_if( +subroutine update_without_if(w) + type(wavefun) :: w + + ! CHECK: %[[SOURCE:.*]] = fir.alloca tuple, i32, i32, !fir.logical<4>> + ! CHECK: %[[CELEN:.*]] = hlfir.designate %{{.*}}{"celen"} + ! CHECK: %[[CELEN_MAP:.*]] = omp.map.info var_ptr(%[[CELEN]] : !fir.ref>, complex) map_clauses(storage) capture(ByRef) + ! CHECK: %[[SOURCE_MAP:.*]] = omp.map.info var_ptr(%[[SOURCE]] {{.*}}) map_clauses(to) capture(ByRef) name(".omp.target.update.source") + ! CHECK: omp.target kernel_type(generic) map_entries(%[[SOURCE_MAP]] -> [[SOURCE_ARG:%[^, ]+]], %[[CELEN_MAP]] -> [[CELEN_ARG:%[^, ]+]] + ! CHECK: %[[CELEN_SOURCE:.*]] = fir.coordinate_of [[SOURCE_ARG]], {{.*}} -> !fir.ref> + ! CHECK: %[[CELEN_VALUE:.*]] = fir.load %[[CELEN_SOURCE]] : !fir.ref> + ! CHECK: fir.store %[[CELEN_VALUE]] to [[CELEN_ARG]] : !fir.ref> + ! CHECK-NOT: omp.target_update + ! CHECK: return + !$omp target update to(w%celen, w%nb, w%isp, w%ldo) +end subroutine + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_pointer( +subroutine update_pointer(w) + type(wavefun) :: w + + ! CHECK: %[[FERWE_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK: %[[PTR_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) {{.*}}name("w%ptr") + ! CHECK: omp.target_update map_entries(%[[FERWE_MAP]], %[[PTR_MAP]], + !$omp target update to(w%ferwe, w%ptr) +end subroutine + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_device( +subroutine update_device(w) + type(wavefun) :: w + + ! CHECK: %[[FERWE_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK: %[[NB_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK: omp.target_update device({{.*}}) map_entries(%[[FERWE_MAP]], %[[NB_MAP]] + !$omp target update to(w%ferwe, w%nb) device(0) +end subroutine + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_single( +subroutine update_single(w) + type(wavefun) :: w + + ! CHECK: %[[MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK-NOT: omp.target kernel_type(generic) + ! CHECK: omp.target_update map_entries(%[[MAP]] + !$omp target update to(w%ferwe) +end subroutine + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_array_element( +subroutine update_array_element(w) + type(wavefun) :: w(2) + + ! CHECK: fir.alloca tuple + ! CHECK: omp.target kernel_type(generic) + ! CHECK-NOT: omp.target_update + !$omp target update to(w(2)%ferwe, w(2)%nb) +end subroutine + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_from( +subroutine update_from(w) + type(wavefun) :: w + + ! CHECK: %[[FERWE_MAP:.*]] = omp.map.info {{.*}} map_clauses(from) + ! CHECK: %[[NB_MAP:.*]] = omp.map.info {{.*}} map_clauses(from) + ! CHECK: omp.target_update map_entries(%[[FERWE_MAP]], %[[NB_MAP]] + !$omp target update from(w%ferwe, w%nb) +end subroutine + +! CHECK-LABEL: func.func @_QMtarget_update_derived_typePupdate_nowait( +subroutine update_nowait(w) + type(wavefun) :: w + + ! CHECK: %[[FERWE_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK: %[[NB_MAP:.*]] = omp.map.info {{.*}} map_clauses(to) + ! CHECK: omp.target_update map_entries(%[[FERWE_MAP]], %[[NB_MAP]]{{.*}}) nowait + !$omp target update to(w%ferwe, w%nb) nowait +end subroutine + +end module