[flang-commits] [flang] [flang][OpenMP] NFC: Extract intrinsic reduction shadow lookup (PR #218389)

Tom Eccles via flang-commits flang-commits at lists.llvm.org
Tue Aug 25 03:52:52 PDT 2026


https://github.com/tblah updated https://github.com/llvm/llvm-project/pull/218389

>From 2b0356789055d3687be9621ab84457384d2de063 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 24 Aug 2026 13:04:37 +0100
Subject: [PATCH 1/2] [flang][OpenMP] NFC: Extract intrinsic reduction shadow
 lookup

Centralize lookup of user-defined reductions that shadow supported
intrinsic procedure reductions. Keep existing behavior while making the
classification available to other OpenMP lowering paths.

This will help with #215997

Assisted-by: Codex
---
 .../flang/Lower/Support/ReductionProcessor.h  |   8 ++
 .../lib/Lower/Support/ReductionProcessor.cpp  | 131 ++++++++++--------
 2 files changed, 81 insertions(+), 58 deletions(-)

diff --git a/flang/include/flang/Lower/Support/ReductionProcessor.h b/flang/include/flang/Lower/Support/ReductionProcessor.h
index b86cc07159246..fdc1e7320684b 100644
--- a/flang/include/flang/Lower/Support/ReductionProcessor.h
+++ b/flang/include/flang/Lower/Support/ReductionProcessor.h
@@ -33,6 +33,7 @@ namespace lower {
 class AbstractConverter;
 } // namespace lower
 namespace semantics {
+class Scope;
 class SemanticsContext;
 } // namespace semantics
 } // namespace Fortran
