[flang-commits] [flang] [mlir] [mlir][OpenMP] Add iterator support to map/motion clause (PR #197047)

via flang-commits flang-commits at lists.llvm.org
Mon May 11 16:54:37 PDT 2026


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

>From 71929a16cbc61eb9b54daac56cbc3ad6798d7eaa Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Tue, 31 Mar 2026 01:06:43 -0500
Subject: [PATCH 1/2] [mlir][OpenMP] Add iterator support to map/motion clause

Extend map/motion clause to support `!omp.iterated<Ty>` handles
alongside map/motion locators.

This is part of feature work for #188061

Assisted with copilot
---
 flang/lib/Lower/OpenMP/OpenMP.cpp             |   3 +-
 .../Optimizer/OpenMP/FunctionFiltering.cpp    |   1 +
 .../Optimizer/OpenMP/LowerWorkdistribute.cpp  |  37 ++---
 flang/lib/Utils/OpenMP.cpp                    |   3 +-
 .../mlir/Dialect/OpenMP/OpenMPClauses.td      |  13 +-
 mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td |  15 +-
 mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp  | 149 +++++++++++++++---
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      |  17 +-
 mlir/test/Dialect/OpenMP/invalid.mlir         |  42 ++++-
 mlir/test/Dialect/OpenMP/ops.mlir             | 101 +++++++++++-
 mlir/test/Target/LLVMIR/openmp-todo.mlir      |  83 ++++++++++
 11 files changed, 403 insertions(+), 61 deletions(-)

diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index fb5014f3394be..de47786572b75 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4371,7 +4371,8 @@ genOpenMPDeclareMapperImpl(lower::AbstractConverter &converter,
   List<Clause> clauses = makeClauses(construct.v.Clauses(), semaCtx);
   ClauseProcessor cp(converter, semaCtx, clauses);
   cp.processMap(loc, stmtCtx, clauseOps);
-  mlir::omp::DeclareMapperInfoOp::create(firOpBuilder, loc, clauseOps.mapVars);
+  mlir::omp::DeclareMapperInfoOp::create(firOpBuilder, loc, clauseOps.mapVars,
+                                         /*map_iterated=*/{});
 }
 
 static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
diff --git a/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp b/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
index b78b33f31b142..70746b3612f76 100644
--- a/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
+++ b/flang/lib/Optimizer/OpenMP/FunctionFiltering.cpp
@@ -311,6 +311,7 @@ class FunctionFilteringPass
       targetOp.setDependKindsAttr(nullptr);
       targetOp.getDependIteratedMutable().clear();
       targetOp.setDependIteratedKindsAttr(nullptr);
+      targetOp.getMapIteratedMutable().clear();
       targetOp.getDeviceMutable().clear();
       targetOp.getIfExprMutable().clear();
       targetOp.getDynGroupprivateSizeMutable().clear();
diff --git a/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp b/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp
index 1d98018ffbd05..4bd46ae4df204 100644
--- a/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp
+++ b/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp
@@ -749,9 +749,9 @@ FailureOr<omp::TargetOp> splitTargetData(omp::TargetOp targetOp,
   auto deviceAddrVars = targetOp.getHasDeviceAddrVars();
   auto devicePtrVars = targetOp.getIsDevicePtrVars();
   // Create the target data op
-  auto targetDataOp =
-      omp::TargetDataOp::create(rewriter, loc, device, ifExpr, outerMapInfos,
-                                deviceAddrVars, devicePtrVars);
+  auto targetDataOp = omp::TargetDataOp::create(
+      rewriter, loc, device, ifExpr, outerMapInfos,
+      /*map_iterated=*/{}, deviceAddrVars, devicePtrVars);
   auto taregtDataBlock = rewriter.createBlock(&targetDataOp.getRegion());
   mlir::omp::TerminatorOp::create(rewriter, loc);
   rewriter.setInsertionPointToStart(taregtDataBlock);
@@ -767,9 +767,10 @@ FailureOr<omp::TargetOp> splitTargetData(omp::TargetOp targetOp,
       targetOp.getHostEvalVars(), targetOp.getIfExpr(),
       targetOp.getInReductionVars(), targetOp.getInReductionByrefAttr(),
       targetOp.getInReductionSymsAttr(), targetOp.getIsDevicePtrVars(),
-      innerMapInfos, targetOp.getNowaitAttr(), targetOp.getPrivateVars(),
-      targetOp.getPrivateSymsAttr(), targetOp.getPrivateNeedsBarrierAttr(),
-      targetOp.getThreadLimitVars(), targetOp.getPrivateMapsAttr());
+      innerMapInfos, targetOp.getMapIterated(), targetOp.getNowaitAttr(),
+      targetOp.getPrivateVars(), targetOp.getPrivateSymsAttr(),
+      targetOp.getPrivateNeedsBarrierAttr(), targetOp.getThreadLimitVars(),
+      targetOp.getPrivateMapsAttr());
   rewriter.inlineRegionBefore(targetOp.getRegion(), newTargetOp.getRegion(),
                               newTargetOp.getRegion().begin());
   rewriter.replaceOp(targetOp, targetDataOp);
@@ -1489,10 +1490,10 @@ genPreTargetOp(omp::TargetOp targetOp, SmallVector<Value> &preMapOperands,
       targetOp.getDynGroupprivateSize(), targetOp.getHasDeviceAddrVars(),
       preHostEvalVars, targetOp.getIfExpr(), targetOp.getInReductionVars(),
       targetOp.getInReductionByrefAttr(), targetOp.getInReductionSymsAttr(),
-      targetOp.getIsDevicePtrVars(), preMapOperands, targetOp.getNowaitAttr(),
-      targetOp.getPrivateVars(), targetOp.getPrivateSymsAttr(),
-      targetOp.getPrivateNeedsBarrierAttr(), targetOp.getThreadLimitVars(),
-      targetOp.getPrivateMapsAttr());
+      targetOp.getIsDevicePtrVars(), preMapOperands, targetOp.getMapIterated(),
+      targetOp.getNowaitAttr(), targetOp.getPrivateVars(),
+      targetOp.getPrivateSymsAttr(), targetOp.getPrivateNeedsBarrierAttr(),
+      targetOp.getThreadLimitVars(), targetOp.getPrivateMapsAttr());
   auto *preTargetBlock = rewriter.createBlock(
       &preTargetOp.getRegion(), preTargetOp.getRegion().begin(), {}, {});
   IRMapping preMapping;
@@ -1582,10 +1583,10 @@ genIsolatedTargetOp(omp::TargetOp targetOp, SmallVector<Value> &postMapOperands,
       targetOp.getDynGroupprivateSize(), targetOp.getHasDeviceAddrVars(),
       isolatedHostEvalVars, targetOp.getIfExpr(), targetOp.getInReductionVars(),
       targetOp.getInReductionByrefAttr(), targetOp.getInReductionSymsAttr(),
-      targetOp.getIsDevicePtrVars(), postMapOperands, targetOp.getNowaitAttr(),
-      targetOp.getPrivateVars(), targetOp.getPrivateSymsAttr(),
-      targetOp.getPrivateNeedsBarrierAttr(), targetOp.getThreadLimitVars(),
-      targetOp.getPrivateMapsAttr());
+      targetOp.getIsDevicePtrVars(), postMapOperands, targetOp.getMapIterated(),
+      targetOp.getNowaitAttr(), targetOp.getPrivateVars(),
+      targetOp.getPrivateSymsAttr(), targetOp.getPrivateNeedsBarrierAttr(),
+      targetOp.getThreadLimitVars(), targetOp.getPrivateMapsAttr());
   auto *isolatedTargetBlock =
       rewriter.createBlock(&isolatedTargetOp.getRegion(),
                            isolatedTargetOp.getRegion().begin(), {}, {});
