[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 00:59:53 PDT 2026


https://github.com/ravurvi20 updated https://github.com/llvm/llvm-project/pull/215485

>From 0ad9bede9cd3fed5332d755aacca6d18b5db58ed 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    | 123 +++++++++-
 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, 525 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..a798a72a8a2c5 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -2043,13 +2043,126 @@ 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