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

>From 10a4a8a8721ed213a2a7662eca5863f41f5de444 Mon Sep 17 00:00:00 2001
From: agozillon <[email protected]>
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         | 10 +++
 .../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                   |  6 ++
 flang/test/Driver/omp-driver-offload.f90      | 38 +++++++++
 ...efault-mapper-allocatable-members-flag.f90 | 81 +++++++++++++++++++
 12 files changed, 169 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 2844ee854a120..71e61e94e3977 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -314,6 +314,12 @@ def fdo_concurrent_to_openmp_EQ : Joined<["-"], 
"fdo-concurrent-to-openmp=">,
   HelpText<"Try to map `do concurrent` loops to OpenMP [none|host|device]">,
       Values<"none, host, device">;
 
+def fimplicit_default_mapper_allocatable_members : Flag<["-"], 
"fimplicit-default-mapper-allocatable-members">,
+  Group<f_Group>, 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>, HelpText<"Disable implicit allocatable member maps in 
compiler-generated OpenMP default mappers">;
+
+
 def J : JoinedOrSeparate<["-"], "J">,
   Flags<[RenderJoined]>, Group<gfortran_Group>, Alias<module_dir>;
 
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index a13c69a5b2a50..3a47333ab0e84 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -876,6 +876,16 @@ void Flang::addOffloadOptions(Compilation &C, const 
InputInfoList &Inputs,
                    /*Default=*/false))
     CmdArgs.push_back("-fopenmp-assume-threads-oversubscription");
 