@@ -1665,10 +1666,10 @@ static omp::TargetOp genPostTargetOp(omp::TargetOp targetOp,
       targetOp.getDynGroupprivateSize(), targetOp.getHasDeviceAddrVars(),
       postHostEvalVars, targetOp.getIfExpr(), targetOp.getInReductionVars(),
       targetOp.getInReductionByrefAttr(), targetOp.getInReductionSymsAttr(),
-      targetOp.getIsDevicePtrVars(), postMapOperands, targetOp.getNowaitAttr(),
-      targetOp.getPrivateVars(), targetOp.getPrivateSymsAttr(),
-      targetOp.getPrivateNeedsBarrierAttr(), targetOp.getThreadLimitVars(),
-      targetOp.getPrivateMapsAttr());
+      targetOp.getIsDevicePtrVars(), postMapOperands, targetOp.getMapIterated(),
+      targetOp.getNowaitAttr(), targetOp.getPrivateVars(),
+      targetOp.getPrivateSymsAttr(), targetOp.getPrivateNeedsBarrierAttr(),
+      targetOp.getThreadLimitVars(), targetOp.getPrivateMapsAttr());
   // Create the block for postTargetOp
   auto *postTargetBlock = rewriter.createBlock(
       &postTargetOp.getRegion(), postTargetOp.getRegion().begin(), {}, {});
diff --git a/flang/lib/Utils/OpenMP.cpp b/flang/lib/Utils/OpenMP.cpp
index cd6b7f310dd75..7d6356cd6d1df 100644
--- a/flang/lib/Utils/OpenMP.cpp
+++ b/flang/lib/Utils/OpenMP.cpp
@@ -256,7 +256,8 @@ mlir::FlatSymbolRefAttr getOrGenImplicitDefaultDeclareMapper(
       /*partialMap=*/true);
 
   clauseMapVars.emplace_back(mapOp);
-  mlir::omp::DeclareMapperInfoOp::create(firOpBuilder, loc, clauseMapVars);
+  mlir::omp::DeclareMapperInfoOp::create(firOpBuilder, loc, clauseMapVars,
+      /*map_iterated=*/{});
   return mlir::FlatSymbolRefAttr::get(firOpBuilder.getContext(), mapperNameStr);
 }
 } // namespace Fortran::utils::openmp
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
index 2a2b695e77c0e..5f667af813178 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
@@ -903,20 +903,25 @@ class OpenMP_MapClauseSkip<
     MapClauseOwningOpInterface
   ];
 
-  let arguments = (ins
-    Variadic<OpenMP_PointerLikeType>:$map_vars
-  );
+  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$map_vars,
+      Variadic<OpenMP_IteratedType>:$map_iterated);
 
   // This assembly format should only be used by operations where `map` does not
   // define entry block arguments. Otherwise, it must be printed and parsed
   // together with the corresponding region.
   let optAssemblyFormat = [{
-    `map_entries` `(` $map_vars `:` type($map_vars) `)`
+    `map_entries` `(`
+      custom<MapEntryList>($map_vars, type($map_vars),
+                           $map_iterated, type($map_iterated)) `)`
   }];
 
   let description = [{
     The optional `map_vars` maps data from the current task's data environment
     to the device data environment.
+
+    The optional `map_iterated` holds iterator-produced handles (from
+    `omp.iterator`) whose bodies create `omp.map.info` ops, specifying
+    map entries expanded at runtime via an iterator modifier.
   }];
 }
 
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index c8c233477c174..e827935306d51 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -1650,12 +1650,13 @@ def TargetOp : OpenMP_Op<"target", traits = [
                        bool *hostEvalTripCount = nullptr);
   }] # clausesExtraClassDeclaration;
 
-  let assemblyFormat = clausesAssemblyFormat # [{
+  let assemblyFormat = clausesAssemblyFormat#[{
     custom<TargetOpRegion>(
         $region, $has_device_addr_vars, type($has_device_addr_vars),
         $host_eval_vars, type($host_eval_vars), $in_reduction_vars,
         type($in_reduction_vars), $in_reduction_byref, $in_reduction_syms,
-        $map_vars, type($map_vars), $private_vars, type($private_vars),
+        $map_vars, type($map_vars), $map_iterated, type($map_iterated),
+        $private_vars, type($private_vars),
         $private_syms, $private_needs_barrier, $private_maps) attr-dict
   }];
 
@@ -2072,12 +2073,10 @@ def DeclareMapperOp : OpenMP_Op<"declare_mapper", [
   let hasRegionVerifier = 1;
 }
 
