[flang-commits] [flang] [llvm] [mlir] [flang][mlir][OpenMP] Support iterator modifier in declare mapper map clause (PR #215485)
Urvi Rav via flang-commits
flang-commits at lists.llvm.org
Tue Aug 11 01:39:22 PDT 2026
https://github.com/ravurvi20 updated https://github.com/llvm/llvm-project/pull/215485
>From 19faf09144eb05d05df311716b15f5d79bd316b5 Mon Sep 17 00:00:00 2001
From: urvi-rav <urvi.rav at hpe.com>
Date: Tue, 11 Aug 2026 02:15:13 -0500
Subject: [PATCH] iterator modifier in declare mapper
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 122 +++++++++-
flang/lib/Lower/OpenMP/Utils.cpp | 5 +-
.../Optimizer/OpenMP/MapInfoFinalization.cpp | 17 ++
.../OpenMP/Todo/declare-mapper-iterator.f90 | 11 -
flang/test/Lower/OpenMP/map-iterator.f90 | 53 +++++
.../llvm/Frontend/OpenMP/OMPIRBuilder.h | 37 ++-
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 212 +++++++++++-------
.../Frontend/OpenMPIRBuilderTest.cpp | 15 +-
.../Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp | 12 +-
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 99 +++++++-
mlir/test/Target/LLVMIR/openmp-iterator.mlir | 62 +++++
mlir/test/Target/LLVMIR/openmp-todo.mlir | 24 --
12 files changed, 524 insertions(+), 145 deletions(-)
delete mode 100644 flang/test/Lower/OpenMP/Todo/declare-mapper-iterator.f90
create mode 100644 flang/test/Lower/OpenMP/map-iterator.f90
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 2f45b70db8fe6..89ea6aad5a2d2 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -2043,13 +2043,125 @@ bool ClauseProcessor::processMap(
}
}
- if (iterator)
- TODO(currentLocation,
- "Support for iterator modifiers is not implemented yet");
TodoLocators(currentLocation, objects);
- processMapObjects(stmtCtx, clauseLocation,
- std::get<omp::ObjectList>(clause.t), mapTypeBits,
+ llvm::SmallVector<IteratorRange> iteratorRanges;
+ llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4> ivSyms;
+ collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms);
+
+ // Objects that reference an iterator induction variable are expanded at
+ // runtime via `omp.iterator`/`map_iterated`; the rest go through the
+ // regular static map-info path below.
+ omp::ObjectList staticObjects;
+ if (!iterator) {
+ staticObjects = objects;
+ } else {
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ for (const omp::Object &object : objects) {
+ if (!hasIteratorIVReference(object, ivSyms)) {
+ staticObjects.push_back(object);
+ continue;
+ }
+
+ // The per-iteration `omp.map.info` this produces lives inside the
+ // `omp.iterator` body and cannot be attached as a `members` operand
+ // of some other (parent) MapInfoOp built outside that region, since
+ // only the aggregated `!omp.iterated<T>` handle escapes the region.
+ // We still register the parent with `parentMemberIndices` (with no
+ // child attached) so `insertChildMapInfoIntoParent` synthesizes the
+ // usual partial/"storage" map for the parent object (e.g. the
+ // `declare mapper` association variable itself); the iterator-driven
+ // child map is emitted separately into `result.mapIterated`, mirroring
+ // how `map_vars`/`members` (static) and `map_iterated` (runtime
+ // expanded) already coexist as sibling operands on the owning op.
+ bool hasParentObj = object.sym()->owner().IsDerivedType();
+ mlir::Value baseAddr;
+ if (hasParentObj) {
+ omp::ObjectList objectList = gatherObjectsOf(object, semaCtx);
+ assert(!objectList.empty() &&
+ "could not find parent objects of derived type member");
+ if (isMemberOrParentAllocatableOrPointer(object, semaCtx))
+ TODO(currentLocation,
+ "Iterator modifier on this derived-type member in a map "
+ "clause is not implemented yet");
+
+ omp::Object baseObject = objectList[0];
+ parentMemberIndices.emplace(baseObject, OmpMapParentAndMemberData{});
+
+ // `objectList` includes an extra entry for the subscripted
+ // reference itself (e.g. [v, v%a, v%a(i)]) on top of one entry per
+ // derived-type level walked, so only a single record-field
+ // placement index means this is a single-level, non-nested member.
+ llvm::SmallVector<int64_t> memberIndices;
+ generateMemberPlacementIndices(object, memberIndices, semaCtx);
+ if (memberIndices.size() != 1)
+ TODO(currentLocation,
+ "Iterator modifier on a nested derived-type member in a map "
+ "clause is not implemented yet");
+
+ fir::factory::AddrAndBoundsInfo parentInfo =
+ Fortran::lower::getDataOperandBaseAddr(
+ converter, firOpBuilder, *baseObject.sym(), clauseLocation,
+ /*unwrapFirBox=*/false);
+ auto recordType = mlir::dyn_cast<fir::RecordType>(
+ fir::unwrapPassByRefType(parentInfo.addr.getType()));
+ if (!recordType)
+ TODO(currentLocation,
+ "Iterator modifier on this derived-type member in a map "
+ "clause is not implemented yet");
+
+ mlir::Type fieldTy = recordType.getType(memberIndices[0]);
+ fir::IntOrValue idxConst = mlir::IntegerAttr::get(
+ firOpBuilder.getI32Type(), memberIndices[0]);
+ baseAddr = fir::CoordinateOp::create(
+ firOpBuilder, clauseLocation, firOpBuilder.getRefType(fieldTy),
+ parentInfo.addr, llvm::SmallVector<fir::IntOrValue, 1>{idxConst});
+ } else {
+ fir::factory::AddrAndBoundsInfo info =
+ Fortran::lower::getDataOperandBaseAddr(
+ converter, firOpBuilder, *object.sym(), clauseLocation,
+ /*unwrapFirBox=*/false);
+ baseAddr = info.addr;
+ }
+ hlfir::Entity entity{baseAddr};
+
+ mlir::Type elemRefTy =
+ fir::ReferenceType::get(entity.getFortranElementType());
+ mlir::Type iterTy = mlir::omp::IteratedType::get(
+ &converter.getMLIRContext(), elemRefTy);
+ mlir::FlatSymbolRefAttr mapperId =
+ resolveMapperId(converter, clauseLocation, object, mapperIdName,
+ mapTypeBits, directive, hasParentObj);
+ std::string objName = object.sym()->name().ToString();
+
+ mlir::Value iterHandle = buildIteratorOp(
+ converter, clauseLocation, iterTy, iteratorRanges,
+ [&](fir::FirOpBuilder &builder, mlir::Location loc,
+ llvm::ArrayRef<mlir::Value> /*ivs*/) -> mlir::Value {
+ lower::StatementContext iterStmtCtx;
+ std::optional<llvm::SmallVector<mlir::Value>> loweredIndices =
+ getIteratorElementIndices(converter, object, iterStmtCtx,
+ loc);
+ if (!loweredIndices)
+ TODO(loc, "object type not supported by iterator modifier");
+
+ mlir::Value iteratedAddr = genIteratorCoordinate(
+ converter, entity, *loweredIndices, loc);
+ auto location = mlir::NameLoc::get(
+ mlir::StringAttr::get(builder.getContext(), objName),
+ iteratedAddr.getLoc());
+ return utils::openmp::createMapInfoOp(
+ builder, location, iteratedAddr,
+ /*varPtrPtr=*/mlir::Value{}, objName, /*bounds=*/{},
+ /*members=*/{}, /*membersIndex=*/mlir::ArrayAttr{},
+ mapTypeBits, mlir::omp::VariableCaptureKind::ByRef,
+ iteratedAddr.getType(), /*partialMap=*/false, mapperId);
+ });
+ result.mapIterated.push_back(iterHandle);
+ }
+ }
+
+ processMapObjects(stmtCtx, clauseLocation, staticObjects, mapTypeBits,
parentMemberIndices, result.mapVars, *ptrMapObjects,
mapperIdName, /*isMotionModifier=*/false, directive);
};
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index 8f57f00d59c58..9176c5a1f6e4b 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -1253,7 +1253,6 @@ std::optional<llvm::SmallVector<mlir::Value>> getIteratorElementIndices(
auto &builder = converter.getFirOpBuilder();
const Fortran::semantics::Symbol *sym = object.sym();
assert(sym && "expected symbol for iterator-dependent object");
- fir::ExtendedValue dataExv = converter.getSymbolExtendedValue(*sym);
mlir::Value one =
builder.createIntegerConstant(loc, builder.getIndexType(), 1);
llvm::SmallVector<mlir::Value> indices;
@@ -1272,6 +1271,10 @@ std::optional<llvm::SmallVector<mlir::Value>> getIteratorElementIndices(
// Get lower bound if not provided by user.
// For example: !$omp task affinity(iterator(i = 1:n, j = 1:m) : a(:i+1,
// j+2))
+ // Only looked up here (not for a top-level symbol like a derived-type
+ // member, which has no standalone symbol map entry) since it is only
+ // needed for this triplet-without-lower-bound case.
+ fir::ExtendedValue dataExv = converter.getSymbolExtendedValue(*sym);
idx = fir::factory::readLowerBound(builder, loc, dataExv, dim, one);
} else {
idx = fir::getBase(
diff --git a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
index 949da8f20cbbe..bdf34c965e6b6 100644
--- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
+++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
@@ -722,6 +722,23 @@ class MapInfoFinalizationPass
if (auto mapUser = llvm::dyn_cast<mlir::omp::MapInfoOp>(user))
return getFirstTargetUser(mapUser);
+
+ // A map produced inside an `omp.iterator` body (for an `iterator`
+ // modifier on a map/motion clause) is only directly used by the
+ // region's `omp.yield`; look through it to the op consuming the
+ // iterator's `map_iterated` result instead.
+ if (llvm::isa<mlir::omp::YieldOp>(user)) {
+ if (auto iterOp = llvm::dyn_cast_if_present<mlir::omp::IteratorOp>(
+ user->getParentOp())) {
+ for (auto *iterUser : iterOp.getIterated().getUsers())
+ if (llvm::isa<mlir::omp::TargetOp, mlir::omp::TargetDataOp,
+ mlir::omp::TargetUpdateOp,
+ mlir::omp::TargetExitDataOp,
+ mlir::omp::TargetEnterDataOp,
+ mlir::omp::DeclareMapperInfoOp>(iterUser))
+ return iterUser;
+ }
+ }
}
return nullptr;
diff --git a/flang/test/Lower/OpenMP/Todo/declare-mapper-iterator.f90 b/flang/test/Lower/OpenMP/Todo/declare-mapper-iterator.f90
deleted file mode 100644
index dacd6d6246595..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/declare-mapper-iterator.f90
+++ /dev/null
@@ -1,11 +0,0 @@
-!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-
-!CHECK: Support for iterator modifiers is not implemented yet
-subroutine f(arg)
- type :: s
- integer :: a(10)
- end type
- type(s) :: arg(:)
-
- !$omp declare mapper(m: s :: v) map(mapper(m), iterator(i = 1:10): v%a(i))
-end
diff --git a/flang/test/Lower/OpenMP/map-iterator.f90 b/flang/test/Lower/OpenMP/map-iterator.f90
new file mode 100644
index 0000000000000..0e6b99ea08bf2
--- /dev/null
+++ b/flang/test/Lower/OpenMP/map-iterator.f90
@@ -0,0 +1,53 @@
+!RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+
+! Tests lowering of the `iterator` modifier on the `map` clause, for a
+! derived-type member accessed through a `declare mapper` and for a plain
+! (non-member) array object.
+
+!===============================================================================
+! declare mapper (derived-type member: v%a(i))
+!===============================================================================
+
+! omp.declare_mapper is always emitted at module scope ahead of any
+! func.func, regardless of where its subroutine appears in the source.
+! CHECK-LABEL: omp.declare_mapper @_QQFfm :
+! CHECK-SAME: [[TY:!fir\.type<_QFfTs\{a:!fir\.array<10xi32>\}>]] {
+! CHECK: ^bb0(%[[ARG0:.*]]: !fir.ref<[[TY]]>):
+! CHECK: %[[V:.*]]:2 = hlfir.declare %[[ARG0]] {uniq_name = "_QFfEv"} : (!fir.ref<[[TY]]>) -> (!fir.ref<[[TY]]>, !fir.ref<[[TY]]>)
+! CHECK: %[[FIELD:.*]] = fir.coordinate_of %[[V]]#0, a : (!fir.ref<[[TY]]>) -> !fir.ref<!fir.array<10xi32>>
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%{{.*}} to %{{.*}} step %{{.*}}) {
+! CHECK: %[[COOR:.*]] = fir.array_coor %[[FIELD]](%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
+! CHECK: %[[MAPINFO:.*]] = omp.map.info var_ptr(%[[COOR]] : !fir.ref<i32>, i32) map_clauses(tofrom) capture(ByRef) -> !fir.ref<i32> {name = "a"}
+! CHECK: omp.yield(%[[MAPINFO]] : !fir.ref<i32>)
+! CHECK: } -> !omp.iterated<!fir.ref<i32>>
+! CHECK: %[[PARENT:.*]] = omp.map.info var_ptr(%[[V]]#1 : !fir.ref<[[TY]]>, [[TY]]) map_clauses(storage) capture(ByRef) members( : : ) -> !fir.ref<[[TY]]> {name = "v", partial_map = true}
+! CHECK: omp.declare_mapper.info map_entries(%[[PARENT]] : !fir.ref<[[TY]]>) map_iterated(%[[IT]] : !omp.iterated<!fir.ref<i32>>)
+subroutine f(arg)
+ type :: s
+ integer :: a(10)
+ end type
+ type(s) :: arg(:)
+
+ !$omp declare mapper(m: s :: v) map(mapper(m), iterator(i = 1:10): v%a(i))
+end
+
+!===============================================================================
+! target map (plain, non-member array object)
+!===============================================================================
+
+! CHECK-LABEL: func.func @_QPf00(
+! CHECK-SAME: %[[A:.*]]: !fir.ref<!fir.array<10xi32>>
+subroutine f00(a)
+ integer :: a(10)
+ !$omp target map(iterator(i = 1:2): a(i))
+ a(1) = 1
+ !$omp end target
+end
+! CHECK: %[[A_DECL:.*]]:2 = hlfir.declare %[[A]](%{{.*}}) dummy_scope %{{.*}} arg 1 {uniq_name = "_QFf00Ea"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%{{.*}} to %{{.*}} step %{{.*}}) {
+! CHECK: %[[COOR:.*]] = fir.array_coor %[[A_DECL]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
+! CHECK: %[[MAPINFO:.*]] = omp.map.info var_ptr(%[[COOR]] : !fir.ref<i32>, i32) map_clauses(tofrom) capture(ByRef) -> !fir.ref<i32> {name = "a"}
+! CHECK: omp.yield(%[[MAPINFO]] : !fir.ref<i32>)
+! CHECK: } -> !omp.iterated<!fir.ref<i32>>
+! CHECK: omp.target {{.*}}map_iterated(%[[IT]] : !omp.iterated<!fir.ref<i32>>)
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
index 1965f7b983805..02fe8b8b92c08 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
@@ -29,6 +29,7 @@
#include "llvm/Support/Error.h"
#include "llvm/TargetParser/Triple.h"
#include <forward_list>
+#include <functional>
#include <map>
#include <optional>
@@ -3004,6 +3005,28 @@ class OpenMPIRBuilder {
MapHasAttachPtrArrayTy HasAttachPtr;
StructNonContiguousInfo NonContigInfo;
+ /// Represents a group of mapper sub-components produced by an `iterator`
+ /// modifier on a `declare mapper`'s map clause: the component count is
+ /// only known at runtime, so these are pushed one at a time via
+ /// __tgt_push_mapper_component from inside a generated loop, rather than
+ /// being added to the arrays above. Only consumed by
+ /// emitUserDefinedMapper; emitOffloadingArrays ignores this field.
+ struct DynamicSegment {
+ /// Runtime (i64) trip count for this segment.
+ Value *Count = nullptr;
+ /// Emits the code computing one iteration's (BasePointer, Pointer,
+ /// Size) triple, given the loop's linear induction variable (0-based,
+ /// i64). Owning (not function_ref): the callable is typically a
+ /// lambda temporary assigned in after construction, which would
+ /// otherwise dangle by the time this is invoked.
+ std::function<SmallVector<Value *, 3>(IRBuilderBase &, Value *)> GenEntry;
+ /// Map type/HasAttachPtr, constant across the segment.
+ omp::OpenMPOffloadMappingFlags Type =
+ omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
+ bool HasAttachPtr = false;
+ };
+ SmallVector<DynamicSegment, 0> DynamicSegments;
+
/// Append arrays in \a CurInfo.
void append(MapInfosTy &CurInfo) {
BasePointers.append(CurInfo.BasePointers.begin(),
@@ -3024,6 +3047,8 @@ class OpenMPIRBuilder {
CurInfo.NonContigInfo.Counts.end());
NonContigInfo.Strides.append(CurInfo.NonContigInfo.Strides.begin(),
CurInfo.NonContigInfo.Strides.end());
+ DynamicSegments.append(CurInfo.DynamicSegments.begin(),
+ CurInfo.DynamicSegments.end());
}
};
using MapInfosOrErrorTy = Expected<MapInfosTy &>;
@@ -4236,7 +4261,7 @@ class OpenMPIRBuilder {
getOrCreateInternalVariable(Type *Ty, const StringRef &Name,
std::optional<unsigned> AddressSpace = {});
- using IteratorBodyGenTy = llvm::function_ref<llvm::Error(
+ using IteratorBodyGenTy = llvm::function_ref<llvm::Expected<InsertPointTy>(
InsertPointTy BodyIP, llvm::Value *LinearIV)>;
/// Create a canonical iterator loop at the current insertion point.
@@ -4256,9 +4281,13 @@ class OpenMPIRBuilder {
/// - The skeleton’s unconditional branch from the loop body is removed
/// before invoking \p BodyGen.
/// - \p BodyGen may freely emit instructions and temporarily introduce
- /// control flow.
- /// - If the loop body does not end with a terminator after \p BodyGen
- /// returns, a branch to the latch is inserted to restore canonical form.
+ /// control flow, possibly using an IRBuilder distinct from this
+ /// OpenMPIRBuilder's own \p Builder; it must therefore report where it
+ /// left off by returning that final insertion point, rather than relying
+ /// on any builder's current cursor.
+ /// - If the returned insertion point's block does not end with a
+ /// terminator after \p BodyGen returns, a branch to the latch is
+ /// inserted there to restore canonical form.
///
/// \param Loc The location where the iterator modifier was encountered.
/// \param TripCount Number of loop iterations.
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index c85cfe15d058c..87e912cf83b31 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -10626,86 +10626,81 @@ Expected<Function *> OpenMPIRBuilder::emitUserDefinedMapper(
Value *ShiftedPreviousSize =
Builder.CreateShl(PreviousSize, Builder.getInt64(getFlagMemberOffset()));
- // Fill up the runtime mapper handle for all components.
- for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
- Value *CurBaseArg = Info->BasePointers[I];
- Value *CurBeginArg = Info->Pointers[I];
- Value *CurSizeArg = Info->Sizes[I];
- Value *CurNameArg = Info->Names.size()
- ? Info->Names[I]
- : Constant::getNullValue(Builder.getPtrTy());
-
- Value *OriMapType = Builder.getInt64(
- static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
- Info->Types[I]));
- auto RawType =
- static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
- Info->Types[I]);
+ // Emits a single mapper sub-component: computes the MEMBER_OF-adjusted map
+ // type, applies map-type-modifier propagation, and pushes it either via a
+ // child mapper function or __tgt_push_mapper_component. Returns the last
+ // basic block emitted, so callers can update the outer per-array-element
+ // loop's PHI incoming block.
+ //
+ // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
+ // current array element (N = __tgt_mapper_num_components() at loop body
+ // start).
+ //
+ // Example 1:
+ // struct S { int x; int *p; };
+ //
+ // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
+ // use: S arr[2]; ... map(arr)
+ // entries per element:
+ //
+ // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
+ // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
+ // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
+ //
+ // Example 2:
+ // struct S1 { int x; int y; };
+ // struct S2 { int z; S1 *s1p; };
+ //
+ // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
+ // s2.s1p->y)
+ // use: S2 arr[2]; ... map(arr)
+ // entries per element:
+ //
+ // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
+ // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
+ // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
+ // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
+ // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
+ //
+ // x/y carry inner MEMBER_OF(2)
+ // which is shifted by N to become MEMBER_OF(N+2).
+ //
+ // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
+ // the combined ALLOC entry for the s1p->x..y block, and the individual
+ // x/y entries that are MEMBER_OF that block, all describe storage
+ // reached through the attach ptr arr[i].s1p.
+ //
+ // Entries of the following kinds do NOT receive a new outer MEMBER_OF
+ // linking them to the parent struct:
+ //
+ // * (*) Entries with HasAttachPtr: they represent pointee data that
+ // occupies a different storage block than the struct being mapped, so
+ // they are not a member of it. They may still be MEMBER_OF an entry
+ // within that pointee block, in which case those pre-existing bits are
+ // shifted -- see (***).
+ // * (**) ATTACH entries: they are not a member of anything — they just
+ // link a ptr to its ptee.
+ // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
+ // its pre-shaped entries already carry their final MEMBER_OF bits.
+ // TODO: set HasAttachPtr from Flang for entries whose storage is the
+ // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
+ // it.
+ //
+ // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
+ // s1p->x/y entries above), those bits are still shifted by N.
+ auto pushComponent = [&](Value *CurBaseArg, Value *CurBeginArg,
+ Value *CurSizeArg, Value *CurNameArg,
+ uint64_t RawType, bool HasAttachPtrFlag,
+ Function *ChildMapperFn) -> BasicBlock * {
+ Value *OriMapType = Builder.getInt64(RawType);
constexpr uint64_t MemberOfMask =
static_cast<uint64_t>(OpenMPOffloadMappingFlags::OMP_MAP_MEMBER_OF);
constexpr uint64_t AttachBit =
static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
OpenMPOffloadMappingFlags::OMP_MAP_ATTACH);
- // Add MEMBER_OF (ShiftedPreviousSize) to group this sub-map with the
- // current array element (N = __tgt_mapper_num_components() at loop body
- // start).
- //
- // Example 1:
- // struct S { int x; int *p; };
- //
- // mapper: #pragma omp declare mapper(id: S s) map(s.x, s.p[0:10])
- // use: S arr[2]; ... map(arr)
- // entries per element:
- //
- // &arr[i], &arr[i].x, sizeof(int), MEMBER_OF(N)|TO|FROM
- // &arr[i].p[0], &arr[i].p[0], 10*sizeof(int), TO|FROM (*)
- // &arr[i].p, &arr[i].p[0], sizeof(int*), ATTACH (**)
- //
- // Example 2:
- // struct S1 { int x; int y; };
- // struct S2 { int z; S1 *s1p; };
- //
- // mapper: #pragma omp declare mapper(S2 s2) map(s2.z, s2.s1p->x,
- // s2.s1p->y)
- // use: S2 arr[2]; ... map(arr)
- // entries per element:
- //
- // &arr[i], &arr[i].z, sizeof(int), MEMBER_OF(N)|TO|FROM
- // &arr[i].s1p[0], &arr[i].s1p->x, sizeof(s1p->x..y), ALLOC (*)
- // &arr[i].s1p[0], &arr[i].s1p->x, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
- // &arr[i].s1p[0], &arr[i].s1p->y, 4, MEMBER_OF(N+2)|TO|FROM (*)(***)
- // &arr[i].s1p, &arr[i].s1p->x, sizeof(ptr), ATTACH (**)
- //
- // x/y carry inner MEMBER_OF(2)
- // which is shifted by N to become MEMBER_OF(N+2).
- //
- // HasAttachPtr is set on all of the s1p entries except the ATTACH one:
- // the combined ALLOC entry for the s1p->x..y block, and the individual
- // x/y entries that are MEMBER_OF that block, all describe storage
- // reached through the attach ptr arr[i].s1p.
- //
- // Entries of the following kinds do NOT receive a new outer MEMBER_OF
- // linking them to the parent struct:
- //
- // * (*) Entries with HasAttachPtr: they represent pointee data that
- // occupies a different storage block than the struct being mapped, so
- // they are not a member of it. They may still be MEMBER_OF an entry
- // within that pointee block, in which case those pre-existing bits are
- // shifted -- see (***).
- // * (**) ATTACH entries: they are not a member of anything — they just
- // link a ptr to its ptee.
- // * All entries when PreserveMemberOfFlags is set (the Flang/MLIR path):
- // its pre-shaped entries already carry their final MEMBER_OF bits.
- // TODO: set HasAttachPtr from Flang for entries whose storage is the
- // pointee's (e.g. s%p(0:10)) and drop PreserveMemberOfFlags in favor of
- // it.
- //
- // (***) If such an entry already has its own MEMBER_OF bits (e.g. the
- // s1p->x/y entries above), those bits are still shifted by N.
Value *MemberMapType;
- if (PreserveMemberOfFlags || (RawType & AttachBit) ||
- Info->HasAttachPtr[I]) {
+ if (PreserveMemberOfFlags || (RawType & AttachBit) || HasAttachPtrFlag) {
if (RawType & MemberOfMask)
MemberMapType = Builder.CreateNUWAdd(OriMapType, ShiftedPreviousSize);
else
@@ -10782,7 +10777,6 @@ Expected<Function *> OpenMPIRBuilder::emitUserDefinedMapper(
OpenMPOffloadMappingFlags::OMP_MAP_TO)));
// In case of tofrom, do nothing.
emitBlock(EndBB, MapperFn);
- LastBB = EndBB;
PHINode *CurMapType =
Builder.CreatePHI(Builder.getInt64Ty(), 4, "omp.maptype");
CurMapType->addIncoming(AllocMapType, AllocBB);
@@ -10830,7 +10824,7 @@ Expected<Function *> OpenMPIRBuilder::emitUserDefinedMapper(
OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS |
OpenMPOffloadMappingFlags::OMP_MAP_DELETE |
OpenMPOffloadMappingFlags::OMP_MAP_CLOSE);
- if (PropagatePresentToPointee && Info->HasAttachPtr[I])
+ if (PropagatePresentToPointee && HasAttachPtrFlag)
ModifierBits |=
static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
OpenMPOffloadMappingFlags::OMP_MAP_PRESENT);
@@ -10847,13 +10841,9 @@ Expected<Function *> OpenMPIRBuilder::emitUserDefinedMapper(
Value *OffloadingArgs[] = {MapperHandle, CurBaseArg, CurBeginArg,
CurSizeArg, FinalMapType, CurNameArg};
-
- auto ChildMapperFn = CustomMapperCB(I);
- if (!ChildMapperFn)
- return ChildMapperFn.takeError();
- if (*ChildMapperFn) {
+ if (ChildMapperFn) {
// Call the corresponding mapper function.
- createRuntimeFunctionCall(*ChildMapperFn, OffloadingArgs)
+ createRuntimeFunctionCall(ChildMapperFn, OffloadingArgs)
->setDoesNotThrow();
} else {
// Call the runtime API __tgt_push_mapper_component to fill up the runtime
@@ -10862,6 +10852,48 @@ Expected<Function *> OpenMPIRBuilder::emitUserDefinedMapper(
getOrCreateRuntimeFunction(M, OMPRTL___tgt_push_mapper_component),
OffloadingArgs);
}
+ return EndBB;
+ };
+
+ // Fill up the runtime mapper handle for all statically-known components.
+ for (unsigned I = 0; I < Info->BasePointers.size(); ++I) {
+ Value *CurNameArg = Info->Names.size()
+ ? Info->Names[I]
+ : Constant::getNullValue(Builder.getPtrTy());
+ Expected<Function *> ChildMapperFn = CustomMapperCB(I);
+ if (!ChildMapperFn)
+ return ChildMapperFn.takeError();
+ LastBB = pushComponent(
+ Info->BasePointers[I], Info->Pointers[I], Info->Sizes[I], CurNameArg,
+ static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
+ Info->Types[I]),
+ Info->HasAttachPtr[I], *ChildMapperFn);
+ }
+
+ // Fill up the runtime mapper handle for components whose count is only
+ // known at runtime (produced by an `iterator` modifier on the mapper's own
+ // map clause): push each one individually, from inside a generated loop,
+ // via the same __tgt_push_mapper_component call used above.
+ for (MapInfosTy::DynamicSegment &Seg : Info->DynamicSegments) {
+ LocationDescription Loc(Builder);
+ auto BodyGen = [&](InsertPointTy BodyIP,
+ Value *LinearIV) -> Expected<InsertPointTy> {
+ Builder.restoreIP(BodyIP);
+ SmallVector<Value *, 3> Entry = Seg.GenEntry(Builder, LinearIV);
+ BasicBlock *EndBB = pushComponent(
+ Entry[0], Entry[1], Entry[2],
+ Constant::getNullValue(Builder.getPtrTy()),
+ static_cast<std::underlying_type_t<OpenMPOffloadMappingFlags>>(
+ Seg.Type),
+ Seg.HasAttachPtr, /*ChildMapperFn=*/nullptr);
+ return InsertPointTy(EndBB, EndBB->end());
+ };
+ InsertPointOrErrorTy AfterIP =
+ createIteratorLoop(Loc, Seg.Count, BodyGen, "mapper.iterator");
+ if (!AfterIP)
+ return AfterIP.takeError();
+ Builder.restoreIP(*AfterIP);
+ LastBB = Builder.GetInsertBlock();
}
// Update the pointer to point to the next element that needs to be mapped,
@@ -12777,11 +12809,17 @@ OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createIteratorLoop(
T->eraseFromParent();
InsertPointTy BodyIP = CLI->getBodyIP();
- if (llvm::Error Err = BodyGen(BodyIP, CLI->getIndVar()))
- return Err;
-
- // Body must either fallthrough to the latch or branch directly to it.
- if (Instruction *BodyTerminator = CLI->getBody()->getTerminatorOrNull()) {
+ llvm::Expected<InsertPointTy> BodyEndIPOrErr =
+ BodyGen(BodyIP, CLI->getIndVar());
+ if (!BodyEndIPOrErr)
+ return BodyEndIPOrErr.takeError();
+
+ // BodyGen reports where it actually left off (it may use an IRBuilder
+ // distinct from this OpenMPIRBuilder's own Builder, and may have emitted
+ // additional basic blocks beyond CLI->getBody()), so check/terminate that
+ // block rather than CLI->getBody() itself.
+ BasicBlock *BodyEndBB = BodyEndIPOrErr->getBlock();
+ if (Instruction *BodyTerminator = BodyEndBB->getTerminatorOrNull()) {
auto *BodyBr = dyn_cast<UncondBrInst>(BodyTerminator);
if (!BodyBr || BodyBr->getSuccessor() != CLI->getLatch()) {
return make_error<StringError>(
@@ -12791,7 +12829,7 @@ OpenMPIRBuilder::InsertPointOrErrorTy OpenMPIRBuilder::createIteratorLoop(
}
} else {
// Ensure we end the loop body by jumping to the latch.
- Builder.SetInsertPoint(CLI->getBody());
+ Builder.SetInsertPoint(BodyEndBB, BodyEndIPOrErr->getPoint());
Builder.CreateBr(CLI->getLatch());
}
diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
index 084dcb0a5847f..a7aa438943967 100644
--- a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
+++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
@@ -7854,10 +7854,11 @@ TEST_F(OpenMPIRBuilderTest, CreateIteratorLoop) {
F->setName("func.unterminated");
IRBuilder<> Builder(BB);
- auto BodyGenCB = [&](InsertPointTy BodyIP, Value *LinearIV) -> Error {
+ auto BodyGenCB = [&](InsertPointTy BodyIP,
+ Value *LinearIV) -> Expected<InsertPointTy> {
Builder.restoreIP(BodyIP);
Builder.CreateAdd(LinearIV, Builder.getInt64(1));
- return Error::success();
+ return Builder.saveIP();
};
OpenMPIRBuilder::LocationDescription Loc(Builder.saveIP(), DL);
@@ -7885,10 +7886,11 @@ TEST_F(OpenMPIRBuilderTest, CreateIteratorLoop) {
BasicBlock::Create(Builder.getContext(), "orig.succ", F2);
Builder.CreateBr(OrigSucc);
- auto BodyGenCB = [&](InsertPointTy BodyIP, Value *LinearIV) -> Error {
+ auto BodyGenCB = [&](InsertPointTy BodyIP,
+ Value *LinearIV) -> Expected<InsertPointTy> {
Builder.restoreIP(BodyIP);
Builder.CreateAdd(LinearIV, Builder.getInt64(1));
- return Error::success();
+ return Builder.saveIP();
};
OpenMPIRBuilder::LocationDescription Loc(InsertPointTy(BB2, BB2->end()),
@@ -7918,7 +7920,8 @@ TEST_F(OpenMPIRBuilderTest, CreateIteratorLoopInvalidLoopBody) {
F->setName("func");
IRBuilder<> Builder(BB);
- auto BodyGenCB = [&](InsertPointTy BodyIP, Value *LinearIV) -> Error {
+ auto BodyGenCB = [&](InsertPointTy BodyIP,
+ Value *LinearIV) -> Expected<InsertPointTy> {
Builder.restoreIP(BodyIP);
Builder.CreateAdd(LinearIV, Builder.getInt64(1));
BasicBlock *BadDest =
@@ -7926,7 +7929,7 @@ TEST_F(OpenMPIRBuilderTest, CreateIteratorLoopInvalidLoopBody) {
Builder.CreateBr(BadDest);
Builder.SetInsertPoint(BadDest);
Builder.CreateUnreachable();
- return Error::success();
+ return Builder.saveIP();
};
OpenMPIRBuilder::LocationDescription Loc(Builder.saveIP(), DL);
diff --git a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
index 2ab1575a510ac..70fb4fd979d99 100644
--- a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
+++ b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
@@ -156,7 +156,17 @@ void mlir::populateOpenMPToLLVMConversionPatterns(LLVMTypeConverter &converter,
[&](omp::MapBoundsType type) -> Type { return type; });
converter.addConversion(
[&](omp::AffinityEntryType type) -> Type { return type; });
- converter.addConversion([&](omp::IteratedType type) -> Type { return type; });
+ // Unlike MapBoundsType/AffinityEntryType, the element type wrapped by
+ // IteratedType is a real (e.g. FIR) pointer-like type that does get
+ // converted elsewhere (e.g. omp.map.info's own result), so this type must
+ // be rebuilt with the element type converted too, or the omp.iterator op's
+ // declared type would go stale relative to its omp.yield operand.
+ converter.addConversion([&](omp::IteratedType type) -> Type {
+ Type convertedElementType = converter.convertType(type.getElementType());
+ if (!convertedElementType)
+ return nullptr;
+ return omp::IteratedType::get(type.getContext(), convertedElementType);
+ });
// Add conversions for all OpenMP operations.
addOpenMPOpConversions<
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index e09bb720ced2d..0169ace2e87ac 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -531,6 +531,34 @@ static LogicalResult checkImplementationStatus(Operation &op) {
result = todo("map/motion clause with iterator modifier");
};
+ // `declare_mapper.info` supports the simple case emitted by Flang's
+ // lowering: a single-dimension iterator whose body directly yields one
+ // bounds-free, member-free omp.map.info. Anything else still falls back to
+ // the generic (not yet implemented) diagnostic above.
+ auto checkDeclareMapperMap = [&todo](omp::DeclareMapperInfoOp op,
+ LogicalResult &result) {
+ for (Value iterVal : op.getMapIterated()) {
+ auto itersOp = iterVal.getDefiningOp<omp::IteratorOp>();
+ bool supported = itersOp && itersOp.getLoopLowerBounds().size() == 1;
+ if (supported) {
+ auto yieldOp =
+ dyn_cast<omp::YieldOp>(itersOp.getRegion().front().getTerminator());
+ supported = yieldOp && yieldOp.getResults().size() == 1;
+ if (supported) {
+ auto mapInfoOp =
+ yieldOp.getResults()[0].getDefiningOp<omp::MapInfoOp>();
+ supported = mapInfoOp && !mapInfoOp.getVarPtrPtr() &&
+ mapInfoOp.getBounds().empty() &&
+ mapInfoOp.getMembers().empty();
+ }
+ }
+ if (!supported) {
+ result = todo("map/motion clause with iterator modifier");
+ return;
+ }
+ }
+ };
+
auto checkDynGroupprivate = [&todo](auto op, LogicalResult &result) {
if (op.getDynGroupprivateSize())
result = todo("dyn_groupprivate");
@@ -619,7 +647,9 @@ static LogicalResult checkImplementationStatus(Operation &op) {
checkThreadLimit(op, result);
})
.Case([&](omp::TargetDataOp op) { checkMap(op, result); })
- .Case([&](omp::DeclareMapperInfoOp op) { checkMap(op, result); })
+ .Case([&](omp::DeclareMapperInfoOp op) {
+ checkDeclareMapperMap(op, result);
+ })
.Default([](Operation &) {
// Assume all clauses for an operation can be translated unless they are
// checked above.
@@ -2985,7 +3015,8 @@ fillIteratorLoop(mlir::omp::IteratorOp itersOp, llvm::IRBuilderBase &builder,
llvm::OpenMPIRBuilder::LocationDescription loc(builder);
auto bodyGen = [&](llvm::OpenMPIRBuilder::InsertPointTy bodyIP,
- llvm::Value *linearIV) -> llvm::Error {
+ llvm::Value *linearIV)
+ -> llvm::Expected<llvm::OpenMPIRBuilder::InsertPointTy> {
llvm::IRBuilderBase::InsertPointGuard guard(builder);
builder.restoreIP(bodyIP);
@@ -3006,7 +3037,7 @@ fillIteratorLoop(mlir::omp::IteratorOp itersOp, llvm::IRBuilderBase &builder,
// clear them to avoid stale entries in ModuleTranslation.
moduleTranslation.forgetMapping(itersRegion);
- return llvm::Error::success();
+ return builder.saveIP();
};
llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
@@ -7754,9 +7785,60 @@ emitUserDefinedMapper(Operation *op, llvm::IRBuilderBase &builder,
genMapInfos(builder, moduleTranslation, dl, combinedInfo, mapData,
targetDirective);
- // Drop the mapping that is no longer necessary so that the same region
- // can be processed multiple times.
- moduleTranslation.forgetMapping(declMapperOp.getRegion());
+ // Components produced by an `iterator` modifier on the mapper's own map
+ // clause: their count is only known at runtime, so they are recorded as
+ // dynamic segments and pushed one at a time (via
+ // __tgt_push_mapper_component) from a generated loop, mirroring how the
+ // depend/affinity clauses already handle iterator-produced entries.
+ for (mlir::Value iterVal : declMapperInfoOp.getMapIterated()) {
+ auto itersOp = iterVal.getDefiningOp<mlir::omp::IteratorOp>();
+ assert(itersOp && "map_iterated value must be defined by omp.iterator");
+ mlir::Block &iteratorRegionBlock = itersOp.getRegion().front();
+ auto yieldOp =
+ cast<mlir::omp::YieldOp>(iteratorRegionBlock.getTerminator());
+ auto mapInfoOp =
+ yieldOp.getResults()[0].getDefiningOp<mlir::omp::MapInfoOp>();
+ assert(mapInfoOp && "expected an omp.map.info in the map_iterated body");
+
+ llvm::Value *sizeVal =
+ builder.getInt64(dl.getTypeSize(mapInfoOp.getVarPtrType()));
+ llvm::omp::OpenMPOffloadMappingFlags mapType =
+ convertClauseMapFlags(mapInfoOp.getMapType());
+ bool hasAttachPtr =
+ (mapType & llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ATTACH) !=
+ llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
+
+ IteratorInfo iterInfo(itersOp, moduleTranslation, builder);
+ llvm::Value *count = iterInfo.getTotalTrips();
+
+ combinedInfo.DynamicSegments.push_back(
+ MapInfosTy::DynamicSegment{count, {}, mapType, hasAttachPtr});
+ MapInfosTy::DynamicSegment &seg = combinedInfo.DynamicSegments.back();
+ // Capture `iterInfo` by value: it already holds the lb/ub/step/trip
+ // llvm::Value*s looked up while `declMapperOp`'s region mapping was
+ // still valid. GenEntry runs later (from within
+ // OpenMPIRBuilder::createIteratorLoop), after the mapping for that
+ // region has been dropped below via forgetMapping, so re-deriving a
+ // fresh IteratorInfo from `itersOp` at that point would fail to find
+ // the lb/ub/step values in the (already cleared) ModuleTranslation
+ // value map.
+ seg.GenEntry = [&moduleTranslation, itersOp, mapInfoOp, sizeVal,
+ iterInfo](llvm::IRBuilderBase &b,
+ llvm::Value *linearIV) mutable
+ -> llvm::SmallVector<llvm::Value *, 3> {
+ mlir::Block &iteratorRegionBlock = itersOp.getRegion().front();
+ if (failed(convertIteratorRegion(
+ linearIV, iterInfo, iteratorRegionBlock, b, moduleTranslation)))
+ return {};
+ // `mapInfoOp` (the omp.yield operand) has no translated value of its
+ // own; its runtime address is its var_ptr operand.
+ llvm::Value *addr =
+ moduleTranslation.lookupValue(mapInfoOp.getVarPtr());
+ moduleTranslation.forgetMapping(itersOp.getRegion());
+ return {addr, addr, sizeVal};
+ };
+ }
+
return combinedInfo;
};
@@ -7770,6 +7852,11 @@ emitUserDefinedMapper(Operation *op, llvm::IRBuilderBase &builder,
llvm::Expected<llvm::Function *> newFn = ompBuilder->emitUserDefinedMapper(
genMapInfoCB, varType, mapperFuncName, customMapperCB,
/*PreserveMemberOfFlags=*/true);
+ // Only now that emitUserDefinedMapper has fully finished (including running
+ // any iterator-produced DynamicSegment::GenEntry callbacks, which look up
+ // values defined in declMapperOp's region) is it safe to drop the mapping,
+ // so that the same region can be processed multiple times.
+ moduleTranslation.forgetMapping(declMapperOp.getRegion());
if (!newFn)
return newFn.takeError();
if ([[maybe_unused]] llvm::Function *mappedFunc =
diff --git a/mlir/test/Target/LLVMIR/openmp-iterator.mlir b/mlir/test/Target/LLVMIR/openmp-iterator.mlir
index ecd45b4d49618..56bc1fa3fc421 100644
--- a/mlir/test/Target/LLVMIR/openmp-iterator.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-iterator.mlir
@@ -1,6 +1,7 @@
// RUN: split-file %s %t
// RUN: mlir-translate --mlir-to-llvmir %t/host.mlir | FileCheck %s --check-prefix=CHECK
// RUN: mlir-translate --mlir-to-llvmir %t/target.mlir | FileCheck %s --check-prefix=TARGET
+// RUN: mlir-translate --mlir-to-llvmir %t/mapper.mlir | FileCheck %s --check-prefix=MAPPER
//--- host.mlir
@@ -513,3 +514,64 @@ module attributes {omp.is_target_device = false, omp.target_triples = ["amdgcn-a
// TARGET: call void @.omp_target_task_proxy_func
// TARGET: call void @__kmpc_omp_task_complete_if0
// TARGET: tail call void @free(ptr %[[DEP_ARR]])
+
+//--- mapper.mlir
+
+// --------------------------------------------------------------------
+// Map clause with iterator modifier inside a declare_mapper body
+// --------------------------------------------------------------------
+
+// The mapper's own `iterator(i = 0:10)` (11 total trips) is emitted as a
+// dynamic segment: each iteration pushes one component via
+// __tgt_push_mapper_component from inside a generated loop nested in the
+// mapper function's usual per-array-element loop, rather than being added
+// to the static offload arrays.
+module attributes {omp.target_triples = ["amdgcn-amd-amdhsa"]} {
+ omp.declare_mapper @mapper_with_iterator : !llvm.struct<"mapper_type", (i32)> {
+ ^bb0(%arg: !llvm.ptr):
+ %c0 = llvm.mlir.constant(0 : i64) : i64
+ %c10 = llvm.mlir.constant(10 : i64) : i64
+ %c1 = llvm.mlir.constant(1 : i64) : i64
+ %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
+ %m = omp.map.info var_ptr(%arg : !llvm.ptr, !llvm.struct<"mapper_type", (i32)>) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+ omp.yield(%m : !llvm.ptr)
+ } -> !omp.iterated<!llvm.ptr>
+ omp.declare_mapper.info map_iterated(%it : !omp.iterated<!llvm.ptr>)
+ }
+
+ llvm.func @target_data_mapper_iterator(%addr : !llvm.ptr) {
+ %map = omp.map.info var_ptr(%addr : !llvm.ptr, !llvm.struct<"mapper_type", (i32)>) map_clauses(tofrom) capture(ByRef) mapper(@mapper_with_iterator) -> !llvm.ptr {name = ""}
+ omp.target_data map_entries(%map : !llvm.ptr) {}
+ llvm.return
+ }
+}
+
+// MAPPER-LABEL: define void @target_data_mapper_iterator
+// MAPPER: call void @__tgt_target_data_begin_mapper(ptr @{{.*}}, i64 -1, i32 1, ptr %{{.*}}, ptr %{{.*}}, ptr @.offload_sizes, ptr @.offload_maptypes, ptr @.offload_mapnames, ptr %.offload_mappers)
+// MAPPER: call void @__tgt_target_data_end_mapper(ptr @{{.*}}, i64 -1, i32 1, ptr %{{.*}}, ptr %{{.*}}, ptr @.offload_sizes, ptr @.offload_maptypes, ptr @.offload_mapnames, ptr %.offload_mappers)
+
+// MAPPER-LABEL: define internal void @.omp_mapper.mapper_with_iterator
+// MAPPER-SAME: (ptr noundef %[[HANDLE:.*]], ptr noundef %{{.*}}, ptr noundef %{{.*}}, i64 noundef %{{.*}}, i64 noundef %{{.*}}, ptr noundef %{{.*}})
+//
+// Per-array-element loop wraps the dynamically-sized mapper iterator loop
+// (trip count = 11, from the mapper's `iterator(i = 0:10)`).
+// MAPPER: omp.arraymap.body:
+// MAPPER: call i64 @__tgt_mapper_num_components(ptr %[[HANDLE]])
+// MAPPER: br label %omp_mapper.iterator.preheader
+//
+// MAPPER: omp_mapper.iterator.header:
+// MAPPER: %[[IV:.*]] = phi i64 [ 0, %omp_mapper.iterator.preheader ], [ %[[NEXT:.*]], %omp_mapper.iterator.inc ]
+// MAPPER: omp_mapper.iterator.cond:
+// MAPPER: %[[CMP:.*]] = icmp ult i64 %[[IV]], 11
+// MAPPER: br i1 %[[CMP]], label %omp_mapper.iterator.body, label %omp_mapper.iterator.exit
+//
+// Body: push one component per iteration via __tgt_push_mapper_component,
+// using the current array element as both base and begin address, and the
+// mapper's own var_ptr type size (4 bytes for the i32 field) as the size.
+// MAPPER: omp_mapper.iterator.body:
+// MAPPER: call void @__tgt_push_mapper_component(ptr %[[HANDLE]], ptr %omp.arraymap.ptrcurrent, ptr %omp.arraymap.ptrcurrent, i64 4, i64 %{{.*}}, ptr null)
+// MAPPER: br label %omp_mapper.iterator.inc
+//
+// MAPPER: omp_mapper.iterator.inc:
+// MAPPER: %[[NEXT]] = add nuw i64 %[[IV]], 1
+// MAPPER: br label %omp_mapper.iterator.header
diff --git a/mlir/test/Target/LLVMIR/openmp-todo.mlir b/mlir/test/Target/LLVMIR/openmp-todo.mlir
index 2aedee1402798..d64deaa1bb469 100644
--- a/mlir/test/Target/LLVMIR/openmp-todo.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-todo.mlir
@@ -815,27 +815,3 @@ llvm.func @target_data_map_iterator(%addr : !llvm.ptr) {
omp.target_data map_iterated(%it : !omp.iterated<!llvm.ptr>) {}
llvm.return
}
-
-// -----
-
-module attributes {omp.target_triples = ["amdgcn-amd-amdhsa"]} {
- omp.declare_mapper @mapper_with_iterator : !llvm.struct<"mapper_type", (i32)> {
- ^bb0(%arg: !llvm.ptr):
- %c0 = llvm.mlir.constant(0 : i64) : i64
- %c10 = llvm.mlir.constant(10 : i64) : i64
- %c1 = llvm.mlir.constant(1 : i64) : i64
- %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
- %m = omp.map.info var_ptr(%arg : !llvm.ptr, !llvm.struct<"mapper_type", (i32)>) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
- omp.yield(%m : !llvm.ptr)
- } -> !omp.iterated<!llvm.ptr>
- // expected-error at below {{not yet implemented: Unhandled clause map/motion clause with iterator modifier in omp.declare_mapper.info operation}}
- omp.declare_mapper.info map_iterated(%it : !omp.iterated<!llvm.ptr>)
- }
-
- llvm.func @target_data_mapper_iterator(%addr : !llvm.ptr) {
- %map = omp.map.info var_ptr(%addr : !llvm.ptr, !llvm.struct<"mapper_type", (i32)>) map_clauses(tofrom) capture(ByRef) mapper(@mapper_with_iterator) -> !llvm.ptr {name = ""}
- // expected-error at below {{LLVM Translation failed for operation: omp.target_data}}
- omp.target_data map_entries(%map : !llvm.ptr) {}
- llvm.return
- }
-}
More information about the flang-commits
mailing list