[flang-commits] [flang] [flang][OpenMP] Lower multi-type user-defined declare reduction (PR #207494)

via flang-commits flang-commits at lists.llvm.org
Sat Jul 4 00:22:40 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Matt (MattPD)

<details>
<summary>Changes</summary>

A user-defined declare reduction covering several types silently miscompiled: every listed type bound one type-less omp.declare_reduction op, so a variable of any but the first type ran through an op built for the wrong type (an !fir.ref<f32> reduction on an i32 op, a mismatch the verifier does not catch). This affects all three forms: operator (.op.), named (myred), and a name that shadows an intrinsic (max/min/iand/ior/ieor, the arm added by llvm-project#<!-- -->205893).

Fixes #<!-- -->207489.

Emit one op per listed type and bind each variable to the op for its type. The directive loop threads a per-type instance index across the parallel type, combiner, and initializer lists so each op gets its own element type, combiner, and initializer.

Name every user-reduction op from one canonical source, getScopedUserReductionName: the reduction symbol's ultimate name, qualified with its owning scope, plus a type and by-ref suffix. Both the directive and clause sides call it, so their names cannot drift, and the use-associated cases converge on the source declaration's name. Because the name also carries the owning scope, a same-spelling same-type operator reduction imported from two modules yields two distinct ops rather than colliding on one.

Suffix the type unconditionally, including for a single-type reduction: a conditional suffix would let a multi-type myred's per-type op @<!-- -->_QQFmyred_i32 collide with a single-type reduction literally named myred_i32, silently binding one reduction's clause to the other's combiner.

An allocatable or pointer reduction of a trivial element type is a clean TODO, deferred to the separate boxed-type reduction work in llvm-project#<!-- -->186765; a mode-independent guard (a boxed operand of a trivial element type) keeps it a clean TODO even under -mmlir --force-byref-reduction. Allocatable character and derived-type reductions still lower.

The same helper also lowers use-associated named reductions, including a rename, and use-associated intrinsic-shadowing reductions.

Assisted-by: Claude Opus 4.8, GPT-5.5.


---

Patch is 128.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207494.diff


50 Files Affected:

- (modified) flang/include/flang/Lower/OpenMP.h (+8) 
- (modified) flang/include/flang/Lower/Support/ReductionProcessor.h (+11-7) 
- (modified) flang/include/flang/Semantics/openmp-utils.h (+18) 
- (modified) flang/lib/Lower/Bridge.cpp (+9) 
- (modified) flang/lib/Lower/OpenMP/ClauseProcessor.cpp (+15-6) 
- (modified) flang/lib/Lower/OpenMP/ClauseProcessor.h (+2-1) 
- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+191-38) 
- (modified) flang/lib/Lower/Support/ReductionProcessor.cpp (+145-70) 
- (modified) flang/lib/Semantics/check-omp-structure.cpp (+4-129) 
- (modified) flang/lib/Semantics/mod-file.cpp (+21-1) 
- (modified) flang/lib/Semantics/openmp-utils.cpp (+139) 
- (modified) flang/lib/Semantics/resolve-names-utils.cpp (+5) 
- (modified) flang/lib/Semantics/resolve-names-utils.h (+11) 
- (added) flang/test/Lower/OpenMP/Todo/declare-reduction-allocatable-trivial.f90 (+33) 
- (removed) flang/test/Lower/OpenMP/Todo/declare-reduction-operator-multiple-types.f90 (-38) 
- (removed) flang/test/Lower/OpenMP/Todo/declare-reduction-operator-use-assoc.f90 (-36) 
- (removed) flang/test/Lower/OpenMP/Todo/multiple-types-declare_reduction.f90 (-51) 
- (added) flang/test/Lower/OpenMP/declare-mapper-hermetic.f90 (+38) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-character-allocatable.f90 (+2-2) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-initializer-component.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-initializer-defined-assign.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-initializer-rhs-call.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-logical-init.f90 (+1-1) 
- (added) flang/test/Lower/OpenMP/declare-reduction-name-type-token-collision.f90 (+43) 
- (added) flang/test/Lower/OpenMP/declare-reduction-named-renamed.f90 (+38) 
- (added) flang/test/Lower/OpenMP/declare-reduction-named-use-assoc.f90 (+37) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-no-initializer-intrinsic.f90 (+2-2) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-collide.f90 (+79) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-operator-derived.f90 (+1-1) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-merged.f90 (+76) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-multiple-types.f90 (+66) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-renamed.f90 (+45) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-separate-combiners.f90 (+106) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-separate-hermetic.f90 (+56) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-separate-reexport.f90 (+93) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-separate-unused-unsupported.f90 (+86) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-separate.f90 (+162) 
- (added) flang/test/Lower/OpenMP/declare-reduction-operator-use-assoc.f90 (+47) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-operator.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-same-name-different-scope.f90 (+2-2) 
- (added) flang/test/Lower/OpenMP/declare-reduction-shadows-intrinsic-multiple-types.f90 (+45) 
- (added) flang/test/Lower/OpenMP/declare-reduction-shadows-intrinsic-use-assoc.f90 (+37) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-shadows-intrinsic.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/declare-reduction-target-intrinsic.f90 (+9-9) 
- (added) flang/test/Lower/OpenMP/multiple-types-declare_reduction.f90 (+55) 
- (modified) flang/test/Lower/OpenMP/omp-declare-reduction-combsub.f90 (+1-2) 
- (modified) flang/test/Lower/OpenMP/omp-declare-reduction-derivedtype.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/omp-declare-reduction-initsub.f90 (+1-1) 
- (modified) flang/test/Lower/OpenMP/omp-declare-reduction.f90 (+1-1) 
- (added) flang/test/Semantics/OpenMP/declare-reduction-operator-modfile-reexport.f90 (+86) 


``````````diff
diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index 882ebad9071bc..90d0e7010c033 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -105,6 +105,14 @@ void materializeOpenMPDeclareMappers(
     Fortran::lower::AbstractConverter &, Fortran::semantics::SemanticsContext &,
     const Fortran::semantics::Scope *scope = nullptr);
 
+// Materialize omp.declare_reduction ops for user-defined operator reduction
+// declarations found in imported modules (separate compilation). If scope
+// is null, materialize for the whole semantics global scope; otherwise, operate
+// recursively starting at scope.
+void materializeOpenMPDeclareReductions(
+    Fortran::lower::AbstractConverter &, Fortran::semantics::SemanticsContext &,
+    const Fortran::semantics::Scope *scope = nullptr);
+
 } // namespace lower
 } // namespace Fortran
 
