[flang-commits] [flang] [flang][OpenMP] Fix component-level initializer in declare reduction (PR #195751)
via flang-commits
flang-commits at lists.llvm.org
Thu May 7 09:08:24 PDT 2026
https://github.com/MattPD updated https://github.com/llvm/llvm-project/pull/195751
>From 8546ad1fa621d0a42b659ac0c32bedb54d695d9c Mon Sep 17 00:00:00 2001
From: "Matt P. Dziubinski" <matt-p.dziubinski at hpe.com>
Date: Sat, 2 May 2026 12:31:13 -0500
Subject: [PATCH] [flang][OpenMP] Fix component-level initializer in declare
reduction
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. When the init value type does
not match the reduction variable type (indicating a component-level
assignment), use the assignment LHS to lower a proper hlfir.designate +
hlfir.assign to the correct component.
Fixes #184927 (with-initializer part; the without-initializer case
remains unsupported).
Assisted-by: Claude Opus 4.6.
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 39 +++++++++++++++---
flang/lib/Lower/OpenMP/ClauseProcessor.h | 3 +-
flang/lib/Lower/OpenMP/OpenMP.cpp | 20 ++++++++-
.../OpenMP/declare-reduction-finalizer.f90 | 3 +-
...eclare-reduction-initializer-component.f90 | 41 +++++++++++++++++++
5 files changed, 97 insertions(+), 9 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/declare-reduction-initializer-component.f90
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 1c39e90a922cf..4eb532acfbc0d 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,21 @@ 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.
+ 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);
+ hlfir::AssignOp::create(builder, loc, rhs, lhs);
+ 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 {
More information about the flang-commits
mailing list