[llvm-branch-commits] [flang] [flang][OpenMP][NFC] Share declare mapper helpers for iterator modifier lowering (PR #197752)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jun 5 14:47:04 PDT 2026
https://github.com/chichunchen updated https://github.com/llvm/llvm-project/pull/197752
>From afa1a62e201c6674ba352e0348e13dce6063aa2c Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Tue, 12 May 2026 15:06:58 -0500
Subject: [PATCH 1/2] [flang][OpenMP][NFC] Share declare mapper helpers for
iterator modifier lowering
Move mapper lookup and implicit default mapper creation into reusable
OpenMP lowering helpers so regular map lowering and iterator-generated
map entries can use the same resolution path.
This prepares Flang iterator modifier lowering for map and motion clauses
without changing the generated IR for existing non-iterator maps.
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 148 +-------------------
flang/lib/Lower/OpenMP/Utils.cpp | 153 +++++++++++++++++++++
flang/lib/Lower/OpenMP/Utils.h | 7 +
3 files changed, 166 insertions(+), 142 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 213b5f783430e..5948e597aa2de 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1722,88 +1722,6 @@ void ClauseProcessor::processMapObjects(
bool isMotionModifier, llvm::omp::Directive directive) const {
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
- auto getSymbolDerivedType = [](const semantics::Symbol &symbol)
- -> const semantics::DerivedTypeSpec * {
- const semantics::Symbol &ultimate = symbol.GetUltimate();
- if (const semantics::DeclTypeSpec *declType = ultimate.GetType())
- if (const auto *derived = declType->AsDerived())
- return derived;
- return nullptr;
- };
-
- auto addImplicitMapper = [&](const omp::Object &object,
- std::string &mapperIdName,
- bool allowGenerate) -> mlir::FlatSymbolRefAttr {
- if (!allowGenerate || mapperIdName.empty())
- return mlir::FlatSymbolRefAttr();
-
- const semantics::DerivedTypeSpec *typeSpec =
- getSymbolDerivedType(*object.sym());
- if (!typeSpec && object.sym()->owner().IsDerivedType())
- typeSpec = object.sym()->owner().derivedTypeSpec();
-
- if (!typeSpec)
- return mlir::FlatSymbolRefAttr();
-
- mlir::Type type = converter.genType(*typeSpec);
- auto recordType = mlir::dyn_cast<fir::RecordType>(type);
- if (!recordType)
- return mlir::FlatSymbolRefAttr();
-
- return utils::openmp::getOrGenImplicitDefaultDeclareMapper(
- converter.getFirOpBuilder(), clauseLocation, recordType, mapperIdName,
- [&](std::string &mapperIdName, llvm::StringRef memberName) {
- defaultMangler(converter, mapperIdName, memberName);
- });
- };
-
- auto getDefaultMapperID =
- [&](const semantics::DerivedTypeSpec *typeSpec) -> std::string {
- if (mlir::isa<mlir::omp::DeclareMapperOp>(
- firOpBuilder.getRegion().getParentOp()) ||
- !typeSpec)
- return {};
-
- std::string mapperIdName =
- typeSpec->name().ToString() + llvm::omp::OmpDefaultMapperName;
- if (auto *sym = converter.getCurrentScope().FindSymbol(mapperIdName)) {
- mapperIdName =
- converter.mangleName(mapperIdName, sym->GetUltimate().owner());
- } else {
- mapperIdName = converter.mangleName(mapperIdName, *typeSpec->GetScope());
- }
-
- // Make sure we don't return a mapper to self.
- if (auto declMapOp = mlir::dyn_cast<mlir::omp::DeclareMapperOp>(
- firOpBuilder.getRegion().getParentOp()))
- if (mapperIdName == declMapOp.getSymName())
- return {};
- return mapperIdName;
- };
-
- auto findMapperIfTypeMatch =
- [&](const semantics::DerivedTypeSpec *objectTypeSpec,
- llvm::StringRef explicitMapperName) -> std::string {
- auto declMapperOp =
- converter.getModuleOp().lookupSymbol<mlir::omp::DeclareMapperOp>(
- explicitMapperName);
- if (!declMapperOp)
- return "__implicit_mapper";
-
- // Verify if the explicit mapper provided matches the type being mapped.
- // If it does return the mapper name, if it doesn't return null-ary.
- mlir::Type mapperType = declMapperOp.getType();
- mlir::Type objectType = converter.genType(*objectTypeSpec);
- auto mapperRecordType = mlir::dyn_cast<fir::RecordType>(mapperType);
- auto objectRecordType = mlir::dyn_cast<fir::RecordType>(objectType);
- if (mapperRecordType && objectRecordType &&
- mapperRecordType.getName() == objectRecordType.getName()) {
- return explicitMapperName.str();
- }
-
- return "__implicit_mapper";
- };
-
for (const omp::Object &object : objects) {
llvm::SmallVector<mlir::Value> bounds;
std::stringstream asFortran;
@@ -1835,62 +1753,9 @@ void ClauseProcessor::processMapObjects(
}
}
- const semantics::DerivedTypeSpec *objectTypeSpec =
- getSymbolDerivedType(*object.sym());
- mlir::FlatSymbolRefAttr mapperId = mlir::FlatSymbolRefAttr();
- if (objectTypeSpec) {
- std::string mapperIdName = mapperIdNameRef.str();
- // if we have an explicit mapper specified, we need to check it matches
- // the type being mapped, if it doesn't we fallback to look for a user
- // default mapper or generate an compiler defined default mapper if
- // relevant. This function will return "__implicit_mapper" if we find that
- // the map isn't relevant to the explicit declare mapper, which allows it
- // to fallback.
- if (!mapperIdName.empty() && mapperIdName != "__implicit_mapper")
- mapperIdName = findMapperIfTypeMatch(objectTypeSpec, mapperIdName);
-
- if (mapperIdName == "__implicit_mapper") {
- mapperIdName = getDefaultMapperID(objectTypeSpec);
- // Currently we do not apply implicit compiler generated delcare mappers
- // to enter, exit or update directives. However, we will syntheize one
- // below if we're not a target enter/exit/update and no user defined
- // implicit declare mapper has been defined and we meet the other
- // conditions
- // TODO/FIXME: Loosen this restriction to comply with the OpenMP
- // specification.
- auto *userDefinedDefault =
- converter.getModuleOp().lookupSymbol(mapperIdName);
- if (!userDefinedDefault && !parentObj.has_value() &&
- (directive != llvm::omp::Directive::OMPD_target_enter_data &&
- directive != llvm::omp::Directive::OMPD_target_exit_data &&
- directive != llvm::omp::Directive::OMPD_target_update)) {
- bool isAllocOrPointer =
- semantics::IsAllocatableOrObjectPointer(object.sym());
- bool isPointer = semantics::IsPointer(*object.sym());
- bool isImplicitMap =
- (mapTypeBits & mlir::omp::ClauseMapFlags::implicit) ==
- mlir::omp::ClauseMapFlags::implicit;
- bool needsDefaultMapper =
- isAllocOrPointer ||
- requiresImplicitDefaultDeclareMapper(*objectTypeSpec);
- // For implicit captures, avoid synthesizing default mappers for
- // pointer entities (which can over-map pointer payloads) and for
- // plain non-allocatable/non-pointer entities. Keep implicit mapper
- // support for allocatables.
- if (isImplicitMap && (isPointer || !isAllocOrPointer))
- needsDefaultMapper = false;
- mapperId = addImplicitMapper(object, mapperIdName,
- /*allowGenerate=*/needsDefaultMapper);
- }
- }
-
- // Make sure we've generated the symbol in one of our previous steps
- // before assigning the symbol.
- if (!mapperIdName.empty() &&
- converter.getModuleOp().lookupSymbol(mapperIdName))
- mapperId = mlir::FlatSymbolRefAttr::get(&converter.getMLIRContext(),
- mapperIdName);
- }
+ mlir::FlatSymbolRefAttr mapperId =
+ resolveMapperId(converter, clauseLocation, object, mapperIdNameRef,
+ mapTypeBits, directive, parentObj.has_value());
// Explicit map captures are captured ByRef by default,
// optimisation passes may alter this to ByCopy or other capture
@@ -2034,10 +1899,10 @@ bool ClauseProcessor::processMap(
}
}
- if (iterator) {
+ if (iterator)
TODO(currentLocation,
"Support for iterator modifiers is not implemented yet");
- }
+
processMapObjects(stmtCtx, clauseLocation,
std::get<omp::ObjectList>(clause.t), mapTypeBits,
parentMemberIndices, result.mapVars, *ptrMapObjects,
@@ -2070,9 +1935,8 @@ bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
// Support motion modifiers: iterator.
std::string mapperIdName = getMapperIdentifier(converter, mapper);
- if (iterator) {
+ if (iterator)
TODO(clauseLocation, "Iterator modifier is not supported yet");
- }
processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
parentMemberIndices, result.mapVars, mapObjects,
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index fff06bc51e3d6..9ff462f402759 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -1017,6 +1017,159 @@ void defaultMangler(Fortran::lower::AbstractConverter &converter,
mapperIdName = converter.mangleName(mapperIdName, memberSym->owner());
}
+static const semantics::DerivedTypeSpec *
+getSymbolDerivedType(const semantics::Symbol &symbol) {
+ const semantics::Symbol &ultimate = symbol.GetUltimate();
+ if (const semantics::DeclTypeSpec *declType = ultimate.GetType())
+ if (const auto *derived = declType->AsDerived())
+ return derived;
+ return nullptr;
+}
+
+static std::string
+getDefaultMapperID(Fortran::lower::AbstractConverter &converter,
+ fir::FirOpBuilder &firOpBuilder,
+ const semantics::DerivedTypeSpec *typeSpec) {
+ if (mlir::isa<mlir::omp::DeclareMapperOp>(
+ firOpBuilder.getRegion().getParentOp()) ||
+ !typeSpec)
+ return {};
+
+ std::string mapperIdName =
+ typeSpec->name().ToString() + llvm::omp::OmpDefaultMapperName;
+ if (auto *sym = converter.getCurrentScope().FindSymbol(mapperIdName)) {
+ mapperIdName =
+ converter.mangleName(mapperIdName, sym->GetUltimate().owner());
+ } else {
+ mapperIdName = converter.mangleName(mapperIdName, *typeSpec->GetScope());
+ }
+
+ // Make sure we don't return a mapper to self.
+ if (auto declMapOp = mlir::dyn_cast<mlir::omp::DeclareMapperOp>(
+ firOpBuilder.getRegion().getParentOp()))
+ if (mapperIdName == declMapOp.getSymName())
+ return {};
+ return mapperIdName;
+}
+
+static std::string
+findMapperIfTypeMatch(Fortran::lower::AbstractConverter &converter,
+ const semantics::DerivedTypeSpec *objectTypeSpec,
+ llvm::StringRef explicitMapperName) {
+ auto declMapperOp =
+ converter.getModuleOp().lookupSymbol<mlir::omp::DeclareMapperOp>(
+ explicitMapperName);
+ if (!declMapperOp)
+ return "__implicit_mapper";
+
+ // Verify if the explicit mapper provided matches the type being mapped.
+ // If it does return the mapper name, if it doesn't return null-ary.
+ mlir::Type mapperType = declMapperOp.getType();
+ mlir::Type objectType = converter.genType(*objectTypeSpec);
+ auto mapperRecordType = mlir::dyn_cast<fir::RecordType>(mapperType);
+ auto objectRecordType = mlir::dyn_cast<fir::RecordType>(objectType);
+ if (mapperRecordType && objectRecordType &&
+ mapperRecordType.getName() == objectRecordType.getName())
+ return explicitMapperName.str();
+
+ return "__implicit_mapper";
+}
+
+static mlir::FlatSymbolRefAttr
+addImplicitMapper(Fortran::lower::AbstractConverter &converter,
+ mlir::Location loc, const omp::Object &object,
+ std::string &mapperIdName, bool allowGenerate) {
+ if (!allowGenerate || mapperIdName.empty())
+ return mlir::FlatSymbolRefAttr();
+
+ const semantics::DerivedTypeSpec *typeSpec =
+ getSymbolDerivedType(*object.sym());
+ if (!typeSpec && object.sym()->owner().IsDerivedType())
+ typeSpec = object.sym()->owner().derivedTypeSpec();
+
+ if (!typeSpec)
+ return mlir::FlatSymbolRefAttr();
+
+ mlir::Type type = converter.genType(*typeSpec);
+ auto recordType = mlir::dyn_cast<fir::RecordType>(type);
+ if (!recordType)
+ return mlir::FlatSymbolRefAttr();
+
+ return utils::openmp::getOrGenImplicitDefaultDeclareMapper(
+ converter.getFirOpBuilder(), loc, recordType, mapperIdName,
+ [&](std::string &mapperIdName, llvm::StringRef memberName) {
+ defaultMangler(converter, mapperIdName, memberName);
+ });
+}
+
+mlir::FlatSymbolRefAttr
+resolveMapperId(Fortran::lower::AbstractConverter &converter,
+ mlir::Location loc, const omp::Object &object,
+ llvm::StringRef mapperIdNameRef,
+ mlir::omp::ClauseMapFlags mapTypeBits,
+ llvm::omp::Directive directive, bool hasParentObj) {
+ const semantics::DerivedTypeSpec *objectTypeSpec =
+ getSymbolDerivedType(*object.sym());
+ if (!objectTypeSpec)
+ return mlir::FlatSymbolRefAttr();
+
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ mlir::FlatSymbolRefAttr mapperId;
+ std::string mapperIdName = mapperIdNameRef.str();
+ // if we have an explicit mapper specified, we need to check it matches
+ // the type being mapped, if it doesn't we fallback to look for a user
+ // default mapper or generate an compiler defined default mapper if
+ // relevant. This function will return "__implicit_mapper" if we find that
+ // the map isn't relevant to the explicit declare mapper, which allows it
+ // to fallback.
+ if (!mapperIdName.empty() && mapperIdName != "__implicit_mapper")
+ mapperIdName =
+ findMapperIfTypeMatch(converter, objectTypeSpec, mapperIdName);
+
+ if (mapperIdName == "__implicit_mapper") {
+ mapperIdName = getDefaultMapperID(converter, firOpBuilder, objectTypeSpec);
+ // Currently we do not apply implicit compiler generated delcare mappers
+ // to enter, exit or update directives. However, we will syntheize one
+ // below if we're not a target enter/exit/update and no user defined
+ // implicit declare mapper has been defined and we meet the other
+ // conditions
+ // TODO/FIXME: Loosen this restriction to comply with the OpenMP
+ // specification.
+ auto *userDefinedDefault =
+ converter.getModuleOp().lookupSymbol(mapperIdName);
+ if (!userDefinedDefault && !hasParentObj &&
+ (directive != llvm::omp::Directive::OMPD_target_enter_data &&
+ directive != llvm::omp::Directive::OMPD_target_exit_data &&
+ directive != llvm::omp::Directive::OMPD_target_update)) {
+ bool isAllocOrPointer =
+ semantics::IsAllocatableOrObjectPointer(object.sym());
+ bool isPointer = semantics::IsPointer(*object.sym());
+ bool isImplicitMap =
+ (mapTypeBits & mlir::omp::ClauseMapFlags::implicit) ==
+ mlir::omp::ClauseMapFlags::implicit;
+ bool needsDefaultMapper =
+ isAllocOrPointer ||
+ requiresImplicitDefaultDeclareMapper(*objectTypeSpec);
+ // For implicit captures, avoid synthesizing default mappers for
+ // pointer entities (which can over-map pointer payloads) and for
+ // plain non-allocatable/non-pointer entities. Keep implicit mapper
+ // support for allocatables.
+ if (isImplicitMap && (isPointer || !isAllocOrPointer))
+ needsDefaultMapper = false;
+ mapperId = addImplicitMapper(converter, loc, object, mapperIdName,
+ /*allowGenerate=*/needsDefaultMapper);
+ }
+ }
+
+ // Make sure we've generated the symbol in one of our previous steps
+ // before assigning the symbol.
+ if (!mapperIdName.empty() &&
+ converter.getModuleOp().lookupSymbol(mapperIdName))
+ mapperId =
+ mlir::FlatSymbolRefAttr::get(&converter.getMLIRContext(), mapperIdName);
+ return mapperId;
+}
+
// Build the array coordinate for an object that uses iterator variables.
// If the object is a section, use the first element of that section
// as the coordinate. Currently only support top-level ArrayRef designators.
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index 7022597a233ca..cee7e187d2d6e 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -225,6 +225,13 @@ mlir::Value genIteratorCoordinate(Fortran::lower::AbstractConverter &converter,
llvm::ArrayRef<mlir::Value> ivs,
mlir::Location loc);
+mlir::FlatSymbolRefAttr
+resolveMapperId(Fortran::lower::AbstractConverter &converter,
+ mlir::Location loc, const omp::Object &object,
+ llvm::StringRef mapperIdName,
+ mlir::omp::ClauseMapFlags mapTypeBits,
+ llvm::omp::Directive directive, bool hasParentObj);
+
std::optional<llvm::SmallVector<mlir::Value>> getIteratorElementIndices(
Fortran::lower::AbstractConverter &converter, const omp::Object &object,
Fortran::lower::StatementContext &stmtCtx, mlir::Location loc);
>From 2c3b35060e4e2096225218545a6b21b9cbbe9dbb Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Fri, 5 Jun 2026 16:04:22 -0500
Subject: [PATCH 2/2] Add doxygen documentation to resolveMapperId
---
flang/lib/Lower/OpenMP/Utils.h | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index cee7e187d2d6e..59b904526d6ae 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -225,6 +225,24 @@ mlir::Value genIteratorCoordinate(Fortran::lower::AbstractConverter &converter,
llvm::ArrayRef<mlir::Value> ivs,
mlir::Location loc);
+/// Resolve the declare mapper symbol to attach to a mapped object.
+///
+/// The default mapper path first looks for a user-defined mapper. If none
+/// exists, it may synthesize a compiler-generated mapper, except for mapped
+/// members whose parent object is also mapped and for target enter data,
+/// target exit data, and target update directives.
+///
+/// \param converter The converter used to query and generate mapper symbols.
+/// \param loc The location to use when generating an implicit mapper.
+/// \param object The mapped object whose type controls mapper resolution.
+/// \param mapperIdName An explicit mapper name, `__implicit_mapper`, or an
+/// empty name.
+/// \param mapTypeBits The map flags used when deciding whether an implicit
+/// mapper should be generated.
+/// \param directive The enclosing OpenMP directive.
+/// \param hasParentObj True if a mapped parent object already owns this object.
+/// \return A symbol reference to the resolved mapper, or a null attribute when
+/// no mapper applies.
mlir::FlatSymbolRefAttr
resolveMapperId(Fortran::lower::AbstractConverter &converter,
mlir::Location loc, const omp::Object &object,
More information about the llvm-branch-commits
mailing list