-def DeclareMapperInfoOp : OpenMP_Op<"declare_mapper.info", [
-    HasParent<"DeclareMapperOp">,
-    Terminator
-  ], clauses = [
-    OpenMP_MapClause
-  ]> {
+def DeclareMapperInfoOp : OpenMP_Op<"declare_mapper.info",
+                                    [AttrSizedOperandSegments,
+                                     HasParent<"DeclareMapperOp">, Terminator],
+                                    clauses = [OpenMP_MapClause]> {
   let summary = "declare mapper info";
   let description = [{
     This Op is used to capture the map information related to it's
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 40ccff7405799..6d2b8f4b46d5c 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -1310,6 +1310,8 @@ static ParseResult parseTargetOpRegion(
     DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms,
     SmallVectorImpl<OpAsmParser::UnresolvedOperand> &mapVars,
     SmallVectorImpl<Type> &mapTypes,
+    SmallVectorImpl<OpAsmParser::UnresolvedOperand> &mapIterated,
+    SmallVectorImpl<Type> &mapIteratedTypes,
     llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &privateVars,
     llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,
     UnitAttr &privateNeedsBarrier, DenseI64ArrayAttr &privateMaps) {
@@ -1321,7 +1323,26 @@ static ParseResult parseTargetOpRegion(
   args.mapArgs.emplace(mapVars, mapTypes);
   args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
                            privateNeedsBarrier, &privateMaps);
-  return parseBlockArgRegion(parser, region, args);
+
+  if (parseBlockArgRegion(parser, region, args))
+    return failure();
+
+  // Parse optional map_iterated_entries (not block args).
+  if (succeeded(parser.parseOptionalKeyword("map_iterated_entries"))) {
+    if (parser.parseLParen() || parser.parseCommaSeparatedList([&]() {
+          OpAsmParser::UnresolvedOperand operand;
+          Type ty;
+          if (parser.parseOperand(operand) || parser.parseColonType(ty))
+            return failure();
+          mapIterated.push_back(operand);
+          mapIteratedTypes.push_back(ty);
+          return success();
+        }) ||
+        parser.parseRParen())
+      return failure();
+  }
+
+  return success();
 }
 
 static ParseResult parseInReductionPrivateRegion(
@@ -1574,8 +1595,9 @@ static void printTargetOpRegion(
     ValueRange hostEvalVars, TypeRange hostEvalTypes,
     ValueRange inReductionVars, TypeRange inReductionTypes,
     DenseBoolArrayAttr inReductionByref, ArrayAttr inReductionSyms,
-    ValueRange mapVars, TypeRange mapTypes, ValueRange privateVars,
-    TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier,
+    ValueRange mapVars, TypeRange mapTypes, ValueRange mapIterated,
+    TypeRange mapIteratedTypes, ValueRange privateVars, TypeRange privateTypes,
+    ArrayAttr privateSyms, UnitAttr privateNeedsBarrier,
     DenseI64ArrayAttr privateMaps) {
   AllRegionPrintArgs args;
   args.hasDeviceAddrArgs.emplace(hasDeviceAddrVars, hasDeviceAddrTypes);
@@ -1586,6 +1608,17 @@ static void printTargetOpRegion(
   args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
                            privateNeedsBarrier, privateMaps);
   printBlockArgRegion(p, op, region, args);
+
+  // Print map_iterated_entries if present (not block args).
+  if (!mapIterated.empty()) {
+    p << " map_iterated_entries(";
+    llvm::interleaveComma(llvm::zip(mapIterated, mapIteratedTypes), p,
+                          [&](auto t) {
+                            auto [val, ty] = t;
+                            p << val << " : " << ty;
+                          });
+    p << ")";
+  }
 }
 
 static void printInReductionPrivateRegion(
@@ -2228,6 +2261,56 @@ static void printMapClause(OpAsmPrinter &p, Operation *op,
   }
 }
 
+/// map-entry-list ::= map-entry
+///                  | map-entry-list `,` map-entry
+/// map-entry ::= ssa-id `:` type
+///             | ssa-id `:` iterated-type
+static ParseResult
+parseMapEntryList(OpAsmParser &parser,
+                  SmallVectorImpl<OpAsmParser::UnresolvedOperand> &mapVars,
+                  SmallVectorImpl<Type> &mapVarTypes,
+                  SmallVectorImpl<OpAsmParser::UnresolvedOperand> &mapIterated,
+                  SmallVectorImpl<Type> &mapIteratedTypes) {
+  // Parse all operands first, then all types in grouped format:
+  //   %v1, %v2 : type1, type2
+  SmallVector<OpAsmParser::UnresolvedOperand> operands;
+  SmallVector<Type> types;
+  if (parser.parseOperandList(operands) || parser.parseColonTypeList(types))
+    return failure();
+  if (operands.size() != types.size())
+    return parser.emitError(parser.getNameLoc(),
+                            "expected same number of operands and types");
+  // Split iterator handles from regular map locators.
+  for (unsigned i = 0, e = operands.size(); i < e; ++i) {
+    if (llvm::isa<mlir::omp::IteratedType>(types[i])) {
+      mapIterated.push_back(operands[i]);
+      mapIteratedTypes.push_back(types[i]);
+    } else {
+      mapVars.push_back(operands[i]);
+      mapVarTypes.push_back(types[i]);
+    }
+  }
+  return success();
+}
+
+/// Print map_entries list with both plain and iterated entries.
+static void printMapEntryList(OpAsmPrinter &p, Operation *op,
+                              OperandRange mapVars, TypeRange mapVarTypes,
+                              OperandRange mapIterated,
+                              TypeRange mapIteratedTypes) {
+  // Print all operands, then all types in grouped format:
+  //   %v1, %v2 : type1, type2
+  llvm::interleaveComma(mapVars, p, [&](Value v) { p << v; });
+  if (!mapVars.empty() && !mapIterated.empty())
+    p << ", ";
+  llvm::interleaveComma(mapIterated, p, [&](Value v) { p << v; });
+  p << " : ";
+  llvm::interleaveComma(mapVarTypes, p, [&](Type t) { p << t; });
+  if (!mapVarTypes.empty() && !mapIteratedTypes.empty())
+    p << ", ";
+  llvm::interleaveComma(mapIteratedTypes, p, [&](Type t) { p << t; });
+}
+
 static ParseResult parseMembersIndex(OpAsmParser &parser,
                                      ArrayAttr &membersIdx) {
   SmallVector<Attribute> values, memberIdxs;
@@ -2313,7 +2396,8 @@ static ParseResult parseCaptureType(OpAsmParser &parser,
   return success();
 }
 
-static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars) {
+static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars,
+                                     OperandRange mapIterated) {
   llvm::DenseSet<mlir::TypedValue<mlir::omp::PointerLikeType>> updateToVars;
   llvm::DenseSet<mlir::TypedValue<mlir::omp::PointerLikeType>> updateFromVars;
 
@@ -2379,6 +2463,26 @@ static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars) {
     }
   }
 
+  // Verify iterated map entries.
+  for (auto iterVal : mapIterated) {
+    auto iterOp = iterVal.getDefiningOp<mlir::omp::IteratorOp>();
+    if (!iterOp)
+      return op->emitOpError() << "'map_iterated' arguments must be defined by "
+                                  "'omp.iterator' ops";
+
+    // Check that the iterator body yields a value defined by omp.map.info.
+    auto &region = iterOp.getRegion();
+    if (!region.empty()) {
+      auto yieldOp = cast<mlir::omp::YieldOp>(region.front().getTerminator());
+      if (yieldOp.getNumOperands() > 0) {
+        auto yieldedVal = yieldOp.getOperand(0);
+        if (!yieldedVal.getDefiningOp<mlir::omp::MapInfoOp>())
+          return op->emitOpError() << "'map_iterated' iterator body must yield "
+                                      "a value defined by 'omp.map.info'";
+      }
+    }
+  }
+
   return success();
 }
 
@@ -2435,13 +2539,13 @@ LogicalResult MapInfoOp::verify() {
 void TargetDataOp::build(OpBuilder &builder, OperationState &state,
                          const TargetDataOperands &clauses) {
   TargetDataOp::build(builder, state, clauses.device, clauses.ifExpr,
-                      clauses.mapVars, clauses.useDeviceAddrVars,
-                      clauses.useDevicePtrVars);
+                      clauses.mapVars, clauses.mapIterated,
+                      clauses.useDeviceAddrVars, clauses.useDevicePtrVars);
 }
 
 LogicalResult TargetDataOp::verify() {
-  if (getMapVars().empty() && getUseDevicePtrVars().empty() &&
-      getUseDeviceAddrVars().empty()) {
+  if (getMapVars().empty() && getMapIterated().empty() &&
+      getUseDevicePtrVars().empty() && getUseDeviceAddrVars().empty()) {
     return ::emitError(this->getLoc(),
                        "At least one of map, use_device_ptr_vars, or "
                        "use_device_addr_vars operand must be present");
@@ -2455,7 +2559,7 @@ LogicalResult TargetDataOp::verify() {
                                       getUseDeviceAddrVars())))
     return failure();
 
-  return verifyMapClause(*this, getMapVars());
+  return verifyMapClause(*this, getMapVars(), getMapIterated());
 }
 
 //===----------------------------------------------------------------------===//
@@ -2470,15 +2574,16 @@ void TargetEnterDataOp::build(
       builder, state, makeArrayAttr(ctx, clauses.dependKinds),
       clauses.dependVars, makeArrayAttr(ctx, clauses.dependIteratedKinds),
       clauses.dependIterated, clauses.device, clauses.ifExpr, clauses.mapVars,
-      clauses.nowait);
+      clauses.mapIterated, clauses.nowait);
 }
 
 LogicalResult TargetEnterDataOp::verify() {
   LogicalResult verifyDependVars =
       verifyDependVarList(*this, getDependKinds(), getDependVars(),
                           getDependIteratedKinds(), getDependIterated());
-  return failed(verifyDependVars) ? verifyDependVars
-                                  : verifyMapClause(*this, getMapVars());
+  return failed(verifyDependVars)
+             ? verifyDependVars
+             : verifyMapClause(*this, getMapVars(), getMapIterated());
 }
 
 //===----------------------------------------------------------------------===//
@@ -2492,15 +2597,16 @@ void TargetExitDataOp::build(OpBuilder &builder, OperationState &state,
       builder, state, makeArrayAttr(ctx, clauses.dependKinds),
       clauses.dependVars, makeArrayAttr(ctx, clauses.dependIteratedKinds),
       clauses.dependIterated, clauses.device, clauses.ifExpr, clauses.mapVars,
-      clauses.nowait);
+      clauses.mapIterated, clauses.nowait);
 }
 
 LogicalResult TargetExitDataOp::verify() {
   LogicalResult verifyDependVars =
       verifyDependVarList(*this, getDependKinds(), getDependVars(),
                           getDependIteratedKinds(), getDependIterated());
-  return failed(verifyDependVars) ? verifyDependVars
-                                  : verifyMapClause(*this, getMapVars());
+  return failed(verifyDependVars)
+             ? verifyDependVars
+             : verifyMapClause(*this, getMapVars(), getMapIterated());
 }
 
 //===----------------------------------------------------------------------===//
