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

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Oct 14 10:47:10 PDT 2026


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

>From 4973d8dd3e24838b24e9ccc99b1157efdc23e7dd 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 1/2] [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 is expanded only over the iterators that appear in it:

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

Iterator ranges use the same canonical loop semantics throughout lowering:

```text
trip_count = canonicalTripCount(lower, upper, step, inclusive)

if any trip_count == 0:
    result = []
    skip iterator region
```

These rules preserve source behavior in edge cases. Fortran ranges include
the endpoint, so lowering marks them inclusive instead of dropping the last
entry. A range such as `5:1:1` is empty and yields no entries instead of being
mistaken for a large range. Bounds and reconstructed iterator values keep
their source integer width, preventing `INTEGER(16)` values from being
truncated.

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    | 172 +++-
 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  | 277 ++++++
 flang/test/Lower/OpenMP/depend-iterator.f90   |  50 +-
 flang/test/Lower/OpenMP/iterator-ranges.f90   | 160 +++
 flang/test/Lower/OpenMP/motion-iterator.f90   | 910 ++++++++++++++++++
 flang/test/Lower/OpenMP/task-affinity.f90     |  12 +-
 ...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 +
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      |  25 +-
 mlir/test/Dialect/OpenMP/iterator-empty.mlir  |  19 +-
 mlir/test/Dialect/OpenMP/ops.mlir             |  12 +
 .../Target/LLVMIR/openmp-iterator-empty.mlir  |   6 +-
 .../LLVMIR/openmp-iterator-multi-block.mlir   |  12 +-
 .../Target/LLVMIR/openmp-iterator-wide.mlir   |  14 +-
 mlir/test/Target/LLVMIR/openmp-iterator.mlir  | 125 ++-
 .../Target/LLVMIR/openmp-taskwait-depend.mlir |   2 +-
 34 files changed, 2415 insertions(+), 145 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

diff --git a/flang/include/flang/Utils/OpenMP.h b/flang/include/flang/Utils/OpenMP.h
index 0cb77e25af9475..8c8da53f90863d 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 b1f29bb23361f0..b6a9645ddb2e6b 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"
 
@@ -295,6 +297,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);
 
@@ -329,6 +332,43 @@ 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, [&]() -> mlir::Value {
+        fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+        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);
+        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 llvm::SmallVector<IteratorRange>
 lowerIteratorRanges(const ClauseTuple &clause,
@@ -1913,6 +1953,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.
@@ -2032,15 +2164,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");
+
+      iteratorRanges = lowerIteratorRanges(clause, converter, stmtCtx);
+    }
+    processMapObjectsWithIterator(
+        stmtCtx, clauseLocation, objects, iteratorRanges, mapTypeBits,
+        parentMemberIndices, result, *ptrMapObjects, mapperIdName,
+        /*isMotionModifier=*/false, directive);
   };
 
   bool clauseFound = findRepeatableClause<omp::clause::Map>(process);
@@ -2055,6 +2196,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;
@@ -2066,16 +2212,16 @@ 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 =
+        lowerIteratorRanges(clause, converter, stmtCtx);
+    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 6b0e0cf606f48f..bac456ca00cd96 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 5ef2af3962ad54..ba2adcd74138b7 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -153,6 +153,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 57978826c5d014..3c5faf005ca9e1 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2553,7 +2553,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);
@@ -2576,7 +2576,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 9e37c02eac3f1f..156670dc4c78db 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -1437,6 +1437,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 c769380aabbb01..743df47c3b23d3 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -252,6 +252,31 @@ mlir::FlatSymbolRefAttr resolveMapperId(
     const omp::Object &object, llvm::StringRef mapperIdName,
     mlir::omp::ClauseMapFlags mapTypeBits, llvm::omp::Directive directive);
 
+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);
+
 /// Walk the already-emitted MLIR parent operations starting from \p op and
 /// collect the implied OpenMP construct traits in outermost-to-innermost
 /// order. Used by metadirective lowering and declare-variant call resolution
diff --git a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
index b790bee65ebe7a..531de2eea5b18d 100644
--- a/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
+++ b/flang/lib/Optimizer/OpenMP/MapInfoFinalization.cpp
@@ -1715,6 +1715,10 @@ class MapInfoFinalizationPass
     });
   }
 
