[llvm-branch-commits] [flang] [Flang][OpenMP] Support iterator modifier in map and motion clauses (PR #197757)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jun 5 14:55:51 PDT 2026


https://github.com/chichunchen updated https://github.com/llvm/llvm-project/pull/197757

>From 4183355619add135222c3c42570666f14525347a Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Tue, 31 Mar 2026 14:00:48 -0500
Subject: [PATCH] [Flang][OpenMP] Support iterator modifiers in map and motion
 clauses

Support iterated array elements and array sections in map and motion clauses for
target data, target enter data, target exit data, and target update constructs.

Preserve mapper resolution for iterated entries, including explicit mappers,
user-defined default mappers, declare mapper entries, and implicit default
mappers.

This PR stacked on top of #197047 and #197752.

This patch is part of the feature work for #188061.

Assisted with copilot.
---
 flang/lib/Lower/OpenMP/ClauseProcessor.cpp    | 110 +++-
 flang/lib/Lower/OpenMP/Utils.cpp              | 183 +++++++
 flang/lib/Lower/OpenMP/Utils.h                |  12 +
 .../Optimizer/OpenMP/MapInfoFinalization.cpp  |  15 +
 .../OpenMP/Todo/declare-mapper-iterator.f90   |  11 -
 .../OpenMP/Todo/from-iterator-modifier.f90    |   8 -
 .../OpenMP/Todo/to-iterator-modifier.f90      |   8 -
 .../Lower/OpenMP/declare-mapper-iterator.f90  |  25 +
 flang/test/Lower/OpenMP/motion-iterator.f90   | 507 ++++++++++++++++++
 9 files changed, 839 insertions(+), 40 deletions(-)
 delete mode 100644 flang/test/Lower/OpenMP/Todo/declare-mapper-iterator.f90
 delete mode 100644 flang/test/Lower/OpenMP/Todo/from-iterator-modifier.f90
 delete mode 100644 flang/test/Lower/OpenMP/Todo/to-iterator-modifier.f90
 create mode 100644 flang/test/Lower/OpenMP/declare-mapper-iterator.f90
 create mode 100644 flang/test/Lower/OpenMP/motion-iterator.f90

diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 5948e597aa2de..0edf0ecf007cc 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -299,6 +299,54 @@ static mlir::Value buildIteratorOp(Fortran::lower::AbstractConverter &converter,
   return itOp.getResult();
 }
 
+// Build an omp.iterator op that yields a MapInfoOp for a single
+// iterated object.
+static mlir::Value buildIteratedMapEntry(
+    Fortran::lower::AbstractConverter &converter,
+    Fortran::semantics::SemanticsContext &semaCtx, mlir::Location loc,
+    llvm::ArrayRef<IteratorRange> iteratorRanges, const omp::Object &object,
+    llvm::StringRef mapperIdName, mlir::omp::ClauseMapFlags mapTypeBits,
+    llvm::omp::Directive directive) {
+  mlir::Type ptrTy =
+      mlir::LLVM::LLVMPointerType::get(&converter.getMLIRContext());
+  mlir::Type iterTy =
+      mlir::omp::IteratedType::get(&converter.getMLIRContext(), ptrTy);
+
+  return buildIteratorOp(
+      converter, loc, iterTy, iteratorRanges,
+      [&](fir::FirOpBuilder &builder, mlir::Location loc,
+          llvm::ArrayRef<mlir::Value> /*ivs*/) -> mlir::Value {
+        lower::StatementContext iterStmtCtx;
+        std::optional<IteratorMapInfo> mapInfo = genIteratorMapInfo(
+            converter, builder, semaCtx, iterStmtCtx, object, loc);
+        if (!mapInfo)
+          TODO(loc, "object type not supported by iterator modifier");
+
+        // Use the array base as var_ptr with bounds so the runtime can
+        // associate this mapping with whole-array mappings via the base
+        // address.
+        mlir::Value baseAddr = mapInfo->entity.getBase();
+        if (mlir::isa<fir::BaseBoxType>(baseAddr.getType()))
+          baseAddr = fir::BoxAddrOp::create(builder, loc, baseAddr);
+        auto ptrLike =
+            llvm::cast<mlir::omp::PointerLikeType>(baseAddr.getType());
+        mlir::TypeAttr varType = mlir::TypeAttr::get(ptrLike.getElementType());
+        mlir::FlatSymbolRefAttr mapperId =
+            resolveMapperId(converter, loc, object, mapperIdName, mapTypeBits,
+                            directive, /*hasParentObj=*/false);
+        mlir::omp::MapInfoOp mapOp = mlir::omp::MapInfoOp::create(
+            builder, loc, ptrTy, baseAddr, varType,
+            builder.getAttr<mlir::omp::ClauseMapFlagsAttr>(mapTypeBits),
+            builder.getAttr<mlir::omp::VariableCaptureKindAttr>(
+                mlir::omp::VariableCaptureKind::ByRef),
+            /*varPtrPtr=*/mlir::Value{}, /*varPtrPtrType=*/nullptr,
+            /*members=*/mlir::ValueRange{},
+            /*membersIndex=*/mlir::ArrayAttr{}, mapInfo->bounds, mapperId,
+            builder.getStringAttr(""), builder.getBoolAttr(false));
+        return mapOp.getResult();
+      });
+}
+
 template <typename ClauseTuple>
 static void collectIteratorIVs(
     const ClauseTuple &clause, Fortran::lower::AbstractConverter &converter,
@@ -1899,14 +1947,30 @@ bool ClauseProcessor::processMap(
       }
     }
 
-    if (iterator)
-      TODO(currentLocation,
-           "Support for iterator modifiers is not implemented yet");
+    if (iterator) {
+      llvm::SmallVector<IteratorRange> iteratorRanges;
+      llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4> ivSyms;
+      collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms);
 
-    processMapObjects(stmtCtx, clauseLocation,
-                      std::get<omp::ObjectList>(clause.t), mapTypeBits,
-                      parentMemberIndices, result.mapVars, *ptrMapObjects,
-                      mapperIdName, /*isMotionModifier=*/false, directive);
+      for (const omp::Object &object : objects) {
+        if (hasIteratorIVReference(object, ivSyms)) {
+          result.mapIterated.push_back(buildIteratedMapEntry(
+              converter, semaCtx, clauseLocation, iteratorRanges, object,
+              mapperIdName, mapTypeBits, directive));
+        } else {
+          omp::ObjectList singleObj{object};
+          processMapObjects(stmtCtx, clauseLocation, singleObj, mapTypeBits,
+                            parentMemberIndices, result.mapVars, *ptrMapObjects,
+                            mapperIdName, /*isMotionModifier=*/false,
+                            directive);
+        }
+      }
+    } else {
+      processMapObjects(stmtCtx, clauseLocation,
+                        std::get<omp::ObjectList>(clause.t), mapTypeBits,
+                        parentMemberIndices, result.mapVars, *ptrMapObjects,
+                        mapperIdName, /*isMotionModifier=*/false, directive);
+    }
   };
 
   bool clauseFound = findRepeatableClause<omp::clause::Map>(process);