+  // This forwards the option to switch off/on mapper generation for scenarios
+  // where the compiler considers it neccessary to generate a mapper to cover
+  // implicit allocatable member mapping inside of derived types to adhere to
+  // specification compliance.
+  if (!Args.hasFlag(
+          options::OPT_fimplicit_default_mapper_allocatable_members,
+          options::OPT_fno_implicit_default_mapper_allocatable_members,
+          /*Default=*/true))
+    CmdArgs.push_back("-fno-implicit-default-mapper-allocatable-members");
+
   if (IsOpenMPDevice) {
     // -fopenmp-is-target-device is passed along to tell the frontend that it 
is
     // generating code for a device, so that only the relevant code is emitted.
diff --git a/flang/include/flang/Frontend/CodeGenOptions.def 
b/flang/include/flang/Frontend/CodeGenOptions.def
index 86994f7710576..92d6e44b2af5b 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 bcbb72f8e50d0..1dbfa249b87bd 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 llvm::cl::opt<bool> enableImplicitDefaultMapperAllocatableMembers;
 
 #endif // FORTRAN_SUPPORT_FLAGS_H_
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp 
b/flang/lib/Frontend/CompilerInvocation.cpp
index d4c6b44e11477..db5886f9d7930 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -561,6 +561,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,
+      /*Default=*/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 95cd8ac0eff53..c2bb21759b6b8 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"
 
@@ -255,6 +256,11 @@ bool CodeGenAction::beginSourceFileAction() {
   if (!res)
     return res;
 
+  enableImplicitDefaultMapperAllocatableMembers.setValue(
+      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 abe1df79f6aef..380325ee6bddd 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4253,7 +4253,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 9e37c02eac3f1..636c8037a2a52 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 =
@@ -1266,8 +1273,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 c769380aabbb0..6112d0c639b97 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 250232448ee94..ea5428590e5d9 100644
--- a/flang/lib/Support/Flags.cpp
+++ b/flang/lib/Support/Flags.cpp
@@ -18,3 +18,9 @@ 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));
+
+llvm::cl::opt<bool> enableImplicitDefaultMapperAllocatableMembers(
+    "enable-implicit-default-mapper-allocatable-members",
+    llvm::cl::desc("Emit implicit mappers for implicit allocatable member "
+                   "mapping in derived types for specification compliance"),
+    llvm::cl::init(true));
diff --git a/flang/test/Driver/omp-driver-offload.f90 
b/flang/test/Driver/omp-driver-offload.f90
index fccd0771d040c..3caf6ff7b72cb 100644
--- a/flang/test/Driver/omp-driver-offload.f90
+++ b/flang/test/Driver/omp-driver-offload.f90
@@ -230,3 +230,41 @@
 ! OFFLOAD-TARGETS-NOT: --offload-targets
 ! OFFLOAD-TARGETS: "{{[^"]*}}flang{{[^"]*}}" "-fc1" "-triple" 
"x86_64-unknown-linux-gnu"
 ! OFFLOAD-TARGETS-SAME: "--offload-targets=amdgpu-amd-amdhsa"
+
+! Verify the implicit default mapper alloactable members flags are applied to 
both host
+! and device phases of the offload compilation when the negative flag is used 
and ignored
+! in the positive case as it is the default.
+
+! RUN: %flang -S -### %s -o %t 2>&1 \
+! RUN: -fopenmp -fimplicit-default-mapper-allocatable-members 
--offload-arch=gfx90a \
+! RUN:   | FileCheck %s --check-prefix=ENABLE-IMP-MAPPER
+
+! ENABLE-IMP-MAPPER: "{{[^"]*}}flang{{[^"]*}}" "-fc1" "-triple" 
"x86_64-unknown-linux-gnu"
+! ENABLE-IMP-MAPPER-SAME: "-fopenmp"
+! ENABLE-IMP-MAPPER-SAME: "--offload-targets=amdgpu-amd-amdhsa"
+! ENABLE-IMP-MAPPER-NOT: implicit-default-mapper-allocatable-members
+! ENABLE-IMP-MAPPER-NEXT: "{{[^"]*}}flang{{[^"]*}}" "-fc1" "-triple" 
"amdgpu9.0a-amd-amdhsa"
+! ENABLE-IMP-MAPPER-SAME: "-fopenmp"
+! ENABLE-IMP-MAPPER-NOT: implicit-default-mapper-allocatable-members
+
+! RUN: %flang -S -### %s -o %t 2>&1 \
+! RUN: -fopenmp -fno-implicit-default-mapper-allocatable-members 
--offload-arch=gfx90a \
+! RUN:   | FileCheck %s --check-prefix=DISABLE-IMP-MAPPER
+
+! DISABLE-IMP-MAPPER: "{{[^"]*}}flang{{[^"]*}}" "-fc1" "-triple" 
"x86_64-unknown-linux-gnu"
+! DISABLE-IMP-MAPPER-SAME: "-fopenmp" {{.*}} 
"-fno-implicit-default-mapper-allocatable-members"
+! DISABLE-IMP-MAPPER-NEXT: "{{[^"]*}}flang{{[^"]*}}" "-fc1" "-triple" 
"amdgpu9.0a-amd-amdhsa"
+! DISABLE-IMP-MAPPER-SAME: "-fopenmp" {{.*}} 
"-fno-implicit-default-mapper-allocatable-members"
+
+! Verify when multiple flags passed the final flag wins out
+
+! RUN: %flang -S -### %s -o %t 2>&1 \
+! RUN: -fopenmp -fimplicit-default-mapper-allocatable-members \
+! RUN:  -fimplicit-default-mapper-allocatable-members \
+! RUN: --offload-arch=gfx90a -fno-implicit-default-mapper-allocatable-members \
+! RUN:   | FileCheck %s --check-prefix=MULTI-IMP-MAPPER
+
+! MULTI-IMP-MAPPER: "{{[^"]*}}flang{{[^"]*}}" "-fc1" "-triple" 
"x86_64-unknown-linux-gnu"
+! MULTI-IMP-MAPPER-SAME: "-fopenmp" {{.*}} 
"-fno-implicit-default-mapper-allocatable-members"
+! MULTI-IMP-MAPPER-NEXT: "{{[^"]*}}flang{{[^"]*}}" "-fc1" "-triple" 
"amdgpu9.0a-amd-amdhsa"
+! MULTI-IMP-MAPPER-SAME: "-fopenmp" {{.*}} 
"-fno-implicit-default-mapper-allocatable-members"
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 0000000000000..2504dc86eb95d
--- /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]] ->

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to