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

via flang-commits flang-commits at lists.llvm.org
Wed Sep 16 11:01:49 PDT 2026


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

>From a26f60f697df9dc7ef4a992f06e655104ad036b9 Mon Sep 17 00:00:00 2001
From: "Chi-Chun, Chen" <chichun.chen at hpe.com>
Date: Tue, 15 Sep 2026 18:06:24 -0500
Subject: [PATCH] [flang][OpenMP] Lower iterator modifiers in map and motion
 clauses

Add iterator lowering for `target data`, `target enter/exit data`,
`target update`, and map clauses inside `declare mapper`.

For example:

```fortran
!$omp target update to(iterator(i=1:n): a(i), b)
```

Conceptually, this becomes:

```text
entries = omp.iterator(i = 1 through n) {
  yield map_info(base = a, bounds = element i)
}
target_update(iterated_maps = entries, ordinary_maps = [b])
```

Keeping the array base and expressing selections through bounds lets the
same approach handle array elements, sections, and descriptor-backed arrays.

Each locator uses only the iterator dimensions it references:

```text
iterator(i=1:n, j=1:m): a(i), b(j), c(i,j)
                        |     |      |
                        i     j    i x j
```

Supports explicit and default mappers, including mapper declarations
imported from other modules.

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

This patch is part of feature work for #188061.

Assisted with codex.
---
 flang/include/flang/Utils/OpenMP.h            |  29 +-
 flang/lib/Lower/OpenMP/ClauseProcessor.cpp    | 224 ++++-
 flang/lib/Lower/OpenMP/ClauseProcessor.h      |  11 +
 flang/lib/Lower/OpenMP/Clauses.cpp            |  12 +
 flang/lib/Lower/OpenMP/OpenMP.cpp             |   5 +-
 flang/lib/Lower/OpenMP/Utils.cpp              | 187 ++++
 flang/lib/Lower/OpenMP/Utils.h                |  25 +
 .../Optimizer/OpenMP/MapInfoFinalization.cpp  |  29 +-
 flang/lib/Semantics/check-omp-structure.cpp   |  66 +-
 flang/lib/Utils/OpenMP.cpp                    |   4 +-
 .../declare-mapper-iterator-unsupported.f90   |  65 ++
 .../OpenMP/Todo/declare-mapper-iterator.f90   |  11 -
 .../OpenMP/Todo/from-iterator-modifier.f90    |   8 -
 .../OpenMP/Todo/map-iterator-ref-attach.f90   |  81 ++
 ...p-motion-iterator-unsupported-locators.f90 |  77 ++
 .../Lower/OpenMP/Todo/target-map-iterator.f90 |  12 +
 .../OpenMP/Todo/to-iterator-modifier.f90      |   8 -
 .../Lower/OpenMP/declare-mapper-iterator.f90  | 293 ++++++
 flang/test/Lower/OpenMP/depend-iterator.f90   |  84 +-
 flang/test/Lower/OpenMP/iterator-ranges.f90   | 160 +++
 flang/test/Lower/OpenMP/motion-iterator.f90   | 951 ++++++++++++++++++
 flang/test/Lower/OpenMP/task-affinity.f90     |   8 +-
 ...arget-enter-data-temp-descriptor-omp61.f90 |  33 +
 .../target-enter-data-temp-descriptor.f90     |  33 +
 .../Transforms/omp-map-info-finalization.fir  |  62 ++
 mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td |   6 +
 mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp  |  21 +-
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      |  62 +-
 mlir/test/Dialect/OpenMP/invalid.mlir         |  28 -
 mlir/test/Dialect/OpenMP/iterator-ranges.mlir |  33 +
 mlir/test/Target/LLVMIR/openmp-iterator.mlir  | 141 ++-
 .../Target/LLVMIR/openmp-taskwait-depend.mlir |   2 +-
 32 files changed, 2552 insertions(+), 219 deletions(-)
 create mode 100644 flang/test/Lower/OpenMP/Todo/declare-mapper-iterator-unsupported.f90
 delete mode 100644 flang/test/Lower/OpenMP/Todo/declare-mapper-iterator.f90
 delete mode 100644 flang/test/Lower/OpenMP/Todo/from-iterator-modifier.f90
 create mode 100644 flang/test/Lower/OpenMP/Todo/map-iterator-ref-attach.f90
 create mode 100644 flang/test/Lower/OpenMP/Todo/map-motion-iterator-unsupported-locators.f90
 create mode 100644 flang/test/Lower/OpenMP/Todo/target-map-iterator.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/iterator-ranges.f90
 create mode 100644 flang/test/Lower/OpenMP/motion-iterator.f90
 create mode 100644 mlir/test/Dialect/OpenMP/iterator-ranges.mlir

diff --git a/flang/include/flang/Utils/OpenMP.h b/flang/include/flang/Utils/OpenMP.h
index 0cb77e25af947..8c8da53f90863 100644
--- a/flang/include/flang/Utils/OpenMP.h
+++ b/flang/include/flang/Utils/OpenMP.h
@@ -20,12 +20,28 @@ namespace Fortran::utils::openmp {
 // TODO We can probably move the stuff inside `Support/OpenMP-utils.h/.cpp` here
 // as well.
 
-/// Create an `omp.map.info` op. Parameters other than the ones documented below
-/// correspond to operation arguments in the OpenMPOps.td file, see op docs for
-/// more details.
+/// Create an `omp.map.info` op. See OpenMPOps.td for map operand semantics.
 ///
-/// \param [in] builder - MLIR operation builder.
-/// \param [in] loc     - Source location of the created op.
+/// \param builder        - Operation builder.
+/// \param loc            - Source location of the created op.
+/// \param baseAddr       - Variable address. Box values are converted to their
+/// data address with `fir.box_addr`.
+/// \param varPtrPtr      - Optional address of the mapped pointer, used for
+/// descriptor base-address maps.
+/// \param name           - Name identifying the mapped object.
+/// \param bounds         - Bounds selecting the mapped elements or section.
+/// \param members        - Child map entries of a structured map.
+/// \param membersIndex   - Placement indices of the child map entries.
+/// \param mapType        - Map type and modifier flags.
+/// \param mapCaptureType - Capture kind of the mapped variable.
+/// \param retTy          - Result type. Replaced by the data address type when
+/// \p baseAddr is a box value.
+/// \param partialMap     - Whether the record is mapped only through selected
+/// components rather than in its entirety.
+/// \param mapperId       - Optional declare mapper symbol reference.
+/// \param varPtrTy       - Pointer type used to derive the `var_ptr` type
+/// attribute. Defaults to \p retTy after box conversion. Set this when an
+/// opaque map result must retain the type of its FIR variable pointer.
 mlir::omp::MapInfoOp createMapInfoOp(mlir::OpBuilder &builder,
     mlir::Location loc, mlir::Value baseAddr, mlir::Value varPtrPtr,
     llvm::StringRef name, llvm::ArrayRef<mlir::Value> bounds,
@@ -33,7 +49,8 @@ mlir::omp::MapInfoOp createMapInfoOp(mlir::OpBuilder &builder,
     mlir::omp::ClauseMapFlags mapType,
     mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy,
     bool partialMap = false,
-    mlir::FlatSymbolRefAttr mapperId = mlir::FlatSymbolRefAttr());
+    mlir::FlatSymbolRefAttr mapperId = mlir::FlatSymbolRefAttr(),
+    mlir::Type varPtrTy = mlir::Type());
 
 /// For an mlir value that does not have storage, allocate temporary storage
 /// (outside the target region), store the value in that storage, and map the
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index b871f630d3dae..a86d65d657064 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -13,6 +13,7 @@
 #include "ClauseProcessor.h"
 #include "Utils.h"
 
+#include "flang/Evaluate/tools.h"
 #include "flang/Lower/ConvertCall.h"
 #include "flang/Lower/ConvertExprToHLFIR.h"
 #include "flang/Lower/OpenMP/Clauses.h"
@@ -23,6 +24,7 @@
 #include "flang/Optimizer/Support/InternalNames.h"
 #include "flang/Semantics/tools.h"
 #include "flang/Utils/OpenMP.h"
+#include "mlir/Interfaces/DataLayoutInterfaces.h"
 #include "llvm/Frontend/OpenMP/OMP.h.inc"
 #include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
 
@@ -256,23 +258,24 @@ static IteratorRange lowerIteratorRange(
   mlir::Value ubVal =
       fir::getBase(converter.genExprValue(toEvExpr(ubExpr), stmtCtx));
 
-  auto toIndex = [](fir::FirOpBuilder &builder, mlir::Location loc,
-                    mlir::Value v) -> mlir::Value {
-    if (v.getType().isIndex())
-      return v;
-    return fir::ConvertOp::create(builder, loc, builder.getIndexType(), v);
-  };
-
-  r.lb = toIndex(builder, loc, lbVal);
-  r.ub = toIndex(builder, loc, ubVal);
-
-  if (stExpr) {
-    mlir::Value stVal =
-        fir::getBase(converter.genExprValue(toEvExpr(*stExpr), stmtCtx));
-    r.step = toIndex(builder, loc, stVal);
-  } else {
-    r.step = mlir::arith::ConstantIndexOp::create(builder, loc, 1);
-  }
+  mlir::Value stVal;
+  if (stExpr)
+    stVal = fir::getBase(converter.genExprValue(toEvExpr(*stExpr), stmtCtx));
+
+  // Use index when it can represent the source range, but retain wider
+  // integers for the bounds, step and induction value when necessary.
+  unsigned width = converter.genType(*r.ivSym).getIntOrFloatBitWidth();
+  for (mlir::Value value : {lbVal, ubVal, stVal})
+    if (value)
+      width = std::max(width, value.getType().getIntOrFloatBitWidth());
+  mlir::Type rangeTy = builder.getIndexType();
+  if (width > builder.getDataLayout().getTypeSizeInBits(rangeTy))
+    rangeTy = builder.getIntegerType(width);
+
+  r.lb = builder.createConvert(loc, rangeTy, lbVal);
+  r.ub = builder.createConvert(loc, rangeTy, ubVal);
+  r.step = stVal ? builder.createConvert(loc, rangeTy, stVal)
+                 : builder.createIntegerConstant(loc, rangeTy, 1);
 
   return r;
 }
@@ -298,6 +301,7 @@ static mlir::Value buildIteratorOp(Fortran::lower::AbstractConverter &converter,
   auto itOp = mlir::omp::IteratorOp::create(
       builder, loc, iterTy, mlir::ValueRange{lbs}, mlir::ValueRange{ubs},
       mlir::ValueRange{steps});
+  itOp.setLoopInclusive(true);
 
   mlir::OpBuilder::InsertionGuard guard(builder);
 
@@ -307,7 +311,7 @@ static mlir::Value buildIteratorOp(Fortran::lower::AbstractConverter &converter,
   llvm::SmallVector<mlir::Value> ivs;
   ivs.reserve(ranges.size());
   for (size_t i = 0; i < ranges.size(); ++i)
-    ivs.push_back(body->addArgument(builder.getIndexType(), loc));
+    ivs.push_back(body->addArgument(ranges[i].lb.getType(), loc));
 
   Fortran::lower::SymMap &symMap = converter.getSymbolMap();
   Fortran::lower::SymMapScope scope(symMap);
@@ -337,12 +341,52 @@ static mlir::Value buildIteratorOp(Fortran::lower::AbstractConverter &converter,
   return itOp.getResult();
 }
 
+// Build an omp.iterator that yields a map entry for one locator.
+static mlir::Value buildIteratedMapEntry(
+    Fortran::lower::AbstractConverter &converter,
+    Fortran::semantics::SemanticsContext &semaCtx, mlir::Location loc,
+    llvm::ArrayRef<IteratorRange> iteratorRanges, const omp::Object &object,
+    const evaluate::ArrayRef &arrayRef, 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;
+        IteratorMapInfo mapInfo = genIteratorMapInfo(
+            converter, builder, semaCtx, iterStmtCtx, object, arrayRef, loc);
+
+        // Keep the array base in var_ptr so element and whole-array maps share
+        // a base address. Bounds select the data for this iteration; descriptor
+        // parent and attachment maps are not part of these entries.
+        mlir::Value baseAddr = mapInfo.entity.getBase();
+        if (mlir::isa<fir::BaseBoxType>(baseAddr.getType()))
+          baseAddr = fir::BoxAddrOp::create(builder, loc, baseAddr);
+        mlir::FlatSymbolRefAttr mapperId =
+            resolveMapperId(converter, loc, object, mapperIdName, mapTypeBits,
+                            directive, /*hasParentObj=*/false);
+        mlir::omp::MapInfoOp mapOp = utils::openmp::createMapInfoOp(
+            builder, loc, baseAddr, /*varPtrPtr=*/mlir::Value{},
+            /*name=*/"", mapInfo.bounds, /*members=*/{},
+            /*membersIndex=*/mlir::ArrayAttr{}, mapTypeBits,
+            mlir::omp::VariableCaptureKind::ByRef, ptrTy,
+            /*partialMap=*/false, mapperId, baseAddr.getType());
+        return mapOp.getResult();
+      });
+}
+
 template <typename ClauseTuple>
 static void collectIteratorIVs(
     const ClauseTuple &clause, Fortran::lower::AbstractConverter &converter,
     Fortran::lower::StatementContext &stmtCtx,
     llvm::SmallVectorImpl<IteratorRange> &iteratorRanges,
-    llvm::SmallPtrSetImpl<const Fortran::semantics::Symbol *> &ivSyms) {
+    llvm::SmallPtrSetImpl<const Fortran::semantics::Symbol *> *ivSyms =
+        nullptr) {
   auto &iteratorModifier =
       std::get<std::optional<omp::clause::Iterator>>(clause.t);
   if (!iteratorModifier.has_value())
@@ -355,8 +399,9 @@ static void collectIteratorIVs(
     iteratorRanges.push_back(lowerIteratorRange<Fortran::evaluate::SomeType>(
         converter, itSpec, stmtCtx, clauseLocation));
 
-  for (const IteratorRange &r : iteratorRanges)
-    ivSyms.insert(&r.ivSym->GetUltimate());
+  if (ivSyms)
+    for (const IteratorRange &r : iteratorRanges)
+      ivSyms->insert(&r.ivSym->GetUltimate());
 }
 
 //===----------------------------------------------------------------------===//
@@ -1051,7 +1096,7 @@ bool ClauseProcessor::processAffinity(
 
         auto &iteratorModifier =
             std::get<std::optional<omp::clause::Iterator>>(clause.t);
-        collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms);
+        collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, &ivSyms);
 
         TodoLocators(clauseLocation, objects);
 
@@ -1514,7 +1559,7 @@ bool ClauseProcessor::processDepend(lower::SymMap &symMap,
 
     llvm::SmallVector<IteratorRange> iteratorRanges;
     llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4> ivSyms;
-    collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms);
+    collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, &ivSyms);
 
     mlir::Type ptrTy =
         mlir::LLVM::LLVMPointerType::get(&converter.getMLIRContext());
@@ -1943,6 +1988,98 @@ void ClauseProcessor::processMapObjects(
   }
 }
 
+// Lower iterator-dependent locators to mapIterated and other locators to
+// mapVars.
+void ClauseProcessor::processMapObjectsWithIterator(
+    lower::StatementContext &stmtCtx, mlir::Location clauseLocation,
+    const omp::ObjectList &objects,
+    llvm::ArrayRef<IteratorRange> iteratorRanges,
+    mlir::omp::ClauseMapFlags mapTypeBits,
+    std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices,
+    mlir::omp::MapClauseOps &result, llvm::SmallVectorImpl<Object> &mapObjects,
+    llvm::StringRef mapperIdNameRef, bool isMotionModifier,
+    llvm::omp::Directive directive) const {
+  if (iteratorRanges.empty()) {
+    processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
+                      parentMemberIndices, result.mapVars, mapObjects,
+                      mapperIdNameRef, isMotionModifier, directive);
+    return;
+  }
+
+  auto declareMapper = mlir::dyn_cast_if_present<mlir::omp::DeclareMapperOp>(
+      converter.getFirOpBuilder().getRegion().getParentOp());
+  auto isDeclareMapperVariable =
+      [&](const IteratorMapObjectAnalysis &analysis) {
+        if (!declareMapper || !analysis.rootSym)
+          return false;
+
+        mlir::Value rootAddress = converter.getSymbolAddress(*analysis.rootSym);
+        while (rootAddress) {
+          if (auto declare = rootAddress.getDefiningOp<hlfir::DeclareOp>()) {
+            rootAddress = declare.getMemref();
+            continue;
+          }
+          if (auto declare = rootAddress.getDefiningOp<fir::DeclareOp>()) {
+            rootAddress = declare.getMemref();
+            continue;
+          }
+          break;
+        }
+
+        return rootAddress == declareMapper.getRegion().front().getArgument(0);
+      };
+
+  constexpr mlir::omp::ClauseMapFlags unsupportedReferenceModifiers =
+      mlir::omp::ClauseMapFlags::ref_ptr | mlir::omp::ClauseMapFlags::ref_ptee |
+      mlir::omp::ClauseMapFlags::attach_always |
+      mlir::omp::ClauseMapFlags::attach_never |
+      mlir::omp::ClauseMapFlags::attach_auto;
+  bool hasUnsupportedReferenceModifier =
+      (mapTypeBits & unsupportedReferenceModifiers) !=
+      mlir::omp::ClauseMapFlags::none;
+
+  // Objects in an iterator-modified clause may independently reference
+  // iterator variables, so handle each object separately.
+  for (const omp::Object &object : objects) {
+    llvm::SmallPtrSet<const semantics::Symbol *, 4> referencedSymbols;
+    if (const auto &ref = object.ref())
+      for (const semantics::SymbolRef &symbol : evaluate::CollectSymbols(*ref))
+        referencedSymbols.insert(&symbol->GetUltimate());
+
+    llvm::SmallVector<IteratorRange> objectRanges;
+    for (const IteratorRange &range : iteratorRanges)
+      if (referencedSymbols.contains(&range.ivSym->GetUltimate()))
+        objectRanges.push_back(range);
+
+    if (!objectRanges.empty()) {
+      if (hasUnsupportedReferenceModifier)
+        TODO(clauseLocation,
+             "iterator modifier with reference or attach modifier");
+      IteratorMapObjectAnalysis analysis = analyzeIteratorMapObject(object);
+      bool isMapperVariable = isDeclareMapperVariable(analysis);
+      if (analysis.isDerivedTypeMember && !isMapperVariable)
+        TODO(clauseLocation, "iterator modifier with derived type member map");
+      if (declareMapper && analysis.rootSym && !isMapperVariable)
+        TODO(clauseLocation,
+             "iterator modifier with locator outside declare mapper variable");
+      if (!analysis.arrayRef)
+        TODO(clauseLocation, "object type not supported by iterator modifier");
+      if (const auto *symbol{object.sym()};
+          symbol && semantics::IsOptional(*symbol))
+        TODO(clauseLocation, "iterator modifier with optional locator");
+      result.mapIterated.push_back(buildIteratedMapEntry(
+          converter, semaCtx, clauseLocation, objectRanges, object,
+          *analysis.arrayRef, mapperIdNameRef, mapTypeBits, directive));
+      continue;
+    }
+
+    omp::ObjectList singleObj{object};
+    processMapObjects(stmtCtx, clauseLocation, singleObj, mapTypeBits,
+                      parentMemberIndices, result.mapVars, mapObjects,
+                      mapperIdNameRef, isMotionModifier, directive);
+  }
+}
+
 /// Extract and mangle the mapper identifier name from a mapper clause.
 /// Returns "__implicit_mapper" if no mapper is specified, or "default" if
 /// the default mapper is specified, otherwise returns the mangled mapper name.
