[flang-commits] [clang] [flang] [Flang][OpenMP] Flag for switching off implicit allocatable member mapping via default declare mapper (PR #228748)

via flang-commits flang-commits at lists.llvm.org
Sat Oct 3 10:32:22 PDT 2026


https://github.com/agozillon created https://github.com/llvm/llvm-project/pull/228748

Part of the OpenMP specification requires implicitly mappiing allocatable members in derived types. However, it is quite an expensive overhead to perform implicit allocatable member mapping as it requires a mapper to do so due to the nature of possibly nested allocatable derived types etc. Alongside a fairly comprehensive map of the record, this in particular comes at a fairly substantial cost for arrays of derived types that trigger this behaviour. Some of this may or may not be mitigatable over time.

But for the moment this PR seeks to follow the example of other Fortran OpenMP compilers (Cray in particular I think) and utilise a flag that can switch this behaviour on and off, so that applications not depending on the crutch of this implicit mapping can deactivte it and not pay the overhead, and those that like the compiler to do this for them can continue to do so.

>From bce7219bb458d4cdd816e729f1871c705b5414f4 Mon Sep 17 00:00:00 2001
From: agozillon <Andrew.Gozillon at amd.com>
Date: Fri, 2 Oct 2026 06:45:12 -0500
Subject: [PATCH] [Flang][OpenMP] Flag for switching off implicit allocatable
 member mapping via default declare mapper

Part of the OpenMP specification requires implicitly mappiing allocatable members in derived types.
However, it is quite an expensive overhead to perform implicit allocatable member mapping as it
requires a mapper to do so due to the nature of possibly nested allocatable derived types etc.
Alongside a fairly comprehensive map of the record, this in particular comes at a fairly substantial
cost for arrays of derived types that trigger this behaviour. Some of this may or may not be
mitigatable over time.

But for the moment this PR seeks to follow the example of other Fortran OpenMP compilers (Cray
in particular I think) and utilise a flag that can switch this behaviour on and off, so that
applications not depending on the crutch of this implicit mapping can deactivte it and not pay
the overhead, and those that like the compiler to do this for them can continue to do so.
---
 clang/include/clang/Options/FlangOptions.td   |  6 ++
 clang/lib/Driver/ToolChains/Flang.cpp         |  2 +
 .../include/flang/Frontend/CodeGenOptions.def |  1 +
 flang/include/flang/Support/Flags.h           |  1 +
 flang/lib/Frontend/CompilerInvocation.cpp     |  5 ++
 flang/lib/Frontend/FrontendActions.cpp        |  6 ++
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  4 +-
 flang/lib/Lower/OpenMP/Utils.cpp              | 13 ++-
 flang/lib/Lower/OpenMP/Utils.h                |  3 +-
 flang/lib/Support/Flags.cpp                   |  2 +
 ...efault-mapper-allocatable-members-flag.f90 | 81 +++++++++++++++++++
 11 files changed, 119 insertions(+), 5 deletions(-)
 create mode 100644 flang/test/Lower/OpenMP/implicit-default-mapper-allocatable-members-flag.f90

diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index 5a45528a39a6d5e..d078e936b6647a1 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -72,6 +72,12 @@ defm real_8_real_16 : BooleanFFlag<"real-8-real-16">, Group<gfortran_Group>;
 defm real_8_real_4 : BooleanFFlag<"real-8-real-4">, Group<gfortran_Group>;
 defm recursive : BooleanFFlag<"recursive">, Group<gfortran_Group>;
 defm second_underscore : BooleanFFlag<"second-underscore">, Group<gfortran_Group>;
+def fimplicit_default_mapper_allocatable_members : Flag<["-"], "fimplicit-default-mapper-allocatable-members">,
+  Group<f_Group>, Visibility<[FlangOption, FC1Option]>,
+  HelpText<"Enable implicit allocatable member maps in compiler-generated OpenMP default mappers">;
+def fno_implicit_default_mapper_allocatable_members : Flag<["-"], "fno-implicit-default-mapper-allocatable-members">,
+  Group<f_Group>, Visibility<[FlangOption, FC1Option]>,
+  HelpText<"Disable implicit allocatable member maps in compiler-generated OpenMP default mappers">;
 defm sign_zero : BooleanFFlag<"sign-zero">, Group<gfortran_Group>;
 defm whole_file : BooleanFFlag<"whole-file">, Group<gfortran_Group>;
 
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 37d0372e7a18ed7..c7e3552c019a1e8 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -386,6 +386,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
        options::OPT_fstack_repack_arrays, options::OPT_fno_stack_repack_arrays,
        options::OPT_ftime_report, options::OPT_ftime_report_EQ,
        options::OPT_funroll_loops, options::OPT_fno_unroll_loops,
+       options::OPT_fimplicit_default_mapper_allocatable_members,
+       options::OPT_fno_implicit_default_mapper_allocatable_members,
        options::OPT_relaxed_c_loc});
 
   Args.addOptOutFlag(CmdArgs, options::OPT_foptimize_sibling_calls,
diff --git a/flang/include/flang/Frontend/CodeGenOptions.def b/flang/include/flang/Frontend/CodeGenOptions.def
index 86994f7710576c0..92d6e44b2af5bf9 100644
--- a/flang/include/flang/Frontend/CodeGenOptions.def
+++ b/flang/include/flang/Frontend/CodeGenOptions.def
@@ -62,6 +62,7 @@ CODEGENOPT(DwarfVersion, 3, 0) ///< Dwarf version
 CODEGENOPT(DebugInfoForProfiling, 1, 0)  ///< Emit extra debug info to make sample profile more accurate.
 CODEGENOPT(PseudoProbeForProfiling, 1, 0) ///< Emit pseudo probes for sample profiling.
 CODEGENOPT(UniqueInternalLinkageNames, 1, 0) ///< Append MD5 hash to internal linkage symbols.
+CODEGENOPT(ImplicitDefaultMapperAllocatableMembers, 1, 1) ///< Map allocatable members in generated OpenMP default mappers.
 
 CODEGENOPT(Underscoring, 1, 1)
 ENUM_CODEGENOPT(FPMaxminBehavior, Fortran::common::FPMaxminBehavior, 2, Fortran::common::FPMaxminBehavior::Legacy)
diff --git a/flang/include/flang/Support/Flags.h b/flang/include/flang/Support/Flags.h
index bcbb72f8e50d059..2e139b50cdc3325 100644
--- a/flang/include/flang/Support/Flags.h
+++ b/flang/include/flang/Support/Flags.h
@@ -13,5 +13,6 @@
 
 extern llvm::cl::opt<bool> enableDelayedPrivatization;
 extern llvm::cl::opt<bool> enableDelayedPrivatizationStaging;
+extern bool enableImplicitDefaultMapperAllocatableMembers;
 
 #endif // FORTRAN_SUPPORT_FLAGS_H_
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 13794f5720e741b..d5106ea3546759c 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -560,6 +560,11 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
     opts.Underscoring = 0;
   }
 