@@ -2514,15 +2620,16 @@ void TargetUpdateOp::build(OpBuilder &builder, OperationState &state,
                         clauses.dependVars,
                         makeArrayAttr(ctx, clauses.dependIteratedKinds),
                         clauses.dependIterated, clauses.device, clauses.ifExpr,
-                        clauses.mapVars, clauses.nowait);
+                        clauses.mapVars, clauses.mapIterated, clauses.nowait);
 }
 
 LogicalResult TargetUpdateOp::verify() {
   LogicalResult verifyDependVars =
       verifyDependVarList(*this, getDependKinds(), getDependVars(),
                           getDependIteratedKinds(), getDependIterated());
-  return failed(verifyDependVars) ? verifyDependVars
-                                  : verifyMapClause(*this, getMapVars());
+  return failed(verifyDependVars)
+             ? verifyDependVars
+             : verifyMapClause(*this, getMapVars(), getMapIterated());
 }
 
 //===----------------------------------------------------------------------===//
@@ -2543,7 +2650,7 @@ void TargetOp::build(OpBuilder &builder, OperationState &state,
       clauses.hasDeviceAddrVars, clauses.hostEvalVars, clauses.ifExpr,
       /*in_reduction_vars=*/{}, /*in_reduction_byref=*/nullptr,
       /*in_reduction_syms=*/nullptr, clauses.isDevicePtrVars, clauses.mapVars,
-      clauses.nowait, clauses.privateVars,
+      clauses.mapIterated, clauses.nowait, clauses.privateVars,
       makeArrayAttr(ctx, clauses.privateSyms), clauses.privateNeedsBarrier,
       clauses.threadLimitVars,
       /*private_maps=*/nullptr);
@@ -2559,7 +2666,7 @@ LogicalResult TargetOp::verify() {
                                       getHasDeviceAddrVars())))
     return failure();
 