@@ -2062,15 +2199,24 @@ 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,
-                      parentMemberIndices, result.mapVars, *ptrMapObjects,
-                      mapperIdName, /*isMotionModifier=*/false, directive);
+    llvm::SmallVector<IteratorRange> iteratorRanges;
+    if (iterator) {
+      // An executable target region needs a stable base capture for every
+      // iterated map object used in its body. Without such a capture, implicit
+      // mapping adds an ordinary whole-object map and changes the requested
+      // extent and direction. Reject this until omp.target can represent those
+      // captures separately from runtime map entries.
+      if (directive == llvm::omp::Directive::OMPD_target)
+        TODO(clauseLocation, "TARGET construct with MAP iterator modifier");
+
+      collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges);
+    }
+    processMapObjectsWithIterator(
+        stmtCtx, clauseLocation, objects, iteratorRanges, mapTypeBits,
+        parentMemberIndices, result, *ptrMapObjects, mapperIdName,
+        /*isMotionModifier=*/false, directive);
   };
 
   bool clauseFound = findRepeatableClause<omp::clause::Map>(process);
@@ -2085,6 +2231,11 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
   std::map<Object, OmpMapParentAndMemberData> parentMemberIndices;
   llvm::SmallVector<Object> mapObjects;
 
+  // OMPD_unknown allows the mapper resolver to synthesize default mappers for
+  // target update. Passing OMPD_target_update would disable that synthesis.
+  constexpr llvm::omp::Directive mapperDirective =
+      llvm::omp::Directive::OMPD_unknown;
+
   auto callbackFn = [&](const auto &clause, const parser::CharBlock &source) {
     mlir::Location clauseLocation = converter.genLocation(source);
     const auto &[expectation, mapper, iterator, objects] = clause.t;
@@ -2096,16 +2247,17 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
     if (expectation && *expectation == omp::clause::To::Expectation::Present)
       mapTypeBits |= mlir::omp::ClauseMapFlags::present;
 
-    // Support motion modifiers: iterator.
     std::string mapperIdName = getMapperIdentifier(converter, mapper);
 
-    if (iterator)
-      TODO(clauseLocation, "Iterator modifier is not supported yet");
     TodoLocators(clauseLocation, objects);
 
-    processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
-                      parentMemberIndices, result.mapVars, mapObjects,
-                      mapperIdName, /*isMotionModifier=*/true);
+    llvm::SmallVector<IteratorRange> iteratorRanges;
+    if (iterator)
+      collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges);
+    processMapObjectsWithIterator(
+        stmtCtx, clauseLocation, objects, iteratorRanges, mapTypeBits,
+        parentMemberIndices, result, mapObjects, mapperIdName,
+        /*isMotionModifier=*/true, mapperDirective);
   };
 
   bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn);
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h
index 6b0e0cf606f48..bac456ca00cd9 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.h
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h
@@ -226,6 +226,17 @@ class ClauseProcessor {
       llvm::StringRef mapperIdNameRef = "", bool isMotionModifier = false,
       llvm::omp::Directive directive = llvm::omp::OMPD_unknown) const;
 
+  void processMapObjectsWithIterator(
+      lower::StatementContext &stmtCtx, mlir::Location clauseLocation,
+      const omp::ObjectList &objects,
+      llvm::ArrayRef<IteratorRange> iteratorRanges,
+      mlir::omp::ClauseMapFlags mapTypeBits,
+      std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices,
+      mlir::omp::MapClauseOps &result,
+      llvm::SmallVectorImpl<Object> &mapObjects,
+      llvm::StringRef mapperIdNameRef = "", bool isMotionModifier = false,
+      llvm::omp::Directive directive = llvm::omp::OMPD_unknown) const;
+
   lower::AbstractConverter &converter;
   semantics::SemanticsContext &semaCtx;
   List<Clause> clauses;
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index ee16ae55af30f..bbad87f59e140 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -152,6 +152,18 @@ Object makeObject(const parser::OmpObject &object,
   if (auto *desg = parser::omp::GetDesignatorFromObj(object))
     return makeObject(*desg, semaCtx);
   if (auto *locator = parser::omp::GetLocatorFromObj(object)) {
+    // Module files are name-resolved but do not pass through
+    // RewriteParseTree. Recover array elements that consequently remain
+    // parsed as function references before converting them for lowering.
+    if (auto *ref = std::get_if<parser::FunctionReference>(&locator->u);
+        ref && semantics::CheckMisparsedArrayElement(semaCtx, *ref)) {
+      auto &mutableObject = const_cast<parser::OmpObject &>(object);
+      auto &mutableLocator = std::get<parser::OmpLocator>(mutableObject.u);
+      auto &mutableRef = std::get<parser::FunctionReference>(mutableLocator.u);
+      mutableObject.u = mutableRef.ConvertToArrayElementRef();
+      return makeObject(std::get<parser::Designator>(mutableObject.u), semaCtx);
+    }
+
     return common::visit( //
         common::visitors{
             [&](const parser::OmpReservedIdentifier &x) {
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index df14d13b76a4b..a471b4493a995 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2662,7 +2662,7 @@ genTargetClauses(lower::AbstractConverter &converter,
                   "storage-associated variable");
     }
   cp.processIsDevicePtr(stmtCtx, clauseOps, isDevicePtrObjects);
-  cp.processMap(loc, stmtCtx, clauseOps, llvm::omp::Directive::OMPD_unknown,
+  cp.processMap(loc, stmtCtx, clauseOps, llvm::omp::Directive::OMPD_target,
                 &mapObjects);
   cp.processNowait(clauseOps);
   cp.processThreadLimit(stmtCtx, clauseOps);
@@ -2685,7 +2685,8 @@ static void genTargetDataClauses(
   ClauseProcessor cp(converter, semaCtx, clauses);
   cp.processDevice(stmtCtx, clauseOps);
   cp.processIf(llvm::omp::Directive::OMPD_target_data, clauseOps);
-  cp.processMap(loc, stmtCtx, clauseOps);
+  cp.processMap(loc, stmtCtx, clauseOps,
+                llvm::omp::Directive::OMPD_target_data);
   cp.processUseDeviceAddr(stmtCtx, clauseOps, useDeviceAddrObjects);
   // Record the source UDA prefix before non-C_PTR UDP operands are promoted.
   sourceUseDeviceAddrCount = useDeviceAddrObjects.size();
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index b52c03ff63fed..6d3495d31c16f 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -1551,6 +1551,193 @@ resolveDeclareVariantCallee(const semantics::Symbol &base,
   return variants[bestIdx];
 }
 
+IteratorMapObjectAnalysis analyzeIteratorMapObject(const omp::Object &object) {
+  IteratorMapObjectAnalysis analysis;
+  const std::optional<ExprTy> &ref = object.ref();
+  if (!ref)
+    return analysis;
+
+  std::optional<evaluate::DataRef> dataRef = evaluate::ExtractDataRef(*ref);
+  if (!dataRef)
+    return analysis;
+
+  analysis.rootSym = &dataRef->GetFirstSymbol();
+
+  const auto *component = std::get_if<evaluate::Component>(&dataRef->u);
+  const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u);
+  analysis.isDerivedTypeMember =
+      component || (arrayRef && arrayRef->base().UnwrapComponent());
+
+  if (!arrayRef || arrayRef->subscript().empty() ||
+      evaluate::ExtractCoarrayRef(*dataRef))
+    return analysis;
+
+  for (const auto &subscript : arrayRef->subscript()) {
+    if (!std::holds_alternative<evaluate::Triplet>(subscript.u) &&
+        subscript.Rank() > 0)
+      return analysis;
+  }
+
+  analysis.arrayRef = *arrayRef;
+  return analysis;
+}
+
+// Lower the array base (v%a for v%a(i)) with shape information for bounds.
+static hlfir::Entity getIteratorMapEntity(
+    Fortran::lower::AbstractConverter &converter, fir::FirOpBuilder &builder,
+    Fortran::semantics::SemanticsContext &semaCtx,
+    Fortran::lower::StatementContext &stmtCtx, const omp::Object &object,
+    const evaluate::ArrayRef &arrayRef, mlir::Location loc) {
+  const semantics::Symbol *sym = object.sym();
+  assert(sym && "expected symbol for iterator object");
+
+  mlir::Value addr;
+  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);
+    addr = info.addr;
+  } else {
+    evaluate::ExpressionAnalyzer ea{semaCtx};
+    std::optional<ExprTy> arrayBase;
+    const evaluate::NamedEntity &base = arrayRef.base();
+    // The ordinary base-address helper takes a symbol, which does not identify
+    // the containing object of a component. Lower the component base reference
+    // here, leaving its subscripts for iterator-local 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);
+    // Mutable-box lowering preserves allocatable and pointer descriptors.
+    fir::ExtendedValue dataExv;
+    if (semantics::IsAllocatableOrPointer(base.GetLastSymbol()))
+      dataExv = converter.genExprMutableBox(loc, *arrayBase);
+    else
+      dataExv = converter.genExprAddr(loc, *arrayBase, stmtCtx);
+    addr = fir::getBase(dataExv);
+  }
+
+  // Load allocatable and pointer descriptors to access data and shape. Keep
+  // the box value until bounds are built; map construction extracts its data
+  // address with fir.box_addr.
+  if (fir::isBoxAddress(addr.getType()))
+    addr = fir::LoadOp::create(builder, loc, addr);
+  return hlfir::Entity{addr};
+}
+
+// Ordinary map lowering derives bounds from a fully lowered locator. Iterator
+// maps keep the array base in var_ptr, so build bounds from the subscripts in
+// the iterator body, relative to the base's Fortran lower bounds.
+//
+// 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 llvm::SmallVector<mlir::Value>
+genIteratorMapBounds(Fortran::lower::AbstractConverter &converter,
+                     hlfir::Entity entity, const evaluate::ArrayRef &arrayRef,
+                     Fortran::lower::StatementContext &stmtCtx,
+                     mlir::Location loc) {
+  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);
+  mlir::Value box;
+  if (entity.isBoxAddressOrValue())
+    box = entity.getBase();
+
+  auto lowerSubscriptToIndex = [&](const SubscriptExpr &expr) -> mlir::Value {
+    mlir::Value value =
+        fir::getBase(converter.genExprValue(toEvExpr(expr), stmtCtx, &loc));
+    return builder.createConvert(loc, idxTy, value);
+  };
+
+  llvm::SmallVector<mlir::Value> bounds;
+  bounds.reserve(arrayRef.subscript().size());
+  for (const auto &[dim, subscript] : llvm::enumerate(arrayRef.subscript())) {
+    mlir::Value baseLb = builder.createConvert(
+        loc, idxTy, hlfir::genLBound(loc, builder, entity, dim));
+    mlir::Value extent;
+    mlir::Value stride = one;
+    bool strideInBytes = false;
+    if (box) {
+      mlir::Value dimValue = builder.createIntegerConstant(loc, idxTy, dim);
+      auto dimInfo = fir::BoxDimsOp::create(builder, loc, idxTy, idxTy, idxTy,
+                                            box, dimValue);
+      extent = dimInfo.getExtent();
+      stride = dimInfo.getByteStride();
+      strideInBytes = true;
+    } else {
+      extent = builder.createConvert(
+          loc, idxTy, hlfir::genExtent(loc, builder, entity, dim));
+    }
+    mlir::Value lbound;
+    mlir::Value ubound;
+
+    if (const auto *triplet =
+            std::get_if<Fortran::evaluate::Triplet>(&subscript.u)) {
+      // Omitted endpoints use the dimension's lower or upper bound, normalized
+      // to zero or extent - 1, respectively.
+      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);
+      }
+
+      // Non-unit and dynamic section strides need noncontiguous map support.
+      std::optional<std::int64_t> sectionStride =
+          Fortran::evaluate::ToInt64(triplet->GetStride());
+      if (!sectionStride || *sectionStride != 1)
+        TODO(loc, "iterator modifier with non-unit array section stride");
+    } else {
+      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,
+        /*stride_in_bytes=*/strideInBytes, /*start_idx=*/baseLb);
+    bounds.push_back(bound);
+  }
+
+  return bounds;
+}
+
+IteratorMapInfo genIteratorMapInfo(
+    Fortran::lower::AbstractConverter &converter, fir::FirOpBuilder &builder,
+    Fortran::semantics::SemanticsContext &semaCtx,
+    Fortran::lower::StatementContext &stmtCtx, const omp::Object &object,
+    const evaluate::ArrayRef &arrayRef, mlir::Location loc) {
+  hlfir::Entity entity = getIteratorMapEntity(converter, builder, semaCtx,
+                                              stmtCtx, object, arrayRef, loc);
+  return IteratorMapInfo{
+      entity, genIteratorMapBounds(converter, entity, arrayRef, stmtCtx, loc)};
+}
+
 } // namespace omp
 } // namespace lower
 } // namespace Fortran
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index 7fde1825b006a..e6ef86ee01c6b 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -258,6 +258,31 @@ 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;
+};
+
+/// Properties needed to validate an iterator map locator without lowering it.
+/// Unsupported DataRef forms are classified without evaluating their bases.
+struct IteratorMapObjectAnalysis {
+  const semantics::Symbol *rootSym = nullptr;
+  bool isDerivedTypeMember = false;
+  // Own the validated reference so lowering can reuse it after analysis.
+  std::optional<evaluate::ArrayRef> arrayRef;
+};
+
+/// Classify a locator and retain its array reference if supported.
+IteratorMapObjectAnalysis analyzeIteratorMapObject(const omp::Object &object);
+
+/// Lower a locator to its array base and iterator-dependent map bounds.
+/// \p arrayRef must be the supported reference returned by locator analysis.
+IteratorMapInfo genIteratorMapInfo(
+    Fortran::lower::AbstractConverter &converter, fir::FirOpBuilder &builder,
+    Fortran::semantics::SemanticsContext &semaCtx,
+    Fortran::lower::StatementContext &stmtCtx, const omp::Object &object,
+    const evaluate::ArrayRef &arrayRef, 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 3b264a196aa4a..5ce5dd3125791 100644
--- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
+++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
@@ -1442,6 +1442,10 @@ class MapInfoFinalizationPass
     return false;
   }
 
+  static bool isNestedInIterator(mlir::omp::MapInfoOp op) {
+    return op->getParentOfType<mlir::omp::IteratorOp>() != nullptr;
+  }
+
   // This pass executes on omp::MapInfoOp's containing descriptor based types
   // (allocatables, pointers, assumed shape etc.) and expanding them into
   // multiple omp::MapInfoOp's for each pointer member contained within the
@@ -1472,19 +1476,26 @@ class MapInfoFinalizationPass
       deferrableDesc.clear();
       expandedBaseAddr.clear();
 