+  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
@@ -1745,19 +1749,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
@@ -1827,7 +1838,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 7376a05d5c2ae9..456ccb8a497701 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -527,6 +527,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.
@@ -4992,9 +5014,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};
@@ -5125,23 +5147,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 aa5ef994252f76..625aaf7c438f05 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 00000000000000..d22b0708d5d303
--- /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 dacd6d6246595e..00000000000000
--- 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 973d1d1d76ba48..00000000000000
--- 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 00000000000000..ff16560140b266
--- /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 00000000000000..93f7fb4d1e7837
--- /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 00000000000000..9de14b25e4d606
--- /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 a587373bf183a7..00000000000000
--- 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 00000000000000..19061c3650ecd4
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-mapper-iterator.f90
@@ -0,0 +1,277 @@
+! 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:     fir.store %[[IV]] 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:.*]]: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}}) {
+! 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:.*]] = arith.constant 1 : i32
+! CHECK:   %[[I_UB:.*]] = arith.constant 4 : i32
+! CHECK:   %[[I_STEP:.*]] = arith.constant 1 : i32
+! CHECK:   %[[J_LB:.*]] = arith.constant 1 : i32
+! CHECK:   %[[J_UB:.*]] = arith.constant 6 : i32
+! CHECK:   %[[J_STEP:.*]] = arith.constant 1 : i32
+! CHECK:   %[[IT:.*]] = omp.iterator(%[[IV_I:.*]]: i32,
+! CHECK-SAME: %[[IV_J:.*]]: i32) =
+! CHECK-SAME: (%[[I_LB]] to %[[I_UB]] step %[[I_STEP]],
+! CHECK-SAME: %[[J_LB]] to %[[J_UB]] step %[[J_STEP]]) {
+! CHECK:     fir.store %[[IV_I]] to %[[IV_I_MEM:.*]] : !fir.ref<i32>
+! CHECK:     %[[IV_I_DECL:.*]]:2 = hlfir.declare %[[IV_I_MEM]]
+! CHECK:     fir.store %[[IV_J]] 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(%{{.*}}: i32) = ({{.*}}) {
+! 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(%{{.*}}: i32) = ({{.*}}) {
+! 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(%{{.*}}: i32) = ({{.*}}) {
+! 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(%{{.*}}: i32) = ({{.*}}) {
+! 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:.*]] = arith.constant -2 : i32
+! CHECK:   %[[UB:.*]] = arith.constant 6 : i32
+! CHECK:   %[[STEP:.*]] = arith.constant 1 : i32
+! CHECK:   %[[IT:.*]] = omp.iterator(%[[IV:.*]]: i32) =
+! CHECK-SAME: (%[[LB]] to %[[UB]] step %[[STEP]]) {
+! CHECK:     fir.store %[[IV]] 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(%{{[^:]+}}: i32) =
+! CHECK: omp.map.info
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+! CHECK: %[[B:.*]] = omp.iterator(%{{[^:]+}}: i32) =
+! 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 275aebe09aa4a8..b3388fcf7cacc5 100644
--- a/flang/test/Lower/OpenMP/depend-iterator.f90
+++ b/flang/test/Lower/OpenMP/depend-iterator.f90
@@ -44,7 +44,7 @@ subroutine depend_per_locator(m)
 ! CHECK: %[[PTR_I:.*]] = fir.convert %[[COOR_I]]
 ! CHECK-SAME: (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK: omp.yield(%[[PTR_I]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT_J:.*]] = omp.iterator(%[[IV_J:.*]]: i32) =
 ! CHECK-SAME: (%[[J_LB]] to %[[J_UB]] step %[[J_STEP]]) {
 ! CHECK: fir.store %[[IV_J]] to %[[MEM_J:.*]] : !fir.ref<i32>
@@ -55,7 +55,7 @@ subroutine depend_per_locator(m)
 ! CHECK: %[[PTR_J:.*]] = fir.convert %[[COOR_J]]
 ! CHECK-SAME: (!fir.ref<i32>) -> !llvm.ptr
 ! CHECK: omp.yield(%[[PTR_J]] : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK-NOT: omp.iterator
 ! CHECK: omp.task depend(taskdependin -> %[[C]]#0 : !fir.ref<i32>,
 ! CHECK-SAME: taskdependin -> %[[IT_I]] : !omp.iterated<!llvm.ptr>,
@@ -104,7 +104,7 @@ subroutine task_depend_iterator_simple()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%[[IV_I64]])
 ! 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: }
@@ -131,7 +131,7 @@ subroutine task_depend_iterator_2d()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %{{.*}} (%[[IV0_I64]], %[[IV1_I64]])
 ! 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()
@@ -148,7 +148,7 @@ subroutine task_depend_iterator_mixed()
 ! CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} uniq_name("_QFtask_depend_iterator_mixedEx")
 ! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: i32) = ({{.*}} 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()
@@ -166,7 +166,7 @@ subroutine task_depend_iterator_step()
 ! CHECK: %[[STEP:.*]] = arith.constant 2 : i32
 ! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: i32) = (%[[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()
@@ -185,12 +185,12 @@ subroutine task_depend_iterator_multi_obj()
 ! CHECK:   %[[COOR1:.*]] = hlfir.designate %[[A]]#0 (%{{.*}})
 ! 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = hlfir.designate %[[B]]#0 (%{{.*}})
 ! 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)
@@ -223,7 +223,7 @@ subroutine task_depend_iterator_expr_subscript()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%[[IDX]])
 ! 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.
@@ -244,12 +244,12 @@ subroutine task_depend_multi_iter_clauses()
 ! CHECK:   %[[COOR1:.*]] = hlfir.designate %[[A]]#0 (%{{.*}})
 ! 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = hlfir.designate %[[B]]#0 (%{{.*}})
 ! 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()
@@ -267,7 +267,7 @@ subroutine task_depend_iterator_negative_step()
 ! CHECK: %[[STEP:.*]] = arith.constant -1 : i32
 ! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: i32) = (%[[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>) {
 
 ! Mixed iterated and non-iterated objects in the same depend clause:
@@ -289,7 +289,7 @@ subroutine task_depend_iterator_mixed_within_clause()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%{{.*}})
 ! 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>) {
 
 !===============================================================================
@@ -316,7 +316,7 @@ subroutine target_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%[[IV_I64]])
 ! 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
@@ -341,15 +341,15 @@ subroutine target_depend_iterator_multi()
 ! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR1:.*]] = hlfir.designate %[[A]]#0 (%{{.*}})
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = hlfir.designate %[[B]]#0 (%{{.*}})
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT3:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR3:.*]] = hlfir.designate %[[C]]#0 (%{{.*}})
 ! 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>>
@@ -377,7 +377,7 @@ subroutine target_enter_data_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%[[IV_I64]])
 ! 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>>)
 