+  opts.ImplicitDefaultMapperAllocatableMembers = args.hasFlag(
+      clang::options::OPT_fimplicit_default_mapper_allocatable_members,
+      clang::options::OPT_fno_implicit_default_mapper_allocatable_members,
+      true);
+
   if (const llvm::opt::Arg *arg =
           args.getLastArg(clang::options::OPT_complex_range_EQ)) {
     llvm::StringRef argValue = llvm::StringRef(arg->getValue());
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 06d67e9417bc44e..c325509587b5331 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -27,6 +27,7 @@
 #include "flang/Optimizer/Transforms/Passes.h"
 #include "flang/Semantics/runtime-type-info.h"
 #include "flang/Semantics/unparse-with-symbols.h"
+#include "flang/Support/Flags.h"
 #include "flang/Support/default-kinds.h"
 #include "flang/Tools/CrossToolHelpers.h"
 
@@ -254,6 +255,11 @@ bool CodeGenAction::beginSourceFileAction() {
   if (!res)
     return res;
 
+  enableImplicitDefaultMapperAllocatableMembers =
+      ci.getInvocation()
+          .getCodeGenOpts()
+          .ImplicitDefaultMapperAllocatableMembers;
+
   timingScopeParse.stop();
   mlir::TimingScope timingScopeMLIRGen = timingScopeRoot.nest(
       mlir::TimingIdentifier::get(timingIdMLIRGen, timingMgr));
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 0b20eb1c16dc116..933fa276a0a77e5 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4218,7 +4218,9 @@ genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
             // where the record type contains an allocatable.
             if (!isPointer &&
                 (hasDefaultMapper ||
-                 (requiresImplicitDefaultDeclareMapper(*typeSpec)))) {
+                 (requiresImplicitDefaultDeclareMapper(
+                     *typeSpec,
+                     enableImplicitDefaultMapperAllocatableMembers)))) {
               if (!hasDefaultMapper) {
                 if (auto recordType = mlir::dyn_cast_or_null<fir::RecordType>(
                         converter.genType(*typeSpec)))
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index e05a3eab6fad6d1..75c262d472a72ba 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -37,6 +37,7 @@
 #include <flang/Semantics/symbol.h>
 #include <flang/Semantics/tools.h>
 #include <flang/Semantics/type.h>
+#include <flang/Support/Flags.h>
 #include <flang/Utils/OpenMP.h>
 #include <llvm/ADT/STLExtras.h>
 #include <llvm/ADT/SmallPtrSet.h>
@@ -76,7 +77,13 @@ namespace Fortran {
 namespace lower {
 namespace omp {
 bool requiresImplicitDefaultDeclareMapper(
-    const semantics::DerivedTypeSpec &typeSpec) {
+    const semantics::DerivedTypeSpec &typeSpec,
+    bool enableImplicitDefaultMapperAllocatableMembers) {
+  // If disabled via -fno-implicit-default-mapper-allocatable-members
+  // return false, as we do not need to emit allocatable member maps.
+  if (!enableImplicitDefaultMapperAllocatableMembers)
+    return false;
+
   llvm::SmallPtrSet<const semantics::DerivedTypeSpec *, 8> visited;
 
   std::function<bool(const semantics::DerivedTypeSpec &)> requiresMapper =
@@ -1267,8 +1274,8 @@ mlir::FlatSymbolRefAttr resolveMapperId(
       bool isImplicitMap =
           (mapTypeBits & mlir::omp::ClauseMapFlags::implicit) ==
           mlir::omp::ClauseMapFlags::implicit;
-      bool needsDefaultMapper =
-          requiresImplicitDefaultDeclareMapper(*objectTypeSpec);
+      bool needsDefaultMapper = requiresImplicitDefaultDeclareMapper(
+          *objectTypeSpec, enableImplicitDefaultMapperAllocatableMembers);
       // 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
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index 4560c9df349b2d3..bc23c005ee63029 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -146,7 +146,8 @@ mlir::Value createParentSymAndGenIntermediateMaps(
     mlir::omp::ClauseMapFlags mapTypeBits);
 
 bool requiresImplicitDefaultDeclareMapper(
-    const semantics::DerivedTypeSpec &typeSpec);
+    const semantics::DerivedTypeSpec &typeSpec,
+    bool enableImplicitDefaultMapperAllocatableMembers);
 
 omp::ObjectList gatherObjectsOf(omp::Object derivedTypeMember,
                                 semantics::SemanticsContext &semaCtx);
diff --git a/flang/lib/Support/Flags.cpp b/flang/lib/Support/Flags.cpp
index 02f64981618dd68..e74ba9f9fbd3757 100644
--- a/flang/lib/Support/Flags.cpp
+++ b/flang/lib/Support/Flags.cpp
@@ -18,3 +18,5 @@ llvm::cl::opt<bool> enableDelayedPrivatizationStaging(
     llvm::cl::desc("For partially supported constructs, emit private/local "
                    "variables as clauses/specifiers on MLIR ops."),
     llvm::cl::init(false));
+
+bool enableImplicitDefaultMapperAllocatableMembers = true;
diff --git a/flang/test/Lower/OpenMP/implicit-default-mapper-allocatable-members-flag.f90 b/flang/test/Lower/OpenMP/implicit-default-mapper-allocatable-members-flag.f90
new file mode 100644
index 000000000000000..2504dc86eb95d5d
--- /dev/null
+++ b/flang/test/Lower/OpenMP/implicit-default-mapper-allocatable-members-flag.f90
@@ -0,0 +1,81 @@
+! RUN: rm -rf %t && split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %t/implicit.f90 -o - | FileCheck %s --check-prefix=IMPLICIT-ON
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -fimplicit-default-mapper-allocatable-members %t/implicit.f90 -o - | FileCheck %s --check-prefix=IMPLICIT-ON
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -fno-implicit-default-mapper-allocatable-members %t/implicit.f90 -o - | FileCheck %s --check-prefix=IMPLICIT-OFF
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 %t/explicit.f90 -o - | FileCheck %s --check-prefix=EXPLICIT-ON
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -fimplicit-default-mapper-allocatable-members %t/explicit.f90 -o - | FileCheck %s --check-prefix=EXPLICIT-ON
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -fno-implicit-default-mapper-allocatable-members %t/explicit.f90 -o - | FileCheck %s --check-prefix=EXPLICIT-OFF
+
+! Check the -f[no-]implicit-default-mapper-allocatable-members switch.
+!
+! With the default/on setting, compiler-generated implicit default mappers for
+! derived types include maps for allocatable and nested-record components.
+! With the off setting, those component maps are suppressed. Cover both:
+!   1. an implicit target-region capture; and
+!   2. an explicit whole-object map that would otherwise synthesize a default
+!      mapper for the derived type.
+
+!--- implicit.f90
+program implicit_capture
+  implicit none
+
+  type :: t
+    integer, allocatable :: a(:)
+  end type
+
+  type(t) :: x
+
+  allocate(x%a(1))
+
+  !$omp target
+    x%a(1) = 1
+  !$omp end target
+end program
+
+!--- explicit.f90
+program explicit_whole_object_map
+  implicit none
+
+  type :: t
+    integer :: scalar
+    integer, allocatable :: a(:)
+  end type
+
+  type(t) :: x
+
+  allocate(x%a(1))
+  x%scalar = 1
+
+  !$omp target map(tofrom: x)
+    x%scalar = x%scalar + 1
+    x%a(1) = x%a(1) + 1
+  !$omp end target
+end program
+
+! IMPLICIT-ON: omp.declare_mapper @[[IM_MAPPER:_QQFt_omp_default_mapper]]
+! IMPLICIT-ON: %[[COORD:.*]] = fir.coordinate_of %{{.*}}#0, a
+! IMPLICIT-ON: %[[PTEE:.*]] = omp.map.info var_ptr(%[[COORD]] {{.*}}) map_clauses(implicit, tofrom, ref_ptee)
+! IMPLICIT-ON: %[[ATTACH:.*]] = omp.map.info var_ptr(%[[COORD]] {{.*}}) map_clauses(attach, ref_ptee)
+! IMPLICIT-ON: %[[PARENT:.*]] = omp.map.info {{.*}}members(%[[PTEE]] : [0]
+! IMPLICIT-ON: omp.declare_mapper.info map_entries(%[[PARENT]], %[[PTEE]], %[[ATTACH]]
+! IMPLICIT-ON: omp.map.info {{.*}}map_clauses(implicit, tofrom) capture(ByRef) mapper(@[[IM_MAPPER]]) name("x")
+! IMPLICIT-ON: omp.target
+
+! IMPLICIT-OFF-NOT: omp.declare_mapper
+! IMPLICIT-OFF: %[[IM_MAP:.*]] = omp.map.info var_ptr(%{{.*}}) map_clauses(implicit, tofrom) capture(ByRef) name("x")
+! IMPLICIT-OFF-NOT: mapper(
+! IMPLICIT-OFF: omp.target {{.*}}map_entries(%[[IM_MAP]] ->
+
+! EXPLICIT-ON: omp.declare_mapper @[[EX_MAPPER:_QQFt_omp_default_mapper]]
+! EXPLICIT-ON: %[[COORD:.*]] = fir.coordinate_of %{{.*}}#0, a
+! EXPLICIT-ON: %[[PTEE:.*]] = omp.map.info var_ptr(%[[COORD]] {{.*}}) map_clauses(implicit, tofrom, ref_ptee)
+! EXPLICIT-ON: %[[ATTACH:.*]] = omp.map.info var_ptr(%[[COORD]] {{.*}}) map_clauses(attach, ref_ptee)
+! EXPLICIT-ON: %[[PARENT:.*]] = omp.map.info {{.*}}members(%[[PTEE]] : [1]
+! EXPLICIT-ON: omp.declare_mapper.info map_entries(%[[PARENT]], %[[PTEE]], %[[ATTACH]]
+! EXPLICIT-ON: omp.map.info {{.*}}map_clauses(tofrom) capture(ByRef) mapper(@[[EX_MAPPER]]) name("x")
+! EXPLICIT-ON: omp.target
+
+! EXPLICIT-OFF-NOT: omp.declare_mapper
+! EXPLICIT-OFF: %[[EX_MAP:.*]] = omp.map.info var_ptr(%{{.*}}) map_clauses(tofrom) capture(ByRef) name("x")
+! EXPLICIT-OFF-NOT: mapper(
+! EXPLICIT-OFF: omp.target {{.*}}map_entries(%[[EX_MAP]] ->



More information about the flang-commits mailing list