[flang-commits] [flang] 0f3d9ce - [flang][OpenMP] Fix component-level initializer in declare reduction (#195751)

via flang-commits flang-commits at lists.llvm.org
Fri May 8 11:17:29 PDT 2026


Author: Matt
Date: 2026-05-08T13:17:23-05:00
New Revision: 0f3d9ce6ce2c1482dbc2c41af3d28ed17d4e8fe0

URL: https://github.com/llvm/llvm-project/commit/0f3d9ce6ce2c1482dbc2c41af3d28ed17d4e8fe0
DIFF: https://github.com/llvm/llvm-project/commit/0f3d9ce6ce2c1482dbc2c41af3d28ed17d4e8fe0.diff

LOG: [flang][OpenMP] Fix component-level initializer in declare reduction (#195751)

When a declare reduction initializer uses a component assignment such as
`initializer(omp_priv%member = 0)`, the lowering would store the scalar
RHS value (i32) directly to the whole derived-type reference, causing a
FIR verification error: `'fir.store' op store value type must match memory reference type`.

The root cause is that `MakeEvaluateExpr` extracts only the RHS
expression
from the `AssignmentStmt`, discarding the LHS component information. The
lowering callback then returns this scalar value which gets stored to
the
wrong type.

Fix this by mirroring the approach already used for combiner
expressions:
pass the parser-level `OmpStylizedInstance` to `processInitializer` so
the
callback can access the typed assignment and lower the full assignment
(both LHS and RHS), correctly handling component designators, function
calls on the RHS, and user-defined assignment.

Fixes #184927 (with-initializer part; the without-initializer case
remains unsupported).

Assisted-by: Claude Opus 4.6.

Co-authored-by: Matt P. Dziubinski <matt-p.dziubinski at hpe.com>

Added: 
    flang/test/Lower/OpenMP/declare-reduction-initializer-component.f90
    flang/test/Lower/OpenMP/declare-reduction-initializer-defined-assign.f90
    flang/test/Lower/OpenMP/declare-reduction-initializer-rhs-call.f90

Modified: 
    flang/lib/Lower/OpenMP/ClauseProcessor.cpp
    flang/lib/Lower/OpenMP/ClauseProcessor.h
    flang/lib/Lower/OpenMP/OpenMP.cpp
    flang/test/Lower/OpenMP/declare-reduction-finalizer.f90

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 1c39e90a922cf..5f5b4fe77f701 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -523,12 +523,26 @@ bool ClauseProcessor::processInclusive(
 }
 
 bool ClauseProcessor::processInitializer(
-    lower::SymMap &symMap,
-    ReductionProcessor::GenInitValueCBTy &genInitValueCB) const {
+    lower::SymMap &symMap, ReductionProcessor::GenInitValueCBTy &genInitValueCB,
+    const parser::OmpStylizedInstance *parserInitInstance) const {
   if (auto *clause = findUniqueClause<omp::clause::Initializer>()) {
-    genInitValueCB = [&, clause](fir::FirOpBuilder &builder, mlir::Location loc,
-                                 mlir::Type type, mlir::Value moldArg,
-                                 mlir::Value privArg) {
+    // Extract the typed assignment from the parser-level instance, if
+    // the initializer is an assignment statement (as opposed to a call).
+    const evaluate::Assignment *assign = nullptr;
+    if (parserInitInstance) {
+      const auto &instance = std::get<parser::OmpStylizedInstance::Instance>(
+          parserInitInstance->t);
+      if (const auto *assignStmt =
+              std::get_if<parser::AssignmentStmt>(&instance.u)) {
+        if (auto *wrapper = assignStmt->typedAssignment.get())
+          if (wrapper->v)
+            assign = &*wrapper->v;
+      }
+    }
+    genInitValueCB = [&, clause, assign](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();
@@ -592,6 +606,37 @@ bool ClauseProcessor::processInitializer(
                 return privVal;
               },
               [&](const auto &expr) -> mlir::Value {
+                // For by-ref reductions with a typed assignment, lower
+                // the full assignment (both LHS and RHS) directly.
+                // This handles both whole-variable (omp_priv = val) and
+                // component-level (omp_priv%member = val) initializers.
+                // Mirror the combiner pattern: dispatch on assign->u to
+                // handle both intrinsic and user-defined assignment.
+                if (privArg && assign) {
+                  lower::StatementContext assignCtx;
+                  hlfir::Entity rhs = lower::convertExprToHLFIR(
+                      loc, converter, assign->rhs, symMap, assignCtx);
+                  rhs = hlfir::loadTrivialScalar(loc, builder, rhs);
+                  hlfir::Entity lhs = lower::convertExprToHLFIR(
+                      loc, converter, assign->lhs, symMap, assignCtx);
+                  common::visit(
+                      common::visitors{
+                          [&](const evaluate::Assignment::Intrinsic &) {
+                            hlfir::AssignOp::create(builder, loc, rhs, lhs);
+                          },
+                          [&](const evaluate::ProcedureRef &procRef) {
+                            lower::convertUserDefinedAssignmentToHLFIR(
+                                loc, converter, procRef, lhs, rhs, symMap);
+                          },
+                          [&](const auto &) {
+                            llvm_unreachable("Unexpected assignment type in "
+                                             "reduction initializer");
+                          },
+                      },
+                      assign->u);
+                  assignCtx.finalizeAndPop();
+                  return mlir::Value{};
+                }
                 mlir::Value exprResult = fir::getBase(convertExprToValue(
                     loc, converter, initExpr, symMap, stmtCtx));
                 if (auto refType = llvm::dyn_cast<fir::ReferenceType>(

diff  --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h
index acf1068efb987..e138b4df30b71 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.h
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h
@@ -97,7 +97,8 @@ class ClauseProcessor {
                         mlir::omp::InclusiveClauseOps &result) const;
   bool processInitializer(
       lower::SymMap &symMap,
-      ReductionProcessor::GenInitValueCBTy &genInitValueCB) const;
+      ReductionProcessor::GenInitValueCBTy &genInitValueCB,
+      const parser::OmpStylizedInstance *parserInitInstance = nullptr) 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 88d28cf94b045..0ffc7bdae85b9 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4215,6 +4215,18 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
   assert(combinerExpr && "Expecting combiner expression");
   auto parserInstIt = combinerExpr->v.begin();
 
+  // Get the parser-level initializer expression (if present) so we can
+  // pass each parser::OmpStylizedInstance to processInitializer.
+  const parser::OmpInitializerExpression *initExpr = nullptr;
+  for (const auto &clause : construct.v.Clauses().v) {
+    initExpr = parser::omp::GetInitializerExpr(clause);
+    if (initExpr)
+      break;
+  }
+  auto parserInitInstIt =
+      initExpr ? initExpr->v.begin()
+               : std::list<parser::OmpStylizedInstance>::const_iterator{};
+
   for (const auto &typeSpec : typeNameList.v) {
     (void)typeSpec; // Currently unused
 
@@ -4257,7 +4269,13 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                                  parserInst);
     ReductionProcessor::GenInitValueCBTy genInitValueCB;
     ClauseProcessor cp(converter, semaCtx, clauses);
-    cp.processInitializer(symTable, genInitValueCB);
+    const parser::OmpStylizedInstance *parserInitInst = nullptr;
+    if (initExpr) {
+      assert(parserInitInstIt != initExpr->v.end() &&
+             "Mismatched initializer instance count");
+      parserInitInst = &*parserInitInstIt++;
+    }
+    cp.processInitializer(symTable, genInitValueCB, parserInitInst);
     mlir::Type redType =
         isByRef
             ? static_cast<mlir::Type>(fir::ReferenceType::get(reductionType))

diff  --git a/flang/test/Lower/OpenMP/declare-reduction-finalizer.f90 b/flang/test/Lower/OpenMP/declare-reduction-finalizer.f90
index 2ec34c446e793..22a653179ce2d 100644
--- a/flang/test/Lower/OpenMP/declare-reduction-finalizer.f90
+++ b/flang/test/Lower/OpenMP/declare-reduction-finalizer.f90
@@ -39,8 +39,7 @@ end module m1
 ! CHECK:          %[[PRIV_DECL:.*]]:2 = hlfir.declare %[[INIT_ARG1]] {uniq_name = "omp_priv"}
 ! CHECK:          %[[INIT_ADDR:.*]] = fir.address_of(@_QQro._QMm1Tt.0)
 ! CHECK:          %[[INIT_DECL:.*]]:2 = hlfir.declare %[[INIT_ADDR]]
-! CHECK:          %[[INIT_VAL:.*]] = fir.load %[[INIT_DECL]]#0
-! CHECK:          fir.store %[[INIT_VAL]] to %[[INIT_ARG1]]
+! CHECK:          hlfir.assign %[[INIT_DECL]]#0 to %[[PRIV_DECL]]#0
 ! CHECK:          omp.yield(%[[INIT_ARG1]] :
 !
 ! -- combiner region

diff  --git a/flang/test/Lower/OpenMP/declare-reduction-initializer-component.f90 b/flang/test/Lower/OpenMP/declare-reduction-initializer-component.f90
new file mode 100644
index 0000000000000..b42fa610d17e0
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-reduction-initializer-component.f90
@@ -0,0 +1,41 @@
+! Test component-level initializer in declare reduction for derived types.
+! Verifies that `initializer(omp_priv%member = 0)` correctly lowers to
+! a component designate + assign (hlfir.designate + hlfir.assign), rather
+! than storing the scalar directly to the whole derived-type reference.
+!
+! This is a regression test for https://github.com/llvm/llvm-project/issues/184927
+
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+subroutine test_component_init()
+  implicit none
+  type :: t
+     integer :: member
+  end type t
+  integer :: i
+  !$omp declare reduction(add_member : t : &
+  !$omp&   omp_out%member = omp_out%member + omp_in%member) &
+  !$omp&   initializer(omp_priv%member = 0)
+  type(t) :: x
+  x%member = 0
+  !$omp parallel do reduction(add_member : x) num_threads(2)
+  do i = 1, 10
+     x%member = x%member + 1
+  end do
+  !$omp end parallel do
+end subroutine
+
+!CHECK: omp.declare_reduction @add_member : !fir.ref<!fir.type<_QFtest_component_initTt{member:i32}>>
+!CHECK-SAME: alloc {
+!CHECK:   %[[ALLOCA:.*]] = fir.alloca !fir.type<_QFtest_component_initTt{member:i32}>
+!CHECK:   omp.yield(%[[ALLOCA]] : !fir.ref<!fir.type<_QFtest_component_initTt{member:i32}>>)
+!CHECK: } init {
+!CHECK: ^bb0(%[[INIT_ARG0:.*]]: !fir.ref<!fir.type<_QFtest_component_initTt{member:i32}>>,
+!CHECK-SAME: %[[INIT_ARG1:.*]]: !fir.ref<!fir.type<_QFtest_component_initTt{member:i32}>>):
+!CHECK:   %[[OMP_ORIG:.*]]:2 = hlfir.declare %[[INIT_ARG0]] {uniq_name = "omp_orig"}
+!CHECK:   %[[OMP_PRIV:.*]]:2 = hlfir.declare %[[INIT_ARG1]] {uniq_name = "omp_priv"}
+!CHECK:   %[[ZERO:.*]] = arith.constant 0 : i32
+!CHECK:   %[[MEMBER:.*]] = hlfir.designate %[[OMP_PRIV]]#0{"member"} : (!fir.ref<!fir.type<_QFtest_component_initTt{member:i32}>>) -> !fir.ref<i32>
+!CHECK:   hlfir.assign %[[ZERO]] to %[[MEMBER]] : i32, !fir.ref<i32>
+!CHECK:   omp.yield(%[[INIT_ARG1]] : !fir.ref<!fir.type<_QFtest_component_initTt{member:i32}>>)
+!CHECK: } combiner {

diff  --git a/flang/test/Lower/OpenMP/declare-reduction-initializer-defined-assign.f90 b/flang/test/Lower/OpenMP/declare-reduction-initializer-defined-assign.f90
new file mode 100644
index 0000000000000..bdf48626fd2b3
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-reduction-initializer-defined-assign.f90
@@ -0,0 +1,60 @@
+! Test user-defined assignment in declare reduction initializer.
+! Verifies that `initializer(omp_priv = t(1))` correctly dispatches to the
+! user-defined `assignment(=)` subroutine, not intrinsic assignment.
+!
+! This is a regression test for https://github.com/llvm/llvm-project/issues/184927
+
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+module m_defined_assign
+  implicit none
+  type :: t
+     integer :: val = -999
+  end type t
+
+  interface assignment(=)
+     module procedure custom_assign
+  end interface
+
+contains
+  subroutine custom_assign(lhs, rhs)
+    type(t), intent(out) :: lhs
+    type(t), intent(in)  :: rhs
+    lhs%val = rhs%val * 10
+  end subroutine
+end module
+
+subroutine test_defined_assign_init()
+  use m_defined_assign
+  implicit none
+  integer :: i
+  type(t) :: x
+
+  !$omp declare reduction(add_t : t : omp_out%val = omp_out%val + omp_in%val) &
+  !$omp&   initializer(omp_priv = t(1))
+
+  x = t(0)
+  !$omp parallel do reduction(add_t : x) num_threads(2)
+  do i = 1, 2
+     x%val = x%val + 1
+  end do
+  !$omp end parallel do
+end subroutine
+
+!CHECK: omp.declare_reduction @add_t :
+!CHECK-SAME: alloc {
+!CHECK:   %[[ALLOCA:.*]] = fir.alloca
+!CHECK:   omp.yield(%[[ALLOCA]] :
+!CHECK: } init {
+!CHECK: ^bb0(%[[INIT_ARG0:.*]]: !fir.ref<!fir.type<_QMm_defined_assignTt{val:i32}>>,
+!CHECK-SAME: %[[INIT_ARG1:.*]]: !fir.ref<!fir.type<_QMm_defined_assignTt{val:i32}>>):
+!CHECK:   %[[OMP_ORIG:.*]]:2 = hlfir.declare %[[INIT_ARG0]] {uniq_name = "omp_orig"}
+!CHECK:   %[[OMP_PRIV:.*]]:2 = hlfir.declare %[[INIT_ARG1]] {uniq_name = "omp_priv"}
+!CHECK:   %[[INIT_ADDR:.*]] = fir.address_of(@_QQro._QMm_defined_assignTt.0)
+!CHECK:   %[[INIT_DECL:.*]]:2 = hlfir.declare %[[INIT_ADDR]]
+!CHECK:   %[[AS_EXPR:.*]] = hlfir.as_expr %[[INIT_DECL]]#0
+!CHECK:   %[[ASSOC:.*]]:3 = hlfir.associate %[[AS_EXPR]] {adapt.valuebyref}
+!CHECK:   fir.call @_QMm_defined_assignPcustom_assign(%[[OMP_PRIV]]#0, %[[ASSOC]]#0)
+!CHECK:   hlfir.end_associate %[[ASSOC]]#1, %[[ASSOC]]#2
+!CHECK:   omp.yield(%[[INIT_ARG1]] :
+!CHECK: } combiner {

diff  --git a/flang/test/Lower/OpenMP/declare-reduction-initializer-rhs-call.f90 b/flang/test/Lower/OpenMP/declare-reduction-initializer-rhs-call.f90
new file mode 100644
index 0000000000000..7d409b27464e2
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-reduction-initializer-rhs-call.f90
@@ -0,0 +1,52 @@
+! Test that a function call on the RHS of a component-level initializer in
+! declare reduction is correctly lowered through the assignment path (not
+! the ProcedureRef/subroutine path). Verifies that the init region contains
+! a call to the function, followed by a component designate and assign.
+!
+! This is a regression test for https://github.com/llvm/llvm-project/issues/184927
+
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+module m
+  implicit none
+  type :: t
+     integer :: member
+  end type t
+contains
+  function init_val() result(res)
+    integer :: res
+    res = 42
+  end function
+end module
+
+subroutine test_rhs_call()
+  use m
+  implicit none
+  integer :: i
+  type(t) :: x
+
+  !$omp declare reduction(add_t : t : omp_out%member = omp_out%member + omp_in%member) &
+  !$omp&   initializer(omp_priv%member = init_val())
+
+  x%member = 0
+  !$omp parallel do reduction(add_t : x) num_threads(2)
+  do i = 1, 2
+     x%member = x%member + 1
+  end do
+  !$omp end parallel do
+end subroutine
+
+!CHECK: omp.declare_reduction @add_t :
+!CHECK-SAME: alloc {
+!CHECK:   %[[ALLOCA:.*]] = fir.alloca !fir.type<_QMmTt{member:i32}>
+!CHECK:   omp.yield(%[[ALLOCA]] :
+!CHECK: } init {
+!CHECK: ^bb0(%[[INIT_ARG0:.*]]: !fir.ref<!fir.type<_QMmTt{member:i32}>>,
+!CHECK-SAME: %[[INIT_ARG1:.*]]: !fir.ref<!fir.type<_QMmTt{member:i32}>>):
+!CHECK:   %[[OMP_ORIG:.*]]:2 = hlfir.declare %[[INIT_ARG0]] {uniq_name = "omp_orig"}
+!CHECK:   %[[OMP_PRIV:.*]]:2 = hlfir.declare %[[INIT_ARG1]] {uniq_name = "omp_priv"}
+!CHECK:   %[[CALL:.*]] = fir.call @_QMmPinit_val() {{.*}} : () -> i32
+!CHECK:   %[[MEMBER:.*]] = hlfir.designate %[[OMP_PRIV]]#0{"member"} : (!fir.ref<!fir.type<_QMmTt{member:i32}>>) -> !fir.ref<i32>
+!CHECK:   hlfir.assign %[[CALL]] to %[[MEMBER]] : i32, !fir.ref<i32>
+!CHECK:   omp.yield(%[[INIT_ARG1]] : !fir.ref<!fir.type<_QMmTt{member:i32}>>)
+!CHECK: } combiner {


        


More information about the flang-commits mailing list