[flang-commits] [flang] [flang][OpenMP] Reuse canonical default mapper names for nested mappings - fix issue #210402 (PR #212251)
via flang-commits
flang-commits at lists.llvm.org
Mon Jul 27 06:43:14 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: laoshd
<details>
<summary>Changes</summary>
Summary
Fix issue #<!-- -->210402: [FLANG][OMP] flang compiled application failed with core dumped at Kernel 'omp target in _QQmain @ 28 (__omp_offloading_3a_5aca0b86__QQmain_l28)' -- Flang OpenMP nested implicit default-mapper naming so it reuses the same canonical symbol naming scheme as explicit declare mapper(default: ...) lowering.
Problem
For nested derived types, implicit mapper generation could derive a mapper symbol from FIR record names that did not match the explicit mapper naming path. That mismatch could create a second synthesized mapper, skip intended pointer-component mapping behavior, and lead to runtime offload faults.
Changes
Add a canonical mapper-name helper: getCanonicalDefaultDeclareMapperName(fir::RecordType)
Use canonical naming in nested implicit mapper generation paths: OpenMP utility mapper generation
DoConcurrent OpenMP conversion mapper path
Add regression test: declare-mapper-explicit-default-nested-pointer.f90
Result
Nested implicit mapper generation now consistently reuses user-defined default mappers by canonical symbol name, avoiding mismatched duplicate mapper creation and preventing the observed offload failure scenario.
---
Full diff: https://github.com/llvm/llvm-project/pull/212251.diff
4 Files Affected:
- (modified) flang/include/flang/Utils/OpenMP.h (+5)
- (modified) flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp (+2-1)
- (modified) flang/lib/Utils/OpenMP.cpp (+23-2)
- (added) flang/test/Lower/OpenMP/declare-mapper-explicit-default-nested-pointer.f90 (+46)
``````````diff
diff --git a/flang/include/flang/Utils/OpenMP.h b/flang/include/flang/Utils/OpenMP.h
index e8627347fd578..0cb77e25af947 100644
--- a/flang/include/flang/Utils/OpenMP.h
+++ b/flang/include/flang/Utils/OpenMP.h
@@ -64,6 +64,11 @@ void cloneOrMapRegionOutsiders(
using RecordMemberMapperMangler =
std::function<void(std::string &mapperId, llvm::StringRef memberName)>;
+/// Build the canonical symbol name for a derived type's default mapper from
+/// the FIR record type. This matches the compiler-generated name shape used by
+/// explicit default declare mapper lowering.
+std::string getCanonicalDefaultDeclareMapperName(fir::RecordType recordType);
+
mlir::FlatSymbolRefAttr getOrGenImplicitDefaultDeclareMapper(
fir::FirOpBuilder &firOpBuilder, mlir::Location loc,
fir::RecordType recordType, llvm::StringRef mapperNameStr,
diff --git a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
index 945250daf1dbe..b50f5ccf6a323 100644
--- a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
+++ b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
@@ -621,7 +621,8 @@ class DoConcurrentConversion
mlir::FlatSymbolRefAttr mapperId;
if (requiresImplcitMapper) {
std::string mapperIdName =
- recordType.getName().str() + llvm::omp::OmpDefaultMapperName;
+ Fortran::utils::openmp::getCanonicalDefaultDeclareMapperName(
+ recordType);
// TODO Add a mangler callback once nested record types are supported.
mapperId = Fortran::utils::openmp::getOrGenImplicitDefaultDeclareMapper(
builder, liveIn.getLoc(), recordType, mapperIdName);
diff --git a/flang/lib/Utils/OpenMP.cpp b/flang/lib/Utils/OpenMP.cpp
index 9c22cc950e3f7..ebc5062eb39f0 100644
--- a/flang/lib/Utils/OpenMP.cpp
+++ b/flang/lib/Utils/OpenMP.cpp
@@ -13,11 +13,33 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Support/InternalNames.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/Transforms/RegionUtils.h"
namespace Fortran::utils::openmp {
+std::string getCanonicalDefaultDeclareMapperName(fir::RecordType recordType) {
+ auto [kind, deconstructed] =
+ fir::NameUniquer::deconstruct(recordType.getName());
+ if (kind != fir::NameUniquer::NameKind::DERIVED_TYPE)
+ return recordType.getName().str() + llvm::omp::OmpDefaultMapperName;
+
+ llvm::SmallVector<llvm::StringRef> modules;
+ llvm::SmallVector<llvm::StringRef> procs;
+ modules.reserve(deconstructed.modules.size());
+ procs.reserve(deconstructed.procs.size());
+ for (const std::string &module : deconstructed.modules)
+ modules.emplace_back(module);
+ for (const std::string &proc : deconstructed.procs) {
+ procs.emplace_back(proc);
+ }
+
+ std::string mapperName = deconstructed.name + llvm::omp::OmpDefaultMapperName;
+ return fir::NameUniquer::doGenerated(
+ modules, procs, deconstructed.blockId, mapperName);
+}
+
mlir::omp::MapInfoOp createMapInfoOp(mlir::OpBuilder &builder,
mlir::Location loc, mlir::Value baseAddr, mlir::Value varPtrPtr,
llvm::StringRef name, llvm::ArrayRef<mlir::Value> bounds,
@@ -260,8 +282,7 @@ mlir::FlatSymbolRefAttr getOrGenImplicitDefaultDeclareMapper(
mlir::FlatSymbolRefAttr mapperId;
if (auto recType = mlir::dyn_cast<fir::RecordType>(
fir::getFortranElementType(memberType))) {
- std::string mapperIdName =
- recType.getName().str() + llvm::omp::OmpDefaultMapperName;
+ std::string mapperIdName = getCanonicalDefaultDeclareMapperName(recType);
mangler(mapperIdName, memberName);
mapperId = getOrGenImplicitDefaultDeclareMapper(
firOpBuilder, loc, recType, mapperIdName, mangler);
diff --git a/flang/test/Lower/OpenMP/declare-mapper-explicit-default-nested-pointer.f90 b/flang/test/Lower/OpenMP/declare-mapper-explicit-default-nested-pointer.f90
new file mode 100644
index 0000000000000..9c41cbb0a00b2
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-mapper-explicit-default-nested-pointer.f90
@@ -0,0 +1,46 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %s -o - | FileCheck %s
+!
+! Test that when an explicit default DECLARE MAPPER is defined for a type (newdata)
+! whose only component is a pointer, and that type is used as an allocatable component
+! of another type (newvec), the compiler-generated implicit mapper for newvec correctly
+! references the user-defined mapper for newdata rather than synthesising a new
+! implicit one that would silently skip the pointer component.
+!
+! An explicit `map(s)` on a newvec variable is the code-path that triggers implicit
+! mapper generation for newvec (unlike an implicit target capture, which suppresses
+! mapper generation for plain non-allocatable/non-pointer derived types).
+
+module m
+ type :: newdata
+ real(8), pointer :: value
+ end type
+ !$omp declare mapper(default: newdata :: v) map(tofrom: v%value)
+
+ type :: newvec
+ integer :: len
+ type(newdata), allocatable :: data(:)
+ end type
+end module
+
+program main
+ use m
+ implicit none
+ type(newvec) :: s
+
+ ! Explicit map(s) triggers implicit mapper generation for newvec.
+ !$omp target data map(s)
+ s%len = 0
+ !$omp end target data
+end program
+
+! The user-defined default mapper for newdata must have tofrom map for v%value.
+! CHECK-DAG: omp.declare_mapper @_QQMmnewdata_omp_default_mapper
+! CHECK-DAG: map_clauses(tofrom)
+! CHECK-DAG: {name = "v%value"}
+
+! The implicit default mapper for newvec must reference the user-defined newdata
+! mapper by its canonical name. Before the fix this referenced a separately-
+! synthesised bogus mapper (@_QMmTnewdata_omp_default_mapper) that had no maps
+! for pointer components, causing a null-address device fault at runtime.
+! CHECK-DAG: omp.declare_mapper @_QQMmnewvec_omp_default_mapper
+! CHECK-DAG: mapper(@_QQMmnewdata_omp_default_mapper)
\ No newline at end of file
``````````
</details>
https://github.com/llvm/llvm-project/pull/212251
More information about the flang-commits
mailing list