@@ -408,7 +408,7 @@ subroutine target_enter_data_depend_iterator_expr()
 ! CHECK:   %[[IDX:.*]] = fir.convert %[[ADD]] : (i32) -> i64
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%[[IDX]])
 ! 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>>)
 
@@ -434,7 +434,7 @@ subroutine target_exit_data_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%[[IV_I64]])
 ! 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>>)
 
@@ -456,11 +456,11 @@ subroutine target_exit_data_depend_iterator_multi()
 ! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: i32) = (%[[LB]] to %[[UB]] step %[[STEP]]) {
 ! CHECK:   %[[COOR1:.*]] = hlfir.designate %[[A]]#0 (%{{.*}})
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: i32) = (%[[LB]] to %[[UB]] step %[[STEP]]) {
 ! CHECK:   %[[COOR2:.*]] = hlfir.designate %[[B]]#0 (%{{.*}})
 ! 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>>)
@@ -487,7 +487,7 @@ subroutine target_update_depend_iterator()
 ! CHECK:   %[[COOR:.*]] = hlfir.designate %[[A]]#0 (%[[IV_I64]])
 ! 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>>)
 
@@ -511,11 +511,11 @@ subroutine target_update_depend_iterator_multi()
 ! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR1:.*]] = hlfir.designate %[[A]]#0 (%{{.*}})
 ! CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