@@ -1935,12 +1999,32 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
     // Support motion modifiers: iterator.
     std::string mapperIdName = getMapperIdentifier(converter, mapper);
 
-    if (iterator)
-      TODO(clauseLocation, "Iterator modifier is not supported yet");
-
-    processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
-                      parentMemberIndices, result.mapVars, mapObjects,
-                      mapperIdName, /*isMotionModifier=*/true);
+    if (iterator) {
+      // Iterator modifier present: route each object to iterated or plain path.
+      llvm::SmallVector<IteratorRange> iteratorRanges;
+      llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4> ivSyms;
+      collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms);
+
+      for (const omp::Object &object : objects) {
+        if (hasIteratorIVReference(object, ivSyms)) {
+          result.mapIterated.push_back(buildIteratedMapEntry(
+              converter, semaCtx, clauseLocation, iteratorRanges, object,
+              mapperIdName, mapTypeBits,
+              llvm::omp::Directive::OMPD_target_update));
+        } else {
+          omp::ObjectList singleObj{object};
+          processMapObjects(stmtCtx, clauseLocation, singleObj, mapTypeBits,
+                            parentMemberIndices, result.mapVars, mapObjects,
+                            mapperIdName, /*isMotionModifier=*/true,
+                            llvm::omp::Directive::OMPD_target_update);
+        }
+      }
+    } else {
+      processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
+                        parentMemberIndices, result.mapVars, mapObjects,
+                        mapperIdName, /*isMotionModifier=*/true,
+                        llvm::omp::Directive::OMPD_target_update);
+    }
   };
 
   bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn);
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index 9ff462f402759..08e7ded996ad6 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -1283,6 +1283,189 @@ mlir::Value genIteratorCoordinate(Fortran::lower::AbstractConverter &converter,
                                   /*typeparams=*/mlir::ValueRange{});
 }
 