@@ -93,6 +94,13 @@ class ReductionProcessor {
   static bool
   supportedIntrinsicProcReduction(const omp::clause::ProcedureDesignator &pd);
 
+  /// Find a user-defined reduction that shadows a supported intrinsic
+  /// procedure reduction for \p type in \p scope.
+  static const semantics::Symbol *findUserDefinedReductionForIntrinsic(
+      const semantics::Scope &scope,
+      const omp::clause::ProcedureDesignator &reductionIntrinsic,
+      const semantics::DeclTypeSpec *type);
+
   static const semantics::SourceName
   getRealName(const semantics::Symbol *symbol);
 
diff --git a/flang/lib/Lower/Support/ReductionProcessor.cpp b/flang/lib/Lower/Support/ReductionProcessor.cpp
index de6aeefa48fc4..7b502880f50f3 100644
--- a/flang/lib/Lower/Support/ReductionProcessor.cpp
+++ b/flang/lib/Lower/Support/ReductionProcessor.cpp
@@ -195,6 +195,26 @@ bool ReductionProcessor::supportedIntrinsicProcReduction(
   return redType;
 }
 
+const semantics::Symbol *
+ReductionProcessor::findUserDefinedReductionForIntrinsic(
+    const semantics::Scope &scope,
+    const omp::clause::ProcedureDesignator &reductionIntrinsic,
+    const semantics::DeclTypeSpec *type) {
+  const semantics::Symbol *symbol = reductionIntrinsic.v.sym();
+  if (!symbol || !type)
+    return nullptr;
+
+  std::string mangledName = "op." + getRealName(symbol).ToString();
+  const semantics::Symbol *reductionSymbol =
+      scope.FindSymbol(parser::CharBlock{mangledName});
+  if (!reductionSymbol)
+    return nullptr;
+
+  const semantics::Symbol &ultimate = reductionSymbol->GetUltimate();
+  const auto *details = ultimate.detailsIf<semantics::UserReductionDetails>();
+  return details && details->SupportsType(*type) ? &ultimate : nullptr;
+}
+
 std::string
 ReductionProcessor::getReductionName(llvm::StringRef name,
                                      const fir::KindMapping &kindMap,
@@ -1344,70 +1364,65 @@ bool ReductionProcessor::processReductionArguments(
         // resolve-names). If one is visible in the current scope and supports
         // the variable's type, bind to the omp.declare_reduction op the
         // directive materialized for it instead of generating the intrinsic.
-        semantics::Symbol *sym = reductionIntrinsic->v.sym();
-        std::string mangledName = "op." + getRealName(sym).ToString();
-        if (const semantics::Symbol *redSym =
-                converter.getCurrentScope().FindSymbol(
-                    parser::CharBlock{mangledName})) {
-          const semantics::Symbol &ultimate = redSym->GetUltimate();
-          const semantics::UserReductionDetails *userDetails =
-              ultimate.detailsIf<semantics::UserReductionDetails>();
-          const semantics::DeclTypeSpec *varType =
-              reductionSymbols[idx]->GetUltimate().GetType();
+        const semantics::DeclTypeSpec *varType =
+            reductionSymbols[idx]->GetUltimate().GetType();
+        if (const semantics::Symbol *userReduction =
+                findUserDefinedReductionForIntrinsic(
+                    converter.getCurrentScope(), *reductionIntrinsic,
+                    varType)) {
+          const semantics::Symbol &ultimate = *userReduction;
           // A user-defined reduction shadows the intrinsic only for the types
           // it is declared for. If it does not cover this variable's type, the
           // user has not redefined the reduction for that type and the
           // implicit intrinsic reduction still applies, so fall through to it.
-          if (userDetails && varType && userDetails->SupportsType(*varType)) {
-            // The user declaration takes precedence over the intrinsic for this
-            // type. A declaration listing several types (or several merged
-            // declarations) is handled the same way as the operator and named
-            // paths: the directive emits one op per type and the variable's
-            // type selects the matching per-type name below. A USE-associated
-            // shadowing reduction is found by FindSymbol as a use wrapper;
-            // naming from its ultimate (name, owner) below binds the source
-            // module's op, materialized on demand here for separate
-            // compilation, exactly as the named path does. A renamed shadowing
-            // intrinsic does not reach here: the renamed name resolves to the
-            // intrinsic rather than the user reduction, so semantics rejects
-            // the clause with a type-incompatibility error before lowering.
-            std::string opName = ReductionProcessor::getScopedUserReductionName(
-                converter, ultimate, namingType, isByRef);
-            mlir::ModuleOp module = builder.getModule();
-            auto existingDecl = module.lookupSymbol<OpType>(opName);
-            // Separate compilation: materialize the imported shadowing
-            // reduction on demand when its defining module is a mod file, then
-            // re-look it up (same-file ops already exist here).
-            if (!existingDecl && semaCtx && ultimate.owner().symbol() &&
-                ultimate.owner().symbol()->test(
-                    semantics::Symbol::Flag::ModFile)) {
-              Fortran::lower::materializeUserReduction(
-                  converter, *semaCtx, ultimate, opName, namingType, isByRef);
-              existingDecl = module.lookupSymbol<OpType>(opName);
-            }
-            if (isBoxedTrivialElemReduction) {
-              // Trivial-element allocatable/pointer: synthesize the boxed op.
-              if (OpType boxedDecl = getOrCreateBoxedUserReduction<OpType>(
-                      converter, semaCtx, ultimate, redType, currentLocation)) {
-                reductionDeclSymbols.push_back(mlir::SymbolRefAttr::get(
-                    builder.getContext(), boxedDecl.getSymName()));
-                ++idx;
-                continue;
-              }
-            }
-            if (!existingDecl ||
-                fir::unwrapRefType(existingDecl.getType()) !=
-                    fir::unwrapRefType(namingType) ||
-                isBoxedTrivialReduction) {
-              TODO(currentLocation,
-                   "OpenMP user-defined reduction declaration was not "
-                   "materialized for this type");
+          // The user declaration takes precedence over the intrinsic for this
+          // type. A declaration listing several types (or several merged
+          // declarations) is handled the same way as the operator and named
+          // paths: the directive emits one op per type and the variable's
+          // type selects the matching per-type name below. A USE-associated
+          // shadowing reduction is found by FindSymbol as a use wrapper;
+          // naming from its ultimate (name, owner) below binds the source
+          // module's op, materialized on demand here for separate
+          // compilation, exactly as the named path does. A renamed shadowing
+          // intrinsic does not reach here: the renamed name resolves to the
+          // intrinsic rather than the user reduction, so semantics rejects
+          // the clause with a type-incompatibility error before lowering.
+          std::string opName = ReductionProcessor::getScopedUserReductionName(
+              converter, ultimate, namingType, isByRef);
+          mlir::ModuleOp module = builder.getModule();
+          auto existingDecl = module.lookupSymbol<OpType>(opName);
+          // Separate compilation: materialize the imported shadowing
+          // reduction on demand when its defining module is a mod file, then
+          // re-look it up (same-file ops already exist here).
+          if (!existingDecl && semaCtx && ultimate.owner().symbol() &&
+              ultimate.owner().symbol()->test(
+                  semantics::Symbol::Flag::ModFile)) {
+            Fortran::lower::materializeUserReduction(
+                converter, *semaCtx, ultimate, opName, namingType, isByRef);
+            existingDecl = module.lookupSymbol<OpType>(opName);
+          }
+          if (isBoxedTrivialElemReduction) {
+            // Trivial-element allocatable/pointer: synthesize the boxed op.
+            if (OpType boxedDecl = getOrCreateBoxedUserReduction<OpType>(
+                    converter, semaCtx, ultimate, redType, currentLocation)) {
+              reductionDeclSymbols.push_back(mlir::SymbolRefAttr::get(
+                  builder.getContext(), boxedDecl.getSymName()));
+              ++idx;
+              continue;
             }
-            reductionDeclSymbols.push_back(mlir::SymbolRefAttr::get(
-                builder.getContext(), existingDecl.getSymName()));
-            ++idx;
-            continue;
           }
+          if (!existingDecl ||
+              fir::unwrapRefType(existingDecl.getType()) !=
+                  fir::unwrapRefType(namingType) ||
+              isBoxedTrivialReduction) {
+            TODO(currentLocation,
+                 "OpenMP user-defined reduction declaration was not "
+                 "materialized for this type");
+          }
+          reductionDeclSymbols.push_back(mlir::SymbolRefAttr::get(
+              builder.getContext(), existingDecl.getSymName()));
+          ++idx;
+          continue;
         }
 
         redId = getReductionType(*reductionIntrinsic);

>From debc066eb15c91931a5deac6e97dfa25b0cac2c3 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 25 Aug 2026 11:52:19 +0100
Subject: [PATCH 2/2] Improve documentation comment

---
 flang/include/flang/Lower/Support/ReductionProcessor.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/flang/include/flang/Lower/Support/ReductionProcessor.h b/flang/include/flang/Lower/Support/ReductionProcessor.h
index fdc1e7320684b..bc6e86a729604 100644
--- a/flang/include/flang/Lower/Support/ReductionProcessor.h
+++ b/flang/include/flang/Lower/Support/ReductionProcessor.h
@@ -94,8 +94,9 @@ class ReductionProcessor {
   static bool
   supportedIntrinsicProcReduction(const omp::clause::ProcedureDesignator &pd);
 
-  /// Find a user-defined reduction that shadows a supported intrinsic
-  /// procedure reduction for \p type in \p scope.
+  /// Return the user-defined reduction that processReductionArguments would
+  /// bind instead of \p reductionIntrinsic for \p type in \p scope, or null if
+  /// it would bind the supported intrinsic.
   static const semantics::Symbol *findUserDefinedReductionForIntrinsic(
       const semantics::Scope &scope,
       const omp::clause::ProcedureDesignator &reductionIntrinsic,



More information about the flang-commits mailing list