diff --git a/flang/include/flang/Lower/Support/ReductionProcessor.h b/flang/include/flang/Lower/Support/ReductionProcessor.h
index a949da875b3a2..e500d24f52bd0 100644
--- a/flang/include/flang/Lower/Support/ReductionProcessor.h
+++ b/flang/include/flang/Lower/Support/ReductionProcessor.h
@@ -96,15 +96,19 @@ class ReductionProcessor {
                                       const fir::KindMapping &kindMap,
                                       mlir::Type ty, bool isByRef);
 
-  /// Returns the module-unique name of the omp.declare_reduction op that
-  /// materializes a user-defined reduction (named or operator). The name is
-  /// derived from the reduction symbol's ultimate name, qualified with its
-  /// owning scope via AbstractConverter::mangleName, so that reductions with
-  /// the same spelling in different modules do not collide. The directive and
-  /// clause lowering must both use this to agree on the op's symbol name.
+  /// Returns the module-unique name of the omp.declare_reduction op for a
+  /// user-defined reduction (named or operator). Derived from the reduction
+  /// symbol's ultimate name, qualified with its owning scope via
+  /// AbstractConverter::mangleName so same-spelling reductions in different
+  /// modules do not collide, then suffixed with the reduction type (via
+  /// getReductionName) so a reduction listing several types produces one op per
+  /// type (single-type is N=1). The directive and clause sides must both call
+  /// this with the same unwrapped type and isByRef, since both tokens are part
+  /// of the name, or the names diverge.
   static std::string
   getScopedUserReductionName(AbstractConverter &converter,
-                             const semantics::Symbol &reductionSymbol);
+                             const semantics::Symbol &reductionSymbol,
+                             mlir::Type reductionType, bool isByRef);
 
   /// This function returns the identity value of the operator \p
   /// reductionOpName. For example:
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 75bab5f6cb8ec..6e91ed4019b3d 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -35,6 +35,7 @@
 #include <vector>
 
 namespace Fortran::semantics {
+class DeclTypeSpec;
 class Scope;
 class SemanticsContext;
 class Symbol;
@@ -116,6 +117,23 @@ bool IsArrayElement(const parser::OmpObject &object, SemanticsContext *semaCtx);
 
 const Symbol *GetHostSymbol(const Symbol &sym);
 
+// Resolve a user-defined reduction visible in scope under the mangled name
+// mangledName (e.g. "op.myop." for operator(.myop.), or a named reduction).
+// Follows USE associations, operator renames, private visibility, and merged
+// generics exactly as the OpenMP semantic checks do, returning the found
+// (non-ultimate) reduction symbol, or null if none is visible. When type is
+// non-null, only a reduction that supports that type is accepted (used to
+// disambiguate an operator that carries reductions for several types).
+const Symbol *FindUserReductionSymbol(const Scope &scope,
+    const parser::CharBlock &mangledName, const DeclTypeSpec *type = nullptr);
+
+// Resolve the user-defined reduction associated with the defined-operator
+// symbol operatorSym. Delegates to FindUserReductionSymbol starting from the
+// operator's owning scope with the operator's mangled ("op...") name; type
+// filters by supported type as above.
+const Symbol *FindOperatorUserReductionSymbol(
+    const Symbol &operatorSym, const DeclTypeSpec *type = nullptr);
+
 bool IsMapEnteringType(parser::OmpMapType::Value type);
 bool IsMapExitingType(parser::OmpMapType::Value type);
 
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 27fa06362a1a8..1bd92049211c7 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -580,6 +580,15 @@ class FirConverter : public Fortran::lower::AbstractConverter {
           *this, bridge.getSemanticsContext());
     });
 