+// Lower the stable base entity for an iterator map/motion locator.
+//
+// MapInfoOp stores this base in var_ptr and represents the iterator-selected
+// elements or sections with map bounds. For component array locators such as
+// v%a(i) or v%a(i:i+1), return v%a so each iteration maps offsets from the
+// same base.
+static std::optional<hlfir::Entity>
+getIteratorMapEntity(Fortran::lower::AbstractConverter &converter,
+                     fir::FirOpBuilder &builder,
+                     Fortran::semantics::SemanticsContext &semaCtx,
+                     Fortran::lower::StatementContext &stmtCtx,
+                     const omp::Object &object, mlir::Location loc) {
+  const semantics::Symbol *sym = object.sym();
+  assert(sym && "expected symbol for iterator object");
+
+  if (!sym->owner().IsDerivedType()) {
+    // Keep the same base address as ordinary map lowering for non-component
+    // objects.
+    fir::factory::AddrAndBoundsInfo info =
+        Fortran::lower::getDataOperandBaseAddr(converter, builder, *sym, loc,
+                                               /*unwrapFirBox=*/false);
+    return hlfir::Entity{info.addr};
+  }
+
+  const std::optional<ExprTy> &ref = object.ref();
+  if (!ref)
+    return std::nullopt;
+
+  auto arrayRef = Fortran::lower::detail::getRef<evaluate::ArrayRef>(*ref);
+  if (!arrayRef)
+    return std::nullopt;
+
+  evaluate::ExpressionAnalyzer ea{semaCtx};
+  std::optional<ExprTy> arrayBase;
+  const evaluate::NamedEntity &base = arrayRef->base();
+  // Component array references carry the array base separately from the
+  // subscript list. Lower that base, e.g. v%a in v%a(i), and leave the
+  // subscript-dependent part to map bounds.
+  if (const semantics::SymbolRef *symRef = base.UnwrapSymbolRef())
+    arrayBase = ea.Designate(evaluate::DataRef{*symRef});
+  else if (const evaluate::Component *component = base.UnwrapComponent())
+    arrayBase = ea.Designate(evaluate::DataRef{*component});
+  else
+    llvm_unreachable("unexpected NamedEntity");
+
+  assert(arrayBase);
+  // Preserve mutable-box lowering for allocatable and pointer bases; MapInfoOp
+  // still records the address returned by the lowered base expression.
+  fir::ExtendedValue dataExv;
+  if (semantics::IsAllocatableOrPointer(base.GetLastSymbol()))
+    dataExv = converter.genExprMutableBox(loc, *arrayBase);
+  else
+    dataExv = converter.genExprAddr(loc, *arrayBase, stmtCtx);
+  return hlfir::Entity{fir::getBase(dataExv)};
+}
+
+// Build normalized map bounds for an iterator-dependent map/motion object.
+// MapInfoOp keeps the array base address as var_ptr; these bounds describe the
+// selected element or contiguous array section for each dimension.
+//
+// Examples:
+//   a(i)     -> lower_bound == upper_bound == i - base lower bound
+//   a(i:i+1) -> lower_bound == i - base lower bound,
+//               upper_bound == i + 1 - base lower bound
+static std::optional<llvm::SmallVector<mlir::Value>>
+genIteratorMapBounds(Fortran::lower::AbstractConverter &converter,
+                     hlfir::Entity entity, const omp::Object &object,
+                     Fortran::lower::StatementContext &stmtCtx,
+                     mlir::Location loc) {
+  const std::optional<ExprTy> &ref = object.ref();
+  assert(ref && "expected iterator-dependent object to have a reference");
+
+  std::optional<Fortran::evaluate::DataRef> dataRef =
+      Fortran::evaluate::ExtractDataRef(*ref);
+  if (!dataRef)
+    return std::nullopt;
+  const auto *arrayRef = std::get_if<Fortran::evaluate::ArrayRef>(&dataRef->u);
+  if (!arrayRef || arrayRef->subscript().empty())
+    return std::nullopt;
+
+  auto &builder = converter.getFirOpBuilder();
+  using SubscriptExpr =
+      Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>;
+  mlir::Type idxTy = builder.getIndexType();
+  mlir::Type boundTy = builder.getType<mlir::omp::MapBoundsType>();
+  mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
+  mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
+
+  auto convertToIndex = [&](mlir::Value value) -> mlir::Value {
+    if (value.getType().isIndex())
+      return value;
+    return fir::ConvertOp::create(builder, loc, idxTy, value);
+  };
+
+  auto lowerSubscriptToIndex = [&](const SubscriptExpr &expr) -> mlir::Value {
+    mlir::Value value = fir::getBase(createSomeExtendedExpression(
+        loc, converter, toEvExpr(expr), converter.getSymbolMap(), stmtCtx));
+    return convertToIndex(value);
+  };
+
+  llvm::SmallVector<mlir::Value> bounds;
+  bounds.reserve(arrayRef->subscript().size());
+  // Translate each Fortran subscript into an OpenMP map bound for the
+  // corresponding array dimension.
+  for (const auto &[dim, subscript] : llvm::enumerate(arrayRef->subscript())) {
+    mlir::Value baseLb =
+        convertToIndex(hlfir::genLBound(loc, builder, entity, dim));
+    mlir::Value extent =
+        convertToIndex(hlfir::genExtent(loc, builder, entity, dim));
+    mlir::Value lbound;
+    mlir::Value ubound;
+
+    if (const auto *triplet =
+            std::get_if<Fortran::evaluate::Triplet>(&subscript.u)) {
+      // Triplet subscripts map a section. Missing lower/upper bounds select the
+      // whole dimension, which normalizes to 0/extent-1.
+      if (std::optional<SubscriptExpr> lowerBound = triplet->lower()) {
+        mlir::Value lower = lowerSubscriptToIndex(*lowerBound);
+        lbound = mlir::arith::SubIOp::create(builder, loc, lower, baseLb);
+      } else {
+        lbound = zero;
+      }
+
+      if (std::optional<SubscriptExpr> upperBound = triplet->upper()) {
+        mlir::Value upper = lowerSubscriptToIndex(*upperBound);
+        ubound = mlir::arith::SubIOp::create(builder, loc, upper, baseLb);
+      } else {
+        ubound = mlir::arith::SubIOp::create(builder, loc, extent, one);
+      }
+
+      // Sema only rejects statically-known non-positive strides, so valid
+      // OpenMP may still reach here with a positive non-unit or dynamic stride.
+      std::optional<std::int64_t> stride =
+          Fortran::evaluate::ToInt64(triplet->GetStride());
+      if (!stride || *stride != 1)
+        TODO(loc, "iterator modifier with non-unit array section stride");
+    } else {
+      // Not handling vector subscripts for now.
+      if (subscript.Rank() > 0)
+        return std::nullopt;
+
+      const auto *indirect =
+          std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>(
+              &subscript.u);
+      assert(indirect && "expected non-triplet subscript");
+
+      // Scalar subscripts map one element, so lower and upper are identical.
+      mlir::Value index = lowerSubscriptToIndex(indirect->value());
+      lbound = mlir::arith::SubIOp::create(builder, loc, index, baseLb);
+      ubound = lbound;
+    }
+
+    mlir::Value bound = mlir::omp::MapBoundsOp::create(
+        builder, loc, boundTy, lbound, ubound, extent, /*stride=*/one,
+        /*stride_in_bytes=*/false, /*start_idx=*/baseLb);
+    bounds.push_back(bound);
+  }
+
+  return bounds;
+}
+
+// Lower an iterated map/motion object to its stable base entity and
+// normalized bounds. The entity serves as var_ptr in MapInfoOp, and the bounds
+// describe the iterator-selected element or section.
+std::optional<IteratorMapInfo>
+genIteratorMapInfo(Fortran::lower::AbstractConverter &converter,
+                   fir::FirOpBuilder &builder,
+                   Fortran::semantics::SemanticsContext &semaCtx,
+                   Fortran::lower::StatementContext &stmtCtx,
+                   const omp::Object &object, mlir::Location loc) {
+  std::optional<hlfir::Entity> entity =
+      getIteratorMapEntity(converter, builder, semaCtx, stmtCtx, object, loc);
+  if (!entity)
+    return std::nullopt;
+
+  std::optional<llvm::SmallVector<mlir::Value>> bounds =
+      genIteratorMapBounds(converter, *entity, object, stmtCtx, loc);
+  if (!bounds)
+    return std::nullopt;
+
+  return IteratorMapInfo{*entity, std::move(*bounds)};
+}
+
 } // namespace omp
 } // namespace lower
 } // namespace Fortran
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index 59b904526d6ae..95b1263fdadb8 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -250,6 +250,18 @@ resolveMapperId(Fortran::lower::AbstractConverter &converter,
                 mlir::omp::ClauseMapFlags mapTypeBits,
                 llvm::omp::Directive directive, bool hasParentObj);
 