-! CHECK: } -> !omp.iterated<!llvm.ptr>
+! CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
 ! CHECK: %[[IT2:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} to {{.*}} step {{.*}}) {
 ! CHECK:   %[[COOR2:.*]] = hlfir.designate %[[B]]#0 (%{{.*}})
 ! 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 00000000000000..ebe576f99997b5
--- /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 : i32
+! CAN: omp.iterator(%{{.*}}: i32) =
+! 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 : i32
+! CAN-DAG: %[[FOUR:.*]] = arith.constant 4 : i32
+! CAN: omp.iterator(%{{.*}}: i32) =
+! 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 : i32
+! CAN-DAG: %[[FOUR:.*]] = arith.constant 4 : i32
+! CAN-DAG: %[[NEG:.*]] = arith.constant -1 : i32
+! CAN: omp.iterator(%{{.*}}: i32) =
+! 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 : i32
+! CAN-DAG: %[[TWO:.*]] = arith.constant 2 : i32
+! CAN: omp.iterator(%{{.*}}: i32) =
+! 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 : i32
+! CAN-DAG: %[[TWO:.*]] = arith.constant 2 : i32
+! CAN-DAG: %[[NEG:.*]] = arith.constant -1 : i32
+! CAN: omp.iterator(%{{.*}}: i32) =
+! 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(%{{[^:]+}}: i32) =
+! CHECK: omp.iterator(%{{[^:]+}}: i32) =
+! CHECK: omp.iterator(%{{[^:]+}}: i32, %{{[^:]+}}: i32) =
+! CHECK: omp.target_update
+! CAN-LABEL: func.func @_QPseparate_ranges
+! CAN: fir.call @_QPextent()
+! CAN: omp.iterator(%{{[^:]+}}: i32) =
+! CAN: omp.iterator(%{{[^:]+}}: i32) =
+! CAN: omp.iterator(%{{[^:]+}}: i32, %{{[^:]+}}: i32) =
+! 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(%{{[^:]+}}: i32) =
+! CAN-LABEL: func.func @_QPunused_empty_range
+! CAN-DAG: %[[ONE:.*]] = arith.constant 1 : i32
+! CAN-DAG: %[[FOUR:.*]] = arith.constant 4 : i32
+! CAN: omp.iterator(%{{[^:]+}}: i32) =
+! 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 00000000000000..8c5b50cf07ced9
--- /dev/null
+++ b/flang/test/Lower/OpenMP/motion-iterator.f90
@@ -0,0 +1,910 @@
+! 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   fir.store %[[IV]] 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:.*]]: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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:.*]] = arith.constant 1 : i32
+! CHECK: %[[N_UB:.*]] = fir.load %[[N]]#0 : !fir.ref<i32>
+! CHECK: %[[N_STEP:.*]] = arith.constant 1 : i32
+! CHECK: %[[M_LB:.*]] = arith.constant 1 : i32
+! CHECK: %[[M_UB:.*]] = fir.load %[[M]]#0 : !fir.ref<i32>
+! CHECK: %[[M_STEP:.*]] = arith.constant 1 : i32
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: i32,
+! CHECK-SAME: %[[IV1:.*]]: i32) =
+! CHECK-SAME: (%[[N_LB]] to %[[N_UB]] step %[[N_STEP]],
+! CHECK-SAME: %[[M_LB]] to %[[M_UB]] step %[[M_STEP]]) {
+! CHECK: fir.store %[[IV0]] to %[[IV0_ADDR:.*]] : !fir.ref<i32>
+! CHECK: %[[IV0_DECL:.*]]:2 = hlfir.declare %[[IV0_ADDR]]
+! CHECK: fir.store %[[IV1]] 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 uniq_name("_QFtarget_update_allocatableEa") fortran_attrs<allocatable>
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} 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 uniq_name("_QFtarget_update_pointerEa") fortran_attrs<pointer>
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} 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 uniq_name("_QFtarget_update_allocatable_2dEa") fortran_attrs<allocatable>
+! 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:.*]] = arith.constant 1 : i32
+! CHECK: %[[N_UB:.*]] = fir.load %[[N]]#0 : !fir.ref<i32>
+! CHECK: %[[N_STEP:.*]] = arith.constant 1 : i32
+! CHECK: %[[M_LB:.*]] = arith.constant 1 : i32
+! CHECK: %[[M_UB:.*]] = fir.load %[[M]]#0 : !fir.ref<i32>
+! CHECK: %[[M_STEP:.*]] = arith.constant 1 : i32
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV0:.*]]: i32,
+! CHECK-SAME: %[[IV1:.*]]: i32) =
+! CHECK-SAME: (%[[N_LB]] to %[[N_UB]] step %[[N_STEP]],
+! CHECK-SAME: %[[M_LB]] to %[[M_UB]] step %[[M_STEP]]) {
+! CHECK: fir.store %[[IV0]] to %[[IV0_ADDR:.*]] : !fir.ref<i32>
+! CHECK: %[[IV0_DECL:.*]]:2 = hlfir.declare %[[IV0_ADDR]]
+! CHECK: fir.store %[[IV1]] 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 uniq_name("_QFtarget_update_deferred_charEa") fortran_attrs<allocatable>
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} 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 uniq_name("_QFtarget_update_class_starEa") fortran_attrs<allocatable>
+! CHECK: %[[IT:.*]] = omp.iterator(%{{.*}}: i32) = ({{.*}} 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   fir.store %[[IV]] 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:.*]] = arith.constant 1 : i32
+! CHECK: %[[I_UB:.*]] = arith.constant 4 : i32
+! CHECK: %[[I_STEP:.*]] = arith.constant 1 : i32
+! CHECK: %[[J_LB:.*]] = arith.constant 1 : i32
+! CHECK: %[[J_UB:.*]] = arith.constant 6 : i32
+! CHECK: %[[J_STEP:.*]] = arith.constant 1 : i32
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV_I:.*]]: i32,
+! CHECK-SAME: %[[IV_J:.*]]: i32) =
+! CHECK-SAME: (%[[I_LB]] to %[[I_UB]] step %[[I_STEP]],
+! CHECK-SAME: %[[J_LB]] to %[[J_UB]] step %[[J_STEP]]) {
+! CHECK:   fir.store %[[IV_I]] to %[[IV_I_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV_I_DECL:.*]]:2 = hlfir.declare %[[IV_I_MEM]]
+! CHECK:   fir.store %[[IV_J]] 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: %[[LB:.*]] = arith.constant 1 : i32
+! CHECK: %[[UB:.*]] = arith.constant 16 : i32
+! CHECK: %[[STEP:.*]] = arith.constant 2 : i32
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: i32) = (%[[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: %[[LB:.*]] = arith.constant 16 : i32
+! CHECK: %[[UB:.*]] = arith.constant 1 : i32
+! CHECK: %[[STEP:.*]] = arith.constant -1 : i32
+! CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: i32) = (%[[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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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:.*]]: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   fir.store %[[IV]] 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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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(%{{.*}}: i32) = ({{.*}} 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   fir.store %[[IV]] 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   fir.store %[[IV]] 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:.*]]: i32, %[[IV1:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}, {{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   fir.store %[[IV0]] to %[[IV0_MEM:.*]] : !fir.ref<i32>
+! CHECK:   %[[IV0_DECL:.*]]:2 = hlfir.declare %[[IV0_MEM]]
+! CHECK:   fir.store %[[IV1]] 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:.*]]: i32) = ({{.*}} to {{.*}} step {{.*}}) {
+! CHECK:   fir.store %[[IV]] 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: %[[LB:.*]] = arith.constant 16 : i32
+! CHECK: %[[UB:.*]] = arith.constant 1 : i32
+! CHECK: %[[STEP:.*]] = arith.constant -1 : i32
+! CHECK: %[[IT1:.*]] = omp.iterator(%{{.*}}: i32) = (%[[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(%{{.*}}: i32) = (%[[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 645dd836f96871..61ea5e13b3eeb0 100644
--- a/flang/test/Lower/OpenMP/task-affinity.f90
+++ b/flang/test/Lower/OpenMP/task-affinity.f90
@@ -296,7 +296,7 @@ subroutine affinity_per_locator(m)
 ! CHECK: %[[ENTRY_I:.*]] = omp.affinity_entry %[[PTR_I]], %[[SIZE_I]]
 ! CHECK: omp.yield(%[[ENTRY_I]] :
 ! CHECK-SAME: !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: %[[IT_J:.*]] = omp.iterator(%[[IV_J:.*]]: i32) =
 ! CHECK-SAME: (%[[J_LB]] to %[[J_UB]] step %[[J_STEP]]) {
 ! CHECK: fir.store %[[IV_J]] to %[[MEM_J:.*]] : !fir.ref<i32>
@@ -310,7 +310,7 @@ subroutine affinity_per_locator(m)
 ! CHECK: %[[ENTRY_J:.*]] = omp.affinity_entry %[[PTR_J]], %[[SIZE_J]]
 ! CHECK: omp.yield(%[[ENTRY_J]] :
 ! CHECK-SAME: !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: %[[C_PTR:.*]] = fir.convert %[[C]]#0
 ! CHECK-SAME: (!fir.ref<i32>) -> !fir.ref<i8>
 ! CHECK: %[[C_ENTRY:.*]] = omp.affinity_entry %[[C_PTR]],
@@ -348,7 +348,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()
@@ -378,7 +378,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()
@@ -413,7 +413,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()
@@ -449,7 +449,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 e766b1b44d0faf..8e0efb765ff6ae 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 bd1eb98ebec60a..3eb8b9d7c50af4 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 e9ac6d5f3d505f..c26e825cacd3c5 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 a839287121396d..5f4dce943765c5 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2739,6 +2739,12 @@ def IteratorOp : OpenMP_Op<"iterator", [AttrSizedOperandSegments],
     The region may contain multiple blocks. Exactly one of them is terminated
     by `omp.yield`; every other block branches to a block of the region.
 
+    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/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 935aa3232fa2ef..339a6c8a04c641 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -2876,6 +2876,8 @@ 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 =
           moduleTranslation.lookupValue(itersOp.getLoopLowerBounds()[d]);
@@ -2885,6 +2887,8 @@ class IteratorInfo {
           moduleTranslation.lookupValue(itersOp.getLoopSteps()[d]);
       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");
@@ -2900,10 +2904,9 @@ class IteratorInfo {
       llvm::Value *start = builder.CreateSExt(lb, countTy);
       llvm::Value *stop = builder.CreateSExt(ub, countTy);
       llvm::Value *step = builder.CreateSExt(st, countTy);
-      llvm::Value *count =
-          moduleTranslation.getOpenMPBuilder()->calculateCanonicalLoopTripCount(
-              builder, start, stop, step, /*IsSigned=*/true,
-              /*InclusiveStop=*/true);
+      llvm::Value *count = ompBuilder.calculateCanonicalLoopTripCount(
+          loc, start, stop, step, /*IsSigned=*/true,
+          itersOp.getLoopInclusive(), "iterator");
       trips[d] = builder.CreateZExtOrTrunc(count, builder.getInt64Ty());
     }
 
@@ -3039,10 +3042,16 @@ static mlir::LogicalResult convertIteratorRegion(
   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);
+    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
     idx =
diff --git a/mlir/test/Dialect/OpenMP/iterator-empty.mlir b/mlir/test/Dialect/OpenMP/iterator-empty.mlir
index c1d8e27571e97d..e8609dba6deddd 100644
--- a/mlir/test/Dialect/OpenMP/iterator-empty.mlir
+++ b/mlir/test/Dialect/OpenMP/iterator-empty.mlir
@@ -1,18 +1,31 @@
 // RUN: mlir-opt %s | mlir-opt | FileCheck %s
+// RUN: mlir-opt %s --canonicalize | FileCheck %s
 
-// A range pointing away from its endpoint is valid and produces no entries.
+// A range pointing away from its endpoint is valid and produces no entries,
+// for either endpoint convention.
 // CHECK-LABEL: func.func @empty_ranges
-func.func @empty_ranges(%x: !llvm.ptr) {
+func.func @empty_ranges(%x: !llvm.ptr)
+    -> (!omp.iterated<!llvm.ptr>, !omp.iterated<!llvm.ptr>,
+        !omp.iterated<!llvm.ptr>) {
   %c1 = arith.constant 1 : index
   %c3 = arith.constant 3 : index
   %neg = arith.constant -1 : index
   // CHECK: omp.iterator
+  // CHECK: } -> !omp.iterated<!llvm.ptr>
   %positive = omp.iterator(%i: index) = (%c3 to %c1 step %c1) {
     omp.yield(%x : !llvm.ptr)
   } -> !omp.iterated<!llvm.ptr>
   // CHECK: omp.iterator
+  // CHECK: } -> !omp.iterated<!llvm.ptr>
   %negative = omp.iterator(%i: index) = (%c1 to %c3 step %neg) {
     omp.yield(%x : !llvm.ptr)
   } -> !omp.iterated<!llvm.ptr>
-  return
+  // CHECK: omp.iterator
+  // CHECK: } inclusive -> !omp.iterated<!llvm.ptr>
+  %inclusive = omp.iterator(%i: index) = (%c3 to %c1 step %c1) {
+    omp.yield(%x : !llvm.ptr)
+  } inclusive -> !omp.iterated<!llvm.ptr>
+  return %positive, %negative, %inclusive
+      : !omp.iterated<!llvm.ptr>, !omp.iterated<!llvm.ptr>,
+        !omp.iterated<!llvm.ptr>
 }
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 7416c3da3b3951..3a18d4b0bc01e8 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -4270,6 +4270,18 @@ func.func @omp_iterator_negative_step(%s2 : !llvm.struct<(ptr, i64)>) -> () {
   return
 }
 
+// CHECK-LABEL: func.func @omp_iterator_inclusive_wide
+func.func @omp_iterator_inclusive_wide(%hi : i128, %st : i128) -> () {
+  // CHECK: omp.iterator(%[[IV:.*]]: i128) = (%{{.*}} to %{{.*}} step %{{.*}}) {
+  // CHECK:   omp.yield(%[[IV]] : i128)
+  // CHECK: } inclusive -> !omp.iterated<i128>
+  %lb = arith.constant 0 : i128
+  %0 = omp.iterator(%iv: i128) = (%lb to %hi step %st) {
+    omp.yield(%iv : i128)
+  } inclusive -> !omp.iterated<i128>
+  return
+}
+
 // CHECK-LABEL: func.func @omp_iterator_multi_block
 func.func @omp_iterator_multi_block(%a : !llvm.ptr, %b : !llvm.ptr) -> () {
   // CHECK: omp.iterator(%[[IV:.*]]: index) = (%{{.*}} to %{{.*}} step %{{.*}}) {
diff --git a/mlir/test/Target/LLVMIR/openmp-iterator-empty.mlir b/mlir/test/Target/LLVMIR/openmp-iterator-empty.mlir
index 794f1e2e50e2a9..a99400f7c2501b 100644
--- a/mlir/test/Target/LLVMIR/openmp-iterator-empty.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-iterator-empty.mlir
@@ -7,7 +7,7 @@ llvm.func @empty_positive(%x: !llvm.ptr) {
   %c3 = llvm.mlir.constant(3 : i64) : i64
   %it = omp.iterator(%i: i64) = (%c3 to %c2 step %c2) {
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -24,7 +24,7 @@ llvm.func @empty_negative(%x: !llvm.ptr) {
   %step = llvm.mlir.constant(-2 : i64) : i64
   %it = omp.iterator(%i: i64) = (%c2 to %c3 step %step) {
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -42,7 +42,7 @@ llvm.func @empty_product(%x: !llvm.ptr) {
   %it = omp.iterator(%i: i64, %j: i64) =
       (%c3 to %c1 step %c1, %c3 to %c1 step %c1) {
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
diff --git a/mlir/test/Target/LLVMIR/openmp-iterator-multi-block.mlir b/mlir/test/Target/LLVMIR/openmp-iterator-multi-block.mlir
index 3487897912eb51..d98dcc4fb15cd7 100644
--- a/mlir/test/Target/LLVMIR/openmp-iterator-multi-block.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-iterator-multi-block.mlir
@@ -12,7 +12,7 @@ llvm.func @depend_diamond(%a : !llvm.ptr, %b : !llvm.ptr) {
     llvm.cond_br %first, ^bb1(%a : !llvm.ptr), ^bb1(%b : !llvm.ptr)
   ^bb1(%p : !llvm.ptr):
     omp.yield(%p : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
   }
@@ -55,7 +55,7 @@ llvm.func @affinity_split(%a : !llvm.ptr, %b : !llvm.ptr) {
     %e = omp.affinity_entry %p, %len
         : (!llvm.ptr, i64) -> !omp.affinity_entry_ty<!llvm.ptr, i64>
     omp.yield(%e : !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
   }
@@ -93,7 +93,7 @@ llvm.func @depend_inner_loop(%addr : !llvm.ptr) {
   ^exit:
     %p = llvm.getelementptr %addr[%acc] : (!llvm.ptr, i64) -> !llvm.ptr, i8
     omp.yield(%p : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
   }
@@ -130,7 +130,7 @@ llvm.func @depend_2d(%a : !llvm.ptr, %b : !llvm.ptr) {
     llvm.cond_br %lt, ^bb1(%a : !llvm.ptr), ^bb1(%b : !llvm.ptr)
   ^bb1(%p : !llvm.ptr):
     omp.yield(%p : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
   }
@@ -160,7 +160,7 @@ llvm.func @depend_narrow(%x : !llvm.ptr) {
   ^next(%v: i32):
     llvm.store %v, %x : i32, !llvm.ptr
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -183,7 +183,7 @@ llvm.func @depend_empty_dynamic(%a : !llvm.ptr, %m : i32, %d : i32) {
   ^bb1(%k : i32):
     %p = llvm.getelementptr %a[%k] : (!llvm.ptr, i32) -> !llvm.ptr, i32
     omp.yield(%p : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.task depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>) {
     omp.terminator
   }
diff --git a/mlir/test/Target/LLVMIR/openmp-iterator-wide.mlir b/mlir/test/Target/LLVMIR/openmp-iterator-wide.mlir
index 013b20636d9dfb..feecf39b44f3ee 100644
--- a/mlir/test/Target/LLVMIR/openmp-iterator-wide.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-iterator-wide.mlir
@@ -7,7 +7,7 @@ llvm.func @empty_positive(%x: !llvm.ptr) {
   %st = llvm.mlir.constant(1 : i128) : i128
   %it = omp.iterator(%i: i128) = (%lb to %ub step %st) {
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -23,7 +23,7 @@ llvm.func @empty_negative(%x: !llvm.ptr) {
   %st = llvm.mlir.constant(-1 : i128) : i128
   %it = omp.iterator(%i: i128) = (%lb to %ub step %st) {
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -38,7 +38,7 @@ llvm.func @dynamic(%x: !llvm.ptr, %lb: i128, %ub: i128, %st: i128) {
   %it = omp.iterator(%i: i128) = (%lb to %ub step %st) {
     llvm.store %i, %x : i128, !llvm.ptr
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -62,7 +62,7 @@ llvm.func @narrow(%x: !llvm.ptr, %lb: i32, %ub: i32, %st: i32) {
   %it = omp.iterator(%i: i32) = (%lb to %ub step %st) {
     llvm.store %i, %x : i32, !llvm.ptr
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -82,7 +82,7 @@ llvm.func @nonempty_positive(%x: !llvm.ptr) {
   %it = omp.iterator(%i: i128) = (%lb to %ub step %st) {
     llvm.store %i, %x : i128, !llvm.ptr
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -102,7 +102,7 @@ llvm.func @nonempty_negative(%x: !llvm.ptr) {
   %it = omp.iterator(%i: i128) = (%lb to %ub step %st) {
     llvm.store %i, %x : i128, !llvm.ptr
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
@@ -122,7 +122,7 @@ llvm.func @wide_step(%x: !llvm.ptr) {
   %it = omp.iterator(%i: i128) = (%lb to %ub step %st) {
     llvm.store %i, %x : i128, !llvm.ptr
     omp.yield(%x : !llvm.ptr)
-  } -> !omp.iterated<!llvm.ptr>
+  } inclusive -> !omp.iterated<!llvm.ptr>
   omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
diff --git a/mlir/test/Target/LLVMIR/openmp-iterator.mlir b/mlir/test/Target/LLVMIR/openmp-iterator.mlir
index ad8d603d7d266d..9ebee61b0237b6 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
@@ -280,7 +280,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
@@ -305,6 +305,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
 // --------------------------------------------------------------------
@@ -316,7 +348,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
@@ -362,7 +394,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
@@ -401,7 +433,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
@@ -444,7 +476,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
@@ -476,7 +508,7 @@ llvm.func @omp_task_depend_iterator_wide_span(%addr : !llvm.ptr) {
   %max = llvm.mlir.constant(9223372036854775807 : i64) : i64
   %it = omp.iterator(%iv: i64) = (%min to %zero step %max) {
     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
   }
@@ -496,7 +528,7 @@ llvm.func @omp_task_depend_iterator_min_step(%addr : !llvm.ptr) {
   %min = llvm.mlir.constant(-9223372036854775808 : i64) : i64
   %it = omp.iterator(%iv: i64) = (%zero to %end step %min) {
     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
   }
@@ -508,6 +540,73 @@ llvm.func @omp_task_depend_iterator_min_step(%addr : !llvm.ptr) {
 // CHECK: call i32 @__kmpc_omp_task_with_deps(
 // CHECK-SAME: ptr {{.*}}, i32 {{.*}}, ptr {{.*}}, i32 1,
 
+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)
+
+// An inclusive i8 range may need 256 entries. Its trip count must not wrap
+// when one is added to the span in the range's own type.
+llvm.func @omp_taskwait_iterator_full_i8_range(%addr : !llvm.ptr) {
+  %lo = llvm.mlir.constant(-128 : i8) : i8
+  %hi = llvm.mlir.constant(127 : i8) : i8
+  %step = llvm.mlir.constant(1 : i8) : i8
+  %it = omp.iterator(%iv: i8) = (%lo to %hi step %step) {
+    %element = llvm.getelementptr %addr[%iv]
+        : (!llvm.ptr, i8) -> !llvm.ptr, i8
+    omp.yield(%element : !llvm.ptr)
+  } inclusive -> !omp.iterated<!llvm.ptr>
+  omp.taskwait depend(taskdependin -> %it : !omp.iterated<!llvm.ptr>)
+  llvm.return
+}
+
+// CHECK-LABEL: define void @omp_taskwait_iterator_full_i8_range
+// CHECK: omp_dep_iterator.cond:
+// CHECK: icmp ult i64 %omp_dep_iterator.iv, 256
+// CHECK: omp_dep_iterator.body:
+// CHECK: getelementptr i8, ptr %{{.*}}, i8 %{{.*}}
+// CHECK: call void @__kmpc_omp_taskwait_deps_51(
+// CHECK-SAME: i32 256, ptr
+
 //--- target.mlir
 
 // --------------------------------------------------------------------
@@ -525,7 +624,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 305cbf9c275181..d8eb1ec73ae083 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
 }

>From 833941b9c1888bacd593c88e3f438255c6bad23a Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Wed, 14 Oct 2026 12:10:43 -0500
Subject: [PATCH 2/2] Select map and motion iterators from source references

Map and motion locators chose their iterator ranges from the folded
designator, so a reference such as a(0*i + 1) lost its iterator and was
mapped unconditionally, even when the range was empty.

Build Map, To and From locator lists with the source iterator references
already used for AFFINITY and DEPEND, and select each locator's ranges from
them. Locators without an iterator modifier are unchanged.
---
 flang/lib/Lower/OpenMP/ClauseProcessor.cpp  | 12 ++----------
 flang/lib/Lower/OpenMP/Clauses.cpp          |  9 ++++++---
 flang/test/Lower/OpenMP/iterator-ranges.f90 | 21 +++++++++++++++++++++
 3 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index b6a9645ddb2e6b..2fb6a396d40252 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -2006,16 +2006,8 @@ void ClauseProcessor::processMapObjectsWithIterator(
   // 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);
-
+    llvm::SmallVector<IteratorRange> objectRanges =
+        getIteratorRangesForObject(object, iteratorRanges);
     if (!objectRanges.empty()) {
       if (hasUnsupportedReferenceModifier)
         TODO(clauseLocation,
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index ba2adcd74138b7..50750e5023597f 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -1080,10 +1080,11 @@ From make(const parser::OmpClause::From &inp,
     return std::nullopt;
   }();
 
+  auto objects = makeIteratorObjects(t5, iterator, semaCtx);
   return From{{/*Expectation=*/maybeExp,
                /*Mappers=*/std::move(mappers),
                /*Iterator=*/std::move(iterator),
-               /*LocatorList=*/makeObjects(t5, semaCtx)}};
+               /*LocatorList=*/std::move(objects)}};
 }
 
 // Full: empty
@@ -1398,12 +1399,13 @@ Map make(const parser::OmpClause::Map &inp,
     return std::nullopt;
   }();
 
+  auto objects = makeIteratorObjects(t2, iterator, semaCtx);
   return Map{{/*MapType=*/std::move(type),
               /*MapTypeModifiers=*/std::move(maybeTypeMods),
               /*AttachModifier=*/std::move(attachMod),
               /*RefModifier=*/std::move(refMod), /*Mapper=*/std::move(mappers),
               /*Iterator=*/std::move(iterator),
-              /*LocatorList=*/makeObjects(t2, semaCtx)}};
+              /*LocatorList=*/std::move(objects)}};
 }
 
 Match make(const parser::OmpClause::Match &inp,
@@ -1820,10 +1822,11 @@ To make(const parser::OmpClause::To &inp,
     return std::nullopt;
   }();
 
+  auto objects = makeIteratorObjects(t5, iterator, semaCtx);
   return To{{/*Expectation=*/maybeExp,
              /*Mappers=*/{std::move(mappers)},
              /*Iterator=*/std::move(iterator),
-             /*LocatorList=*/makeObjects(t5, semaCtx)}};
+             /*LocatorList=*/std::move(objects)}};
 }
 
 UnifiedAddress make(const parser::OmpClause::UnifiedAddress &inp,
diff --git a/flang/test/Lower/OpenMP/iterator-ranges.f90 b/flang/test/Lower/OpenMP/iterator-ranges.f90
index ebe576f99997b5..b017126aa89edd 100644
--- a/flang/test/Lower/OpenMP/iterator-ranges.f90
+++ b/flang/test/Lower/OpenMP/iterator-ranges.f90
@@ -124,6 +124,27 @@ subroutine unused_empty_range(a)
 ! CAN: omp.iterator(%{{[^:]+}}: i32) =
 ! CAN-SAME: (%[[ONE]] to %[[FOUR]] step %[[ONE]])
 
+! A reference removed by folding still selects its range, so an empty range
+! removes the locator instead of mapping it unconditionally.
+subroutine folded_reference(a, n)
+  integer, parameter :: stride = 0
+  integer :: a(10), n
+  !$omp target update from(iterator(i=1:n): a(stride*i + 1))
+  !$omp target update to(iterator(i=1:n): a(stride*i + 1))
+  !$omp target enter data map(iterator(i=1:0), to: a(1 + 0*i))
+end
+! CHECK-LABEL: func.func @_QPfolded_reference
+! CHECK: omp.iterator(%{{[^:]+}}: i32) =
+! CHECK: omp.target_update map_iterated(
+! CHECK: omp.iterator(%{{[^:]+}}: i32) =
+! CHECK: omp.target_update map_iterated(
+! CHECK: omp.iterator(%{{[^:]+}}: i32) =
+! CHECK: omp.target_enter_data map_iterated(
+! CAN-LABEL: func.func @_QPfolded_reference
+! CAN: omp.target_update map_iterated(
+! CAN: omp.target_update map_iterated(
+! CAN: omp.target_enter_data map_iterated(
+
 ! Values beyond the index width must reach the iterator and its body intact.
 subroutine wide_range(a, hi, step)
   integer :: a(2)



More information about the Mlir-commits mailing list