+    // Ensure imported OpenMP user-defined operator declare reductions are
+    // materialized at module scope before lowering any reduction clauses that
+    // may reference them (the separate-compilation counterpart of same-file
+    // module reduction lowering).
+    createBuilderOutsideOfFuncOpAndDo([&]() {
+      Fortran::lower::materializeOpenMPDeclareReductions(
+          *this, bridge.getSemanticsContext());
+    });
+
     // Create definitions of intrinsic module constants.
     createBuilderOutsideOfFuncOpAndDo(
         [&]() { createIntrinsicModuleDefinitions(pft); });
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index ee88a24f97263..0d203afc4a168 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -538,7 +538,8 @@ bool ClauseProcessor::processInclusive(
 
 bool ClauseProcessor::processInitializer(
     lower::SymMap &symMap, ReductionProcessor::GenInitValueCBTy &genInitValueCB,
-    const parser::OmpStylizedInstance *parserInitInstance) const {
+    const parser::OmpStylizedInstance *parserInitInstance,
+    unsigned instanceIdx) const {
   if (auto *clause = findUniqueClause<omp::clause::Initializer>()) {
     // Extract the typed assignment from the parser-level instance, if
     // the initializer is an assignment statement (as opposed to a call).
@@ -553,13 +554,21 @@ bool ClauseProcessor::processInitializer(
             assign = &*wrapper->v;
       }
     }
-    genInitValueCB = [&, clause, assign](fir::FirOpBuilder &builder,
-                                         mlir::Location loc, mlir::Type type,
-                                         mlir::Value moldArg,
-                                         mlir::Value privArg) {
+    // A multi-type declare reduction carries one initializer instance per
+    // listed type (parallel to the combiner instances and typeNameList); select
+    // the one for the type being lowered (index instanceIdx). Single-type is
+    // index 0 of one. Capture instanceIdx by value: the callback runs later
+    // (during createDeclareReductionHelper), after this parameter's lifetime
+    // ends.
+    assert(instanceIdx < clause->v.size() &&
+           "initializer instance index out of range");
+    genInitValueCB = [&, clause, assign,
+                      instanceIdx](fir::FirOpBuilder &builder,
+                                   mlir::Location loc, mlir::Type type,
+                                   mlir::Value moldArg, mlir::Value privArg) {
       lower::SymMapScope scope(symMap);
       mlir::Value ompPrivVar;
-      const StylizedInstance &inst = clause->v.front();
+      const StylizedInstance &inst = clause->v[instanceIdx];
 
       for (const Object &object :
            std::get<StylizedInstance::Variables>(inst.t)) {
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h
index 1435af3e844e9..61c4db49c3877 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.h
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h
@@ -97,7 +97,8 @@ class ClauseProcessor {
   bool processInitializer(
       lower::SymMap &symMap,
       ReductionProcessor::GenInitValueCBTy &genInitValueCB,
-      const parser::OmpStylizedInstance *parserInitInstance = nullptr) const;
+      const parser::OmpStylizedInstance *parserInitInstance = nullptr,
+      unsigned instanceIdx = 0) const;
   bool processMergeable(mlir::omp::MergeableClauseOps &result) const;
   bool processNogroup(mlir::omp::NogroupClauseOps &result) const;
   bool processNotinbranch(mlir::omp::NotinbranchClauseOps &result) const;
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index d630d825062b7..b18a4f5b34e63 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -42,6 +42,7 @@
 #include "flang/Parser/openmp-utils.h"
 #include "flang/Parser/parse-tree.h"
 #include "flang/Parser/tools.h"
+#include "flang/Semantics/expression.h"
 #include "flang/Semantics/openmp-directive-sets.h"
 #include "flang/Semantics/openmp-utils.h"
 #include "flang/Semantics/tools.h"
@@ -4505,7 +4506,7 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
 
 static ReductionProcessor::GenCombinerCBTy processReductionCombiner(
     lower::AbstractConverter &converter, lower::SymMap &symTable,
-    semantics::SemanticsContext &semaCtx, const clause::Combiner &combiner,
+    semantics::SemanticsContext &semaCtx, const StylizedInstance &combinerInst,
     const parser::OmpStylizedInstance &parserInst) {
   // Extract the typed assignment from the parser-level instance, if
   // the combiner is an assignment statement (as opposed to a call).
@@ -4519,7 +4520,11 @@ static ReductionProcessor::GenCombinerCBTy processReductionCombiner(
         assign = &*wrapper->v;
   }
   ReductionProcessor::GenCombinerCBTy genCombinerCB;
-  const StylizedInstance &inst = combiner.v.front();
+  // Use the combiner instance for the type currently being lowered (one per
+  // listed type, in typeNameList order): a multi-type declare reduction carries
+  // a distinct stylized instance per type so omp_out/omp_in are re-typed for
+  // each. Single-type is index 0 of one.
+  const StylizedInstance &inst = combinerInst;
   semantics::SomeExpr evalExpr = std::get<StylizedInstance::Instance>(inst.t);
 
   genCombinerCB = [&, evalExpr, assign](fir::FirOpBuilder &builder,
@@ -4680,24 +4685,33 @@ static bool isSimpleReductionType(mlir::Type reductionType) {
   return false;
 }
 
-// Getting the type from a symbol compared to a DeclSpec is simpler since we do
-// not need to consider derived vs intrinsic types. Semantics is guaranteed to
-// generate these symbols.
+// Compute the reduction's element type from the given combiner instance's
+// stylized declaration symbol (omp_out), without checking whether lowering
+// supports it. A multi-type declare reduction carries one combiner instance per
+// listed type (typeNameList order), so the caller passes the instance for the
+// type being lowered. Shared by getReductionType (same-file, enforces support
+// with a TODO) and materializeOpenMPDeclareReductions (skips an unsupported
+// unused import via isSimpleReductionType instead of aborting the consumer).
+// Getting the type from that symbol compared to the declared type-list (a
+// DeclarationTypeSpec) is simpler since we do not need to consider derived vs
+// intrinsic types. Semantics is guaranteed to generate these symbols.
 static mlir::Type
-getReductionType(lower::AbstractConverter &converter,
-                 const parser::OmpReductionSpecifier &specifier) {
-  const auto &combinerExpression =
-      std::get<std::optional<parser::OmpCombinerExpression>>(specifier.t)
-          .value();
-  const parser::OmpStylizedInstance &combinerInstance =
-      combinerExpression.v.front();
+computeReductionType(lower::AbstractConverter &converter,
+                     const parser::OmpStylizedInstance &combinerInstance) {
   const std::list<parser::OmpStylizedDeclaration> &declList =
       std::get<std::list<parser::OmpStylizedDeclaration>>(combinerInstance.t);
   const parser::OmpStylizedDeclaration &decl = declList.front();
   const auto &name = std::get<parser::ObjectName>(decl.var.t);
   const auto &symbol = semantics::SymbolRef(*name.symbol);
-  mlir::Type reductionType = converter.genType(symbol);
+  return converter.genType(symbol);
+}
 
+// Return the reduction's element type, emitting a TODO if lowering does not
+// support it.
+static mlir::Type
+getReductionType(lower::AbstractConverter &converter,
+                 const parser::OmpStylizedInstance &combinerInstance) {
+  mlir::Type reductionType = computeReductionType(converter, combinerInstance);
   if (!isSimpleReductionType(reductionType))
     TODO(converter.getCurrentLocation(),
          "declare reduction currently only supports trivial types, "
@@ -4733,10 +4747,16 @@ appendCombiner(const parser::OmpDeclareReductionDirective &construct,
   llvm_unreachable("Expecting reduction combiner");
 }
 
-static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
-                   semantics::SemanticsContext &semaCtx,
-                   lower::pft::Evaluation &eval,
-                   const parser::OmpDeclareReductionDirective &construct) {
+// Lower a single declare-reduction directive. Serves both same-file lowering
+// (symOpt null) and separate-compilation materialization of an imported
+// reduction (symOpt is the source symbol from
+// materializeOpenMPDeclareReductions, with a fresh SymMap). Mirrors
+// genOpenMPDeclareMapperImpl.
+static void genOpenMPDeclareReductionImpl(
+    lower::AbstractConverter &converter, lower::SymMap &symTable,
+    semantics::SemanticsContext &semaCtx,
+    const parser::OmpDeclareReductionDirective &construct,
+    const semantics::Symbol *symOpt = nullptr) {
   if (semaCtx.langOptions().OpenMPSimd)
     return;
 
@@ -4774,6 +4794,10 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
       initExpr ? initExpr->v.begin()
                : std::list<parser::OmpStylizedInstance>::const_iterator{};
 
+  // Index of the type currently being lowered within the parallel per-type
+  // lists: typeNameList.v, the clause-level combiner instances (combiner.v),
+  // and the clause-level initializer instances. Single-type is index 0 of one.
+  unsigned instanceIdx = 0;
   for (const auto &typeSpec : typeNameList.v) {
     (void)typeSpec; // Currently unused
 
@@ -4781,7 +4805,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
            "Mismatched combiner instance count");
     const parser::OmpStylizedInstance &parserInst = *parserInstIt++;
 
-    mlir::Type reductionType = getReductionType(converter, specifier);
+    mlir::Type reductionType = getReductionType(converter, parserInst);
     bool isByRef = ReductionProcessor::doReductionByRef(reductionType);
     // Compute the canonical reduction name the same way
     // processReductionArguments does.
@@ -4802,41 +4826,54 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                           -> std::string {
                         // Directive side of the user-defined operator reduction
                         // naming contract (the clause side is in
-                        // ReductionProcessor::processReductionArguments).
-                        // opName.v.sym() is the reduction symbol
-                        // "op<spelling>". Only single-declaration, single-type
-                        // reductions are supported; otherwise emit a clean
-                        // TODO.
+                        // ReductionProcessor::processReductionArguments). Name
+                        // the op via getScopedUserReductionName from the
+                        // symbol's ultimate (name, owner) plus the per-type
+                        // suffix, byte-identical to the clause reference.
+                        // symOpt supplies the source symbol for separate
+                        // compilation, else opName.v.sym(). Runs once per
+                        // listed type (the enclosing loop walks typeNameList in
+                        // lockstep with the per-type combiner/initializer
+                        // instances), emitting one op per (operator, type);
+                        // single-type is N=1.
                         const semantics::Symbol &redSym =
-                            opName.v.sym()->GetUltimate();
+                            symOpt ? symOpt->GetUltimate()
+                                   : opName.v.sym()->GetUltimate();
                         const auto *userDetails =
                             redSym.detailsIf<semantics::UserReductionDetails>();
-                        if (!userDetails || typeNameList.v.size() != 1 ||
-                            userDetails->GetDeclList().size() != 1 ||
-                            userDetails->GetTypeList().size() != 1)
+                        if (!userDetails)
                           TODO(converter.getCurrentLocation(),
                                "OpenMP user-defined operator declare reduction "
-                               "with multiple declarations or multiple types");
+                               "without user reduction details");
                         return ReductionProcessor::getScopedUserReductionName(
-                            converter, redSym);
+                            converter, redSym, reductionType, isByRef);
                       },
                   },
                   defOp.u);
             },
             [&](const clause::ProcedureDesignator &pd) -> std::string {
-              // Qualify the name with the scope in which the user-defined
-              // reduction is declared so that reductions with the same name
-              // in different scopes produce distinct omp.declare_reduction ops.
-              const semantics::Symbol *sym = pd.v.sym();
-              std::string name = sym->name().ToString();
-              return converter.mangleName(name, sym->GetUltimate().owner());
+              // Directive side of the named-reduction naming contract (clause
+              // side in ReductionProcessor::processReductionArguments). Name
+              // the op via getScopedUserReductionName from the source symbol's
+              // ultimate (name, owner) plus the per-type suffix, byte-identical
+              // to the clause reference. symOpt supplies the source symbol for
+              // separate compilation, else pd.v.sym(); GetUltimate keeps the
+              // name stable across a plain `use`. reductionType is the declared
+              // element type (already canonical), so unlike the clause side it
+              // needs no namingType normalization.
+              const semantics::Symbol &redSym =
+                  (symOpt ? *symOpt : *pd.v.sym()).GetUltimate();
+              return ReductionProcessor::getScopedUserReductionName(
+                  converter, redSym, reductionType, isByRef);
             },
         },
         redOp.u);
 
+    assert(instanceIdx < combiner.v.size() &&
+           "Mismatched combiner instance count");
     ReductionProcessor::GenCombinerCBTy genCombinerCB =
-        processReductionCombiner(converter, symTable, semaCtx, combiner,
-                                 parserInst);
+        processReductionCombiner(converter, symTable, semaCtx,
+                                 combiner.v[instanceIdx], parserInst);
     const parser::OmpStylizedInstance *parserInitInst = nullptr;
     if (initExpr) {
       assert(parserInitInstIt != initExpr->v.end() &&
@@ -4860,7 +4897,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
 
     ReductionProcessor::GenInitValueCBTy genInitValueCB;
     ClauseProcessor cp(converter, semaCtx, clauses);
-    if (!cp.processInitializer(symTable, genInitValueCB, parserInitInst)) {
+    if (!cp.processInitializer(symTable, genInitValueCB, parserInitInst,
+                               instanceIdx)) {
       // No initializer clause provided. Per OpenMP, initialize as
       // default-initialized using the shared inline init helper.
       const semantics::DerivedTypeSpec *derivedTypeSpec = nullptr;
@@ -4913,9 +4951,22 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
         mlir::o...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/207494


More information about the flang-commits mailing list