+struct IteratorMapInfo {
+  hlfir::Entity entity;
+  llvm::SmallVector<mlir::Value> bounds;
+};
+
+std::optional<IteratorMapInfo>
+genIteratorMapInfo(Fortran::lower::AbstractConverter &converter,
+                   fir::FirOpBuilder &builder,
+                   Fortran::semantics::SemanticsContext &semaCtx,
+                   Fortran::lower::StatementContext &stmtCtx,
+                   const omp::Object &object, mlir::Location loc);
+
 std::optional<llvm::SmallVector<mlir::Value>> getIteratorElementIndices(
     Fortran::lower::AbstractConverter &converter, const omp::Object &object,
     Fortran::lower::StatementContext &stmtCtx, mlir::Location loc);
diff --git a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
index d4b343de988f2..1f5e264e3db67 100644
--- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
+++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
@@ -723,6 +723,21 @@ class MapInfoFinalizationPass
 
       if (auto mapUser = llvm::dyn_cast<mlir::omp::MapInfoOp>(user))
         return getFirstTargetUser(mapUser);
+
+      // MapInfoOp inside an omp.iterator body is yielded back to the iterator,
+      // whose result is used by the target op.
+      if (llvm::isa<mlir::omp::YieldOp>(user)) {
+        if (auto iterOp = user->getParentOfType<mlir::omp::IteratorOp>()) {
+          for (auto *iterUser : iterOp->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/Todo/from-iterator-modifier.f90 b/flang/test/Lower/OpenMP/Todo/from-iterator-modifier.f90
deleted file mode 100644
index 973d1d1d76ba4..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/from-iterator-modifier.f90
+++ /dev/null
@@ -1,8 +0,0 @@
-!RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-
-!CHECK: not yet implemented: Iterator modifier is not supported yet
-subroutine f00(x)
-  integer :: x(10)
-  !$omp target update from(iterator(i = 1:2): x(i))
-end
diff --git a/flang/test/Lower/OpenMP/Todo/to-iterator-modifier.f90 b/flang/test/Lower/OpenMP/Todo/to-iterator-modifier.f90
deleted file mode 100644
index a587373bf183a..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/to-iterator-modifier.f90
+++ /dev/null
@@ -1,8 +0,0 @@
-!RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-
-!CHECK: not yet implemented: Iterator modifier is not supported yet
-subroutine f00(x)
-  integer :: x(10)
-  !$omp target update to(iterator(i = 1:2): x(i))
-end
diff --git a/flang/test/Lower/OpenMP/declare-mapper-iterator.f90 b/flang/test/Lower/OpenMP/declare-mapper-iterator.f90
new file mode 100644
index 0000000000000..ff8de637a2ab0
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-mapper-iterator.f90
@@ -0,0 +1,25 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s | FileCheck %s
+
+subroutine declare_mapper_iterator(arg)
+  type :: s
+    integer :: a(10)
+  end type
+  type(s) :: arg(:)
+
+  !$omp declare mapper(m: s :: v) map(iterator(i = 1:10): v%a(i))
+end
+
+! CHECK-LABEL: omp.declare_mapper
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_iteratorTs{{.*}}>):
+! CHECK:   %[[DECL:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFdeclare_mapper_iteratorEv"}
+! CHECK:   %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:     %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:     %[[A:.*]] = hlfir.designate %[[DECL]]#0{"a"} {{.*}} : (!fir.ref<!fir.type<_QFdeclare_mapper_iteratorTs{{.*}}>>, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
+! CHECK:     %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64
+! CHECK:     %[[IV_IDX:.*]] = fir.convert %[[IV_I64]] : (i64) -> index
+! CHECK:     %[[LB:.*]] = arith.subi %[[IV_IDX]], %{{.*}} : index
+! CHECK:     %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%[[LB]] : index) upper_bound(%[[LB]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:     %[[MAP:.*]] = omp.map.info var_ptr(%[[A]] : !fir.ref<!fir.array<10xi32>>, !fir.array<10xi32>) map_clauses(tofrom) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:     omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK:   } -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
diff --git a/flang/test/Lower/OpenMP/motion-iterator.f90 b/flang/test/Lower/OpenMP/motion-iterator.f90
new file mode 100644
index 0000000000000..a4c98d29d1914
--- /dev/null
+++ b/flang/test/Lower/OpenMP/motion-iterator.f90
@@ -0,0 +1,507 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s | FileCheck %s
+
+! Tests for the iterator modifier on map and to/from motion clauses.
+
+!===============================================================================
+! target update
+!===============================================================================
+
+subroutine target_update_to_simple()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_to_simple()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_to_simpleEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:   %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64
+! CHECK:   %[[IV_IDX:.*]] = fir.convert %[[IV_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[IV_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%[[LB]] : index) upper_bound(%[[LB]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_from_simple()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target update from(iterator(i = 1:n): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_from_simple()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_from_simpleEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(from) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_to_section()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n-1): a(i:i+1))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_to_section()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_to_sectionEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:   %[[LB_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64
+! CHECK:   %[[LB_IDX:.*]] = fir.convert %[[LB_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[LB_IDX]], %{{.*}} : index
+! CHECK:   %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[UB_EXPR:.*]] = arith.addi %[[IV_I32]], %[[C1_I32]] : i32
+! CHECK:   %[[UB_I64:.*]] = fir.convert %[[UB_EXPR]] : (i32) -> i64
+! CHECK:   %[[UB_IDX:.*]] = fir.convert %[[UB_I64]] : (i64) -> index
+! CHECK:   %[[UB:.*]] = arith.subi %[[UB_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%[[LB]] : index) upper_bound(%[[UB]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_2d()
+  integer, parameter :: n = 4, m = 6
+  integer :: a(n, m)
+  integer :: i, j
+
+  !$omp target update to(iterator(i = 1:n, j = 1:m): a(i, j))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_2d()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_2dEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: index, %[[IV1:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}, {{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV0_I32:.*]] = fir.convert %[[IV0]] : (index) -> i32
+! CHECK:   %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32
+! CHECK:   %[[IV0_I64:.*]] = fir.convert %[[IV0_I32]] : (i32) -> i64
+! CHECK:   %[[IV0_IDX:.*]] = fir.convert %[[IV0_I64]] : (i64) -> index
+! CHECK:   %[[LB0:.*]] = arith.subi %[[IV0_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS0:.*]] = omp.map.bounds lower_bound(%[[LB0]] : index) upper_bound(%[[LB0]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[IV1_I64:.*]] = fir.convert %[[IV1_I32]] : (i32) -> i64
+! CHECK:   %[[IV1_IDX:.*]] = fir.convert %[[IV1_I64]] : (i64) -> index
+! CHECK:   %[[LB1:.*]] = arith.subi %[[IV1_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS1:.*]] = omp.map.bounds lower_bound(%[[LB1]] : index) upper_bound(%[[LB1]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<4x6xi32>>, !fir.array<4x6xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS0]], %[[BOUNDS1]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_step()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n:2): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_step()
+! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32
+! CHECK: %[[LB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index
+! CHECK: %[[UB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index
+! CHECK: %[[C2_I32:.*]] = arith.constant 2 : i32
+! CHECK: %[[STEP:.*]] = fir.convert %[[C2_I32]] : (i32) -> index
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_negative_step()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target update to(iterator(i = n:1:-1): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_negative_step()
+! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32
+! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[LB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index
+! CHECK: %[[UB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index
+! CHECK: %[[CM1_I32:.*]] = arith.constant -1 : i32
+! CHECK: %[[STEP:.*]] = fir.convert %[[CM1_I32]] : (i32) -> index
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_multi_obj()
+  integer, parameter :: n = 16
+  integer :: a(n), b(n)
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i), b(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_multi_obj()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_multi_objEa"}
+! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_multi_objEb"}
+! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS1:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP1:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS1]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP1]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS2:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP2:.*]] = omp.map.info var_ptr(%[[B]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS2]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP2]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT1]], %[[IT2]] : !omp.iterated<!llvm.ptr>, !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_mixed_same_clause()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target update to(iterator(i = 2:n:2): a(1), a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_mixed_same_clause()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_mixed_same_clauseEa"}
+! CHECK: %[[MAP_PLAIN:.*]] = omp.map.info var_ptr(%[[A]]#1 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds({{.*}}) -> !fir.ref<!fir.array<16xi32>> {name = "a(1)"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS_IT:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP_IT:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS_IT]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP_IT]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_entries(%[[MAP_PLAIN]] : !fir.ref<!fir.array<16xi32>>) map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_multi_clause()
+  integer, parameter :: n = 8
+  integer :: a(n), b(n)
+  integer :: i, j
+
+  !$omp target update to(iterator(i = 1:n): a(i)) &
+  !$omp&              from(iterator(j = 1:n:2): b(j))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_multi_clause()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_multi_clauseEa"}
+! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_multi_clauseEb"}
+! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS1:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP1:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<8xi32>>, !fir.array<8xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS1]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP1]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS2:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP2:.*]] = omp.map.info var_ptr(%[[B]]#0 : !fir.ref<!fir.array<8xi32>>, !fir.array<8xi32>) map_clauses(from) capture(ByRef) bounds(%[[BOUNDS2]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP2]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT1]], %[[IT2]] : !omp.iterated<!llvm.ptr>, !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_mixed_clauses()
+  integer, parameter :: n = 16
+  integer :: a(n), b(n)
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i)) from(b)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_mixed_clauses()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_mixed_clausesEa"}
+! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_mixed_clausesEb"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS_IT:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP_IT:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS_IT]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP_IT]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]]#1 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(from) capture(ByRef) bounds({{.*}}) -> !fir.ref<!fir.array<16xi32>> {name = "b"}
+! CHECK: omp.target_update map_entries(%[[MAP_B]] : !fir.ref<!fir.array<16xi32>>) map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_mapper()
+  type :: s
+    integer :: a
+  end type
+  type(s) :: x(10)
+  integer :: i
+
+  !$omp declare mapper(m: s :: v) map(to: v%a)
+  !$omp target update to(mapper(m), iterator(i = 1:10): x(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_mapper()
+! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_mapperEx"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[X]]#0 : !fir.ref<!fir.array<10x!fir.type<_QFtarget_update_mapperTs{a:i32}>>>, !fir.array<10x!fir.type<_QFtarget_update_mapperTs{a:i32}>>) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_mapperm) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_default_mapper()
+  type :: s
+    integer :: a
+  end type
+  type(s) :: x
+
+  !$omp declare mapper(s :: v) map(to: v%a)
+  !$omp target update to(x)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_default_mapper()
+! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtarget_update_default_mapperEx"}
+! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[X]]#1 : !fir.ref<!fir.type<_QFtarget_update_default_mapperTs{a:i32}>>, !fir.type<_QFtarget_update_default_mapperTs{a:i32}>) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_default_mappers_omp_default_mapper) -> !fir.ref<!fir.type<_QFtarget_update_default_mapperTs{a:i32}>> {name = "x"}
+! CHECK: omp.target_update map_entries(%[[MAP]] : !fir.ref<!fir.type<_QFtarget_update_default_mapperTs{a:i32}>>)
+
+subroutine target_update_iterated_default_mapper()
+  type :: s
+    integer :: a
+  end type
+  type(s) :: x(10)
+  integer :: i
+
+  !$omp declare mapper(s :: v) map(to: v%a)
+  !$omp target update to(iterator(i = 1:10): x(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_iterated_default_mapper()
+! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_iterated_default_mapperEx"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[X]]#0 : !fir.ref<!fir.array<10x!fir.type<_QFtarget_update_iterated_default_mapperTs{a:i32}>>>, !fir.array<10x!fir.type<_QFtarget_update_iterated_default_mapperTs{a:i32}>>) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_iterated_default_mappers_omp_default_mapper) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+!===============================================================================
+! target data
+!===============================================================================
+
+subroutine target_data_section()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target data map(iterator(i = 1:n-1), tofrom: a(i:i+1))
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_data_section()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_data_sectionEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:   %[[LB_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64
+! CHECK:   %[[LB_IDX:.*]] = fir.convert %[[LB_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[LB_IDX]], %{{.*}} : index
+! CHECK:   %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[UB_EXPR:.*]] = arith.addi %[[IV_I32]], %[[C1_I32]] : i32
+! CHECK:   %[[UB_I64:.*]] = fir.convert %[[UB_EXPR]] : (i32) -> i64
+! CHECK:   %[[UB_IDX:.*]] = fir.convert %[[UB_I64]] : (i64) -> index
+! CHECK:   %[[UB:.*]] = arith.subi %[[UB_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%[[LB]] : index) upper_bound(%[[UB]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(tofrom) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_data_mapper()
+  type :: s
+    integer :: a
+  end type
+  type(s) :: x(10)
+  integer :: i
+
+  !$omp declare mapper(m: s :: v) map(to: v%a)
+  !$omp target data map(mapper(m), iterator(i = 1:10), tofrom: x(i))
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_data_mapper()
+! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_data_mapperEx"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[X]]#0 : !fir.ref<!fir.array<10x!fir.type<_QFtarget_data_mapperTs{a:i32}>>>, !fir.array<10x!fir.type<_QFtarget_data_mapperTs{a:i32}>>) map_clauses(tofrom) capture(ByRef) mapper(@_QQFtarget_data_mapperm) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_data_default_mapper()
+  type :: s
+    integer :: a
+  end type
+  type(s) :: x(10)
+  integer :: i
+
+  !$omp declare mapper(s :: v) map(to: v%a)
+  !$omp target data map(iterator(i = 1:10), tofrom: x(i))
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_data_default_mapper()
+! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_data_default_mapperEx"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[X]]#0 : !fir.ref<!fir.array<10x!fir.type<_QFtarget_data_default_mapperTs{a:i32}>>>, !fir.array<10x!fir.type<_QFtarget_data_default_mapperTs{a:i32}>>) map_clauses(tofrom) capture(ByRef) mapper(@_QQFtarget_data_default_mappers_omp_default_mapper) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_data_implicit_mapper()
+  type :: s
+    integer, allocatable :: a(:)
+  end type
+  type(s) :: x(10)
+  integer :: i
+
+  !$omp target data map(iterator(i = 1:10), tofrom: x(i))
+  !$omp end target data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_data_implicit_mapper()
+! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_data_implicit_mapperEx"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[X]]#0 : !fir.ref<!fir.array<10x!fir.type<_QFtarget_data_implicit_mapperTs{a:!fir.box<!fir.heap<!fir.array<?xi32>>>}>>>, !fir.array<10x!fir.type<_QFtarget_data_implicit_mapperTs{a:!fir.box<!fir.heap<!fir.array<?xi32>>>}>>) map_clauses(tofrom) capture(ByRef) mapper(@{{.*omp_default_mapper}}) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+!===============================================================================
+! target enter data
+!===============================================================================
+
+subroutine target_enter_data_simple()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target enter data map(iterator(i = 1:n), to: a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_enter_data_simple()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_enter_data_simpleEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:   %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64
+! CHECK:   %[[IV_IDX:.*]] = fir.convert %[[IV_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[IV_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%[[LB]] : index) upper_bound(%[[LB]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_enter_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_enter_data_section()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target enter data map(iterator(i = 1:n-2), to: a(i:i+2))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_enter_data_section()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_enter_data_sectionEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:   %[[LB_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64
+! CHECK:   %[[LB_IDX:.*]] = fir.convert %[[LB_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[LB_IDX]], %{{.*}} : index
+! CHECK:   %[[C2_I32:.*]] = arith.constant 2 : i32
+! CHECK:   %[[UB_EXPR:.*]] = arith.addi %[[IV_I32]], %[[C2_I32]] : i32
+! CHECK:   %[[UB_I64:.*]] = fir.convert %[[UB_EXPR]] : (i32) -> i64
+! CHECK:   %[[UB_IDX:.*]] = fir.convert %[[UB_I64]] : (i64) -> index
+! CHECK:   %[[UB:.*]] = arith.subi %[[UB_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%[[LB]] : index) upper_bound(%[[UB]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_enter_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! Expression-based subscript using multiple iterator variables: a((i-1)*m+j)
+! maps a 2D logical iteration space onto a 1D array.
+subroutine target_enter_data_expr_subscript()
+  integer, parameter :: m = 4
+  integer, parameter :: n = m * m
+  integer :: a(n)
+  integer :: i, j
+
+  !$omp target enter data map(iterator(i = 1:m, j = 1:m), to: a((i-1)*m+j))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_enter_data_expr_subscript()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_enter_data_expr_subscriptEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: index, %[[IV1:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}, {{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV0_I32:.*]] = fir.convert %[[IV0]] : (index) -> i32
+! CHECK:   %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32
+! CHECK:   %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[SUB:.*]] = arith.subi %[[IV0_I32]], %[[C1_I32]] : i32
+! CHECK:   %[[NOREASSOC:.*]] = fir.no_reassoc %[[SUB]] : i32
+! CHECK:   %[[MUL:.*]] = arith.muli %{{.*}}, %[[NOREASSOC]] : i32
+! CHECK:   %[[ADD:.*]] = arith.addi %[[MUL]], %[[IV1_I32]] : i32
+! CHECK:   %[[IDX:.*]] = fir.convert %[[ADD]] : (i32) -> i64
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_enter_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+!===============================================================================
+! target exit data
+!===============================================================================
+
+subroutine target_exit_data_simple()
+  integer, parameter :: n = 16
+  integer :: a(n)
+  integer :: i
+
+  !$omp target exit data map(iterator(i = 1:n), from: a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_exit_data_simple()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_exit_data_simpleEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:   %[[IV_I64:.*]] = fir.convert %[[IV_I32]] : (i32) -> i64
+! CHECK:   %[[IV_IDX:.*]] = fir.convert %[[IV_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[IV_IDX]], %{{.*}} : index
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%[[LB]] : index) upper_bound(%[[LB]] : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(from) capture(ByRef) bounds(%[[BOUNDS]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_exit_data map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! Multiple objects with negative step, producing separate iterators.
+subroutine target_exit_data_multi_obj()
+  integer, parameter :: n = 16
+  integer :: a(n), b(n)
+  integer :: i
+
+  !$omp target exit data map(iterator(i = n:1:-1), from: a(i), b(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_exit_data_multi_obj()
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_exit_data_multi_objEa"}
+! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_exit_data_multi_objEb"}
+! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32
+! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[LB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index
+! CHECK: %[[UB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index
+! CHECK: %[[CM1_I32:.*]] = arith.constant -1 : i32
+! CHECK: %[[STEP:.*]] = fir.convert %[[CM1_I32]] : (i32) -> index
+! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) {
+! CHECK:   %[[BOUNDS1:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP1:.*]] = omp.map.info var_ptr(%[[A]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(from) capture(ByRef) bounds(%[[BOUNDS1]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP1]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) {
+! CHECK:   %[[BOUNDS2:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index)
+! CHECK:   %[[MAP2:.*]] = omp.map.info var_ptr(%[[B]]#0 : !fir.ref<!fir.array<16xi32>>, !fir.array<16xi32>) map_clauses(from) capture(ByRef) bounds(%[[BOUNDS2]]) -> !llvm.ptr {name = ""}
+! CHECK:   omp.yield(%[[MAP2]] : !llvm.ptr)
+! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_exit_data map_iterated(%[[IT1]], %[[IT2]] : !omp.iterated<!llvm.ptr>, !omp.iterated<!llvm.ptr>)



More information about the llvm-branch-commits mailing list