-  if (failed(verifyMapClause(*this, getMapVars())))
+  if (failed(verifyMapClause(*this, getMapVars(), getMapIterated())))
     return failure();
 
   if (failed(verifyDynGroupprivateClause(
@@ -3401,7 +3508,7 @@ LogicalResult DistributeOp::verifyRegions() {
 //===----------------------------------------------------------------------===//
 
 LogicalResult DeclareMapperInfoOp::verify() {
-  return verifyMapClause(*this, getMapVars());
+  return verifyMapClause(*this, getMapVars(), getMapIterated());
 }
 
 LogicalResult DeclareMapperOp::verifyRegions() {
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 6df55fbbece78..90edd8987e7cc 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -388,6 +388,10 @@ static LogicalResult checkImplementationStatus(Operation &op) {
     if (op.hasThreadLimitMultiDim())
       result = todo("thread_limit with multi-dimensional values");
   };
+  auto checkMapIteratorModifier = [&todo](auto op, LogicalResult &result) {
+    if (!op.getMapIterated().empty())
+      result = todo("map/motion clause with iterator modifier");
+  };
 
   auto checkDynGroupprivate = [&todo](auto op, LogicalResult &result) {
     if (op.getDynGroupprivateSize())
@@ -450,15 +454,22 @@ static LogicalResult checkImplementationStatus(Operation &op) {
       .Case([&](omp::SimdOp op) { checkReduction(op, result); })
       .Case<omp::AtomicReadOp, omp::AtomicWriteOp, omp::AtomicUpdateOp,
             omp::AtomicCaptureOp>([&](auto op) { checkHint(op, result); })
-      .Case<omp::TargetEnterDataOp, omp::TargetExitDataOp>(
-          [&](auto op) { checkDepend(op, result); })
-      .Case([&](omp::TargetUpdateOp op) { checkDepend(op, result); })
+      .Case<omp::TargetEnterDataOp, omp::TargetExitDataOp>([&](auto op) {
+        checkDepend(op, result);
+        checkMapIteratorModifier(op, result);
+      })
+      .Case([&](omp::TargetUpdateOp op) {
+        checkDepend(op, result);
+        checkMapIteratorModifier(op, result);
+      })
       .Case([&](omp::TargetOp op) {
         checkAllocate(op, result);
         checkBare(op, result);
         checkInReduction(op, result);
+        checkMapIteratorModifier(op, result);
         checkThreadLimit(op, result);
       })
+      .Case([&](omp::TargetDataOp op) { checkMapIteratorModifier(op, result); })
       .Default([](Operation &) {
         // Assume all clauses for an operation can be translated unless they are
         // checked above.
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index 04725a69c8559..c5c1bb1340616 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -2394,7 +2394,7 @@ func.func @omp_target_enter_data(%map1: memref<?xi32>) {
 func.func @omp_target_enter_data_depend(%a: memref<?xi32>) {
   %0 = omp.map.info var_ptr(%a: memref<?xi32>, tensor<?xi32>) map_clauses(to) capture(ByRef) -> memref<?xi32>
   // expected-error @below {{op expected as many depend values as depend variables}}
-  omp.target_enter_data map_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 1, 0, 0, 0, 0>}
+  omp.target_enter_data map_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 1, 0, 0, 0, 0, 0>}
   return
 }
 
@@ -2412,7 +2412,7 @@ func.func @omp_target_exit_data(%map1: memref<?xi32>) {
 func.func @omp_target_exit_data_depend(%a: memref<?xi32>) {
   %0 = omp.map.info var_ptr(%a: memref<?xi32>, tensor<?xi32>) map_clauses(from) capture(ByRef) -> memref<?xi32>
   // expected-error @below {{op expected as many depend values as depend variables}}
-  omp.target_exit_data map_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 1, 0, 0, 0, 0>}
+  omp.target_exit_data map_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 1, 0, 0, 0, 0, 0>}
   return
 }
 
@@ -2493,7 +2493,7 @@ llvm.mlir.global internal @_QFsubEx() : i32
 func.func @omp_target_update_data_depend(%a: memref<?xi32>) {
   %0 = omp.map.info var_ptr(%a: memref<?xi32>, tensor<?xi32>) map_clauses(to) capture(ByRef) -> memref<?xi32>
   // expected-error @below {{op expected as many depend values as depend variables}}
-  omp.target_update map_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 1, 0, 0, 0, 0>}
+  omp.target_update map_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 1, 0, 0, 0, 0, 0>}
   return
 }
 
@@ -2595,7 +2595,7 @@ func.func @omp_target_depend(%data_var: memref<i32>) {
   // expected-error @below {{op expected as many depend values as depend variables}}
     "omp.target"(%data_var) ({
       "omp.terminator"() : () -> ()
-    }) {depend_kinds = [], operandSegmentSizes = array<i32: 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>} : (memref<i32>) -> ()
+    }) {depend_kinds = [], operandSegmentSizes = array<i32: 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>} : (memref<i32>) -> ()
    "func.return"() : () -> ()
 }
 
@@ -3512,6 +3512,39 @@ func.func @iterator_yield_type_mismatch(%lb : index, %ub : index, %st : index) {
 }
 
 // -----
+
+func.func @map_iterated_not_iterator(%it : !omp.iterated<!llvm.ptr>) {
+  // expected-error @below {{'omp.target_update' op 'map_iterated' arguments must be defined by 'omp.iterator' ops}}
+  omp.target_update map_entries(%it : !omp.iterated<!llvm.ptr>)
+  return
+}
+
+// -----
+
+func.func @map_iterated_yield_not_map_info(%lb : index, %ub : index, %st : index,
+                                            %addr : !llvm.ptr) {
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    omp.yield(%addr : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{'omp.target_enter_data' op 'map_iterated' iterator body must yield a value defined by 'omp.map.info'}}
+  omp.target_enter_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
+func.func @map_iterated_yield_not_map_info_exit(%lb : index, %ub : index, %st : index,
+                                                  %addr : !llvm.ptr) {
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    omp.yield(%addr : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{'omp.target_exit_data' op 'map_iterated' iterator body must yield a value defined by 'omp.map.info'}}
+  omp.target_exit_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
 func.func @target_allocmem_invalid_uniq_name(%device : i32) -> () {
 // expected-error @below {{op attribute 'uniq_name' failed to satisfy constraint: string attribute}}
   %0 = omp.target_allocmem %device : i32, i64 {uniq_name=2}
@@ -3519,6 +3552,7 @@ func.func @target_allocmem_invalid_uniq_name(%device : i32) -> () {
 }
 
 // -----
+
 func.func @target_allocmem_invalid_bindc_name(%device : i32) -> () {
 // expected-error @below {{op attribute 'bindc_name' failed to satisfy constraint: string attribute}}
   %0 = omp.target_allocmem %device : i32, i64 {bindc_name=2}
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 826e36e3f7b19..2b8d278d31af9 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -871,7 +871,7 @@ func.func @omp_target(%if_cond : i1, %device : si32,  %num_threads : i32, %devic
     "omp.target"(%device, %if_cond, %num_threads) ({
        // CHECK: omp.terminator
        omp.terminator
-    }) {nowait, operandSegmentSizes = array<i32: 0,0,0,0,1,0,0,0,1,0,0,0,0,1>} : ( si32, i1, i32 ) -> ()
+    }) {nowait, operandSegmentSizes = array<i32: 0,0,0,0,1,0,0,0,1,0,0,0,0,0,1>} : ( si32, i1, i32 ) -> ()
 
     // Test with optional map clause.
     // CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[VAL_1:.*]] : memref<?xi32>, tensor<?xi32>)   map_clauses(always, to) capture(ByRef) -> memref<?xi32> {name = ""}
@@ -4018,3 +4018,102 @@ llvm.mlir.global internal @gp() : i32
 llvm.mlir.global internal @any() : i32
 llvm.mlir.global internal @host() : i32
 llvm.mlir.global internal @nohost() : i32
+
+// -----
+
+// CHECK-LABEL: func.func @omp_target_update_map_iterated
+func.func @omp_target_update_map_iterated(%lb : index, %ub : index, %step : index,
+                                           %addr : !llvm.ptr) -> () {
+  // CHECK: %[[MAP:.*]] = omp.map.info var_ptr(%{{.*}} : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+  %map = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+
+  // CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%{{.*}} to %{{.*}} step %{{.*}}) {
+  // CHECK:   %[[M:.*]] = omp.map.info var_ptr(%{{.*}} : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+  // CHECK:   omp.yield(%[[M]] : !llvm.ptr)
+  // CHECK: } -> !omp.iterated<!llvm.ptr>
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %step) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+
+  // CHECK: omp.target_update map_entries(%[[MAP]], %[[IT]] : !llvm.ptr, !omp.iterated<!llvm.ptr>)
+  omp.target_update map_entries(%map, %it : !llvm.ptr, !omp.iterated<!llvm.ptr>)
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_target_enter_data_map_iterated
+func.func @omp_target_enter_data_map_iterated(%lb : index, %ub : index, %step : index,
+                                               %addr : !llvm.ptr) -> () {
+  // CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%{{.*}} to %{{.*}} step %{{.*}}) {
+  // CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
+  // CHECK: } -> !omp.iterated<!llvm.ptr>
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %step) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+
+  // CHECK: omp.target_enter_data map_entries(%[[IT]] : !omp.iterated<!llvm.ptr>)
+  omp.target_enter_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_target_exit_data_map_iterated
+func.func @omp_target_exit_data_map_iterated(%lb : index, %ub : index, %step : index,
+                                              %addr : !llvm.ptr) -> () {
+  // CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%{{.*}} to %{{.*}} step %{{.*}}) {
+  // CHECK:   omp.yield(%{{.*}} : !llvm.ptr)
+  // CHECK: } -> !omp.iterated<!llvm.ptr>
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %step) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(from) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+
+  // CHECK: omp.target_exit_data map_entries(%[[IT]] : !omp.iterated<!llvm.ptr>)
+  omp.target_exit_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_target_data_map_iterated
+func.func @omp_target_data_map_iterated(%lb : index, %ub : index, %step : index,
+                                         %addr : !llvm.ptr) -> () {
+  %map = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+
+  // CHECK: %[[IT:.*]] = omp.iterator
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %step) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+
+  // CHECK: omp.target_data map_entries(%{{.*}}, %[[IT]] : !llvm.ptr, !omp.iterated<!llvm.ptr>)
+  omp.target_data map_entries(%map, %it : !llvm.ptr, !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_target_map_iterated
+func.func @omp_target_map_iterated(%lb : index, %ub : index, %step : index,
+                                    %addr : !llvm.ptr) -> () {
+  // CHECK: %[[MAP:.*]] = omp.map.info
+  %map = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+
+  // CHECK: %[[IT:.*]] = omp.iterator
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %step) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+
+  // CHECK: omp.target map_entries(%[[MAP]] -> %{{.*}} : !llvm.ptr) {
+  // CHECK:   omp.terminator
+  // CHECK: } map_iterated_entries(%[[IT]] : !omp.iterated<!llvm.ptr>)
+  omp.target map_entries(%map -> %arg0 : !llvm.ptr) {
+    omp.terminator
+  } map_iterated_entries(%it : !omp.iterated<!llvm.ptr>)
+  return
+}
diff --git a/mlir/test/Target/LLVMIR/openmp-todo.mlir b/mlir/test/Target/LLVMIR/openmp-todo.mlir
index 295ba54dbfb38..03127b872937b 100644
--- a/mlir/test/Target/LLVMIR/openmp-todo.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-todo.mlir
@@ -493,3 +493,86 @@ llvm.func @wsloop_order(%lb : i32, %ub : i32, %step : i32) {
   }
   llvm.return
 }
+
+// -----
+
+llvm.func @target_enter_data_map_iterator(%addr : !llvm.ptr) {
+  %c0 = llvm.mlir.constant(0 : i64) : i64
+  %c10 = llvm.mlir.constant(10 : i64) : i64
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error at below {{not yet implemented: Unhandled clause map/motion clause with iterator modifier in omp.target_enter_data operation}}
+  // expected-error at below {{LLVM Translation failed for operation: omp.target_enter_data}}
+  omp.target_enter_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  llvm.return
+}
+
+// -----
+
+llvm.func @target_exit_data_map_iterator(%addr : !llvm.ptr) {
+  %c0 = llvm.mlir.constant(0 : i64) : i64
+  %c10 = llvm.mlir.constant(10 : i64) : i64
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(from) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error at below {{not yet implemented: Unhandled clause map/motion clause with iterator modifier in omp.target_exit_data operation}}
+  // expected-error at below {{LLVM Translation failed for operation: omp.target_exit_data}}
+  omp.target_exit_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  llvm.return
+}
+
+// -----
+
+llvm.func @target_update_map_iterator(%addr : !llvm.ptr) {
+  %c0 = llvm.mlir.constant(0 : i64) : i64
+  %c10 = llvm.mlir.constant(10 : i64) : i64
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error at below {{not yet implemented: Unhandled clause map/motion clause with iterator modifier in omp.target_update operation}}
+  // expected-error at below {{LLVM Translation failed for operation: omp.target_update}}
+  omp.target_update map_entries(%it : !omp.iterated<!llvm.ptr>)
+  llvm.return
+}
+
+// -----
+
+llvm.func @target_data_map_iterator(%addr : !llvm.ptr) {
+  %c0 = llvm.mlir.constant(0 : i64) : i64
+  %c10 = llvm.mlir.constant(10 : i64) : i64
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error at below {{not yet implemented: Unhandled clause map/motion clause with iterator modifier in omp.target_data operation}}
+  // expected-error at below {{LLVM Translation failed for operation: omp.target_data}}
+  omp.target_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  llvm.return
+}
+
+// -----
+
+llvm.func @target_map_iterator(%addr : !llvm.ptr) {
+  %c0 = llvm.mlir.constant(0 : i64) : i64
+  %c10 = llvm.mlir.constant(10 : i64) : i64
+  %c1 = llvm.mlir.constant(1 : i64) : i64
+  %map = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+  %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error at below {{not yet implemented: Unhandled clause map/motion clause with iterator modifier in omp.target operation}}
+  // expected-error at below {{LLVM Translation failed for operation: omp.target}}
+  omp.target map_entries(%map -> %arg0 : !llvm.ptr) {
+    omp.terminator
+  } map_iterated_entries(%it : !omp.iterated<!llvm.ptr>)
+  llvm.return
+}

>From 1d1453e21f9a1f4440fd356f0508942e5f0ac2f9 Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Mon, 11 May 2026 17:21:21 -0500
Subject: [PATCH 2/2] Add verifier check for iterated map info and tests for
 declare mapper

---
 mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp  | 139 ++++++++++--------
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      |   6 +
 mlir/test/Dialect/OpenMP/invalid.mlir         | 110 +++++++++++++-
 mlir/test/Dialect/OpenMP/ops.mlir             |  18 +++
 mlir/test/Target/LLVMIR/openmp-todo.mlir      |  24 +++
 5 files changed, 236 insertions(+), 61 deletions(-)

diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 6d2b8f4b46d5c..a4d333ffe7940 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -2396,6 +2396,66 @@ static ParseResult parseCaptureType(OpAsmParser &parser,
   return success();
 }
 
+static LogicalResult verifyMapInfoForMapClause(
+    Operation *op, mlir::omp::MapInfoOp mapInfoOp,
+    llvm::DenseSet<mlir::TypedValue<mlir::omp::PointerLikeType>> &updateToVars,
+    llvm::DenseSet<mlir::TypedValue<mlir::omp::PointerLikeType>>
+        &updateFromVars) {
+  mlir::omp::ClauseMapFlags mapTypeBits = mapInfoOp.getMapType();
+
+  bool to = mapTypeToBool(mapTypeBits, ClauseMapFlags::to);
+  bool from = mapTypeToBool(mapTypeBits, ClauseMapFlags::from);
+  bool del = mapTypeToBool(mapTypeBits, ClauseMapFlags::del);
+
+  bool always = mapTypeToBool(mapTypeBits, ClauseMapFlags::always);
+  bool close = mapTypeToBool(mapTypeBits, ClauseMapFlags::close);
+  bool implicit = mapTypeToBool(mapTypeBits, ClauseMapFlags::implicit);
+
+  if ((isa<TargetDataOp>(op) || isa<TargetOp>(op)) && del)
+    return emitError(op->getLoc(),
+                     "to, from, tofrom and alloc map types are permitted");
+
+  if (isa<TargetEnterDataOp>(op) && (from || del))
+    return emitError(op->getLoc(), "to and alloc map types are permitted");
+
+  if (isa<TargetExitDataOp>(op) && to)
+    return emitError(op->getLoc(),
+                     "from, release and delete map types are permitted");
+
+  if (isa<TargetUpdateOp>(op)) {
+    if (del) {
+      return emitError(op->getLoc(),
+                       "at least one of to or from map types must be "
+                       "specified, other map types are not permitted");
+    }
+
+    if (!to && !from) {
+      return emitError(op->getLoc(),
+                       "at least one of to or from map types must be "
+                       "specified, other map types are not permitted");
+    }
+
+    auto updateVar = mapInfoOp.getVarPtr();
+
+    if ((to && from) || (to && updateFromVars.contains(updateVar)) ||
+        (from && updateToVars.contains(updateVar))) {
+      return emitError(
+          op->getLoc(),
+          "either to or from map types can be specified, not both");
+    }
+
+    if (always || close || implicit) {
+      return emitError(
+          op->getLoc(),
+          "present, mapper and iterator map type modifiers are permitted");
+    }
+
+    to ? updateToVars.insert(updateVar) : updateFromVars.insert(updateVar);
+  }
+
+  return success();
+}
+
 static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars,
                                      OperandRange mapIterated) {
   llvm::DenseSet<mlir::TypedValue<mlir::omp::PointerLikeType>> updateToVars;
@@ -2406,57 +2466,9 @@ static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars,
       return emitError(op->getLoc(), "missing map operation");
 
     if (auto mapInfoOp = mapOp.getDefiningOp<mlir::omp::MapInfoOp>()) {
-      mlir::omp::ClauseMapFlags mapTypeBits = mapInfoOp.getMapType();
-
-      bool to = mapTypeToBool(mapTypeBits, ClauseMapFlags::to);
-      bool from = mapTypeToBool(mapTypeBits, ClauseMapFlags::from);
-      bool del = mapTypeToBool(mapTypeBits, ClauseMapFlags::del);
-
-      bool always = mapTypeToBool(mapTypeBits, ClauseMapFlags::always);
-      bool close = mapTypeToBool(mapTypeBits, ClauseMapFlags::close);
-      bool implicit = mapTypeToBool(mapTypeBits, ClauseMapFlags::implicit);
-
-      if ((isa<TargetDataOp>(op) || isa<TargetOp>(op)) && del)
-        return emitError(op->getLoc(),
-                         "to, from, tofrom and alloc map types are permitted");
-
-      if (isa<TargetEnterDataOp>(op) && (from || del))
-        return emitError(op->getLoc(), "to and alloc map types are permitted");
-
-      if (isa<TargetExitDataOp>(op) && to)
-        return emitError(op->getLoc(),
-                         "from, release and delete map types are permitted");
-
-      if (isa<TargetUpdateOp>(op)) {
-        if (del) {
-          return emitError(op->getLoc(),
-                           "at least one of to or from map types must be "
-                           "specified, other map types are not permitted");
-        }
-
-        if (!to && !from) {
-          return emitError(op->getLoc(),
-                           "at least one of to or from map types must be "
-                           "specified, other map types are not permitted");
-        }
-
-        auto updateVar = mapInfoOp.getVarPtr();
-
-        if ((to && from) || (to && updateFromVars.contains(updateVar)) ||
-            (from && updateToVars.contains(updateVar))) {
-          return emitError(
-              op->getLoc(),
-              "either to or from map types can be specified, not both");
-        }
-
-        if (always || close || implicit) {
-          return emitError(
-              op->getLoc(),
-              "present, mapper and iterator map type modifiers are permitted");
-        }
-
-        to ? updateToVars.insert(updateVar) : updateFromVars.insert(updateVar);
-      }
+      if (failed(verifyMapInfoForMapClause(op, mapInfoOp, updateToVars,
+                                           updateFromVars)))
+        return failure();
     } else if (!isa<DeclareMapperInfoOp>(op)) {
       return emitError(op->getLoc(),
                        "map argument is not a map entry operation");
@@ -2472,15 +2484,22 @@ static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars,
 
     // Check that the iterator body yields a value defined by omp.map.info.
     auto &region = iterOp.getRegion();
-    if (!region.empty()) {
-      auto yieldOp = cast<mlir::omp::YieldOp>(region.front().getTerminator());
-      if (yieldOp.getNumOperands() > 0) {
-        auto yieldedVal = yieldOp.getOperand(0);
-        if (!yieldedVal.getDefiningOp<mlir::omp::MapInfoOp>())
-          return op->emitOpError() << "'map_iterated' iterator body must yield "
-                                      "a value defined by 'omp.map.info'";
-      }
-    }
+    if (region.empty())
+      continue;
+
+    auto yieldOp = dyn_cast<mlir::omp::YieldOp>(region.front().getTerminator());
+    if (!yieldOp || yieldOp.getNumOperands() == 0)
+      continue;
+
+    auto yieldedMapInfo =
+        yieldOp.getOperand(0).getDefiningOp<mlir::omp::MapInfoOp>();
+    if (!yieldedMapInfo)
+      return op->emitOpError() << "'map_iterated' iterator body must yield "
+                                  "a value defined by 'omp.map.info'";
+
+    if (failed(verifyMapInfoForMapClause(op, yieldedMapInfo, updateToVars,
+                                         updateFromVars)))
+      return failure();
   }
 
   return success();
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 90edd8987e7cc..d67090a989218 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -470,6 +470,9 @@ static LogicalResult checkImplementationStatus(Operation &op) {
         checkThreadLimit(op, result);
       })
       .Case([&](omp::TargetDataOp op) { checkMapIteratorModifier(op, result); })
+      .Case([&](omp::DeclareMapperInfoOp op) {
+        checkMapIteratorModifier(op, result);
+      })
       .Default([](Operation &) {
         // Assume all clauses for an operation can be translated unless they are
         // checked above.
@@ -6213,6 +6216,9 @@ emitUserDefinedMapper(Operation *op, llvm::IRBuilderBase &builder,
          "function only supported for host device codegen");
   auto declMapperOp = cast<omp::DeclareMapperOp>(op);
   auto declMapperInfoOp = declMapperOp.getDeclareMapperInfo();
+  if (failed(checkImplementationStatus(*declMapperInfoOp)))
+    return llvm::make_error<PreviouslyReportedError>();
+
   DataLayout dl = DataLayout(declMapperOp->getParentOfType<ModuleOp>());
   llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
   llvm::Type *varType = moduleTranslation.convertType(declMapperOp.getType());
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index c5c1bb1340616..5acedf3f3cf16 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -3545,6 +3545,115 @@ func.func @map_iterated_yield_not_map_info_exit(%lb : index, %ub : index, %st :
 
 // -----
 
+func.func @target_enter_data_map_iterated_invalid_from(%lb : index, %ub : index,
+                                                       %st : index,
+                                                       %addr : !llvm.ptr) {
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(from) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{to and alloc map types are permitted}}
+  omp.target_enter_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
+func.func @target_exit_data_map_iterated_invalid_to(%lb : index, %ub : index,
+                                                    %st : index,
+                                                    %addr : !llvm.ptr) {
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{from, release and delete map types are permitted}}
+  omp.target_exit_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
+func.func @target_update_map_iterated_invalid_delete(%lb : index, %ub : index,
+                                                     %st : index,
+                                                     %addr : !llvm.ptr) {
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(delete) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{at least one of to or from map types must be specified, other map types are not permitted}}
+  omp.target_update map_entries(%it : !omp.iterated<!llvm.ptr>)
+  return
+}
+
+// -----
+
+func.func @target_update_map_iterated_invalid_conflict(%lb : index, %ub : index,
+                                                       %st : index,
+                                                       %addr : !llvm.ptr) {
+  %mapv = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(from) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{either to or from map types can be specified, not both}}
+  omp.target_update map_entries(%mapv, %it : !llvm.ptr, !omp.iterated<!llvm.ptr>)
+  return
+}
+
+// -----
+
+func.func @target_data_map_iterated_invalid_delete(%lb : index, %ub : index,
+                                                   %st : index,
+                                                   %addr : !llvm.ptr) {
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(delete) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{to, from, tofrom and alloc map types are permitted}}
+  omp.target_data map_entries(%it : !omp.iterated<!llvm.ptr>) {}
+  return
+}
+
+// -----
+
+func.func @target_map_iterated_invalid_delete(%lb : index, %ub : index,
+                                              %st : index,
+                                              %addr : !llvm.ptr) {
+  %mapv = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    %m = omp.map.info var_ptr(%addr : !llvm.ptr, i32) map_clauses(delete) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%m : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{to, from, tofrom and alloc map types are permitted}}
+  omp.target map_entries(%mapv -> %arg0 : !llvm.ptr) {
+    omp.terminator
+  } map_iterated_entries(%it : !omp.iterated<!llvm.ptr>)
+  return
+}
+
+// -----
+
+omp.declare_mapper @declare_mapper_iterated_not_iterator : !llvm.struct<"mapper_type", (i32)> {
+^bb0(%arg: !llvm.ptr, %it: !omp.iterated<!llvm.ptr>):
+  // expected-error @below {{'omp.declare_mapper.info' op 'map_iterated' arguments must be defined by 'omp.iterator' ops}}
+  omp.declare_mapper.info map_entries(%it : !omp.iterated<!llvm.ptr>)
+}
+
+// -----
+
+omp.declare_mapper @declare_mapper_iterated_yield_not_map_info : !llvm.struct<"mapper_type", (i32)> {
+^bb0(%arg: !llvm.ptr):
+  %lb = arith.constant 0 : index
+  %ub = arith.constant 4 : index
+  %st = arith.constant 1 : index
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %st) {
+    omp.yield(%arg : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // expected-error @below {{'omp.declare_mapper.info' op 'map_iterated' iterator body must yield a value defined by 'omp.map.info'}}
+  omp.declare_mapper.info map_entries(%it : !omp.iterated<!llvm.ptr>)
+}
+
+// -----
 func.func @target_allocmem_invalid_uniq_name(%device : i32) -> () {
 // expected-error @below {{op attribute 'uniq_name' failed to satisfy constraint: string attribute}}
   %0 = omp.target_allocmem %device : i32, i64 {uniq_name=2}
@@ -3552,7 +3661,6 @@ func.func @target_allocmem_invalid_uniq_name(%device : i32) -> () {
 }
 
 // -----
-
 func.func @target_allocmem_invalid_bindc_name(%device : i32) -> () {
 // expected-error @below {{op attribute 'bindc_name' failed to satisfy constraint: string attribute}}
   %0 = omp.target_allocmem %device : i32, i64 {bindc_name=2}
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 2b8d278d31af9..4036e5b5e9ff5 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -1001,6 +1001,24 @@ omp.declare_mapper @my_mapper : !llvm.struct<"my_type", (i32)> {
   omp.declare_mapper.info map_entries(%decl_map_info : !llvm.ptr)
 }
 
+// CHECK: omp.declare_mapper @my_mapper_iterated : !llvm.struct<"my_iter_type", (i32)>
+omp.declare_mapper @my_mapper_iterated : !llvm.struct<"my_iter_type", (i32)> {
+^bb0(%arg: !llvm.ptr):
+  %lb = arith.constant 0 : index
+  %ub = arith.constant 4 : index
+  %step = arith.constant 1 : index
+  // CHECK: %[[IT:.*]] = omp.iterator(%[[IV:.*]]: index) = (%{{.*}} to %{{.*}} step %{{.*}}) {
+  // CHECK:   %[[DECL_MAP_INFO:.*]] = omp.map.info var_ptr(%{{.*}} : !llvm.ptr, !llvm.struct<"my_iter_type", (i32)>) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+  // CHECK:   omp.yield(%[[DECL_MAP_INFO]] : !llvm.ptr)
+  // CHECK: } -> !omp.iterated<!llvm.ptr>
+  %it = omp.iterator(%iv: index) = (%lb to %ub step %step) {
+    %decl_map_info = omp.map.info var_ptr(%arg : !llvm.ptr, !llvm.struct<"my_iter_type", (i32)>) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+    omp.yield(%decl_map_info : !llvm.ptr)
+  } -> !omp.iterated<!llvm.ptr>
+  // CHECK: omp.declare_mapper.info map_entries(%[[IT]] : !omp.iterated<!llvm.ptr>)
+  omp.declare_mapper.info map_entries(%it : !omp.iterated<!llvm.ptr>)
+}
+
 // CHECK-LABEL: func @wsloop_reduction
 func.func @wsloop_reduction(%lb : index, %ub : index, %step : index) {
   %c1 = arith.constant 1 : i32
diff --git a/mlir/test/Target/LLVMIR/openmp-todo.mlir b/mlir/test/Target/LLVMIR/openmp-todo.mlir
index 03127b872937b..c55117a81f7cc 100644
--- a/mlir/test/Target/LLVMIR/openmp-todo.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-todo.mlir
@@ -576,3 +576,27 @@ llvm.func @target_map_iterator(%addr : !llvm.ptr) {
   } map_iterated_entries(%it : !omp.iterated<!llvm.ptr>)
   llvm.return
 }
+
+// -----
+
+module attributes {omp.target_triples = ["amdgcn-amd-amdhsa"]} {
+  omp.declare_mapper @mapper_with_iterator : !llvm.struct<"mapper_type", (i32)> {
+  ^bb0(%arg: !llvm.ptr):
+    %c0 = llvm.mlir.constant(0 : i64) : i64
+    %c10 = llvm.mlir.constant(10 : i64) : i64
+    %c1 = llvm.mlir.constant(1 : i64) : i64
+    %it = omp.iterator(%iv: i64) = (%c0 to %c10 step %c1) {
+      %m = omp.map.info var_ptr(%arg : !llvm.ptr, !llvm.struct<"mapper_type", (i32)>) map_clauses(tofrom) capture(ByRef) -> !llvm.ptr {name = ""}
+      omp.yield(%m : !llvm.ptr)
+    } -> !omp.iterated<!llvm.ptr>
+    // expected-error at below {{not yet implemented: Unhandled clause map/motion clause with iterator modifier in omp.declare_mapper.info operation}}
+    omp.declare_mapper.info map_entries(%it : !omp.iterated<!llvm.ptr>)
+  }
+
+  llvm.func @target_data_mapper_iterator(%addr : !llvm.ptr) {
+    %map = omp.map.info var_ptr(%addr : !llvm.ptr, !llvm.struct<"mapper_type", (i32)>) map_clauses(tofrom) capture(ByRef) mapper(@mapper_with_iterator) -> !llvm.ptr {name = ""}
+    // expected-error at below {{LLVM Translation failed for operation: omp.target_data}}
+    omp.target_data map_entries(%map : !llvm.ptr) {}
+    llvm.return
+  }
+}



More information about the flang-commits mailing list