-      // Walk all of the existing maps for parents with child maps and then
-      // make sure to appropriately bind them to the target region that the
-      // parent is bound to. Necessary for the next implicit record member
-      // map step which depends on this canonicalization step. This step
-      // is executed again as the final step of this pass to maintain
-      // map to block argument consistency.
-      func->walk([&](mlir::omp::MapInfoOp op) {
+      // Iterator maps may depend on region-local values. Descriptor expansion
+      // and target operand updates must not expose those values outside the
+      // iterator region.
+      auto walkNonIteratedMapInfoOps = [&](auto &&callback) {
+        func->walk([&](mlir::omp::MapInfoOp op) {
+          if (!isNestedInIterator(op))
+            callback(op);
+        });
+      };
+
+      // Bind existing member maps to their parent's target region. Repeat after
+      // descriptor expansion to keep map operands and block arguments
+      // consistent.
+      walkNonIteratedMapInfoOps([&](mlir::omp::MapInfoOp op) {
         mlir::Operation *targetUser = getFirstTargetUser(op);
         assert(targetUser && "expected user of map operation was not found");
         addImplicitMembersToTarget(op, builder, targetUser);
       });
 
-      func->walk([&](mlir::omp::MapInfoOp op) {
+      walkNonIteratedMapInfoOps([&](mlir::omp::MapInfoOp op) {
         // NOTE: Currently only supports a single user for the MapInfoOp. This
         // is fine for the moment, as the Fortran frontend will generate a
         // new MapInfoOp with at most one user currently. In the case of
@@ -1544,7 +1555,7 @@ class MapInfoFinalizationPass
       // Wait until after we have generated all of our maps to add them onto
       // the target's block arguments, simplifying the process as there would be
       // no need to avoid accidental duplicate additions.
-      func->walk([&](mlir::omp::MapInfoOp op) {
+      walkNonIteratedMapInfoOps([&](mlir::omp::MapInfoOp op) {
         mlir::Operation *targetUser = getFirstTargetUser(op);
         assert(targetUser && "expected user of map operation was not found");
         addImplicitMembersToTarget(op, builder, targetUser);
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index f282140e47b6b..a79fdab13ee11 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -523,6 +523,28 @@ class OmpUnitedTaskDesignatorChecker {
   SemanticsContext &context_;
 };
 
+class OmpIteratorReferenceChecker {
+public:
+  explicit OmpIteratorReferenceChecker(
+      const UnorderedSymbolSet &iteratorSymbols)
+      : iteratorSymbols_{iteratorSymbols} {}
+
+  template <typename T> bool Pre(const T &) { return !found_; }
+  template <typename T> void Post(const T &) {}
+
+  bool Pre(const parser::Name &name) {
+    if (name.symbol && iteratorSymbols_.count(name.symbol->GetUltimate()) != 0)
+      found_ = true;
+    return !found_;
+  }
+
+  bool found() const { return found_; }
+
+private:
+  const UnorderedSymbolSet &iteratorSymbols_;
+  bool found_{false};
+};
+
 bool OmpStructureChecker::IsAllowedClause(llvm::omp::Clause clauseId) {
   // Do not do clause checks while processing METADIRECTIVE.
   // See comment in CheckAllowedClause.
@@ -4947,9 +4969,9 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
         "The specification of modifiers without comma separators for the "
         "'MAP' clause has been deprecated in OpenMP 5.2"_port_en_US);
   }
-  if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
-    CheckIteratorModifier(*iter);
-  }
+  const auto *iterator{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)};
+  if (iterator)
+    CheckIteratorModifier(*iterator);
 
   using Directive = llvm::omp::Directive;
   Directive dir{GetContext().directive};
@@ -5080,23 +5102,39 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
     }
   }
 
-  // If we are an enter or exit map, iterate over the maps and add them to
-  // containers that track if the symbol has been referenced in both an
-  // enter/exit map in the current scope, if it falls into the category of
-  // having a temporary stack descriptor. If we have reference modifiers, we
-  // ignore the warning and trust that the user knows what they are doing
-  // already, as they are aware the type comes with a descriptor and pointer
-  // combination.
-  //
-  // We will utilise this information to emit a warning later if the neccesary
-  // conditions are met, where we have an enter map without a corresponding exit
-  // in the current scope.
+  // Track temporary descriptor maps to warn about enter maps without matching
+  // exits in this scope. Reference modifiers control descriptor mapping
+  // explicitly. Iterator-dependent maps transfer only selected data, so they
+  // neither introduce a descriptor map nor release one on exit.
   bool hasRefModifier{
       OmpGetUniqueModifier<parser::OmpRefModifier>(modifiers) != nullptr};
   if (!hasRefModifier &&
       (llvm::is_contained(leafs, Directive::OMPD_target_enter_data) ||
           llvm::is_contained(leafs, Directive::OMPD_target_exit_data))) {
+    UnorderedSymbolSet iteratorSymbols;
+    if (iterator) {
+      for (const parser::OmpIteratorSpecifier &iterSpec : iterator->v) {
+        const auto &typeDecl{std::get<parser::TypeDeclarationStmt>(iterSpec.t)};
+        const auto &entities{
+            std::get<std::list<parser::EntityDecl>>(typeDecl.t)};
+        for (const parser::EntityDecl &entity : entities) {
+          const auto &name{std::get<parser::ObjectName>(entity.t)};
+          if (name.symbol)
+            iteratorSymbols.insert(name.symbol->GetUltimate());
+        }
+      }
+    }
+
     for (const parser::OmpObject &object : objects.v) {
+      bool referencesIterator{false};
+      if (const auto *designator{GetDesignatorFromObj(object)}) {
+        OmpIteratorReferenceChecker checker{iteratorSymbols};
+        parser::Walk(*designator, checker);
+        referencesIterator = checker.found();
+      }
+      if (referencesIterator)
+        continue;
+
       if (const Symbol *sym{GetObjectSymbol(object, /*ultimate=*/true)}) {
         if (HasTemporaryStackDescriptor(*sym)) {
           auto maybeSource{GetObjectSource(object)};
diff --git a/flang/lib/Utils/OpenMP.cpp b/flang/lib/Utils/OpenMP.cpp
index aa5ef994252f7..625aaf7c438f0 100644
--- a/flang/lib/Utils/OpenMP.cpp
+++ b/flang/lib/Utils/OpenMP.cpp
@@ -65,7 +65,7 @@ mlir::omp::MapInfoOp createMapInfoOp(mlir::OpBuilder &builder,
     llvm::ArrayRef<mlir::Value> members, mlir::ArrayAttr membersIndex,
     mlir::omp::ClauseMapFlags mapType,
     mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy,
-    bool partialMap, mlir::FlatSymbolRefAttr mapperId) {
+    bool partialMap, mlir::FlatSymbolRefAttr mapperId, mlir::Type varPtrTy) {
 
   auto getPtrVarType = [](mlir::Type ptrType) {
     mlir::TypeAttr varType = mlir::TypeAttr::get(
@@ -85,7 +85,7 @@ mlir::omp::MapInfoOp createMapInfoOp(mlir::OpBuilder &builder,
     retTy = baseAddr.getType();
   }
 
-  auto varPtrType = getPtrVarType(retTy);
+  auto varPtrType = getPtrVarType(varPtrTy ? varPtrTy : retTy);
   auto varPtrPtrTy =
       varPtrPtr ? getPtrVarType(varPtrPtr.getType()) : mlir::TypeAttr{};
 
diff --git a/flang/test/Lower/OpenMP/Todo/declare-mapper-iterator-unsupported.f90 b/flang/test/Lower/OpenMP/Todo/declare-mapper-iterator-unsupported.f90
new file mode 100644
index 0000000000000..d22b0708d5d30
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/declare-mapper-iterator-unsupported.f90
@@ -0,0 +1,65 @@
+! RUN: split-file %s %t
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/coarray.f90 2>&1 | \
+! RUN:   FileCheck %t/coarray.f90
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/external-array.f90 2>&1 | \
+! RUN:   FileCheck %t/external-array.f90
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/external-member.f90 2>&1 | \
+! RUN:   FileCheck %t/external-member.f90
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/object-type.f90 2>&1 | \
+! RUN:   FileCheck %t/object-type.f90
+
+! Unsupported iterator locators inside declare mapper.
+
+!--- coarray.f90
+! CHECK: not yet implemented: iterator modifier with locator outside
+! CHECK-SAME: declare mapper variable
+
+module declare_mapper_iterator_coarray
+  integer, save :: a(10)[*]
+  type :: t
+    integer :: x
+  end type
+
+  !$omp declare mapper(mm: t :: v) map(iterator(i = 1:10): a(i)[1])
+end module
+
+!--- external-array.f90
+! CHECK: not yet implemented: iterator modifier with locator outside
+! CHECK-SAME: declare mapper variable
+
+module declare_mapper_external_array
+  integer :: tbl(100)
+  type :: t
+    real :: a(100)
+  end type
+
+  !$omp declare mapper(mm: t :: v) &
+  !$omp& map(iterator(i = 1:100): v%a(i), tbl(i))
+end module
+
+!--- external-member.f90
+! CHECK: not yet implemented: iterator modifier with derived type member map
+
+module declare_mapper_external_member
+  type :: t
+    integer :: a(10)
+  end type
+  type(t) :: w
+
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:10): w%a(i))
+end module
+
+!--- object-type.f90
+! CHECK: not yet implemented: object type not supported by iterator modifier
+
+module declare_mapper_iterator_object_type
+  type :: t
+    complex :: c(10)
+  end type
+
+  !$omp declare mapper(mm: t :: v) map(iterator(i = 1:10): v%c(i)%re)
+end module
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/map-iterator-ref-attach.f90 b/flang/test/Lower/OpenMP/Todo/map-iterator-ref-attach.f90
new file mode 100644
index 0000000000000..ff16560140b26
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/map-iterator-ref-attach.f90
@@ -0,0 +1,81 @@
+! RUN: split-file %s %t
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=61 -o - %t/ref-ptr.f90 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=61 -o - %t/ref-ptee.f90 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=61 -o - %t/ref-ptr-ptee.f90 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=61 -o - %t/attach-always.f90 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=61 -o - %t/attach-never.f90 2>&1 | FileCheck %s
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=61 -o - %t/attach-auto.f90 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=61 \
+! RUN:   -o /dev/null %t/plain-object.f90
+
+! CHECK: not yet implemented: iterator modifier with reference or attach
+! CHECK-SAME: modifier
+
+!--- ref-ptr.f90
+subroutine ref_ptr(a)
+  integer, pointer :: a(:)
+  integer :: i
+
+  !$omp target data map(ref_ptr, iterator(i = 1:10), to: a(i))
+  !$omp end target data
+end subroutine
+
+!--- ref-ptee.f90
+subroutine ref_ptee(a)
+  integer, pointer :: a(:)
+  integer :: i
+
+  !$omp target data map(ref_ptee, iterator(i = 1:10), to: a(i))
+  !$omp end target data
+end subroutine
+
+!--- ref-ptr-ptee.f90
+subroutine ref_ptr_ptee(a)
+  integer, pointer :: a(:)
+  integer :: i
+
+  !$omp target data map(ref_ptr_ptee, iterator(i = 1:10), to: a(i))
+  !$omp end target data
+end subroutine
+
+!--- attach-always.f90
+subroutine attach_always(a)
+  integer, pointer :: a(:)
+  integer :: i
+
+  !$omp target data map(attach(always), iterator(i = 1:10), to: a(i))
+  !$omp end target data
+end subroutine
+
+!--- attach-never.f90
+subroutine attach_never(a)
+  integer, pointer :: a(:)
+  integer :: i
+
+  !$omp target data map(attach(never), iterator(i = 1:10), to: a(i))
+  !$omp end target data
+end subroutine
+
+!--- attach-auto.f90
+subroutine attach_auto(a)
+  integer, pointer :: a(:)
+  integer :: i
+
+  !$omp target data map(attach(auto), iterator(i = 1:10), to: a(i))
+  !$omp end target data
+end subroutine
+
+!--- plain-object.f90
+subroutine plain_object(a)
+  integer, pointer :: a(:)
+  integer :: i
+
+  !$omp target data map(ref_ptr, iterator(i = 1:10), to: a)
+  !$omp end target data
+end subroutine
diff --git a/flang/test/Lower/OpenMP/Todo/map-motion-iterator-unsupported-locators.f90 b/flang/test/Lower/OpenMP/Todo/map-motion-iterator-unsupported-locators.f90
new file mode 100644
index 0000000000000..93f7fb4d1e783
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/map-motion-iterator-unsupported-locators.f90
@@ -0,0 +1,77 @@
+! RUN: split-file %s %t
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/array-member.f90 2>&1 | \
+! RUN:   FileCheck %t/array-member.f90
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/map-member.f90 2>&1 | \
+! RUN:   FileCheck %t/map-member.f90
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/motion-member.f90 2>&1 | \
+! RUN:   FileCheck %t/motion-member.f90
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/map-optional.f90 2>&1 | \
+! RUN:   FileCheck %t/map-optional.f90
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %t/motion-optional.f90 2>&1 | \
+! RUN:   FileCheck %t/motion-optional.f90
+
+! Unsupported iterator locators in map and motion clauses.
+
+!--- array-member.f90
+! CHECK: not yet implemented: iterator modifier with derived type member map
+
+subroutine map_iterator_array_derived_member
+  type :: t
+    integer :: b
+  end type
+  type(t) :: x(10)
+  integer :: i
+
+  !$omp target data map(iterator(i = 1:10), tofrom: x(i)%b)
+  !$omp end target data
+end subroutine
+
+!--- map-member.f90
+subroutine target_data_derived_member_iterator()
+  type :: s
+    integer :: a(10)
+  end type
+  type(s) :: x
+  integer :: i
+
+  !CHECK: not yet implemented: iterator modifier with derived type member map
+  !$omp target data map(iterator(i = 1:10), tofrom: x%a(i))
+  !$omp end target data
+end subroutine
+
+!--- motion-member.f90
+subroutine target_update_derived_member_iterator()
+  type :: s
+    integer :: a(10)
+  end type
+  type(s) :: x
+  integer :: i
+
+  !CHECK: not yet implemented: iterator modifier with derived type member map
+  !$omp target update to(iterator(i = 1:10): x%a(i))
+end subroutine
+
+!--- map-optional.f90
+subroutine target_enter_data_optional_iterator(a, n)
+  integer, optional, intent(inout) :: a(:)
+  integer, intent(in) :: n
+  integer :: i
+
+  ! CHECK: not yet implemented: iterator modifier with optional locator
+  !$omp target enter data map(iterator(i = 1:n), to: a(i))
+end subroutine
+
+!--- motion-optional.f90
+subroutine target_update_optional_iterator(a, n)
+  integer, allocatable, optional, intent(inout) :: a(:)
+  integer, intent(in) :: n
+  integer :: i
+
+  ! CHECK: not yet implemented: iterator modifier with optional locator
+  !$omp target update to(iterator(i = 1:n): a(i))
+end subroutine
diff --git a/flang/test/Lower/OpenMP/Todo/target-map-iterator.f90 b/flang/test/Lower/OpenMP/Todo/target-map-iterator.f90
new file mode 100644
index 0000000000000..9de14b25e4d60
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/target-map-iterator.f90
@@ -0,0 +1,12 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp \
+! RUN:   -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+
+subroutine target_map_iterator()
+  integer :: a(8)
+  integer :: i
+
+  ! CHECK: not yet implemented: TARGET construct with MAP iterator modifier
+  !$omp target map(iterator(i = 1:8), to: a(i))
+  a(1) = 42
+  !$omp end target
+end subroutine
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..03f27e1d36138
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-mapper-iterator.f90
@@ -0,0 +1,293 @@
+! RUN: rm -rf %t && split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 \
+! RUN:   %t/maps.f90 -o - | FileCheck %t/maps.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 \
+! RUN:   %t/ranges.f90 -o %t/ranges.mlir
+! RUN: FileCheck %t/ranges.f90 < %t/ranges.mlir
+! RUN: fir-opt --canonicalize --verify-each %t/ranges.mlir | \
+! RUN:   FileCheck %t/ranges.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 \
+! RUN:   -module-dir %t %t/mapper.f90 -o /dev/null
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 \
+! RUN:   -J %t %t/consumer.f90 -o - | FileCheck %t/consumer.f90
+
+!--- maps.f90
+subroutine declare_mapper_nondefault_lb()
+  type :: t
+    integer :: a(-2:7)
+  end type
+  !$omp declare mapper(m: t :: v) map(iterator(i = -2:6): v%a(i))
+end
+
+subroutine declare_mapper_alloc_section()
+  type :: t
+    integer, allocatable :: a(:)
+  end type
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:9): v%a(i:i+1))
+end
+
+subroutine declare_mapper_multi()
+  type :: t
+    integer :: a(10)
+    integer :: b(10)
+  end type
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:10): v%a(i), v%b(i))
+end
+
+subroutine declare_mapper_section()
+  type :: t
+    integer :: a(10)
+  end type
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:9): v%a(i:i+1))
+end
+
+subroutine declare_mapper_2d()
+  type :: t
+    integer :: a(4, 6)
+  end type
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:4, j = 1:6): v%a(i, j))
+end
+
+subroutine declare_mapper_pointer()
+  type :: t
+    integer, pointer :: a(:)
+  end type
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:10): v%a(i))
+end
+
+subroutine declare_mapper_allocatable(arg)
+  type :: t
+    integer, allocatable :: a(:)
+  end type
+  type(t) :: arg(:)
+
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:10): v%a(i))
+end
+
+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 @_QQFdeclare_mapper_iteratorm
+! 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:     fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:     %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:     %[[A:.*]] = hlfir.designate %[[DECL]]#0{"a"} {{.*}} : (!fir.ref<!fir.type<_QFdeclare_mapper_iteratorTs{{.*}}>>, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
+! CHECK:     %[[IV_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:     %[[IV_I64:.*]] = fir.convert %[[IV_LD]] : (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]]) name("") -> !llvm.ptr
+! CHECK:     omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! CHECK-LABEL: omp.declare_mapper @_QQFdeclare_mapper_allocatablem
+! CHECK: ^bb0(%[[ARG2:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_allocatableTt{{.*}}>):
+! CHECK:   %[[DECL2:.*]]:2 = hlfir.declare %[[ARG2]] {uniq_name = "_QFdeclare_mapper_allocatableEv"}
+! CHECK:   %[[IT2:.*]] = omp.iterator(%[[IV2:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:     %[[BOX_REF:.*]] = hlfir.designate %[[DECL2]]#0{"a"}{{.*}} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+! CHECK:     %[[BOX:.*]] = fir.load %[[BOX_REF]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+! CHECK:     %[[DIMS0:.*]]:3 = fir.box_dims %[[BOX]], %{{.*}} : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+! CHECK:     %[[DIMS1:.*]]:3 = fir.box_dims %[[BOX]], %{{.*}} : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+! CHECK:     %[[BOUNDS2:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%[[DIMS1]]#1 : index) stride(%[[DIMS1]]#2 : index) start_idx(%[[DIMS0]]#0 : index) stride_in_bytes(true)
+! CHECK:     %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+! CHECK:     %[[MAP2:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.heap<!fir.array<?xi32>>, i32) map_clauses(tofrom) capture(ByRef) bounds(%[[BOUNDS2]]) name("") -> !llvm.ptr
+! CHECK:     omp.yield(%[[MAP2]] : !llvm.ptr)
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT2]] : !omp.iterated<!llvm.ptr>)
+
+! CHECK-LABEL: omp.declare_mapper @_QQFdeclare_mapper_pointerm
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_pointerTt{{.*}}>):
+! CHECK:   %[[DECL:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFdeclare_mapper_pointerEv"}
+! CHECK:   %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}}) {
+! CHECK:     %[[BOX_REF:.*]] = hlfir.designate %[[DECL]]#0{"a"}{{.*}} -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+! CHECK:     %[[BOX:.*]] = fir.load %[[BOX_REF]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+! CHECK:     %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.ptr<!fir.array<?xi32>>>) -> !fir.ptr<!fir.array<?xi32>>
+! CHECK:     %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.ptr<!fir.array<?xi32>>, i32) map_clauses(tofrom) capture(ByRef) bounds(%{{.*}}) name("") -> !llvm.ptr
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! CHECK-LABEL: omp.declare_mapper @_QQFdeclare_mapper_2dm
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_2dTt{{.*}}>):
+! CHECK:   %[[DECL:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFdeclare_mapper_2dEv"}
+! CHECK:   %[[I_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[I_UB_I32:.*]] = arith.constant 4 : i32
+! CHECK:   %[[I_LB:.*]] = fir.convert %[[I_LB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK:   %[[I_UB:.*]] = fir.convert %[[I_UB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK:   %[[I_STEP:.*]] = arith.constant 1 : index
+! CHECK:   %[[J_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[J_UB_I32:.*]] = arith.constant 6 : i32
+! CHECK:   %[[J_LB:.*]] = fir.convert %[[J_LB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK:   %[[J_UB:.*]] = fir.convert %[[J_UB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK:   %[[J_STEP:.*]] = arith.constant 1 : index
+! CHECK:   %[[IT:.*]] = omp.iterator(%[[IV_I:.*]]: index,
+! CHECK-SAME: %[[IV_J:.*]]: index) =
+! CHECK-SAME: (%[[I_LB]] to %[[I_UB]] step %[[I_STEP]],
+! CHECK-SAME: %[[J_LB]] to %[[J_UB]] step %[[J_STEP]]) {
+! CHECK:     %[[IV_I_I32:.*]] = fir.convert %[[IV_I]]
+! CHECK-SAME: (index) -> i32
+! CHECK:     fir.store %[[IV_I_I32]] to %[[IV_I_MEM:.*]] : !fir.ref<i32>
+! CHECK:     %[[IV_I_DECL:.*]]:2 = hlfir.declare %[[IV_I_MEM]]
+! CHECK:     %[[IV_J_I32:.*]] = fir.convert %[[IV_J]]
+! CHECK-SAME: (index) -> i32
+! CHECK:     fir.store %[[IV_J_I32]] to %[[IV_J_MEM:.*]] : !fir.ref<i32>
+! CHECK:     %[[IV_J_DECL:.*]]:2 = hlfir.declare %[[IV_J_MEM]]
+! CHECK:     %[[EXT_I:.*]] = arith.constant 4 : index
+! CHECK:     %[[EXT_J:.*]] = arith.constant 6 : index
+! CHECK:     %[[A:.*]] = hlfir.designate %[[DECL]]#0{"a"}{{.*}} -> !fir.ref<!fir.array<4x6xi32>>
+! CHECK:     %[[IV_I_LD:.*]] = fir.load %[[IV_I_DECL]]#0
+! CHECK-SAME: !fir.ref<i32>
+! CHECK:     %[[IV_I_I64:.*]] = fir.convert %[[IV_I_LD]]
+! CHECK-SAME: (i32) -> i64
+! CHECK:     %[[IV_I_IDX:.*]] = fir.convert %[[IV_I_I64]]
+! CHECK-SAME: (i64) -> index
+! CHECK:     %[[LOC_I:.*]] = arith.subi %[[IV_I_IDX]], %{{.*}} : index
+! CHECK:     %[[B_I:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[LOC_I]] : index)
+! CHECK-SAME: upper_bound(%[[LOC_I]] : index)
+! CHECK-SAME: extent(%[[EXT_I]] : index)
+! CHECK:     %[[IV_J_LD:.*]] = fir.load %[[IV_J_DECL]]#0
+! CHECK-SAME: !fir.ref<i32>
+! CHECK:     %[[IV_J_I64:.*]] = fir.convert %[[IV_J_LD]]
+! CHECK-SAME: (i32) -> i64
+! CHECK:     %[[IV_J_IDX:.*]] = fir.convert %[[IV_J_I64]]
+! CHECK-SAME: (i64) -> index
+! CHECK:     %[[LOC_J:.*]] = arith.subi %[[IV_J_IDX]], %{{.*}} : index
+! CHECK:     %[[B_J:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[LOC_J]] : index)
+! CHECK-SAME: upper_bound(%[[LOC_J]] : index)
+! CHECK-SAME: extent(%[[EXT_J]] : index)
+! CHECK:     %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]
+! CHECK-SAME: : !fir.ref<!fir.array<4x6xi32>>,
+! CHECK-SAME: !fir.array<4x6xi32>) map_clauses(tofrom)
+! CHECK-SAME: capture(ByRef) bounds(%[[B_I]], %[[B_J]])
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! CHECK-LABEL: omp.declare_mapper @_QQFdeclare_mapper_sectionm
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_sectionTt{{.*}}>):
+! CHECK:   %[[DECL:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFdeclare_mapper_sectionEv"}
+! CHECK:   %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}}) {
+! CHECK:     %[[A:.*]] = hlfir.designate %[[DECL]]#0{"a"}{{.*}} -> !fir.ref<!fir.array<10xi32>>
+! CHECK:     %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i32
+! 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]] : !fir.ref<!fir.array<10xi32>>, !fir.array<10xi32>) map_clauses(tofrom) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! CHECK-LABEL: omp.declare_mapper @_QQFdeclare_mapper_multim
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_multiTt{{.*}}>):
+! CHECK:   %[[DECL:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFdeclare_mapper_multiEv"}
+! CHECK:   %[[IT_A:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}}) {
+! CHECK:     %[[A:.*]] = hlfir.designate %[[DECL]]#0{"a"}{{.*}} -> !fir.ref<!fir.array<10xi32>>
+! CHECK:     %[[MAP_A:.*]] = omp.map.info var_ptr(%[[A]] : !fir.ref<!fir.array<10xi32>>, !fir.array<10xi32>) map_clauses(tofrom) capture(ByRef) bounds(%{{.*}}) name("") -> !llvm.ptr
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   %[[IT_B:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}}) {
+! CHECK:     %[[B:.*]] = hlfir.designate %[[DECL]]#0{"b"}{{.*}} -> !fir.ref<!fir.array<10xi32>>
+! CHECK:     %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]] : !fir.ref<!fir.array<10xi32>>, !fir.array<10xi32>) map_clauses(tofrom) capture(ByRef) bounds(%{{.*}}) name("") -> !llvm.ptr
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT_A]], %[[IT_B]] : !omp.iterated<!llvm.ptr>, !omp.iterated<!llvm.ptr>)
+
+! CHECK-LABEL: omp.declare_mapper @_QQFdeclare_mapper_alloc_sectionm
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_alloc_sectionTt{{.*}}>):
+! CHECK:   %[[DECL:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFdeclare_mapper_alloc_sectionEv"}
+! CHECK:   %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}}) {
+! CHECK:     %[[BOX_REF:.*]] = hlfir.designate %[[DECL]]#0{"a"}{{.*}} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+! CHECK:     %[[BOX:.*]] = fir.load %[[BOX_REF]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+! CHECK:     %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i32
+! CHECK:     %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index) stride_in_bytes(true)
+! CHECK:     %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+! CHECK:     %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.heap<!fir.array<?xi32>>, i32) map_clauses(tofrom) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! CHECK-LABEL: omp.declare_mapper @_QQFdeclare_mapper_nondefault_lbm
+! CHECK: ^bb0(%[[ARG:.*]]: !fir.ref<!fir.type<_QFdeclare_mapper_nondefault_lbTt{{.*}}>):
+! CHECK:   %[[DECL:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFdeclare_mapper_nondefault_lbEv"}
+! CHECK:   %[[LB_I32:.*]] = arith.constant -2 : i32
+! CHECK:   %[[UB_I32:.*]] = arith.constant 6 : i32
+! CHECK:   %[[LB:.*]] = fir.convert %[[LB_I32]] : (i32) -> index
+! CHECK:   %[[UB:.*]] = fir.convert %[[UB_I32]] : (i32) -> index
+! CHECK:   %[[STEP:.*]] = arith.constant 1 : index
+! CHECK:   %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) =
+! CHECK-SAME: (%[[LB]] to %[[UB]] step %[[STEP]]) {
+! CHECK:     %[[IV_I32:.*]] = fir.convert %[[IV]] : (index) -> i32
+! CHECK:     fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:     %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:     %[[EXTENT:.*]] = arith.constant 10 : index
+! CHECK:     %[[START:.*]] = arith.constant -2 : index
+! CHECK:     %[[SHAPE:.*]] = fir.shape_shift %[[START]], %[[EXTENT]]
+! CHECK:     %[[BOX:.*]] = hlfir.designate %[[DECL]]#0{"a"}
+! CHECK-SAME: shape %[[SHAPE]]
+! CHECK-SAME: -> !fir.box<!fir.array<10xi32>>
+! CHECK:     %[[DIM:.*]] = arith.constant 0 : index
+! CHECK:     %[[DIMS:.*]]:3 = fir.box_dims %[[BOX]], %[[DIM]]
+! CHECK:     %[[IV_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:     %[[IV_I64:.*]] = fir.convert %[[IV_LD]] : (i32) -> i64
+! CHECK:     %[[IV_IDX:.*]] = fir.convert %[[IV_I64]] : (i64) -> index
+! CHECK:     %[[OFFSET:.*]] = arith.subi %[[IV_IDX]], %[[START]] : index
+! CHECK:     %[[BOUNDS:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[OFFSET]] : index)
+! CHECK-SAME: upper_bound(%[[OFFSET]] : index)
+! CHECK-SAME: extent(%[[DIMS]]#1 : index)
+! CHECK-SAME: stride(%[[DIMS]]#2 : index)
+! CHECK-SAME: start_idx(%[[START]] : index)
+! CHECK-SAME: stride_in_bytes(true)
+! CHECK:     %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.array<10xi32>>) -> !fir.ref<!fir.array<10xi32>>
+! CHECK:     %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.ref<!fir.array<10xi32>>, !fir.array<10xi32>) map_clauses(tofrom) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK:   omp.declare_mapper.info map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+!--- ranges.f90
+subroutine mapper_ranges()
+  type :: t
+    integer :: a(4), b(4)
+  end type
+  !$omp declare mapper(m: t :: v) &
+  !$omp& map(iterator(i=1:4, j=2:1): v%a(i), v%b(j))
+end
+
+! CHECK-LABEL: omp.declare_mapper @_QQFmapper_rangesm
+! CHECK: %[[A:.*]] = omp.iterator(%{{[^:]+}}: index) =
+! CHECK: omp.map.info
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: %[[B:.*]] = omp.iterator(%{{[^:]+}}: index) =
+! CHECK: omp.map.info
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.declare_mapper.info
+! CHECK-SAME: map_iterated(%[[A]], %[[B]]
+
+!--- mapper.f90
+module iterator_mapper_mod
+  type :: t
+    integer :: a(10)
+  end type
+  !$omp declare mapper(m: t :: v) map(iterator(i = 1:10): v%a(i))
+end module
+
+!--- consumer.f90
+subroutine use_iterator_mapper_mod
+  use iterator_mapper_mod
+  integer :: k
+  k = 1
+end subroutine
+
+! CHECK-LABEL: omp.declare_mapper @_QQMiterator_mapper_modm
+! CHECK: %[[ITER:.*]] = omp.iterator
+! CHECK: %[[MAP:.*]] = omp.map.info
+! CHECK: omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: omp.declare_mapper.info map_iterated(%[[ITER]]
diff --git a/flang/test/Lower/OpenMP/depend-iterator.f90 b/flang/test/Lower/OpenMP/depend-iterator.f90
index 0a11d73a45576..cb82faaef00bf 100644
--- a/flang/test/Lower/OpenMP/depend-iterator.f90
+++ b/flang/test/Lower/OpenMP/depend-iterator.f90
@@ -33,7 +33,7 @@ subroutine task_depend_iterator_simple()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) {
 ! CHECK:   omp.terminator
 ! CHECK: }
@@ -63,7 +63,7 @@ subroutine task_depend_iterator_2d()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %{{.*}}(%[[SHAPE]]) %[[IV0_I64]], %[[IV1_I64]] : (!fir.ref<!fir.array<4x6xi32>>, !fir.shape<2>, i64, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependinout -> %[[IT]] : !omp.iterated<!llvm.ptr>) {
 
 subroutine task_depend_iterator_mixed()
@@ -80,7 +80,7 @@ subroutine task_depend_iterator_mixed()
 ! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtask_depend_iterator_mixedEx"}
 ! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependout -> %[[X]]#0 : !fir.ref<i32>, taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) {
 
 subroutine task_depend_iterator_step()
@@ -95,13 +95,13 @@ subroutine task_depend_iterator_step()
 ! CHECK-LABEL: func.func @_QPtask_depend_iterator_step()
 ! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
 ! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32
+! CHECK: %[[C2_I32:.*]] = arith.constant 2 : 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:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) {
 
 subroutine task_depend_iterator_multi_obj()
@@ -120,12 +120,12 @@ subroutine task_depend_iterator_multi_obj()
 ! CHECK:   %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR1:.*]] = fir.convert %[[COOR1]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR1]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%[[IV2:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR2:.*]] = fir.convert %[[COOR2]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR2]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependinout -> %[[IT1]] : !omp.iterated<!llvm.ptr>, taskdependinout -> %[[IT2]] : !omp.iterated<!llvm.ptr>) {
 
 ! Expression-based subscript using multiple iterator variables: a((i-1)*m+j)
@@ -160,7 +160,7 @@ subroutine task_depend_iterator_expr_subscript()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %[[IDX]] : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependout -> %[[IT]] : !omp.iterated<!llvm.ptr>) {
 
 ! Multiple depend clauses each with their own iterator on the same task.
@@ -181,12 +181,12 @@ subroutine task_depend_multi_iter_clauses()
 ! CHECK:   %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<8xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR1:.*]] = fir.convert %[[COOR1]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR1]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%[[IV2:.*]]: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<8xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR2:.*]] = fir.convert %[[COOR2]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR2]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependin -> %[[IT1]] : !omp.iterated<!llvm.ptr>, taskdependout -> %[[IT2]] : !omp.iterated<!llvm.ptr>) {
 
 subroutine task_depend_iterator_negative_step()
@@ -201,15 +201,47 @@ subroutine task_depend_iterator_negative_step()
 ! CHECK-LABEL: func.func @_QPtask_depend_iterator_negative_step()
 ! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32
 ! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[CM1_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:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) {
 
+subroutine task_depend_iterator_wide(a, hi, step)
+  integer :: a(2)
+  integer(16) :: hi, step
+
+  !$omp task depend(iterator(integer(16) :: i = 0_16:hi:step), &
+  !$omp&                  in: a(int(i / step) + 1))
+  !$omp end task
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtask_depend_iterator_wide
+! CHECK: %[[HI:.*]] = fir.load {{.*}} : !fir.ref<i128>
+! CHECK: %[[STEP:.*]] = fir.load {{.*}} : !fir.ref<i128>
+! CHECK: omp.iterator(%[[IV:.*]]: i128) =
+! CHECK-SAME: ({{.*}} to %[[HI]] step %[[STEP]])
+! CHECK: fir.store %[[IV]] to {{.*}} : !fir.ref<i128>
+! CHECK: arith.divsi {{.*}} : i128
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+
+subroutine task_depend_iterator_empty(a)
+  integer :: a(5)
+
+  !$omp task depend(iterator(i = 5:1), in: a(i))
+  !$omp end task
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtask_depend_iterator_empty
+! CHECK: %[[FIVE:.*]] = arith.constant 5 : i32
+! CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+! CHECK: omp.iterator(%{{.*}}: index) =
+! CHECK-SAME: ({{.*}} to {{.*}} step {{.*}})
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+
 ! Mixed iterated and non-iterated objects in the same depend clause:
 ! a(1) does not reference the iterator IV, so it is lowered as a regular
 ! (non-iterated) depend var, while a(i) produces an omp.iterator.
@@ -229,7 +261,7 @@ subroutine task_depend_iterator_mixed_within_clause()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: omp.task depend(taskdependin -> %[[A1]] : !fir.ref<i32>, taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) {
 
 !===============================================================================
@@ -258,7 +290,7 @@ subroutine target_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(tofrom) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<16xi32>>
 ! CHECK: omp.target kernel_type(generic) depend(taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) map_entries(%[[MAP]] -> %{{.*}} : !fir.ref<!fir.array<16xi32>>) {
 ! CHECK:   omp.terminator
@@ -283,15 +315,15 @@ subroutine target_depend_iterator_multi()
 ! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<8xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<8xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT3:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR3:.*]] = fir.array_coor %[[C]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<8xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(tofrom) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<8xi32>>
 ! CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]]#1 : {{.*}}) map_clauses(tofrom) capture(ByRef) bounds({{.*}}) name("b") -> !fir.ref<!fir.array<8xi32>>
 ! CHECK: %[[MAP_C:.*]] = omp.map.info var_ptr(%[[C]]#1 : {{.*}}) map_clauses(implicit, tofrom) capture(ByRef) bounds({{.*}}) name("c") -> !fir.ref<!fir.array<8xi32>>
@@ -321,7 +353,7 @@ subroutine target_enter_data_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<16xi32>>
 ! CHECK: omp.target_enter_data depend(taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) map_entries(%[[MAP]] : !fir.ref<!fir.array<16xi32>>)
 
@@ -354,7 +386,7 @@ subroutine target_enter_data_depend_iterator_expr()
 ! CHECK:   %[[IDX:.*]] = fir.convert %[[ADD]] : (i32) -> i64
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %[[IDX]] : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<16xi32>>
 ! CHECK: omp.target_enter_data depend(taskdependinout -> %[[A1]] : !fir.ref<i32>, taskdependinout -> %[[IT]] : !omp.iterated<!llvm.ptr>) map_entries(%[[MAP]] : !fir.ref<!fir.array<16xi32>>)
 
@@ -382,7 +414,7 @@ subroutine target_exit_data_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<16xi32>>
 ! CHECK: omp.target_exit_data depend(taskdependout -> %[[IT]] : !omp.iterated<!llvm.ptr>) map_entries(%[[MAP]] : !fir.ref<!fir.array<16xi32>>)
 
@@ -400,18 +432,18 @@ subroutine target_exit_data_depend_iterator_multi()
 ! CHECK: %[[B:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_exit_data_depend_iterator_multiEb"}
 ! CHECK: %[[C16_I32:.*]] = arith.constant 16 : i32
 ! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[CM1_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:   %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = (%[[LB]] to %[[UB]] step %[[STEP]]) {
 ! CHECK:   %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<16xi32>>
 ! CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) name("b") -> !fir.ref<!fir.array<16xi32>>
 ! CHECK: omp.target_exit_data depend(taskdependout -> %[[IT1]] : !omp.iterated<!llvm.ptr>, taskdependout -> %[[IT2]] : !omp.iterated<!llvm.ptr>) map_entries(%[[MAP_A]], %[[MAP_B]] : !fir.ref<!fir.array<16xi32>>, !fir.ref<!fir.array<16xi32>>)
@@ -440,7 +472,7 @@ subroutine target_update_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = fir.array_coor %[[A]]#0(%[[SHAPE]]) %[[IV_I64]] : (!fir.ref<!fir.array<16xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   %[[PTR:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK:   omp.yield(%[[PTR]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<16xi32>>
 ! CHECK: omp.target_update depend(taskdependin -> %[[IT]] : !omp.iterated<!llvm.ptr>) map_entries(%[[MAP]] : !fir.ref<!fir.array<16xi32>>)
 
@@ -464,11 +496,11 @@ subroutine target_update_depend_iterator_multi()
 ! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR1:.*]] = fir.array_coor %[[A]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<8xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = fir.array_coor %[[B]]#0(%{{.*}}) %{{.*}} : (!fir.ref<!fir.array<8xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[A]]#1 : {{.*}}) map_clauses(to) capture(ByRef) bounds({{.*}}) name("a") -> !fir.ref<!fir.array<8xi32>>
 ! CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[B]]#1 : {{.*}}) map_clauses(from) capture(ByRef) bounds({{.*}}) name("b") -> !fir.ref<!fir.array<8xi32>>
 ! CHECK: omp.target_update depend(taskdependinout -> %[[X]]#0 : !fir.ref<i32>, taskdependin -> %[[IT1]] : !omp.iterated<!llvm.ptr>, taskdependout -> %[[IT2]] : !omp.iterated<!llvm.ptr>) map_entries(%[[MAP_A]], %[[MAP_B]] : !fir.ref<!fir.array<8xi32>>, !fir.ref<!fir.array<8xi32>>)
diff --git a/flang/test/Lower/OpenMP/iterator-ranges.f90 b/flang/test/Lower/OpenMP/iterator-ranges.f90
new file mode 100644
index 0000000000000..7c34288adff75
--- /dev/null
+++ b/flang/test/Lower/OpenMP/iterator-ranges.f90
@@ -0,0 +1,160 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o %t
+! RUN: FileCheck %s < %t
+! RUN: fir-opt --canonicalize --verify-each %t | FileCheck %s --check-prefix=CAN
+
+! Fortran endpoints are inclusive, including singleton and descending ranges.
+subroutine singleton(a)
+  integer :: a(4)
+  !$omp target update to(iterator(i=1:1): a(i))
+end
+! CHECK-LABEL: func.func @_QPsingleton
+! CHECK: omp.iterator
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CAN-LABEL: func.func @_QPsingleton
+! CAN: %[[ONE:.*]] = arith.constant 1 : index
+! CAN: omp.iterator(%{{.*}}: index) =
+! CAN-SAME: (%[[ONE]] to %[[ONE]] step %[[ONE]])
+! CAN: } inclusive -> !omp.iterated<!llvm.ptr>
+
+subroutine ascending(a)
+  integer :: a(4)
+  !$omp target enter data map(iterator(i=1:4), to: a(i))
+end
+! CHECK-LABEL: func.func @_QPascending
+! CHECK: omp.iterator
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CAN-LABEL: func.func @_QPascending
+! CAN-DAG: %[[ONE:.*]] = arith.constant 1 : index
+! CAN-DAG: %[[FOUR:.*]] = arith.constant 4 : index
+! CAN: omp.iterator(%{{.*}}: index) =
+! CAN-SAME: (%[[ONE]] to %[[FOUR]] step %[[ONE]])
+! CAN: } inclusive -> !omp.iterated<!llvm.ptr>
+
+subroutine descending(a)
+  integer :: a(4)
+  !$omp target exit data map(iterator(i=4:1:-1), from: a(i))
+end
+! CHECK-LABEL: func.func @_QPdescending
+! CHECK: omp.iterator
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CAN-LABEL: func.func @_QPdescending
+! CAN-DAG: %[[ONE:.*]] = arith.constant 1 : index
+! CAN-DAG: %[[FOUR:.*]] = arith.constant 4 : index
+! CAN-DAG: %[[NEG:.*]] = arith.constant -1 : index
+! CAN: omp.iterator(%{{.*}}: index) =
+! CAN-SAME: (%[[FOUR]] to %[[ONE]] step %[[NEG]])
+! CAN: } inclusive -> !omp.iterated<!llvm.ptr>
+
+! Empty ranges must survive canonicalization and verification. The containing
+! directive and its unrelated map entry must remain present.
+subroutine empty_positive(a, b)
+  integer :: a(4), b
+  !$omp target update to(iterator(i=2:1): a(i), b) nowait
+end
+! CHECK-LABEL: func.func @_QPempty_positive
+! CHECK: omp.iterator
+! CAN-LABEL: func.func @_QPempty_positive
+! CAN-DAG: %[[ONE:.*]] = arith.constant 1 : index
+! CAN-DAG: %[[TWO:.*]] = arith.constant 2 : index
+! CAN: omp.iterator(%{{.*}}: index) =
+! CAN-SAME: (%[[TWO]] to %[[ONE]] step %[[ONE]])
+! CAN: } inclusive -> !omp.iterated<!llvm.ptr>
+! CAN: omp.target_update
+! CAN-SAME: map_entries(
+! CAN-SAME: map_iterated(
+! CAN-SAME: nowait
+
+subroutine empty_negative(a)
+  integer :: a(4)
+  !$omp target update from(iterator(i=1:2:-1): a(i))
+end
+! CHECK-LABEL: func.func @_QPempty_negative
+! CHECK: omp.iterator
+! CAN-LABEL: func.func @_QPempty_negative
+! CAN-DAG: %[[ONE:.*]] = arith.constant 1 : index
+! CAN-DAG: %[[TWO:.*]] = arith.constant 2 : index
+! CAN-DAG: %[[NEG:.*]] = arith.constant -1 : index
+! CAN: omp.iterator(%{{.*}}: index) =
+! CAN-SAME: (%[[ONE]] to %[[TWO]] step %[[NEG]])
+! CAN: } inclusive -> !omp.iterated<!llvm.ptr>
+
+subroutine dynamic_range(a, lo, hi, step)
+  integer :: a(4), lo, hi, step
+  !$omp target data map(iterator(i=lo:hi:step), to: a(i))
+  !$omp end target data
+end
+! CHECK-LABEL: func.func @_QPdynamic_range
+! CHECK: omp.iterator
+! CAN-LABEL: func.func @_QPdynamic_range
+! CAN: omp.iterator
+! CAN: } inclusive -> !omp.iterated<!llvm.ptr>
+! CAN: omp.target_data
+! CAN-SAME: map_iterated(
+
+! Each locator uses only its own dimensions. An unused empty range cannot
+! suppress a(i), and unrelated range expressions are still evaluated.
+subroutine separate_ranges(a, b, c, n)
+  integer :: a(4), b(4), c(4, 4), n
+  integer, external :: extent
+  !$omp target update to(iterator(i=1:4, j=1:n, k=1:extent()): &
+  !$omp& a(i), b(j), c(i,j))
+end
+! CHECK-LABEL: func.func @_QPseparate_ranges
+! CHECK: fir.call @_QPextent()
+! CHECK: omp.iterator(%{{[^:]+}}: index) =
+! CHECK: omp.iterator(%{{[^:]+}}: index) =
+! CHECK: omp.iterator(%{{[^:]+}}: index, %{{[^:]+}}: index) =
+! CHECK: omp.target_update
+! CAN-LABEL: func.func @_QPseparate_ranges
+! CAN: fir.call @_QPextent()
+! CAN: omp.iterator(%{{[^:]+}}: index) =
+! CAN: omp.iterator(%{{[^:]+}}: index) =
+! CAN: omp.iterator(%{{[^:]+}}: index, %{{[^:]+}}: index) =
+! CAN: omp.target_update
+
+subroutine unused_empty_range(a)
+  integer :: a(4)
+  !$omp target enter data map(iterator(i=1:4, j=1:0), to: a(i))
+end
+! CHECK-LABEL: func.func @_QPunused_empty_range
+! CHECK: omp.iterator(%{{[^:]+}}: index) =
+! CAN-LABEL: func.func @_QPunused_empty_range
+! CAN-DAG: %[[ONE:.*]] = arith.constant 1 : index
+! CAN-DAG: %[[FOUR:.*]] = arith.constant 4 : index
+! CAN: omp.iterator(%{{[^:]+}}: index) =
+! CAN-SAME: (%[[ONE]] to %[[FOUR]] step %[[ONE]])
+
+! Values beyond the index width must reach the iterator and its body intact.
+subroutine wide_range(a, hi, step)
+  integer :: a(2)
+  integer(16) :: hi, step
+  !$omp target update to(iterator(integer(16) :: i=0_16:hi:step): &
+  !$omp& a(int(i/step)+1))
+end
+! CHECK-LABEL: func.func @_QPwide_range
+! CHECK: %[[HI:.*]] = fir.load {{.*}} : !fir.ref<i128>
+! CHECK: %[[STEP:.*]] = fir.load {{.*}} : !fir.ref<i128>
+! CHECK-NOT: fir.convert
+! CHECK: omp.iterator(%[[IV:.*]]: i128) =
+! CHECK-SAME: ({{.*}} to %[[HI]] step %[[STEP]])
+! CHECK: fir.store %[[IV]] to {{.*}} : !fir.ref<i128>
+! CHECK: arith.divsi {{.*}} : i128
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CAN-LABEL: func.func @_QPwide_range
+! CAN: omp.iterator(%{{.*}}: i128) =
+! CAN: arith.divsi {{.*}} : i128
+! CAN: } inclusive -> !omp.iterated<!llvm.ptr>
+
+subroutine wide_default_step(a, hi)
+  integer :: a(2)
+  integer(16) :: hi
+  !$omp target enter data map(iterator(integer(16) :: i=hi:hi), &
+  !$omp& to: a(int(i-hi)+1))
+end
+! CHECK-LABEL: func.func @_QPwide_default_step
+! CHECK: %[[ONE:.*]] = arith.constant 1 : i128
+! CHECK: omp.iterator(%{{.*}}: i128) =
+! CHECK-SAME: ({{.*}} to {{.*}} step %[[ONE]])
+! CAN-LABEL: func.func @_QPwide_default_step
+! CAN: omp.iterator(%{{.*}}: i128) =
+! CAN: } inclusive -> !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..58d71ac9509f6
--- /dev/null
+++ b/flang/test/Lower/OpenMP/motion-iterator.f90
@@ -0,0 +1,951 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 \
+! RUN:   %t/motion.f90 -o - | FileCheck %t/motion.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 \
+! RUN:   %t/descriptors.f90 -o - | FileCheck %t/descriptors.f90 \
+! RUN:   --implicit-check-not=fir.box_offset \
+! RUN:   --implicit-check-not=map_entries \
+! RUN:   --implicit-check-not='map_clauses(attach'
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 \
+! RUN:   %t/implicit-mapper.f90 -o - | FileCheck %t/implicit-mapper.f90
+
+!--- motion.f90
+! 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:   fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:   %[[IV_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[IV_I64:.*]] = fir.convert %[[IV_LD]] : (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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_assumed_shape(a, n)
+  integer, intent(in) :: n
+  real :: a(:)
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_assumed_shape
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {uniq_name = "_QFtarget_update_assumed_shapeEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[DIMS:.*]]:3 = fir.box_dims %[[A]]#0, %{{.*}} : (!fir.box<!fir.array<?xf32>>, index) -> (index, index, index)
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%[[DIMS]]#1 : index) stride(%[[DIMS]]#2 : index) start_idx(%{{.*}} : index) stride_in_bytes(true)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !fir.ref<!fir.array<?xf32>>, f32) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_assumed_shape_2d(a, n, m)
+  integer, intent(in) :: n, m
+  real :: a(:, :)
+  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_assumed_shape_2d
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {uniq_name = "_QFtarget_update_assumed_shape_2dEa"}
+! CHECK: %[[M:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 3
+! CHECK-SAME: uniq_name = "_QFtarget_update_assumed_shape_2dEm"
+! CHECK: %[[N:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 2
+! CHECK-SAME: uniq_name = "_QFtarget_update_assumed_shape_2dEn"
+! CHECK: %[[N_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[N_UB_I32:.*]] = fir.load %[[N]]#0 : !fir.ref<i32>
+! CHECK: %[[N_LB:.*]] = fir.convert %[[N_LB_I32]] : (i32) -> index
+! CHECK: %[[N_UB:.*]] = fir.convert %[[N_UB_I32]] : (i32) -> index
+! CHECK: %[[N_STEP:.*]] = arith.constant 1 : index
+! CHECK: %[[M_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[M_UB_I32:.*]] = fir.load %[[M]]#0 : !fir.ref<i32>
+! CHECK: %[[M_LB:.*]] = fir.convert %[[M_LB_I32]] : (i32) -> index
+! CHECK: %[[M_UB:.*]] = fir.convert %[[M_UB_I32]] : (i32) -> index
+! CHECK: %[[M_STEP:.*]] = arith.constant 1 : index
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: index,
+! CHECK-SAME: %[[IV1:.*]]: index) =
+! CHECK-SAME: (%[[N_LB]] to %[[N_UB]] step %[[N_STEP]],
+! CHECK-SAME: %[[M_LB]] to %[[M_UB]] step %[[M_STEP]]) {
+! CHECK: %[[IV0_I32:.*]] = fir.convert %[[IV0]] : (index) -> i32
+! CHECK: fir.store %[[IV0_I32]] to %[[IV0_ADDR:.*]] : !fir.ref<i32>
+! CHECK: %[[IV0_DECL:.*]]:2 = hlfir.declare %[[IV0_ADDR]]
+! CHECK: %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32
+! CHECK: fir.store %[[IV1_I32]] to %[[IV1_ADDR:.*]] : !fir.ref<i32>
+! CHECK: %[[IV1_DECL:.*]]:2 = hlfir.declare %[[IV1_ADDR]]
+! CHECK: %[[START0:.*]] = arith.constant 1 : index
+! CHECK: %[[DIM0:.*]] = arith.constant 0 : index
+! CHECK: %[[DIMS0:.*]]:3 = fir.box_dims %[[A]]#0, %[[DIM0]]
+! CHECK: %[[IV0_LOAD:.*]] = fir.load %[[IV0_DECL]]#0
+! CHECK: %[[IV0_I64:.*]] = fir.convert %[[IV0_LOAD]] : (i32) -> i64
+! CHECK: %[[IV0_INDEX:.*]] = fir.convert %[[IV0_I64]] : (i64) -> index
+! CHECK: %[[INDEX0:.*]] = arith.subi %[[IV0_INDEX]], %[[START0]]
+! CHECK: %[[BOUNDS0:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[INDEX0]] : index)
+! CHECK-SAME: upper_bound(%[[INDEX0]] : index)
+! CHECK-SAME: extent(%[[DIMS0]]#1 : index)
+! CHECK-SAME: stride(%[[DIMS0]]#2 : index)
+! CHECK-SAME: start_idx(%[[START0]] : index)
+! CHECK-SAME: stride_in_bytes(true)
+! CHECK: %[[START1:.*]] = arith.constant 1 : index
+! CHECK: %[[DIM1:.*]] = arith.constant 1 : index
+! CHECK: %[[DIMS1:.*]]:3 = fir.box_dims %[[A]]#0, %[[DIM1]]
+! CHECK: %[[IV1_LOAD:.*]] = fir.load %[[IV1_DECL]]#0
+! CHECK: %[[IV1_I64:.*]] = fir.convert %[[IV1_LOAD]] : (i32) -> i64
+! CHECK: %[[IV1_INDEX:.*]] = fir.convert %[[IV1_I64]] : (i64) -> index
+! CHECK: %[[INDEX1:.*]] = arith.subi %[[IV1_INDEX]], %[[START1]]
+! CHECK: %[[BOUNDS1:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[INDEX1]] : index)
+! CHECK-SAME: upper_bound(%[[INDEX1]] : index)
+! CHECK-SAME: extent(%[[DIMS1]]#1 : index)
+! CHECK-SAME: stride(%[[DIMS1]]#2 : index)
+! CHECK-SAME: start_idx(%[[START1]] : index)
+! CHECK-SAME: stride_in_bytes(true)
+! CHECK: %[[BASE:.*]] = fir.box_addr %[[A]]#0
+! CHECK: %[[MAP:.*]] = omp.map.info
+! CHECK-SAME: var_ptr(%[[BASE]] : !fir.ref<!fir.array<?x?xf32>>, f32)
+! CHECK-SAME: map_clauses(to)
+! CHECK-SAME: capture(ByRef)
+! CHECK-SAME: bounds(%[[BOUNDS0]], %[[BOUNDS1]])
+! CHECK-SAME: name("")
+! CHECK-SAME: -> !llvm.ptr
+! CHECK: omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_allocatable(a, n)
+  integer, allocatable :: a(:)
+  integer, intent(in) :: n
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_allocatable
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtarget_update_allocatableEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOX:.*]] = fir.load %[[A]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+! CHECK:   %[[DIMS0:.*]]:3 = fir.box_dims %[[BOX]], %{{.*}} : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+! CHECK:   %[[DIMS1:.*]]:3 = fir.box_dims %[[BOX]], %{{.*}} : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%[[DIMS1]]#1 : index) stride(%[[DIMS1]]#2 : index) start_idx(%[[DIMS0]]#0 : index) stride_in_bytes(true)
+! CHECK:   %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.heap<!fir.array<?xi32>>, i32) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_pointer(a, n)
+  integer, pointer :: a(:)
+  integer, intent(in) :: n
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_pointer
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtarget_update_pointerEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOX:.*]] = fir.load %[[A]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
+! CHECK:   %[[DIMS0:.*]]:3 = fir.box_dims %[[BOX]], %{{.*}} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+! CHECK:   %[[DIMS1:.*]]:3 = fir.box_dims %[[BOX]], %{{.*}} : (!fir.box<!fir.ptr<!fir.array<?xi32>>>, index) -> (index, index, index)
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%[[DIMS1]]#1 : index) stride(%[[DIMS1]]#2 : index) start_idx(%[[DIMS0]]#0 : index) stride_in_bytes(true)
+! CHECK:   %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.ptr<!fir.array<?xi32>>>) -> !fir.ptr<!fir.array<?xi32>>
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.ptr<!fir.array<?xi32>>, i32) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_allocatable_2d(a, n, m)
+  integer, allocatable :: a(:, :)
+  integer, intent(in) :: 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_allocatable_2d
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtarget_update_allocatable_2dEa"}
+! CHECK: %[[M:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 3
+! CHECK-SAME: uniq_name = "_QFtarget_update_allocatable_2dEm"
+! CHECK: %[[N:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 2
+! CHECK-SAME: uniq_name = "_QFtarget_update_allocatable_2dEn"
+! CHECK: %[[N_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[N_UB_I32:.*]] = fir.load %[[N]]#0 : !fir.ref<i32>
+! CHECK: %[[N_LB:.*]] = fir.convert %[[N_LB_I32]] : (i32) -> index
+! CHECK: %[[N_UB:.*]] = fir.convert %[[N_UB_I32]] : (i32) -> index
+! CHECK: %[[N_STEP:.*]] = arith.constant 1 : index
+! CHECK: %[[M_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[M_UB_I32:.*]] = fir.load %[[M]]#0 : !fir.ref<i32>
+! CHECK: %[[M_LB:.*]] = fir.convert %[[M_LB_I32]] : (i32) -> index
+! CHECK: %[[M_UB:.*]] = fir.convert %[[M_UB_I32]] : (i32) -> index
+! CHECK: %[[M_STEP:.*]] = arith.constant 1 : index
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: index,
+! CHECK-SAME: %[[IV1:.*]]: index) =
+! CHECK-SAME: (%[[N_LB]] to %[[N_UB]] step %[[N_STEP]],
+! CHECK-SAME: %[[M_LB]] to %[[M_UB]] step %[[M_STEP]]) {
+! CHECK: %[[IV0_I32:.*]] = fir.convert %[[IV0]] : (index) -> i32
+! CHECK: fir.store %[[IV0_I32]] to %[[IV0_ADDR:.*]] : !fir.ref<i32>
+! CHECK: %[[IV0_DECL:.*]]:2 = hlfir.declare %[[IV0_ADDR]]
+! CHECK: %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32
+! CHECK: fir.store %[[IV1_I32]] to %[[IV1_ADDR:.*]] : !fir.ref<i32>
+! CHECK: %[[IV1_DECL:.*]]:2 = hlfir.declare %[[IV1_ADDR]]
+! CHECK: %[[BOX:.*]] = fir.load %[[A]]#0
+! CHECK: %[[C0:.*]] = arith.constant 0 : index
+! CHECK: %[[DIMS0_LB:.*]]:3 = fir.box_dims %[[BOX]], %[[C0]]
+! CHECK: %[[C0_EXTENT:.*]] = arith.constant 0 : index
+! CHECK: %[[DIMS0_EXTENT:.*]]:3 = fir.box_dims %[[BOX]], %[[C0_EXTENT]]
+! CHECK: %[[IV0_LOAD:.*]] = fir.load %[[IV0_DECL]]#0
+! CHECK: %[[IV0_I64:.*]] = fir.convert %[[IV0_LOAD]] : (i32) -> i64
+! CHECK: %[[IV0_INDEX:.*]] = fir.convert %[[IV0_I64]] : (i64) -> index
+! CHECK: %[[INDEX0:.*]] = arith.subi %[[IV0_INDEX]], %[[DIMS0_LB]]#0
+! CHECK: %[[BOUNDS0:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[INDEX0]] : index)
+! CHECK-SAME: upper_bound(%[[INDEX0]] : index)
+! CHECK-SAME: extent(%[[DIMS0_EXTENT]]#1 : index)
+! CHECK-SAME: stride(%[[DIMS0_EXTENT]]#2 : index)
+! CHECK-SAME: start_idx(%[[DIMS0_LB]]#0 : index)
+! CHECK-SAME: stride_in_bytes(true)
+! CHECK: %[[C1:.*]] = arith.constant 1 : index
+! CHECK: %[[DIMS1_LB:.*]]:3 = fir.box_dims %[[BOX]], %[[C1]]
+! CHECK: %[[C1_EXTENT:.*]] = arith.constant 1 : index
+! CHECK: %[[DIMS1_EXTENT:.*]]:3 = fir.box_dims %[[BOX]], %[[C1_EXTENT]]
+! CHECK: %[[IV1_LOAD:.*]] = fir.load %[[IV1_DECL]]#0
+! CHECK: %[[IV1_I64:.*]] = fir.convert %[[IV1_LOAD]] : (i32) -> i64
+! CHECK: %[[IV1_INDEX:.*]] = fir.convert %[[IV1_I64]] : (i64) -> index
+! CHECK: %[[INDEX1:.*]] = arith.subi %[[IV1_INDEX]], %[[DIMS1_LB]]#0
+! CHECK: %[[BOUNDS1:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[INDEX1]] : index)
+! CHECK-SAME: upper_bound(%[[INDEX1]] : index)
+! CHECK-SAME: extent(%[[DIMS1_EXTENT]]#1 : index)
+! CHECK-SAME: stride(%[[DIMS1_EXTENT]]#2 : index)
+! CHECK-SAME: start_idx(%[[DIMS1_LB]]#0 : index)
+! CHECK-SAME: stride_in_bytes(true)
+! CHECK: %[[BASE:.*]] = fir.box_addr %[[BOX]]
+! CHECK: %[[MAP:.*]] = omp.map.info
+! CHECK-SAME: var_ptr(%[[BASE]] : !fir.heap<!fir.array<?x?xi32>>, i32)
+! CHECK-SAME: map_clauses(to)
+! CHECK-SAME: capture(ByRef)
+! CHECK-SAME: bounds(%[[BOUNDS0]], %[[BOUNDS1]])
+! CHECK-SAME: name("")
+! CHECK-SAME: -> !llvm.ptr
+! CHECK: omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_deferred_char(a, n)
+  character(:), allocatable :: a(:)
+  integer, intent(in) :: n
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_deferred_char
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtarget_update_deferred_charEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOX:.*]] = fir.load %[[A]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index) stride_in_bytes(true)
+! CHECK:   %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>) -> !fir.heap<!fir.array<?x!fir.char<1,?>>>
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.heap<!fir.array<?x!fir.char<1,?>>>, !fir.char<1,?>) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+subroutine target_update_class_star(a, n)
+  class(*), allocatable :: a(:)
+  integer, intent(in) :: n
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:n): a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_class_star
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtarget_update_class_starEa"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[BOX:.*]] = fir.load %[[A]]#0 : !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%{{.*}} : index) stride(%{{.*}} : index) start_idx(%{{.*}} : index) stride_in_bytes(true)
+! CHECK:   %[[BASE:.*]] = fir.box_addr %[[BOX]] : (!fir.class<!fir.heap<!fir.array<?xnone>>>) -> !fir.heap<!fir.array<?xnone>>
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.heap<!fir.array<?xnone>>, none) map_clauses(to) capture(ByRef) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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:   fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:   %[[IV_LB_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[LB_I64:.*]] = fir.convert %[[IV_LB_LD]] : (i32) -> i64
+! CHECK:   %[[LB_IDX:.*]] = fir.convert %[[LB_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[LB_IDX]], %{{.*}} : index
+! CHECK:   %[[IV_UB_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[UB_EXPR:.*]] = arith.addi %[[IV_UB_LD]], %[[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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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: %[[EXT_I:.*]] = arith.constant 4 : index
+! CHECK: %[[EXT_J:.*]] = arith.constant 6 : index
+! CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtarget_update_2dEa"}
+! CHECK: %[[I_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[I_UB_I32:.*]] = arith.constant 4 : i32
+! CHECK: %[[I_LB:.*]] = fir.convert %[[I_LB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK: %[[I_UB:.*]] = fir.convert %[[I_UB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK: %[[I_STEP:.*]] = arith.constant 1 : index
+! CHECK: %[[J_LB_I32:.*]] = arith.constant 1 : i32
+! CHECK: %[[J_UB_I32:.*]] = arith.constant 6 : i32
+! CHECK: %[[J_LB:.*]] = fir.convert %[[J_LB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK: %[[J_UB:.*]] = fir.convert %[[J_UB_I32]]
+! CHECK-SAME: (i32) -> index
+! CHECK: %[[J_STEP:.*]] = arith.constant 1 : index
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV_I:.*]]: index,
+! CHECK-SAME: %[[IV_J:.*]]: index) =
+! CHECK-SAME: (%[[I_LB]] to %[[I_UB]] step %[[I_STEP]],
+! CHECK-SAME: %[[J_LB]] to %[[J_UB]] step %[[J_STEP]]) {
+! CHECK:   %[[IV_I_I32:.*]] = fir.convert %[[IV_I]]
+! CHECK-SAME: (index) -> i32
+! CHECK:   fir.store %[[IV_I_I32]] to %[[IV_I_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_I_DECL:.*]]:2 = hlfir.declare %[[IV_I_MEM]]
+! CHECK:   %[[IV_J_I32:.*]] = fir.convert %[[IV_J]]
+! CHECK-SAME: (index) -> i32
+! CHECK:   fir.store %[[IV_J_I32]] to %[[IV_J_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_J_DECL:.*]]:2 = hlfir.declare %[[IV_J_MEM]]
+! CHECK:   %[[IV_I_LD:.*]] = fir.load %[[IV_I_DECL]]#0
+! CHECK-SAME: !fir.ref<i32>
+! CHECK:   %[[IV_I_I64:.*]] = fir.convert %[[IV_I_LD]]
+! CHECK-SAME: (i32) -> i64
+! CHECK:   %[[IV_I_IDX:.*]] = fir.convert %[[IV_I_I64]]
+! CHECK-SAME: (i64) -> index
+! CHECK:   %[[LOC_I:.*]] = arith.subi %[[IV_I_IDX]], %{{.*}} : index
+! CHECK:   %[[B_I:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[LOC_I]] : index)
+! CHECK-SAME: upper_bound(%[[LOC_I]] : index)
+! CHECK-SAME: extent(%[[EXT_I]] : index)
+! CHECK:   %[[IV_J_LD:.*]] = fir.load %[[IV_J_DECL]]#0
+! CHECK-SAME: !fir.ref<i32>
+! CHECK:   %[[IV_J_I64:.*]] = fir.convert %[[IV_J_LD]]
+! CHECK-SAME: (i32) -> i64
+! CHECK:   %[[IV_J_IDX:.*]] = fir.convert %[[IV_J_I64]]
+! CHECK-SAME: (i64) -> index
+! CHECK:   %[[LOC_J:.*]] = arith.subi %[[IV_J_IDX]], %{{.*}} : index
+! CHECK:   %[[B_J:.*]] = omp.map.bounds
+! CHECK-SAME: lower_bound(%[[LOC_J]] : index)
+! CHECK-SAME: upper_bound(%[[LOC_J]] : index)
+! CHECK-SAME: extent(%[[EXT_J]] : index)
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[A]]#0
+! CHECK-SAME: : !fir.ref<!fir.array<4x6xi32>>,
+! CHECK-SAME: !fir.array<4x6xi32>) map_clauses(to)
+! CHECK-SAME: capture(ByRef) bounds(%[[B_I]], %[[B_J]])
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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: %[[C2_I32:.*]] = arith.constant 2 : i32
+! CHECK: %[[LB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index
+! CHECK: %[[UB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index
+! 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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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: %[[CM1_I32:.*]] = arith.constant -1 : i32
+! CHECK: %[[LB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index
+! CHECK: %[[UB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index
+! 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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP1]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP2]] : !llvm.ptr)
+! CHECK: } inclusive -> !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({{.*}}) name("a(1)") -> !fir.ref<!fir.array<16xi32>>
+! 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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP_IT]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP1]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP2]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP_IT]] : !llvm.ptr)
+! CHECK: } inclusive -> !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({{.*}}) name("b") -> !fir.ref<!fir.array<16xi32>>
+! 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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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) name("x") -> !fir.ref<!fir.type<_QFtarget_update_default_mapperTs{a:i32}>>
+! 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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_update map_iterated(%[[IT]] : !omp.iterated<!llvm.ptr>)
+
+! Assumed-shape array of a derived type with a mapper, mapped per iteration.
+subroutine target_update_assumed_shape_mapper(x, n)
+  type :: s
+    integer :: a
+  end type
+  type(s) :: x(:)
+  integer, intent(in) :: n
+  integer :: i
+
+  !$omp declare mapper(s :: v) map(to: v%a)
+  !$omp target update to(iterator(i = 1:n): x(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_update_assumed_shape_mapper
+! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} dummy_scope %{{.*}} arg 1 {uniq_name = "_QFtarget_update_assumed_shape_mapperEx"}
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: index) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   %[[DIMS:.*]]:3 = fir.box_dims %[[X]]#0, %{{.*}} : (!fir.box<!fir.array<?x!fir.type<_QFtarget_update_assumed_shape_mapperTs{a:i32}>>>, index) -> (index, index, index)
+! CHECK:   %[[BOUNDS:.*]] = omp.map.bounds lower_bound(%{{.*}} : index) upper_bound(%{{.*}} : index) extent(%[[DIMS]]#1 : index) stride(%[[DIMS]]#2 : index) start_idx(%{{.*}} : index) stride_in_bytes(true)
+! CHECK:   %[[BASE:.*]] = fir.box_addr %[[X]]#0 : (!fir.box<!fir.array<?x!fir.type<_QFtarget_update_assumed_shape_mapperTs{a:i32}>>>) -> !fir.ref<!fir.array<?x!fir.type<_QFtarget_update_assumed_shape_mapperTs{a:i32}>>>
+! CHECK:   %[[MAP:.*]] = omp.map.info var_ptr(%[[BASE]] : !fir.ref<!fir.array<?x!fir.type<_QFtarget_update_assumed_shape_mapperTs{a:i32}>>>, !fir.type<_QFtarget_update_assumed_shape_mapperTs{a:i32}>) map_clauses(to) capture(ByRef) mapper(@_QQFtarget_update_assumed_shape_mappers_omp_default_mapper) bounds(%[[BOUNDS]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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:   fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:   %[[IV_LB_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[LB_I64:.*]] = fir.convert %[[IV_LB_LD]] : (i32) -> i64
+! CHECK:   %[[LB_IDX:.*]] = fir.convert %[[LB_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[LB_IDX]], %{{.*}} : index
+! CHECK:   %[[IV_UB_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[UB_EXPR:.*]] = arith.addi %[[IV_UB_LD]], %[[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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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:   fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:   %[[IV_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[IV_I64:.*]] = fir.convert %[[IV_LD]] : (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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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:   fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:   %[[IV_LB_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[LB_I64:.*]] = fir.convert %[[IV_LB_LD]] : (i32) -> i64
+! CHECK:   %[[LB_IDX:.*]] = fir.convert %[[LB_I64]] : (i64) -> index
+! CHECK:   %[[LB:.*]] = arith.subi %[[LB_IDX]], %{{.*}} : index
+! CHECK:   %[[IV_UB_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[C2_I32:.*]] = arith.constant 2 : i32
+! CHECK:   %[[UB_EXPR:.*]] = arith.addi %[[IV_UB_LD]], %[[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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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:   fir.store %[[IV0_I32]] to %[[IV0_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV0_DECL:.*]]:2 = hlfir.declare %[[IV0_MEM]]
+! CHECK:   %[[IV1_I32:.*]] = fir.convert %[[IV1]] : (index) -> i32
+! CHECK:   fir.store %[[IV1_I32]] to %[[IV1_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV1_DECL:.*]]:2 = hlfir.declare %[[IV1_MEM]]
+! CHECK:   %[[IV0_LD:.*]] = fir.load %[[IV0_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[C1_I32:.*]] = arith.constant 1 : i32
+! CHECK:   %[[SUB:.*]] = arith.subi %[[IV0_LD]], %[[C1_I32]] : i32
+! CHECK:   %[[NOREASSOC:.*]] = hlfir.no_reassoc %[[SUB]] : i32
+! CHECK:   %[[MUL:.*]] = arith.muli %{{.*}}, %[[NOREASSOC]] : i32
+! CHECK:   %[[IV1_LD:.*]] = fir.load %[[IV1_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[ADD:.*]] = arith.addi %[[MUL]], %[[IV1_LD]] : 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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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:   fir.store %[[IV_I32]] to %[[IV_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_DECL:.*]]:2 = hlfir.declare %[[IV_MEM]]
+! CHECK:   %[[IV_LD:.*]] = fir.load %[[IV_DECL]]#0 : !fir.ref<i32>
+! CHECK:   %[[IV_I64:.*]] = fir.convert %[[IV_LD]] : (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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP]] : !llvm.ptr)
+! CHECK: } inclusive -> !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: %[[CM1_I32:.*]] = arith.constant -1 : i32
+! CHECK: %[[LB:.*]] = fir.convert %[[C16_I32]] : (i32) -> index
+! CHECK: %[[UB:.*]] = fir.convert %[[C1_I32]] : (i32) -> index
+! 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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP1]] : !llvm.ptr)
+! CHECK: } inclusive -> !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]]) name("") -> !llvm.ptr
+! CHECK:   omp.yield(%[[MAP2]] : !llvm.ptr)
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: omp.target_exit_data map_iterated(%[[IT1]], %[[IT2]] : !omp.iterated<!llvm.ptr>, !omp.iterated<!llvm.ptr>)
+
+!--- descriptors.f90
+! Verify that descriptor-backed iterator maps describe only selected data and
+! MapInfoFinalization does not add descriptor parent or attachment maps.
+
+subroutine iter_map_assumed_shape(a, n)
+  integer :: a(:)
+  integer :: n
+  integer :: i
+
+  !$omp target enter data map(iterator(i = 1:n), to: a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPiter_map_assumed_shape(
+! CHECK: %[[IT:.*]] = omp.iterator
+! CHECK: %[[ADDR:.*]] = fir.box_addr
+! CHECK: %[[MAP:.*]] = omp.map.info
+! CHECK-SAME: var_ptr(%[[ADDR]]
+! CHECK-SAME: map_clauses(to) capture(ByRef)
+! CHECK-SAME: bounds(%{{.*}})
+! CHECK: omp.yield(%[[MAP]]
+! CHECK: } inclusive -> !omp.iterated
+! CHECK: omp.target_enter_data map_iterated(%[[IT]]
+
+subroutine iter_map_allocatable(a, n)
+  integer, allocatable :: a(:)
+  integer :: n
+  integer :: i
+
+  !$omp target enter data map(iterator(i = 1:n), to: a(i))
+end subroutine
+
+! CHECK-LABEL: func.func @_QPiter_map_allocatable(
+! CHECK: %[[IT:.*]] = omp.iterator
+! CHECK: %[[BOX:.*]] = fir.load
+! CHECK: %[[ADDR:.*]] = fir.box_addr %[[BOX]]
+! CHECK: %[[MAP:.*]] = omp.map.info
+! CHECK-SAME: var_ptr(%[[ADDR]]
+! CHECK-SAME: map_clauses(to) capture(ByRef)
+! CHECK-SAME: bounds(%{{.*}})
+! CHECK: omp.yield(%[[MAP]]
+! CHECK: } inclusive -> !omp.iterated
+! CHECK: omp.target_enter_data map_iterated(%[[IT]]
+
+!--- implicit-mapper.f90
+subroutine implicit_mapper_to()
+  type :: t
+    integer, allocatable :: a(:)
+  end type
+  type(t) :: x
+
+  !$omp target update to(x)
+end subroutine
+
+subroutine implicit_mapper_from()
+  type :: inner
+    integer, allocatable :: a(:)
+  end type
+  type :: outer
+    type(inner) :: nested
+  end type
+  type(outer) :: x
+
+  !$omp target update from(x)
+end subroutine
+
+subroutine implicit_mapper_iterator()
+  type :: t
+    integer, allocatable :: a(:)
+  end type
+  type(t) :: x(4)
+  integer :: i
+
+  !$omp target update to(iterator(i = 1:4): x(i))
+end subroutine
+
+! CHECK-DAG: omp.declare_mapper
+! CHECK-DAG: omp.declare_mapper
+! CHECK-DAG: omp.declare_mapper
+
+! CHECK-LABEL: func.func @_QPimplicit_mapper_to
+! CHECK: omp.map.info {{.*}} map_clauses(to) {{.*}}mapper(@{{.*}})
+! CHECK: omp.target_update
+
+! CHECK-LABEL: func.func @_QPimplicit_mapper_from
+! CHECK: omp.map.info {{.*}} map_clauses(from) {{.*}}mapper(@{{.*}})
+! CHECK: omp.target_update
+
+! CHECK-LABEL: func.func @_QPimplicit_mapper_iterator
+! CHECK: omp.iterator
+! CHECK: omp.map.info {{.*}} map_clauses(to) {{.*}}mapper(@{{.*}})
+! CHECK: omp.target_update {{.*}}map_iterated
diff --git a/flang/test/Lower/OpenMP/task-affinity.f90 b/flang/test/Lower/OpenMP/task-affinity.f90
index 01c44e4a20d71..b5e218481744c 100644
--- a/flang/test/Lower/OpenMP/task-affinity.f90
+++ b/flang/test/Lower/OpenMP/task-affinity.f90
@@ -295,7 +295,7 @@ subroutine task_affinity_iterator_simple()
 ! CHECK:   %[[ADDRI8:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !fir.ref<i8>
 ! CHECK:   %[[ENTRY:.*]] = omp.affinity_entry %[[ADDRI8]], %[[C4]] : (!fir.ref<i8>, i64) -> !omp.affinity_entry_ty<!fir.ref<i8>, i64>
 ! CHECK:   omp.yield(%[[ENTRY]] : !omp.affinity_entry_ty<!fir.ref<i8>, i64>)
-! CHECK: } -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
+! CHECK: } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
 ! CHECK: omp.task affinity(%{{.*}} : !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>) {
 
 subroutine task_affinity_iterator_nondefault_lb()
@@ -327,7 +327,7 @@ subroutine task_affinity_iterator_nondefault_lb()
 ! CHECK:   %[[ADDRI8_NDLB:.*]] = fir.convert %[[COOR_NDLB]] : (!fir.ref<i32>) -> !fir.ref<i8>
 ! CHECK:   %[[ENTRY_NDLB:.*]] = omp.affinity_entry %[[ADDRI8_NDLB]], %[[ELEM_NDLB_I64]] : (!fir.ref<i8>, i64) -> !omp.affinity_entry_ty<!fir.ref<i8>, i64>
 ! CHECK:   omp.yield(%[[ENTRY_NDLB]] : !omp.affinity_entry_ty<!fir.ref<i8>, i64>)
-! CHECK: } -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
+! CHECK: } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
 ! CHECK: omp.task affinity(%[[ITERATED_NDLB]] : !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>) {
 
 subroutine task_affinity_iterator_nondefault_lb_2d()
@@ -364,7 +364,7 @@ subroutine task_affinity_iterator_nondefault_lb_2d()
 ! CHECK:   %[[ADDRI8_NDLB2:.*]] = fir.convert %[[COOR_NDLB2]] : (!fir.ref<i32>) -> !fir.ref<i8>
 ! CHECK:   %[[ENTRY_NDLB2:.*]] = omp.affinity_entry %[[ADDRI8_NDLB2]], %[[ELEM_NDLB2_I64]] : (!fir.ref<i8>, i64) -> !omp.affinity_entry_ty<!fir.ref<i8>, i64>
 ! CHECK:   omp.yield(%[[ENTRY_NDLB2]] : !omp.affinity_entry_ty<!fir.ref<i8>, i64>)
-! CHECK: } -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
+! CHECK: } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
 ! CHECK: omp.task affinity(%[[ITERATED_NDLB2]] : !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>) {
 
 subroutine task_affinity_iterator_multi_dimension()
@@ -403,7 +403,7 @@ subroutine task_affinity_iterator_multi_dimension()
 ! CHECK:   %[[ADDRI8:.*]] = fir.convert %[[COOR]] : (!fir.ref<i32>) -> !fir.ref<i8>
 ! CHECK:   %[[ENTRY:.*]] = omp.affinity_entry %[[ADDRI8]], %[[C4]] : (!fir.ref<i8>, i64) -> !omp.affinity_entry_ty<!fir.ref<i8>, i64>
 ! CHECK:   omp.yield(%[[ENTRY]] : !omp.affinity_entry_ty<!fir.ref<i8>, i64>)
-! CHECK: } -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
+! CHECK: } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>
 ! CHECK: omp.task affinity(%[[ITER]] : !omp.iterated<!omp.affinity_entry_ty<!fir.ref<i8>, i64>>)
 
 subroutine task_affinity_iterator_reordered()
diff --git a/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90 b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90
index e766b1b44d0fa..8e0efb765ff6a 100644
--- a/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90
+++ b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor-omp61.f90
@@ -95,3 +95,36 @@ subroutine test_pointer_dummy_no_warning(ptr)
   integer, pointer, intent(inout) :: ptr(:)
   !$omp target enter data map(to: ptr)
 end subroutine
+
+subroutine test_iterator_assumed_shape_no_warning(arr, n)
+  integer, intent(inout) :: arr(:)
+  integer, intent(in) :: n
+  integer :: i
+  !$omp target enter data map(iterator(i = 1:n), to: arr(i))
+end subroutine
+
+subroutine test_iterator_local_allocatable_no_warning()
+  integer, allocatable :: local_arr(:)
+  integer :: i
+  allocate(local_arr(100))
+  !$omp target enter data map(iterator(i = 1:100), to: local_arr(i))
+  deallocate(local_arr)
+end subroutine
+
+subroutine test_iterator_mixed_object_warning(arr, other, n)
+  integer, intent(inout) :: arr(:)
+  integer, intent(inout) :: other(n)
+  integer, intent(in) :: n
+  integer :: i
+  !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data [-Wopenmp-usage]
+  !$omp target enter data map(iterator(i = 1:n), to: arr, other(i))
+end subroutine
+
+subroutine test_plain_enter_iterator_exit_warning(arr, n)
+  integer, intent(inout) :: arr(:)
+  integer, intent(in) :: n
+  integer :: i
+  !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference. To avoid mapping the descriptor utilize OpenMP's ref_ptee reference modifier to map just the data [-Wopenmp-usage]
+  !$omp target enter data map(to: arr)
+  !$omp target exit data map(iterator(i = 1:n), from: arr(i))
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90 b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90
index bd1eb98ebec60..3eb8b9d7c50af 100644
--- a/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90
+++ b/flang/test/Semantics/OpenMP/target-enter-data-temp-descriptor.f90
@@ -91,3 +91,36 @@ subroutine test_local_allocatable_with_exit()
   !$omp target exit data map(from: local_arr)
   deallocate(local_arr)
 end subroutine
+
+subroutine test_iterator_assumed_shape_no_warning(arr, n)
+  integer, intent(inout) :: arr(:)
+  integer, intent(in) :: n
+  integer :: i
+  !$omp target enter data map(iterator(i = 1:n), to: arr(i))
+end subroutine
+
+subroutine test_iterator_local_allocatable_no_warning()
+  integer, allocatable :: local_arr(:)
+  integer :: i
+  allocate(local_arr(100))
+  !$omp target enter data map(iterator(i = 1:100), to: local_arr(i))
+  deallocate(local_arr)
+end subroutine
+
+subroutine test_iterator_mixed_object_warning(arr, other, n)
+  integer, intent(inout) :: arr(:)
+  integer, intent(inout) :: other(n)
+  integer, intent(in) :: n
+  integer :: i
+  !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+  !$omp target enter data map(iterator(i = 1:n), to: arr, other(i))
+end subroutine
+
+subroutine test_plain_enter_iterator_exit_warning(arr, n)
+  integer, intent(inout) :: arr(:)
+  integer, intent(in) :: n
+  integer :: i
+  !WARNING: The map of 'arr' may include a descriptor that is created locally. Mapping this descriptor without an appropriate TARGET EXIT DATA in the same scope may result in the device retaining an invalid descriptor reference [-Wopenmp-usage]
+  !$omp target enter data map(to: arr)
+  !$omp target exit data map(iterator(i = 1:n), from: arr(i))
+end subroutine
diff --git a/flang/test/Transforms/omp-map-info-finalization.fir b/flang/test/Transforms/omp-map-info-finalization.fir
index e83febfda4294..f8fba4b202fd3 100644
--- a/flang/test/Transforms/omp-map-info-finalization.fir
+++ b/flang/test/Transforms/omp-map-info-finalization.fir
@@ -43,6 +43,68 @@ func.func @test_descriptor_expansion_pass(%arg0: !fir.box<!fir.array<?xi32>>) {
 
 // -----
 
+func.func @test_iterator_descriptor_map_is_not_expanded(%arg0: !fir.box<!fir.array<?xf32>>) {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %dims:3 = fir.box_dims %arg0, %c0 : (!fir.box<!fir.array<?xf32>>, index) -> (index, index, index)
+  %it = omp.iterator(%iv: index) = (%c1 to %c10 step %c1) {
+    %addr = fir.box_addr %arg0 : (!fir.box<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
+    %lb = arith.subi %iv, %c1 : index
+    %bounds = omp.map.bounds lower_bound(%lb : index) upper_bound(%lb : index) extent(%dims#1 : index) stride(%dims#2 : index) start_idx(%dims#0 : index) stride_in_bytes(true)
+    %map = omp.map.info var_ptr(%addr : !fir.ref<!fir.array<?xf32>>, !fir.array<?xf32>) map_clauses(to) capture(ByRef) bounds(%bounds) name("") -> !fir.ref<!fir.array<?xf32>>
+    omp.yield(%map : !fir.ref<!fir.array<?xf32>>)
+  } -> !omp.iterated<!fir.ref<!fir.array<?xf32>>>
+  omp.target_enter_data map_iterated(%it : !omp.iterated<!fir.ref<!fir.array<?xf32>>>) {}
+  return
+}
+
+// CHECK-LABEL: func.func @test_iterator_descriptor_map_is_not_expanded
+// CHECK-NOT: fir.box_offset
+// CHECK: omp.iterator
+// CHECK: omp.map.info var_ptr({{.*}} : !fir.ref<!fir.array<?xf32>>, !fir.array<?xf32>) map_clauses(to) capture(ByRef) bounds({{.*}}) name("") -> !fir.ref<!fir.array<?xf32>>
+// CHECK: omp.target_enter_data map_iterated
+
+// -----
+
+func.func @test_iterator_member_map_is_not_finalized(
+    %arg0: !fir.ref<!fir.type<_QFiterator_memberTt{x:i32}>>) {
+  %c1 = arith.constant 1 : index
+  %c10 = arith.constant 10 : index
+  %it = omp.iterator(%iv: index) = (%c1 to %c10 step %c1) {
+    %member = fir.coordinate_of %arg0, x :
+        (!fir.ref<!fir.type<_QFiterator_memberTt{x:i32}>>) -> !fir.ref<i32>
+    %member_map = omp.map.info
+        var_ptr(%member : !fir.ref<i32>, i32)
+        map_clauses(close, to) capture(ByRef) name("record%x")
+        -> !fir.ref<i32>
+    %parent_map = omp.map.info
+        var_ptr(%arg0 :
+          !fir.ref<!fir.type<_QFiterator_memberTt{x:i32}>>,
+          !fir.type<_QFiterator_memberTt{x:i32}>)
+        map_clauses(to) capture(ByRef)
+        members(%member_map : [0] : !fir.ref<i32>) name("record")
+        partial_map(true) -> !fir.ref<!fir.type<_QFiterator_memberTt{x:i32}>>
+    omp.yield(
+      %parent_map : !fir.ref<!fir.type<_QFiterator_memberTt{x:i32}>>)
+  } -> !omp.iterated<
+    !fir.ref<!fir.type<_QFiterator_memberTt{x:i32}>>>
+  omp.target_enter_data map_iterated(
+    %it : !omp.iterated<
+      !fir.ref<!fir.type<_QFiterator_memberTt{x:i32}>>>) {}
+  return
+}
+
+// CHECK-LABEL: func.func @test_iterator_member_map_is_not_finalized
+// CHECK: omp.iterator
+// CHECK: omp.map.info
+// CHECK-SAME: map_clauses(close, to)
+// CHECK: omp.map.info
+// CHECK-SAME: members(
+// CHECK: omp.target_enter_data map_iterated
+
+// -----
+
 func.func @test_derived_type_map_operand_and_block_addition(%arg0: !fir.ref<!fir.type<_QFTdtype{ix:i32,rx:f32,zx:complex<f32>,nested:!fir.box<!fir.heap<!fir.type<_QFTdtype>>>,ry:f32}>>) {
   %0 = hlfir.designate %arg0{"rx"}   : (!fir.ref<!fir.type<_QFTdtype{ix:i32,rx:f32,zx:complex<f32>,nested:!fir.box<!fir.heap<!fir.type<_QFTdtype>>>,ry:f32}>>) -> !fir.ref<f32>
   %1 = omp.map.info var_ptr(%0 : !fir.ref<f32>, f32) map_clauses(from) capture(ByRef) name("scalar_struct%rx") -> !fir.ref<f32>
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index b2ad6abd40ed3..0f63acd6f2554 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2705,6 +2705,12 @@ def IteratorOp : OpenMP_Op<"iterator", [AttrSizedOperandSegments, SingleBlock],
     can be directly consumed by OpenMP clauses that accept iterator modifiers,
     such as `affinity`, `map`, `to`, `from`, or `depend`.
 
+    Each range has a nonzero step and an exclusive upper bound unless
+    `inclusive` is specified. A range is empty when its lower bound is beyond
+    its upper bound in the direction of the step (or equal to an exclusive
+    upper bound). If any range is empty, the result is an empty list and the
+    region is not evaluated.
+
     Example:
       %it = omp.iterator(%i, %j) =
         (%lb_i to %ub_i step %st_i, %lb_j to %ub_j step %st_j) {
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 3c9bd4781be64..46e9e29dbdab3 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -5732,28 +5732,9 @@ LogicalResult IteratorOp::verify() {
   if (!iteratedTy)
     return emitOpError() << "result must be omp.iterated<entry_ty>";
 
-  for (auto [lb, ub, step] : llvm::zip_equal(
-           getLoopLowerBounds(), getLoopUpperBounds(), getLoopSteps())) {
+  for (Value step : getLoopSteps()) {
     if (matchPattern(step, m_Zero()))
       return emitOpError() << "loop step must not be zero";
-
-    IntegerAttr lbAttr;
-    IntegerAttr ubAttr;
-    IntegerAttr stepAttr;
-    if (!matchPattern(lb, m_Constant(&lbAttr)) ||
-        !matchPattern(ub, m_Constant(&ubAttr)) ||
-        !matchPattern(step, m_Constant(&stepAttr)))
-      continue;
-
-    const APInt &lbVal = lbAttr.getValue();
-    const APInt &ubVal = ubAttr.getValue();
-    const APInt &stepVal = stepAttr.getValue();
-    if (stepVal.isStrictlyPositive() && lbVal.sgt(ubVal))
-      return emitOpError() << "positive loop step requires lower bound to be "
-                              "less than or equal to upper bound";
-    if (stepVal.isNegative() && lbVal.slt(ubVal))
-      return emitOpError() << "negative loop step requires lower bound to be "
-                              "greater than or equal to upper bound";
   }
 
   Block &b = getRegion().front();
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index e5e3e0cc6d944..06b2db246d2f5 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -2760,9 +2760,9 @@ class TaskContextStructManager {
 /// IteratorInfo extracts and prepares loop bounds information from an
 /// mlir::omp::IteratorOp for lowering to LLVM IR.
 ///
-/// It computes the per-dimension trip counts and the total linearized trip
-/// count, casted to i64. These are used to build a canonical loop and to
-/// reconstruct the physical induction variables inside the loop body.
+/// It retains the source-width bounds and steps used to reconstruct physical
+/// induction variables. Per-dimension trip counts are converted to i64 for
+/// runtime list sizes and the linearized iterator loop.
 class IteratorInfo {
 private:
   llvm::SmallVector<llvm::Value *> lowerBounds;
@@ -2772,16 +2772,12 @@ class IteratorInfo {
   unsigned dims;
   llvm::Value *totalTrips;
 
-  llvm::Value *lookUpAsI64(mlir::Value val, const LLVM::ModuleTranslation &mt,
-                           llvm::IRBuilderBase &builder) {
+  llvm::Value *lookUpInteger(mlir::Value val,
+                             const LLVM::ModuleTranslation &mt) {
     llvm::Value *v = mt.lookupValue(val);
-    if (!v)
+    if (!v || !v->getType()->isIntegerTy())
       return nullptr;
-    if (v->getType()->isIntegerTy(64))
-      return v;
-    if (v->getType()->isIntegerTy())
-      return builder.CreateSExtOrTrunc(v, builder.getInt64Ty());
-    return nullptr;
+    return v;
   }
 
 public:
@@ -2794,15 +2790,19 @@ class IteratorInfo {
     steps.resize(dims);
     trips.resize(dims);
 
+    llvm::OpenMPIRBuilder &ompBuilder = *moduleTranslation.getOpenMPBuilder();
+    llvm::OpenMPIRBuilder::LocationDescription loc(builder);
     for (unsigned d = 0; d < dims; ++d) {
-      llvm::Value *lb = lookUpAsI64(itersOp.getLoopLowerBounds()[d],
-                                    moduleTranslation, builder);
-      llvm::Value *ub = lookUpAsI64(itersOp.getLoopUpperBounds()[d],
-                                    moduleTranslation, builder);
+      llvm::Value *lb =
+          lookUpInteger(itersOp.getLoopLowerBounds()[d], moduleTranslation);
+      llvm::Value *ub =
+          lookUpInteger(itersOp.getLoopUpperBounds()[d], moduleTranslation);
       llvm::Value *st =
-          lookUpAsI64(itersOp.getLoopSteps()[d], moduleTranslation, builder);
+          lookUpInteger(itersOp.getLoopSteps()[d], moduleTranslation);
       assert(lb && ub && st &&
              "Expect lowerBounds, upperBounds, and steps in IteratorOp");
+      assert(lb->getType() == ub->getType() && lb->getType() == st->getType() &&
+             "Expect matching iterator range types");
       assert((!llvm::isa<llvm::ConstantInt>(st) ||
               !llvm::cast<llvm::ConstantInt>(st)->isZero()) &&
              "Expect non-zero step in IteratorOp");
@@ -2811,11 +2811,10 @@ class IteratorInfo {
       upperBounds[d] = ub;
       steps[d] = st;
 
-      // trips = ((ub - lb) / step) + 1  (inclusive ub, assume positive step)
-      llvm::Value *diff = builder.CreateSub(ub, lb);
-      llvm::Value *div = builder.CreateSDiv(diff, st);
-      trips[d] = builder.CreateAdd(
-          div, llvm::ConstantInt::get(builder.getInt64Ty(), 1));
+      llvm::Value *tripCount = ompBuilder.calculateCanonicalLoopTripCount(
+          loc, lb, ub, st, /*IsSigned=*/true, itersOp.getLoopInclusive(),
+          "iterator");
+      trips[d] = builder.CreateZExtOrTrunc(tripCount, builder.getInt64Ty());
     }
 
     totalTrips = llvm::ConstantInt::get(builder.getInt64Ty(), 1);
@@ -2952,12 +2951,21 @@ convertIteratorRegion(llvm::Value *linearIV, IteratorInfo &iterInfo,
   llvm::Value *tmp = linearIV;
   for (int d = (int)iterInfo.getDims() - 1; d >= 0; --d) {
     llvm::Value *trip = iterInfo.getTrips()[d];
-    // idx_d = tmp % trip_d
-    llvm::Value *idx = builder.CreateURem(tmp, trip);
-    // tmp = tmp / trip_d
-    tmp = builder.CreateUDiv(tmp, trip);
-
-    // physIV_d = lb_d + idx_d * step_d
+    llvm::Value *zero = llvm::ConstantInt::get(trip->getType(), 0);
+    llvm::Value *one = llvm::ConstantInt::get(trip->getType(), 1);
+    // A zero trip count makes the linearized loop empty. Use one in its
+    // unreachable IV decoder so the generated IR contains no division by zero.
+    llvm::Value *divisor =
+        builder.CreateSelect(builder.CreateICmpEQ(trip, zero), one, trip);
+    // idx_d = tmp % divisor_d
+    llvm::Value *idx = builder.CreateURem(tmp, divisor);
+    // tmp = tmp / divisor_d
+    tmp = builder.CreateUDiv(tmp, divisor);
+
+    // physIV_d = lb_d + idx_d * step_d. Reconstruct it at the range's
+    // original width so translating the iterator body cannot lose IV bits.
+    idx =
+        builder.CreateZExtOrTrunc(idx, iterInfo.getLowerBounds()[d]->getType());
     llvm::Value *physIV = builder.CreateAdd(
         iterInfo.getLowerBounds()[d],
         builder.CreateMul(idx, iterInfo.getSteps()[d]), "omp.it.phys_iv");
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index 73f63e13f90f6..4c1e9c0d0d958 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -4827,34 +4827,6 @@ func.func @iterator_zero_step(%s2 : !llvm.struct<(ptr, i64)>) {
 
 // -----
 
-func.func @iterator_positive_step_wrong_direction(%s2 : !llvm.struct<(ptr, i64)>) {
-  %lb = arith.constant 1000 : index
-  %ub = arith.constant -1 : index
-  %st = arith.constant 10 : index
-
-  // expected-error at +1 {{positive loop step requires lower bound to be less than or equal to upper bound}}
-  %0 = omp.iterator(%iv: index) = (%lb to %ub step %st) {
-    omp.yield(%s2 : !llvm.struct<(ptr, i64)>)
-  } -> !omp.iterated<!llvm.struct<(ptr, i64)>>
-  return
-}
-
-// -----
-
-func.func @iterator_negative_step_wrong_direction(%s2 : !llvm.struct<(ptr, i64)>) {
-  %lb = arith.constant -1000 : index
-  %ub = arith.constant 4 : index
-  %st = arith.constant -999 : index
-
-  // expected-error at +1 {{negative loop step requires lower bound to be greater than or equal to upper bound}}
-  %0 = omp.iterator(%iv: index) = (%lb to %ub step %st) {
-    omp.yield(%s2 : !llvm.struct<(ptr, i64)>)
-  } -> !omp.iterated<!llvm.struct<(ptr, i64)>>
-  return
-}
-
-// -----
-
 func.func @iterator_missing_yield(%lb : index, %ub : index, %st : index) {
   // expected-error at +1 {{region must be terminated by omp.yield}}
   %0 = omp.iterator(%i: index) = (%lb to %ub step %st) {
diff --git a/mlir/test/Dialect/OpenMP/iterator-ranges.mlir b/mlir/test/Dialect/OpenMP/iterator-ranges.mlir
new file mode 100644
index 0000000000000..8f10532635045
--- /dev/null
+++ b/mlir/test/Dialect/OpenMP/iterator-ranges.mlir
@@ -0,0 +1,33 @@
+// RUN: mlir-opt %s | FileCheck %s
+// RUN: mlir-opt %s --canonicalize --verify-each | FileCheck %s
+
+// Empty ranges yield empty lists, for either endpoint convention.
+// CHECK-LABEL: func.func @empty_ranges
+func.func @empty_ranges() -> (!omp.iterated<index>, !omp.iterated<index>) {
+  %one = arith.constant 1 : index
+  %two = arith.constant 2 : index
+  %neg = arith.constant -1 : index
+  // CHECK: omp.iterator
+  // CHECK: } inclusive -> !omp.iterated<index>
+  %a = omp.iterator(%i: index) = (%two to %one step %one) {
+    omp.yield(%i : index)
+  } inclusive -> !omp.iterated<index>
+  // CHECK: omp.iterator
+  // CHECK: } -> !omp.iterated<index>
+  %b = omp.iterator(%i: index) = (%one to %two step %neg) {
+    omp.yield(%i : index)
+  } -> !omp.iterated<index>
+  return %a, %b : !omp.iterated<index>, !omp.iterated<index>
+}
+
+// CHECK-LABEL: func.func @wide_range
+func.func @wide_range(%hi: i128, %step: i128) -> !omp.iterated<i128> {
+  %zero = arith.constant 0 : i128
+  // CHECK: omp.iterator(%{{.*}}: i128) =
+  // CHECK: omp.yield(%{{.*}} : i128)
+  // CHECK: } inclusive -> !omp.iterated<i128>
+  %a = omp.iterator(%i: i128) = (%zero to %hi step %step) {
+    omp.yield(%i : i128)
+  } inclusive -> !omp.iterated<i128>
+  return %a : !omp.iterated<i128>
+}
diff --git a/mlir/test/Target/LLVMIR/openmp-iterator.mlir b/mlir/test/Target/LLVMIR/openmp-iterator.mlir
index 7fdc6f15b0311..916af59ebeaef 100644
--- a/mlir/test/Target/LLVMIR/openmp-iterator.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-iterator.mlir
@@ -21,7 +21,7 @@ llvm.func @task_affinity_iterator_1d(%arr: !llvm.ptr {llvm.nocapture}) {
         %entry = omp.affinity_entry %arr, %len
             : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
         omp.yield(%entry : !omp.affinity_entry_ty<!llvm.ptr, i64>)
-      } -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
+      } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
 
       omp.task affinity(%it : !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>) {
         omp.terminator
@@ -86,7 +86,7 @@ llvm.func @task_affinity_iterator_3d(%arr: !llvm.ptr {llvm.nocapture}) {
         %entry = omp.affinity_entry %arr, %len
             : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
         omp.yield(%entry : !omp.affinity_entry_ty<!llvm.ptr, i64>)
-      } -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
+      } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
 
       omp.task affinity(%it : !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>) {
         omp.terminator
@@ -151,14 +151,14 @@ llvm.func @task_affinity_iterator_multiple(%arr: !llvm.ptr {llvm.nocapture}) {
         %entry0 = omp.affinity_entry %arr, %len
             : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
         omp.yield(%entry0 : !omp.affinity_entry_ty<!llvm.ptr, i64>)
-      } -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
+      } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
 
       // second iterator: 1-D (3)
       %it1 = omp.iterator(%k: i64) = (%c1 to %c3 step %c1) {
         %entry1 = omp.affinity_entry %arr, %len
             : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
         omp.yield(%entry1 : !omp.affinity_entry_ty<!llvm.ptr, i64>)
-      } -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
+      } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
 
       // Multiple iterators in a single affinity clause.
       omp.task affinity(%it0: !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>,
@@ -247,7 +247,7 @@ llvm.func @task_affinity_iterator_dynamic_tripcount(
         %entry = omp.affinity_entry %arr, %len
             : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
         omp.yield(%entry : !omp.affinity_entry_ty<!llvm.ptr, i64>)
-      } -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
+      } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
 
       omp.task affinity(%it : !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>) {
         omp.terminator
@@ -260,9 +260,13 @@ llvm.func @task_affinity_iterator_dynamic_tripcount(
 }
 
 // CHECK-LABEL: define internal void @task_affinity_iterator_dynamic_tripcount
-// CHECK: [[DIFF:%.*]] = sub i64 {{.*}}, {{.*}}
-// CHECK: [[DIV:%.*]] = sdiv i64 [[DIFF]], {{.*}}
-// CHECK: [[TRIPS:%.*]] = add i64 [[DIV]], 1
+// CHECK: [[INCR:%.*]] = select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
+// CHECK-NEXT: [[BEGIN:%.*]] = select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
+// CHECK-NEXT: [[END:%.*]] = select i1 {{.*}}, i64 {{.*}}, i64 {{.*}}
+// CHECK-NEXT: {{.*}} = sub nsw i64 [[END]], [[BEGIN]]
+// CHECK-NEXT: [[EMPTY:%.*]] = icmp slt i64 [[END]], [[BEGIN]]
+// CHECK: [[COUNT:%.*]] = add i64 {{.*}}, 1
+// CHECK: [[TRIPS:%.*]] = select i1 [[EMPTY]], i64 0, i64 [[COUNT]]
 // CHECK: [[SCALED:%.*]] = mul i64 1, [[TRIPS]]
 // CHECK: [[AFFLIST:%.*]] = alloca { i64, i64, i32 }, i64 [[SCALED]]
 
@@ -277,7 +281,7 @@ llvm.func @task_affinity_iterator_negative_step(%arr: !llvm.ptr {llvm.nocapture}
         %entry = omp.affinity_entry %arr, %i
             : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
         omp.yield(%entry : !omp.affinity_entry_ty<!llvm.ptr, i64>)
-      } -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
+      } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
 
       omp.task affinity(%it : !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>) {
         omp.terminator
@@ -302,6 +306,38 @@ llvm.func @task_affinity_iterator_negative_step(%arr: !llvm.ptr {llvm.nocapture}
 // CHECK: [[LENPTR:%.*]] = getelementptr inbounds nuw { i64, i64, i32 }, ptr [[ENTRY]], i32 0, i32 1
 // CHECK: store i64 [[PHYSIV]], ptr [[LENPTR]]
 
+llvm.func @task_affinity_iterator_empty(%arr: !llvm.ptr {llvm.nocapture}) {
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %c5 = llvm.mlir.constant(5 : i64) : i64
+  %len = llvm.mlir.constant(4 : i64) : i64
+
+  omp.parallel {
+    omp.single {
+      %it = omp.iterator(%i: i64) = (%c5 to %c1 step %c1) {
+        %entry = omp.affinity_entry %arr, %len
+            : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
+        omp.yield(%entry : !omp.affinity_entry_ty<!llvm.ptr, i64>)
+      } inclusive -> !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>
+
+      omp.task affinity(
+          %it : !omp.iterated<!omp.affinity_entry_ty<!llvm.ptr, i64>>) {
+        omp.terminator
+      }
+      omp.terminator
+    }
+    omp.terminator
+  }
+  llvm.return
+}
+
+// CHECK-LABEL: define internal void @task_affinity_iterator_empty
+// CHECK: %[[EMPTY_AFFINITY:.*]] = alloca { i64, i64, i32 }, i64 0
+// CHECK: omp_iterator.cond:
+// CHECK: icmp ult i64 %omp_iterator.iv, 0
+// CHECK: call i32 @__kmpc_omp_reg_task_with_affinity(
+// CHECK-SAME: ptr @{{[^,]+}}, i32 %{{[^,]+}}, ptr %{{[^,]+}},
+// CHECK-SAME: i32 0, ptr %[[EMPTY_AFFINITY]])
+
 // --------------------------------------------------------------------
 // Depend clause
 // --------------------------------------------------------------------
@@ -313,7 +349,7 @@ llvm.func @omp_task_depend_iterator_simple(%addr : !llvm.ptr) {
 
   %it = omp.iterator(%iv: i64) = (%c1 to %c10 step %step) {
     omp.yield(%addr : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
 
   omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
@@ -359,7 +395,7 @@ llvm.func @omp_task_depend_iterator_mixed(%addr : !llvm.ptr, %plain : !llvm.ptr)
 
   %it = omp.iterator(%iv: i64) = (%c1 to %c10 step %step) {
     omp.yield(%addr : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
 
   omp.task depend(taskdependout -> %plain : !llvm.ptr, taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
@@ -398,7 +434,7 @@ llvm.func @omp_task_depend_iterator_dynamic(%addr : !llvm.ptr,
     %lb : i64, %ub : i64, %step : i64) {
   %it = omp.iterator(%iv: i64) = (%lb to %ub step %step) {
     omp.yield(%addr : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
 
   omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
@@ -409,9 +445,13 @@ llvm.func @omp_task_depend_iterator_dynamic(%addr : !llvm.ptr,
 // CHECK-LABEL: define void @omp_task_depend_iterator_dynamic
 //
 // Tripcount computation from dynamic bounds
-// CHECK: %[[DIFF:.*]] = sub i64 %{{.*}}, %{{.*}}
-// CHECK: %[[DIV:.*]] = sdiv i64 %[[DIFF]], %{{.*}}
-// CHECK: %[[TRIPS:.*]] = add i64 %[[DIV]], 1
+// CHECK: %[[INCR:.*]] = select i1 %{{.*}}, i64 %{{.*}}, i64 %{{.*}}
+// CHECK-NEXT: %[[BEGIN:.*]] = select i1 %{{.*}}, i64 %{{.*}}, i64 %{{.*}}
+// CHECK-NEXT: %[[END:.*]] = select i1 %{{.*}}, i64 %{{.*}}, i64 %{{.*}}
+// CHECK-NEXT: %{{.*}} = sub nsw i64 %[[END]], %[[BEGIN]]
+// CHECK-NEXT: %[[EMPTY:.*]] = icmp slt i64 %[[END]], %[[BEGIN]]
+// CHECK: %[[COUNT:.*]] = add i64 %{{.*}}, 1
+// CHECK: %[[TRIPS:.*]] = select i1 %[[EMPTY]], i64 0, i64 %[[COUNT]]
 // CHECK: %[[SCALED:.*]] = mul i64 1, %[[TRIPS]]
 // Dynamic total = 0 + scaled trip count
 // CHECK: %[[TOTAL:.*]] = add i64 0, %[[SCALED]]
@@ -430,7 +470,7 @@ llvm.func @omp_task_depend_iterator_dynamic_mixed(%addr : !llvm.ptr,
     %plain : !llvm.ptr, %lb : i64, %ub : i64, %step : i64) {
   %it = omp.iterator(%iv: i64) = (%lb to %ub step %step) {
     omp.yield(%addr : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
 
   omp.task depend(taskdependout -> %plain : !llvm.ptr, taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
@@ -453,6 +493,73 @@ llvm.func @omp_task_depend_iterator_dynamic_mixed(%addr : !llvm.ptr,
 // CHECK: call i32 @__kmpc_omp_task_with_deps(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 %[[NDEPS2]], ptr %[[DEP_ARR2]], i32 0, ptr null)
 // CHECK: tail call void @free(ptr %[[DEP_ARR2]])
 
+llvm.func @omp_task_depend_iterator_empty(%addr : !llvm.ptr) {
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %c5 = llvm.mlir.constant(5 : i64) : i64
+
+  %it = omp.iterator(%iv: i64) = (%c5 to %c1 step %c1) {
+    omp.yield(%addr : !llvm.ptr)
+  } inclusive -> !omp.iterated<!llvm.ptr>
+
+  omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
+    omp.terminator
+  }
+  llvm.return
+}
+
+// CHECK-LABEL: define void @omp_task_depend_iterator_empty
+// CHECK: %[[EMPTY_DEP_ARR:.*]] = tail call ptr @malloc(i64 0)
+// CHECK: omp_dep_iterator.cond:
+// CHECK: icmp ult i64 %omp_dep_iterator.iv, 0
+// CHECK: call i32 @__kmpc_omp_task_with_deps(
+// CHECK-SAME: ptr @{{[^,]+}}, i32 %{{[^,]+}}, ptr %{{[^,]+}},
+// CHECK-SAME: i32 0, ptr %[[EMPTY_DEP_ARR]], i32 0, ptr null)
+
+llvm.func @omp_task_depend_iterator_exclusive(%addr : !llvm.ptr) {
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %c5 = llvm.mlir.constant(5 : i64) : i64
+
+  %it = omp.iterator(%iv: i64) = (%c1 to %c5 step %c1) {
+    omp.yield(%addr : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+
+  omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
+    omp.terminator
+  }
+  llvm.return
+}
+
+// CHECK-LABEL: define void @omp_task_depend_iterator_exclusive
+// CHECK: %[[EXCLUSIVE_DEP_ARR:.*]] = tail call ptr @malloc(i64 80)
+// CHECK: omp_dep_iterator.cond:
+// CHECK: icmp ult i64 %omp_dep_iterator.iv, 4
+// CHECK: call i32 @__kmpc_omp_task_with_deps(
+// CHECK-SAME: ptr @{{[^,]+}}, i32 %{{[^,]+}}, ptr %{{[^,]+}},
+// CHECK-SAME: i32 4, ptr %[[EXCLUSIVE_DEP_ARR]], i32 0, ptr null)
+
+llvm.func @omp_task_depend_iterator_wide(
+    %addr : !llvm.ptr, %lb : i128, %ub : i128, %step : i128) {
+  %it = omp.iterator(%iv: i128) = (%lb to %ub step %step) {
+    %element = llvm.getelementptr %addr[%iv]
+        : (!llvm.ptr, i128) -> !llvm.ptr, i8
+    omp.yield(%element : !llvm.ptr)
+  } inclusive -> !omp.iterated<!llvm.ptr>
+
+  omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
+    omp.terminator
+  }
+  llvm.return
+}
+
+// CHECK-LABEL: define void @omp_task_depend_iterator_wide
+// CHECK: %[[WIDE_TRIPS:.*]] = select i1 {{.*}}, i128 0, i128 {{.*}}
+// CHECK: %[[WIDE_TRIPS_I64:.*]] = trunc i128 %[[WIDE_TRIPS]] to i64
+// CHECK: omp_dep_iterator.body:
+// CHECK: %[[WIDE_IDX:.*]] = zext i64 {{.*}} to i128
+// CHECK: %[[WIDE_STEP:.*]] = mul i128 %[[WIDE_IDX]], %{{.*}}
+// CHECK: %[[WIDE_IV:.*]] = add i128 %{{.*}}, %[[WIDE_STEP]]
+// CHECK: getelementptr i8, ptr %{{.*}}, i128 %[[WIDE_IV]]
+
 //--- target.mlir
 
 // --------------------------------------------------------------------
@@ -470,7 +577,7 @@ module attributes {omp.is_target_device = false, omp.target_triples = ["amdgcn-a
 
     %it = omp.iterator(%iv: i64) = (%c1 to %c10 step %step) {
       omp.yield(%addr : !llvm.ptr)
-    } -> !omp.iterated<!llvm.ptr>
+    } inclusive -> !omp.iterated<!llvm.ptr>
 
     %map = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) name("data") -> !llvm.ptr
     omp.target kernel_type(generic) depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) map_entries(%map -> %arg0 : !llvm.ptr) {
diff --git a/mlir/test/Target/LLVMIR/openmp-taskwait-depend.mlir b/mlir/test/Target/LLVMIR/openmp-taskwait-depend.mlir
index fd29efeca3b9f..55f4ebd240b1e 100644
--- a/mlir/test/Target/LLVMIR/openmp-taskwait-depend.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-taskwait-depend.mlir
@@ -18,7 +18,7 @@ llvm.func @taskwait_depend_iterator(%x: !llvm.ptr) {
 
   %ix = omp.iterator(%i: i64) = (%c1 to %c10 step %step) {
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %ix : !omp.iterated<!llvm.ptr>)
   llvm.return
 }



More information about the flang-commits mailing list