[flang-commits] [flang] [flang][OpenMP] Diagnose task reduction array sections and elements (PR #215997)
Tom Eccles via flang-commits
flang-commits at lists.llvm.org
Mon Sep 7 05:58:44 PDT 2026
https://github.com/tblah updated https://github.com/llvm/llvm-project/pull/215997
>From 00c5d450d4a5678a0c951b3acf9dd7dc286e574b Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Wed, 12 Aug 2026 15:27:01 +0100
Subject: [PATCH 01/20] [flang][OpenMP] Diagnose privatized array section
reductions
Contiguous array sections can hit the same task and taskloop
base-symbol rebinding problem as array elements. Diagnose these
cases until reduction object identity is preserved through region
binding.
Check only symbols that become delayed-private block arguments so
eager taskloop privatization remains supported. Cover task and
taskloop section diagnostics.
This issue was previously reported here:
https://github.com/llvm/llvm-project/pull/215617#discussion_r3766204295
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 35 ++++++------
...ction-array-element-task-privatization.f90 | 53 ++++++++++++++++---
2 files changed, 64 insertions(+), 24 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 7503d33c8df38..6fccf71db3e51 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -156,9 +156,9 @@ makeObjects(llvm::ArrayRef<const semantics::Symbol *> syms) {
return objects;
}
-static bool hasPrivatizedArrayElementReduction(
+static bool hasPrivatizedArrayReductionObject(
llvm::ArrayRef<Object> reductionObjects,
- const llvm::SetVector<const semantics::Symbol *> &privatizedSymbols) {
+ llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols) {
for (const Object &object : reductionObjects) {
if (!object.sym() || !object.ref())
continue;
@@ -167,10 +167,7 @@ static bool hasPrivatizedArrayElementReduction(
if (!dataRef)
continue;
const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u);
- if (!arrayRef ||
- llvm::any_of(arrayRef->subscript(), [](const auto &subscript) {
- return std::holds_alternative<evaluate::Triplet>(subscript.u);
- }))
+ if (!arrayRef)
continue;
const semantics::Symbol &ultimate = object.sym()->GetUltimate();
@@ -4350,10 +4347,10 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
/*useDelayedPrivatization=*/true, symTable);
dsp.processStep1(&clauseOps);
- if (hasPrivatizedArrayElementReduction(inReductionObjects,
- dsp.getAllSymbolsToPrivatize()))
- TODO(loc, "TASK construct with IN_REDUCTION of an array element whose "
- "base array is privatized");
+ if (hasPrivatizedArrayReductionObject(inReductionObjects,
+ dsp.getDelayedPrivSymbols()))
+ TODO(loc, "TASK construct with IN_REDUCTION of an array element or section "
+ "whose base array is privatized");
ObjectEntryBlockArgs taskArgs;
taskArgs.priv.objects = makeObjects(dsp.getDelayedPrivSymbols());
@@ -4973,14 +4970,16 @@ static mlir::omp::TaskloopContextOp genStandaloneTaskloop(
enableDelayedPrivatization, symTable);
dsp.processStep1(&taskloopClauseOps);
- if (hasPrivatizedArrayElementReduction(inReductionObjects,
- dsp.getAllSymbolsToPrivatize()))
- TODO(loc, "TASKLOOP construct with IN_REDUCTION of an array element whose "
- "base array is privatized");
- if (hasPrivatizedArrayElementReduction(reductionObjects,
- dsp.getAllSymbolsToPrivatize()))
- TODO(loc, "TASKLOOP construct with REDUCTION of an array element whose "
- "base array is privatized");
+ if (hasPrivatizedArrayReductionObject(inReductionObjects,
+ dsp.getDelayedPrivSymbols()))
+ TODO(loc,
+ "TASKLOOP construct with IN_REDUCTION of an array element or section "
+ "whose base array is privatized");
+ if (hasPrivatizedArrayReductionObject(reductionObjects,
+ dsp.getDelayedPrivSymbols()))
+ TODO(loc,
+ "TASKLOOP construct with REDUCTION of an array element or section "
+ "whose base array is privatized");
mlir::omp::LoopNestOperands loopNestClauseOps;
llvm::SmallVector<const semantics::Symbol *> iv;
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 770f309d1d160..bdce803fed4d5 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -5,14 +5,27 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION-SECTION
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-in-bbc.mlir %t/taskloop-in.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-in-fc1.mlir %t/taskloop-in.f90
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-section-bbc.mlir %t/taskloop-reduction-section.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-section-fc1.mlir %t/taskloop-reduction-section.f90
-! An array element in a task reduction and the implicitly firstprivate base
-! array are represented by separate block arguments. Reject these constructs
-! until lowering can bind references to the correct argument.
+! An array element or section in a task reduction and the implicitly
+! firstprivate base array are represented by separate block arguments. Reject
+! these constructs until lowering can bind references to the correct argument.
-! TASK: not yet implemented: TASK construct with IN_REDUCTION of an array element whose base array is privatized
-! TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element whose base array is privatized
-! TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element whose base array is privatized
+! TASK: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
+! TASK-SECTION: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
!--- task.f90
subroutine task_reduction_element(a)
@@ -24,6 +37,34 @@ subroutine task_reduction_element(a)
!$omp end taskgroup
end subroutine
+!--- task-section.f90
+subroutine task_reduction_section(a)
+ integer :: a(4)
+ !$omp taskgroup task_reduction(+: a(2:3))
+ !$omp task in_reduction(+: a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
+!--- taskloop-in-section.f90
+subroutine taskloop_in_reduction_section(a, n)
+ integer :: a(4), n
+ !$omp taskloop in_reduction(+: a(2:3))
+ do i = 1, n
+ a(2:3) = a(2:3) + i
+ end do
+end subroutine
+
+!--- taskloop-reduction-section.f90
+subroutine taskloop_reduction_section(a, n)
+ integer :: a(4), n
+ !$omp taskloop reduction(+: a(2:3))
+ do i = 1, n
+ a(2:3) = a(2:3) + i
+ end do
+end subroutine
+
!--- taskloop-in.f90
subroutine taskloop_in_reduction_element(a, n)
integer :: a(4), n
>From bfa08272a7b236b8cc634ee3eaf5cc385292c241 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Fri, 14 Aug 2026 17:43:52 +0100
Subject: [PATCH 02/20] [flang][OpenMP] Preserve eager reduction diagnostics
Keep diagnosing taskloop array-element reductions when eager
privatization is selected. Restrict the relaxed eager behavior to array
sections while delayed privatization continues to diagnose both elements
and sections.
Extend the regression matrix to cover both taskloop reduction clause
forms in eager mode and retain positive eager array-section coverage.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 48 ++++++++++++++-----
...ction-array-element-task-privatization.f90 | 14 ++++--
2 files changed, 45 insertions(+), 17 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 6fccf71db3e51..d1c2af93ab4be 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -158,7 +158,8 @@ makeObjects(llvm::ArrayRef<const semantics::Symbol *> syms) {
static bool hasPrivatizedArrayReductionObject(
llvm::ArrayRef<Object> reductionObjects,
- llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols) {
+ llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols,
+ bool includeArraySections) {
for (const Object &object : reductionObjects) {
if (!object.sym() || !object.ref())
continue;
@@ -167,7 +168,11 @@ static bool hasPrivatizedArrayReductionObject(
if (!dataRef)
continue;
const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u);
- if (!arrayRef)
+ if (!arrayRef ||
+ (!includeArraySections &&
+ llvm::any_of(arrayRef->subscript(), [](const auto &subscript) {
+ return std::holds_alternative<evaluate::Triplet>(subscript.u);
+ })))
continue;
const semantics::Symbol &ultimate = object.sym()->GetUltimate();
@@ -4348,7 +4353,8 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
dsp.processStep1(&clauseOps);
if (hasPrivatizedArrayReductionObject(inReductionObjects,
- dsp.getDelayedPrivSymbols()))
+ dsp.getDelayedPrivSymbols(),
+ /*includeArraySections=*/true))
TODO(loc, "TASK construct with IN_REDUCTION of an array element or section "
"whose base array is privatized");
@@ -4970,16 +4976,32 @@ static mlir::omp::TaskloopContextOp genStandaloneTaskloop(
enableDelayedPrivatization, symTable);
dsp.processStep1(&taskloopClauseOps);
- if (hasPrivatizedArrayReductionObject(inReductionObjects,
- dsp.getDelayedPrivSymbols()))
- TODO(loc,
- "TASKLOOP construct with IN_REDUCTION of an array element or section "
- "whose base array is privatized");
- if (hasPrivatizedArrayReductionObject(reductionObjects,
- dsp.getDelayedPrivSymbols()))
- TODO(loc,
- "TASKLOOP construct with REDUCTION of an array element or section "
- "whose base array is privatized");
+ llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols =
+ enableDelayedPrivatization ? dsp.getDelayedPrivSymbols()
+ : dsp.getAllSymbolsToPrivatize().getArrayRef();
+ if (hasPrivatizedArrayReductionObject(
+ inReductionObjects, privatizedSymbols,
+ /*includeArraySections=*/enableDelayedPrivatization)) {
+ if (enableDelayedPrivatization)
+ TODO(loc, "TASKLOOP construct with IN_REDUCTION of an array element or "
+ "section whose base array is privatized");
+ else
+ TODO(loc,
+ "TASKLOOP construct with IN_REDUCTION of an array element whose "
+ "base array is privatized");
+ }
+ if (hasPrivatizedArrayReductionObject(
+ reductionObjects, privatizedSymbols,
+ /*includeArraySections=*/enableDelayedPrivatization)) {
+ if (enableDelayedPrivatization)
+ TODO(loc,
+ "TASKLOOP construct with REDUCTION of an array element or section "
+ "whose base array is privatized");
+ else
+ TODO(loc,
+ "TASKLOOP construct with REDUCTION of an array element whose base "
+ "array is privatized");
+ }
mlir::omp::LoopNestOperands loopNestClauseOps;
llvm::SmallVector<const semantics::Symbol *> iv;
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index bdce803fed4d5..7cf176b1d64b2 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -11,10 +11,14 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION-SECTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION-SECTION
-! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-in-bbc.mlir %t/taskloop-in.f90
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-in-fc1.mlir %t/taskloop-in.f90
-! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-section-bbc.mlir %t/taskloop-reduction-section.f90
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-section-fc1.mlir %t/taskloop-reduction-section.f90
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-in.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-IN
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-in.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-IN
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-reduction.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-REDUCTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-reduction.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-REDUCTION
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-in-section-bbc.mlir %t/taskloop-in-section.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-in-section-fc1.mlir %t/taskloop-in-section.f90
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-reduction-section-bbc.mlir %t/taskloop-reduction-section.f90
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-reduction-section-fc1.mlir %t/taskloop-reduction-section.f90
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -26,6 +30,8 @@
! TASK-SECTION: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
+! EAGER-TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element whose base array is privatized
+! EAGER-TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element whose base array is privatized
!--- task.f90
subroutine task_reduction_element(a)
>From a599a8f04162b10a56b41fc36c708f57a9bf5c2f Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 17 Aug 2026 10:58:15 +0100
Subject: [PATCH 03/20] Diagnose eager array section reductions
Keep the taskloop reduction diagnostic for array sections when eager
privatization is selected. Eager lowering otherwise creates a reduction
for the whole base array instead of one reduction per section element.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 43 +++++--------------
...ction-array-element-task-privatization.f90 | 28 +++++++++---
2 files changed, 33 insertions(+), 38 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index d1c2af93ab4be..3a979032968fd 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -158,8 +158,7 @@ makeObjects(llvm::ArrayRef<const semantics::Symbol *> syms) {
static bool hasPrivatizedArrayReductionObject(
llvm::ArrayRef<Object> reductionObjects,
- llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols,
- bool includeArraySections) {
+ llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols) {
for (const Object &object : reductionObjects) {
if (!object.sym() || !object.ref())
continue;
@@ -168,11 +167,7 @@ static bool hasPrivatizedArrayReductionObject(
if (!dataRef)
continue;
const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u);
- if (!arrayRef ||
- (!includeArraySections &&
- llvm::any_of(arrayRef->subscript(), [](const auto &subscript) {
- return std::holds_alternative<evaluate::Triplet>(subscript.u);
- })))
+ if (!arrayRef)
continue;
const semantics::Symbol &ultimate = object.sym()->GetUltimate();
@@ -4353,8 +4348,7 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
dsp.processStep1(&clauseOps);
if (hasPrivatizedArrayReductionObject(inReductionObjects,
- dsp.getDelayedPrivSymbols(),
- /*includeArraySections=*/true))
+ dsp.getDelayedPrivSymbols()))
TODO(loc, "TASK construct with IN_REDUCTION of an array element or section "
"whose base array is privatized");
@@ -4979,29 +4973,14 @@ static mlir::omp::TaskloopContextOp genStandaloneTaskloop(
llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols =
enableDelayedPrivatization ? dsp.getDelayedPrivSymbols()
: dsp.getAllSymbolsToPrivatize().getArrayRef();
- if (hasPrivatizedArrayReductionObject(
- inReductionObjects, privatizedSymbols,
- /*includeArraySections=*/enableDelayedPrivatization)) {
- if (enableDelayedPrivatization)
- TODO(loc, "TASKLOOP construct with IN_REDUCTION of an array element or "
- "section whose base array is privatized");
- else
- TODO(loc,
- "TASKLOOP construct with IN_REDUCTION of an array element whose "
- "base array is privatized");
- }
- if (hasPrivatizedArrayReductionObject(
- reductionObjects, privatizedSymbols,
- /*includeArraySections=*/enableDelayedPrivatization)) {
- if (enableDelayedPrivatization)
- TODO(loc,
- "TASKLOOP construct with REDUCTION of an array element or section "
- "whose base array is privatized");
- else
- TODO(loc,
- "TASKLOOP construct with REDUCTION of an array element whose base "
- "array is privatized");
- }
+ if (hasPrivatizedArrayReductionObject(inReductionObjects, privatizedSymbols))
+ TODO(loc,
+ "TASKLOOP construct with IN_REDUCTION of an array element or section "
+ "whose base array is privatized");
+ if (hasPrivatizedArrayReductionObject(reductionObjects, privatizedSymbols))
+ TODO(loc,
+ "TASKLOOP construct with REDUCTION of an array element or section "
+ "whose base array is privatized");
mlir::omp::LoopNestOperands loopNestClauseOps;
llvm::SmallVector<const semantics::Symbol *> iv;
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 7cf176b1d64b2..fa9b6fbd38f1d 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -15,10 +15,12 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-in.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-IN
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-reduction.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-REDUCTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-reduction.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-REDUCTION
-! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-in-section-bbc.mlir %t/taskloop-in-section.f90
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-in-section-fc1.mlir %t/taskloop-in-section.f90
-! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o %t/eager-reduction-section-bbc.mlir %t/taskloop-reduction-section.f90
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o %t/eager-reduction-section-fc1.mlir %t/taskloop-reduction-section.f90
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-in-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-IN-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-in-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-IN-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-REDUCTION-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-REDUCTION-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-udr-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-UDR-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-udr-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-UDR-SECTION
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -30,8 +32,11 @@
! TASK-SECTION: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
-! EAGER-TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element whose base array is privatized
-! EAGER-TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element whose base array is privatized
+! EAGER-TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
+! EAGER-TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
+! EAGER-TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
+! EAGER-TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
+! EAGER-TASKLOOP-UDR-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
!--- task.f90
subroutine task_reduction_element(a)
@@ -71,6 +76,17 @@ subroutine taskloop_reduction_section(a, n)
end do
end subroutine
+!--- taskloop-udr-section.f90
+subroutine taskloop_udr_section(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp taskloop reduction(myred : a(2:3))
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ end do
+end subroutine
+
!--- taskloop-in.f90
subroutine taskloop_in_reduction_element(a, n)
integer :: a(4), n
>From a935448ac95247dd631c6710c171fac953a75c0e Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 18 Aug 2026 10:45:11 +0100
Subject: [PATCH 04/20] Lower full array reduction sections
A full-extent array section such as a(:) uses the same descriptor as its
base array during reduction lowering. Data-sharing analysis nevertheless
marked the base as implicitly firstprivate. This produced a duplicate binding
or triggered the array-section TODO diagnostic.
Recognize omitted-bound, unit-stride sections on reduction clauses. Omit the
redundant implicit firstprivate capture while keeping element and partial
section handling unchanged. Cover task and taskloop reductions, including
user-defined reductions and both taskloop privatization modes.
Assisted-by: Codex
---
.../lib/Lower/OpenMP/DataSharingProcessor.cpp | 47 ++++++++++
flang/lib/Lower/OpenMP/DataSharingProcessor.h | 2 +
...-array-full-section-task-privatization.f90 | 87 +++++++++++++++++++
3 files changed, 136 insertions(+)
create mode 100644 flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 1d39c1a8d4b77..6aae152a977b4 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -13,6 +13,8 @@
#include "DataSharingProcessor.h"
#include "Utils.h"
+#include "flang/Evaluate/fold.h"
+#include "flang/Evaluate/tools.h"
#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Support/PrivateReductionUtils.h"
@@ -32,6 +34,29 @@
#include "llvm/Frontend/OpenMP/OMP.h"
#include <variant>
+namespace {
+static bool isWholeArraySection(const Fortran::lower::omp::Object &object) {
+ if (!object.ref())
+ return false;
+
+ std::optional<Fortran::evaluate::DataRef> dataRef =
+ Fortran::evaluate::ExtractDataRef(*object.ref());
+ if (!dataRef)
+ return false;
+
+ const auto *arrayRef = std::get_if<Fortran::evaluate::ArrayRef>(&dataRef->u);
+ if (!arrayRef)
+ return false;
+
+ return llvm::all_of(
+ arrayRef->subscript(), [](const Fortran::evaluate::Subscript &sub) {
+ const auto *triplet = std::get_if<Fortran::evaluate::Triplet>(&sub.u);
+ return triplet && !triplet->GetLower() && !triplet->GetUpper() &&
+ Fortran::evaluate::ToInt64(triplet->GetStride()) == 1;
+ });
+}
+} // namespace
+
namespace Fortran {
namespace lower {
namespace omp {
@@ -264,6 +289,13 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
return false;
};
+ auto collectWholeArrayReductionSymbols = [&](const auto &reductionClause) {
+ const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
+ for (const Object &object : objects)
+ if (object.sym() && isWholeArraySection(object))
+ wholeArrayReductionSymbols.insert(&object.sym()->GetUltimate());
+ };
+
for (const omp::Clause &clause : clauses) {
if (const auto &privateClause =
std::get_if<omp::clause::Private>(&clause.u)) {
@@ -295,6 +327,15 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
} else {
collectOmpObjectListSymbol(objects, explicitlyPrivatizedSymbols);
}
+ } else if (const auto *inReductionClause =
+ std::get_if<omp::clause::InReduction>(&clause.u)) {
+ collectWholeArrayReductionSymbols(*inReductionClause);
+ } else if (const auto *reductionClause =
+ std::get_if<omp::clause::Reduction>(&clause.u)) {
+ collectWholeArrayReductionSymbols(*reductionClause);
+ } else if (const auto *taskReductionClause =
+ std::get_if<omp::clause::TaskReduction>(&clause.u)) {
+ collectWholeArrayReductionSymbols(*taskReductionClause);
}
}
@@ -538,6 +579,12 @@ void DataSharingProcessor::collectPrivatizedSymbols(
return false;
if (collectImplicit) {
+ // A full-extent section is lowered through the same descriptor as its
+ // base array. Do not create a second implicit firstprivate descriptor;
+ // the reduction region argument must be the binding used in the body.
+ if (wholeArrayReductionSymbols.contains(&sym->GetUltimate()))
+ return false;
+
// If we're a combined construct with a target region, implicit
// firstprivate captures, should only belong to the target region
// and not be added/captured by later directives. Parallel regions
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.h b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
index 557584c21123f..dcb09c20c63f8 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.h
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
@@ -95,6 +95,8 @@ class DataSharingProcessor {
llvm::SmallVector<mlir::Value> loopIVs;
// Symbols in private, firstprivate, and/or lastprivate clauses.
llvm::SetVector<const semantics::Symbol *> explicitlyPrivatizedSymbols;
+ // Base symbols of reduction objects written as full-extent sections.
+ llvm::SetVector<const semantics::Symbol *> wholeArrayReductionSymbols;
llvm::SetVector<const semantics::Symbol *> defaultSymbols;
llvm::SetVector<const semantics::Symbol *> allPrivatizedSymbols;
llvm::SetVector<const semantics::Symbol *> conditionalLastPrivatizedSymbols;
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
new file mode 100644
index 0000000000000..948f9f9d8793c
--- /dev/null
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -0,0 +1,87 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task.f90 | FileCheck %s --check-prefix=TASK --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
+
+! A full-extent section uses the same descriptor as its base array. Check that
+! it is bound only to the reduction argument, rather than also being captured
+! as an implicit firstprivate object.
+
+! TASK-LABEL: func.func @_QPtask_full_section
+! TASK: omp.taskgroup task_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[TASKGROUP_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK: %[[TASKGROUP_DECL:.*]]:2 = hlfir.declare %[[TASKGROUP_ARG]]
+! TASK: omp.task in_reduction(byref @add_reduction_byref_box_4xi32 %[[TASKGROUP_DECL]]#0 -> %[[TASK_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK: %[[TASK_DECL:.*]]:2 = hlfir.declare %[[TASK_ARG]]
+! TASK: %[[TASK_BOX:.*]] = fir.load %[[TASK_DECL]]#0
+! TASK: %[[TASK_SECTION:.*]] = hlfir.designate %[[TASK_BOX]]
+! TASK: hlfir.elemental
+! TASK: %[[TASK_ELEMENT:.*]] = hlfir.designate %[[TASK_SECTION]]
+! TASK: %[[TASK_VALUE:.*]] = fir.load %[[TASK_ELEMENT]]
+! TASK: arith.addi %[[TASK_VALUE]]
+
+! TASKLOOP-LABEL: func.func @_QPtaskloop_in_full_section
+! TASKLOOP: omp.taskloop.context in_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[IN_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASKLOOP: %[[IN_DECL:.*]]:2 = hlfir.declare %[[IN_ARG]]
+! TASKLOOP: %[[IN_BOX:.*]] = fir.load %[[IN_DECL]]#0
+! TASKLOOP: %[[IN_SECTION:.*]] = hlfir.designate %[[IN_BOX]]
+! TASKLOOP: hlfir.elemental
+! TASKLOOP: %[[IN_ELEMENT:.*]] = hlfir.designate %[[IN_SECTION]]
+! TASKLOOP: %[[IN_VALUE:.*]] = fir.load %[[IN_ELEMENT]]
+! TASKLOOP: arith.addi %[[IN_VALUE]]
+
+! TASKLOOP-LABEL: func.func @_QPtaskloop_reduction_full_section
+! TASKLOOP: omp.taskloop.context {{.*}}reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[RED_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASKLOOP: %[[RED_DECL:.*]]:2 = hlfir.declare %[[RED_ARG]]
+! TASKLOOP: %[[RED_BOX:.*]] = fir.load %[[RED_DECL]]#0
+! TASKLOOP: %[[RED_SECTION:.*]] = hlfir.designate %[[RED_BOX]]
+! TASKLOOP: hlfir.elemental
+! TASKLOOP: %[[RED_ELEMENT:.*]] = hlfir.designate %[[RED_SECTION]]
+! TASKLOOP: %[[RED_VALUE:.*]] = fir.load %[[RED_ELEMENT]]
+! TASKLOOP: arith.addi %[[RED_VALUE]]
+
+! TASKLOOP-LABEL: func.func @_QPtaskloop_udr_full_section
+! TASKLOOP: omp.taskloop.context {{.*}}reduction(byref @_QQFtaskloop_udr_full_sectionmyred_byref_box_4xi32 {{.*}} -> %[[UDR_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASKLOOP: %[[UDR_DECL:.*]]:2 = hlfir.declare %[[UDR_ARG]]
+! TASKLOOP: %[[UDR_BOX:.*]] = fir.load %[[UDR_DECL]]#0
+! TASKLOOP: %[[UDR_SECTION:.*]] = hlfir.designate %[[UDR_BOX]]
+! TASKLOOP: hlfir.elemental
+! TASKLOOP: %[[UDR_ELEMENT:.*]] = hlfir.designate %[[UDR_SECTION]]
+! TASKLOOP: %[[UDR_VALUE:.*]] = fir.load %[[UDR_ELEMENT]]
+! TASKLOOP: arith.addi %[[UDR_VALUE]]
+
+!--- task.f90
+subroutine task_full_section(a)
+ integer :: a(-2:1)
+ !$omp taskgroup task_reduction(+: a(:))
+ !$omp task in_reduction(+: a(:))
+ a(:) = a(:) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
+!--- taskloop.f90
+subroutine taskloop_in_full_section(a, n)
+ integer :: a(-2:1), n
+ !$omp taskloop in_reduction(+: a(:))
+ do i = 1, n
+ a(:) = a(:) + i
+ end do
+end subroutine
+
+subroutine taskloop_reduction_full_section(a, n)
+ integer :: a(-2:1), n
+ !$omp taskloop reduction(+: a(:))
+ do i = 1, n
+ a(:) = a(:) + i
+ end do
+end subroutine
+
+subroutine taskloop_udr_full_section(a)
+ integer :: a(-2:1), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp taskloop reduction(myred : a(:))
+ do i = 1, 1
+ a(:) = a(:) + i
+ end do
+end subroutine
>From 4930197db9e433fde219220c8f8e4a26cafede33 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 18 Aug 2026 11:33:41 +0100
Subject: [PATCH 05/20] Handle default DSA for full sections
Full-extent reduction sections use the base array descriptor, but the
data-sharing exception only covered ordinary implicit captures. A task
default(private) or default(firstprivate) therefore still created a
competing private descriptor and reached the task reduction TODO.
Apply the exception to default-selected private symbols as well, while
leaving explicit private clauses on their existing path. Cover both default
forms and verify that body expressions use the declared reduction argument.
Assisted-by: Codex
---
.../lib/Lower/OpenMP/DataSharingProcessor.cpp | 18 ++++++---
...-array-full-section-task-privatization.f90 | 40 +++++++++++++++++++
2 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 6aae152a977b4..d5414b81e64f8 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -558,10 +558,13 @@ void DataSharingProcessor::collectPrivatizedSymbols(
const llvm::SetVector<const semantics::Symbol *> &symbolsInNestedRegions,
llvm::SetVector<const semantics::Symbol *> *symbols) {
// Filter-out symbols that must not be privatized.
+ bool collectDefaultPrivate = false;
bool collectImplicit = false;
bool collectPreDetermined = false;
bool collectIndirectRefs = !flag.has_value();
if (!collectIndirectRefs) {
+ collectDefaultPrivate = *flag == semantics::Symbol::Flag::OmpPrivate ||
+ *flag == semantics::Symbol::Flag::OmpFirstPrivate;
collectImplicit = *flag == semantics::Symbol::Flag::OmpImplicit;
collectPreDetermined = *flag == semantics::Symbol::Flag::OmpPreDetermined;
}
@@ -578,13 +581,16 @@ void DataSharingProcessor::collectPrivatizedSymbols(
if (sym->test(semantics::Symbol::Flag::OmpLinear) && !inTarget)
return false;
- if (collectImplicit) {
- // A full-extent section is lowered through the same descriptor as its
- // base array. Do not create a second implicit firstprivate descriptor;
- // the reduction region argument must be the binding used in the body.
- if (wholeArrayReductionSymbols.contains(&sym->GetUltimate()))
- return false;
+ // A full-extent section is lowered through the same descriptor as its base
+ // array. Do not create a second private descriptor when privatization is
+ // selected implicitly or by a default clause; the reduction region
+ // argument must be the binding used in the body. Explicit private clauses
+ // are collected separately in collectSymbolsForPrivatization().
+ if ((collectImplicit || collectDefaultPrivate) &&
+ wholeArrayReductionSymbols.contains(&sym->GetUltimate()))
+ return false;
+ if (collectImplicit) {
// If we're a combined construct with a target region, implicit
// firstprivate captures, should only belong to the target region
// and not be added/captured by later directives. Parallel regions
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
index 948f9f9d8793c..89f7868432dc3 100644
--- a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -1,5 +1,6 @@
! RUN: split-file %s %t
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task.f90 | FileCheck %s --check-prefix=TASK --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-default.f90 | FileCheck %s --check-prefix=TASK-DEFAULT --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate --implicit-check-not=Ea_private
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
@@ -19,6 +20,26 @@
! TASK: %[[TASK_VALUE:.*]] = fir.load %[[TASK_ELEMENT]]
! TASK: arith.addi %[[TASK_VALUE]]
+! TASK-DEFAULT-LABEL: func.func @_QPtask_default_firstprivate_full_section
+! TASK-DEFAULT: omp.task in_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[FIRSTPRIVATE_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-DEFAULT: %[[FIRSTPRIVATE_DECL:.*]]:2 = hlfir.declare %[[FIRSTPRIVATE_ARG]]
+! TASK-DEFAULT: %[[FIRSTPRIVATE_BOX:.*]] = fir.load %[[FIRSTPRIVATE_DECL]]#0
+! TASK-DEFAULT: %[[FIRSTPRIVATE_SECTION:.*]] = hlfir.designate %[[FIRSTPRIVATE_BOX]]
+! TASK-DEFAULT: hlfir.elemental
+! TASK-DEFAULT: %[[FIRSTPRIVATE_ELEMENT:.*]] = hlfir.designate %[[FIRSTPRIVATE_SECTION]]
+! TASK-DEFAULT: %[[FIRSTPRIVATE_VALUE:.*]] = fir.load %[[FIRSTPRIVATE_ELEMENT]]
+! TASK-DEFAULT: arith.addi %[[FIRSTPRIVATE_VALUE]]
+
+! TASK-DEFAULT-LABEL: func.func @_QPtask_default_private_full_section
+! TASK-DEFAULT: omp.task in_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[PRIVATE_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-DEFAULT: %[[PRIVATE_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_ARG]]
+! TASK-DEFAULT: %[[PRIVATE_BOX:.*]] = fir.load %[[PRIVATE_DECL]]#0
+! TASK-DEFAULT: %[[PRIVATE_SECTION:.*]] = hlfir.designate %[[PRIVATE_BOX]]
+! TASK-DEFAULT: hlfir.elemental
+! TASK-DEFAULT: %[[PRIVATE_ELEMENT:.*]] = hlfir.designate %[[PRIVATE_SECTION]]
+! TASK-DEFAULT: %[[PRIVATE_VALUE:.*]] = fir.load %[[PRIVATE_ELEMENT]]
+! TASK-DEFAULT: arith.addi %[[PRIVATE_VALUE]]
+
! TASKLOOP-LABEL: func.func @_QPtaskloop_in_full_section
! TASKLOOP: omp.taskloop.context in_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[IN_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
! TASKLOOP: %[[IN_DECL:.*]]:2 = hlfir.declare %[[IN_ARG]]
@@ -59,6 +80,25 @@ subroutine task_full_section(a)
!$omp end taskgroup
end subroutine
+!--- task-default.f90
+subroutine task_default_firstprivate_full_section(a)
+ integer :: a(-2:1)
+ !$omp taskgroup task_reduction(+: a(:))
+ !$omp task default(firstprivate) in_reduction(+: a(:))
+ a(:) = a(:) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
+subroutine task_default_private_full_section(a)
+ integer :: a(-2:1)
+ !$omp taskgroup task_reduction(+: a(:))
+ !$omp task default(private) in_reduction(+: a(:))
+ a(:) = a(:) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
!--- taskloop.f90
subroutine taskloop_in_full_section(a, n)
integer :: a(-2:1), n
>From 7be5b5a2409dc899b50505b7e0467876540d06a6 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 18 Aug 2026 11:54:24 +0100
Subject: [PATCH 06/20] Test multidimensional reduction sections
Add rank-two full and mixed array section task reductions. This protects the
all-dimensions whole-section classification from regressions while preserving
the TODO for partial sections.
Assisted-by: Codex
---
...ction-array-element-task-privatization.f90 | 13 ++++++++++++
...-array-full-section-task-privatization.f90 | 21 +++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index fa9b6fbd38f1d..3b498ebcd9cab 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -7,6 +7,8 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SECTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-rank-two-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-RANK-TWO-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-rank-two-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-RANK-TWO-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SECTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-REDUCTION-SECTION
@@ -30,6 +32,7 @@
! TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! TASK-SECTION: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASK-RANK-TWO-SECTION: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
@@ -58,6 +61,16 @@ subroutine task_reduction_section(a)
!$omp end taskgroup
end subroutine
+!--- task-rank-two-section.f90
+subroutine task_reduction_rank_two_section(a)
+ integer :: a(4, 4)
+ !$omp taskgroup task_reduction(+: a(:, 2))
+ !$omp task in_reduction(+: a(:, 2))
+ a(:, 2) = a(:, 2) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
!--- taskloop-in-section.f90
subroutine taskloop_in_reduction_section(a, n)
integer :: a(4), n
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
index 89f7868432dc3..b1927ec049fe5 100644
--- a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -20,6 +20,18 @@
! TASK: %[[TASK_VALUE:.*]] = fir.load %[[TASK_ELEMENT]]
! TASK: arith.addi %[[TASK_VALUE]]
+! TASK-LABEL: func.func @_QPtask_rank_two_full_section
+! TASK: omp.taskgroup task_reduction(byref @add_reduction_byref_box_4x4xi32 {{.*}} -> %[[RANK_TWO_TASKGROUP_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4x4xi32>>>)
+! TASK: %[[RANK_TWO_TASKGROUP_DECL:.*]]:2 = hlfir.declare %[[RANK_TWO_TASKGROUP_ARG]]
+! TASK: omp.task in_reduction(byref @add_reduction_byref_box_4x4xi32 %[[RANK_TWO_TASKGROUP_DECL]]#0 -> %[[RANK_TWO_TASK_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4x4xi32>>>)
+! TASK: %[[RANK_TWO_TASK_DECL:.*]]:2 = hlfir.declare %[[RANK_TWO_TASK_ARG]]
+! TASK: %[[RANK_TWO_TASK_BOX:.*]] = fir.load %[[RANK_TWO_TASK_DECL]]#0
+! TASK: %[[RANK_TWO_TASK_SECTION:.*]] = hlfir.designate %[[RANK_TWO_TASK_BOX]]
+! TASK: hlfir.elemental
+! TASK: %[[RANK_TWO_TASK_ELEMENT:.*]] = hlfir.designate %[[RANK_TWO_TASK_SECTION]]
+! TASK: %[[RANK_TWO_TASK_VALUE:.*]] = fir.load %[[RANK_TWO_TASK_ELEMENT]]
+! TASK: arith.addi %[[RANK_TWO_TASK_VALUE]]
+
! TASK-DEFAULT-LABEL: func.func @_QPtask_default_firstprivate_full_section
! TASK-DEFAULT: omp.task in_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[FIRSTPRIVATE_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
! TASK-DEFAULT: %[[FIRSTPRIVATE_DECL:.*]]:2 = hlfir.declare %[[FIRSTPRIVATE_ARG]]
@@ -80,6 +92,15 @@ subroutine task_full_section(a)
!$omp end taskgroup
end subroutine
+subroutine task_rank_two_full_section(a)
+ integer :: a(-2:1, -1:2)
+ !$omp taskgroup task_reduction(+: a(:, :))
+ !$omp task in_reduction(+: a(:, :))
+ a(:, :) = a(:, :) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
!--- task-default.f90
subroutine task_default_firstprivate_full_section(a)
integer :: a(-2:1)
>From b60ab7711d24b8d24f47db84c19ca30252618a2c Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 18 Aug 2026 17:44:33 +0100
Subject: [PATCH 07/20] Fix quoting
---
.../reduction-array-full-section-task-privatization.f90 | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
index b1927ec049fe5..5e417abf23d51 100644
--- a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -1,8 +1,8 @@
! RUN: split-file %s %t
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task.f90 | FileCheck %s --check-prefix=TASK --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-default.f90 | FileCheck %s --check-prefix=TASK-DEFAULT --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate --implicit-check-not=Ea_private
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not=not\ yet\ implemented --implicit-check-not=Ea_firstprivate
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task.f90 | FileCheck %s --check-prefix=TASK --implicit-check-not="not yet implemented" --implicit-check-not=Ea_firstprivate
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-default.f90 | FileCheck %s --check-prefix=TASK-DEFAULT --implicit-check-not="not yet implemented" --implicit-check-not=Ea_firstprivate --implicit-check-not=Ea_private
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not="not yet implemented" --implicit-check-not=Ea_firstprivate
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not="not yet implemented" --implicit-check-not=Ea_firstprivate
! A full-extent section uses the same descriptor as its base array. Check that
! it is bound only to the reduction argument, rather than also being captured
>From 220a04798c3aeb024c45ab557cb7ace81d0501b9 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Thu, 20 Aug 2026 15:22:25 +0100
Subject: [PATCH 08/20] Recognize explicit full array sections
Explicitly bounded sections may still cover their complete base array.
Compare constant section bounds with the declared bounds before treating
them as partial.
Assisted-by: Codex
---
.../lib/Lower/OpenMP/DataSharingProcessor.cpp | 25 -----------
flang/lib/Lower/OpenMP/Utils.cpp | 42 ++++++++++++++++++
flang/lib/Lower/OpenMP/Utils.h | 2 +
...-array-full-section-task-privatization.f90 | 43 +++++++++++++++++++
4 files changed, 87 insertions(+), 25 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index d5414b81e64f8..670705d809a85 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -13,8 +13,6 @@
#include "DataSharingProcessor.h"
#include "Utils.h"
-#include "flang/Evaluate/fold.h"
-#include "flang/Evaluate/tools.h"
#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Support/PrivateReductionUtils.h"
@@ -34,29 +32,6 @@
#include "llvm/Frontend/OpenMP/OMP.h"
#include <variant>
-namespace {
-static bool isWholeArraySection(const Fortran::lower::omp::Object &object) {
- if (!object.ref())
- return false;
-
- std::optional<Fortran::evaluate::DataRef> dataRef =
- Fortran::evaluate::ExtractDataRef(*object.ref());
- if (!dataRef)
- return false;
-
- const auto *arrayRef = std::get_if<Fortran::evaluate::ArrayRef>(&dataRef->u);
- if (!arrayRef)
- return false;
-
- return llvm::all_of(
- arrayRef->subscript(), [](const Fortran::evaluate::Subscript &sub) {
- const auto *triplet = std::get_if<Fortran::evaluate::Triplet>(&sub.u);
- return triplet && !triplet->GetLower() && !triplet->GetUpper() &&
- Fortran::evaluate::ToInt64(triplet->GetStride()) == 1;
- });
-}
-} // namespace
-
namespace Fortran {
namespace lower {
namespace omp {
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index 4e7cf5ca34cc3..01aaaabb0d138 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -74,6 +74,48 @@ llvm::cl::opt<bool> treatIndexAsSection(
namespace Fortran {
namespace lower {
namespace omp {
+bool isWholeArraySection(const Object &object) {
+ if (!object.sym() || !object.ref())
+ return false;
+
+ std::optional<evaluate::DataRef> dataRef =
+ evaluate::ExtractDataRef(*object.ref());
+ if (!dataRef)
+ return false;
+
+ const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u);
+ if (!arrayRef)
+ return false;
+
+ const semantics::ArraySpec *shape = object.sym()->GetUltimate().GetShape();
+ if (!shape || shape->size() != arrayRef->subscript().size())
+ return false;
+
+ auto matchesDeclaredBound = [](const auto *sectionBound,
+ const auto &declaredBound) {
+ if (!sectionBound)
+ return true;
+ const auto &explicitDeclaredBound = declaredBound.GetExplicit();
+ if (!explicitDeclaredBound)
+ return false;
+ std::optional<std::int64_t> sectionValue = evaluate::ToInt64(*sectionBound);
+ std::optional<std::int64_t> declaredValue =
+ evaluate::ToInt64(*explicitDeclaredBound);
+ return sectionValue && declaredValue && sectionValue == declaredValue;
+ };
+
+ for (auto [subscript, declaredDimension] :
+ llvm::zip_equal(arrayRef->subscript(), *shape)) {
+ const auto *triplet = std::get_if<evaluate::Triplet>(&subscript.u);
+ if (!triplet || evaluate::ToInt64(triplet->GetStride()) != 1 ||
+ !matchesDeclaredBound(triplet->GetLower(),
+ declaredDimension.lbound()) ||
+ !matchesDeclaredBound(triplet->GetUpper(), declaredDimension.ubound()))
+ return false;
+ }
+ return true;
+}
+
bool requiresImplicitDefaultDeclareMapper(
const semantics::DerivedTypeSpec &typeSpec) {
llvm::SmallPtrSet<const semantics::DerivedTypeSpec *, 8> visited;
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index 94f85c43f7033..f86ebf0e2966f 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -137,6 +137,8 @@ void generateMemberPlacementIndices(
bool isMemberOrParentAllocatableOrPointer(
const Object &object, Fortran::semantics::SemanticsContext &semaCtx);
+bool isWholeArraySection(const Object &object);
+
mlir::Value createParentSymAndGenIntermediateMaps(
mlir::Location clauseLocation, Fortran::lower::AbstractConverter &converter,
semantics::SemanticsContext &semaCtx, lower::StatementContext &stmtCtx,
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
index 5e417abf23d51..75782389a989c 100644
--- a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -32,6 +32,10 @@
! TASK: %[[RANK_TWO_TASK_VALUE:.*]] = fir.load %[[RANK_TWO_TASK_ELEMENT]]
! TASK: arith.addi %[[RANK_TWO_TASK_VALUE]]
+! TASK-LABEL: func.func @_QPtask_explicit_full_section
+! TASK: omp.taskgroup task_reduction(byref @add_reduction_byref_box_4xi32
+! TASK: omp.task in_reduction(byref @add_reduction_byref_box_4xi32
+
! TASK-DEFAULT-LABEL: func.func @_QPtask_default_firstprivate_full_section
! TASK-DEFAULT: omp.task in_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[FIRSTPRIVATE_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
! TASK-DEFAULT: %[[FIRSTPRIVATE_DECL:.*]]:2 = hlfir.declare %[[FIRSTPRIVATE_ARG]]
@@ -82,6 +86,12 @@
! TASKLOOP: %[[UDR_VALUE:.*]] = fir.load %[[UDR_ELEMENT]]
! TASKLOOP: arith.addi %[[UDR_VALUE]]
+! TASKLOOP-LABEL: func.func @_QPtaskloop_explicit_full_section
+! TASKLOOP: omp.taskloop.context {{.*}}reduction(byref @add_reduction_byref_box_4xi32
+
+! TASKLOOP-LABEL: func.func @_QPtaskloop_rank_two_explicit_full_section
+! TASKLOOP: omp.taskloop.context {{.*}}reduction(byref @add_reduction_byref_box_4x4xi32
+
!--- task.f90
subroutine task_full_section(a)
integer :: a(-2:1)
@@ -101,6 +111,15 @@ subroutine task_rank_two_full_section(a)
!$omp end taskgroup
end subroutine
+subroutine task_explicit_full_section(a)
+ integer :: a(-2:1)
+ !$omp taskgroup task_reduction(+: a(-2:1))
+ !$omp task in_reduction(+: a(-2:1))
+ a(-2:1) = a(-2:1) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
!--- task-default.f90
subroutine task_default_firstprivate_full_section(a)
integer :: a(-2:1)
@@ -146,3 +165,27 @@ subroutine taskloop_udr_full_section(a)
a(:) = a(:) + i
end do
end subroutine
+
+subroutine taskloop_explicit_full_section(a)
+ integer :: a(-2:1), i
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop reduction(+: a(-2:1))
+ do i = 1, 1
+ a(-2:1) = a(-2:1) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+subroutine taskloop_rank_two_explicit_full_section(a)
+ integer :: a(-2:1, -1:2), i
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop reduction(+: a(-2:1, -1:2))
+ do i = 1, 1
+ a(-2:1, -1:2) = a(-2:1, -1:2) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
>From 3883420cb0f1b86fc3a824b8fca0e9dde06a0c02 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Thu, 20 Aug 2026 16:10:33 +0100
Subject: [PATCH 09/20] Recognize runtime full array sections
Recognize full sections whose explicit bounds fold to the base array's
bounds, including LBOUND and UBOUND results evaluated in the default
integer kind before conversion to the subscript kind.
Compare folded bounds by expression identity, not procedure-interface
equivalence, which can equate unrelated same-position dummy arguments.
Only the default integer kind is handled. Bounds using a nondefault
KIND= result are not recognized as full sections.
Assisted-by: Codex
---
.../lib/Lower/OpenMP/DataSharingProcessor.cpp | 2 +-
flang/lib/Lower/OpenMP/Utils.cpp | 34 +++++++++++--------
flang/lib/Lower/OpenMP/Utils.h | 4 ++-
...ction-array-element-task-privatization.f90 | 21 ++++++++++++
...-array-full-section-task-privatization.f90 | 17 ++++++++++
5 files changed, 62 insertions(+), 16 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 670705d809a85..42669b2a7ec6d 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -267,7 +267,7 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
auto collectWholeArrayReductionSymbols = [&](const auto &reductionClause) {
const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
for (const Object &object : objects)
- if (object.sym() && isWholeArraySection(object))
+ if (object.sym() && isWholeArraySection(object, semaCtx))
wholeArrayReductionSymbols.insert(&object.sym()->GetUltimate());
};
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index 01aaaabb0d138..4fcb37bd4bd20 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -14,6 +14,7 @@
#include "ClauseFinder.h"
#include "flang/Evaluate/fold.h"
+#include "flang/Evaluate/shape.h"
#include "flang/Evaluate/tools.h"
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
@@ -74,7 +75,8 @@ llvm::cl::opt<bool> treatIndexAsSection(
namespace Fortran {
namespace lower {
namespace omp {
-bool isWholeArraySection(const Object &object) {
+bool isWholeArraySection(const Object &object,
+ semantics::SemanticsContext &semaCtx) {
if (!object.sym() || !object.ref())
return false;
@@ -91,26 +93,30 @@ bool isWholeArraySection(const Object &object) {
if (!shape || shape->size() != arrayRef->subscript().size())
return false;
- auto matchesDeclaredBound = [](const auto *sectionBound,
- const auto &declaredBound) {
+ auto matchesBaseBound = [&](const auto *sectionBound,
+ evaluate::MaybeExtentExpr baseBound) {
if (!sectionBound)
return true;
- const auto &explicitDeclaredBound = declaredBound.GetExplicit();
- if (!explicitDeclaredBound)
+ if (!baseBound)
return false;
- std::optional<std::int64_t> sectionValue = evaluate::ToInt64(*sectionBound);
- std::optional<std::int64_t> declaredValue =
- evaluate::ToInt64(*explicitDeclaredBound);
- return sectionValue && declaredValue && sectionValue == declaredValue;
+ evaluate::ExtentExpr foldedSectionBound = evaluate::Fold(
+ semaCtx.foldingContext(), evaluate::ExtentExpr{*sectionBound});
+ evaluate::ExtentExpr foldedBaseBound =
+ evaluate::Fold(semaCtx.foldingContext(), std::move(*baseBound));
+ return evaluate::IsSameOrConvertOf(
+ evaluate::AsGenericExpr(std::move(foldedSectionBound)),
+ evaluate::AsGenericExpr(std::move(foldedBaseBound)));
};
- for (auto [subscript, declaredDimension] :
- llvm::zip_equal(arrayRef->subscript(), *shape)) {
+ for (auto [dimension, subscript] : llvm::enumerate(arrayRef->subscript())) {
const auto *triplet = std::get_if<evaluate::Triplet>(&subscript.u);
if (!triplet || evaluate::ToInt64(triplet->GetStride()) != 1 ||
- !matchesDeclaredBound(triplet->GetLower(),
- declaredDimension.lbound()) ||
- !matchesDeclaredBound(triplet->GetUpper(), declaredDimension.ubound()))
+ !matchesBaseBound(triplet->GetLower(),
+ evaluate::GetLBOUND(semaCtx.foldingContext(),
+ arrayRef->base(), dimension)) ||
+ !matchesBaseBound(triplet->GetUpper(),
+ evaluate::GetUBOUND(semaCtx.foldingContext(),
+ arrayRef->base(), dimension)))
return false;
}
return true;
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index f86ebf0e2966f..a90e854c7a352 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -29,6 +29,7 @@ class RecordType;
namespace Fortran {
namespace semantics {
+class SemanticsContext;
class Symbol;
namespace omp {
class OmpVariantMatchContext;
@@ -137,7 +138,8 @@ void generateMemberPlacementIndices(
bool isMemberOrParentAllocatableOrPointer(
const Object &object, Fortran::semantics::SemanticsContext &semaCtx);
-bool isWholeArraySection(const Object &object);
+bool isWholeArraySection(const Object &object,
+ semantics::SemanticsContext &semaCtx);
mlir::Value createParentSymAndGenIntermediateMaps(
mlir::Location clauseLocation, Fortran::lower::AbstractConverter &converter,
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 3b498ebcd9cab..323241c3a65a6 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -23,6 +23,8 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-REDUCTION-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-udr-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-UDR-SECTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-udr-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-UDR-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-cross-scope-bounds.f90 2>&1 | FileCheck %s --check-prefix=TASK-CROSS-SCOPE-BOUNDS
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-cross-scope-bounds.f90 2>&1 | FileCheck %s --check-prefix=TASK-CROSS-SCOPE-BOUNDS
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -40,6 +42,7 @@
! EAGER-TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-UDR-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
+! TASK-CROSS-SCOPE-BOUNDS: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
!--- task.f90
subroutine task_reduction_element(a)
@@ -117,3 +120,21 @@ subroutine taskloop_reduction_element(a, n)
a(2) = a(2) + i
end do
end subroutine
+
+!--- task-cross-scope-bounds.f90
+subroutine task_cross_scope_bounds(n)
+ integer :: n
+ integer :: a(n)
+
+contains
+ subroutine inner(m)
+ integer :: m
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp taskgroup task_reduction(+: a(1:m))
+ !$omp task in_reduction(+: a(1:m))
+ a(1:m) = a(1:m) + 1
+ !$omp end task
+ !$omp end taskgroup
+ end subroutine
+end subroutine
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
index 75782389a989c..d30a71de4debc 100644
--- a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -36,6 +36,14 @@
! TASK: omp.taskgroup task_reduction(byref @add_reduction_byref_box_4xi32
! TASK: omp.task in_reduction(byref @add_reduction_byref_box_4xi32
+! TASK-LABEL: func.func @_QPtask_dynamic_full_section
+! TASK: omp.taskgroup task_reduction(byref @add_reduction_byref_box_Uxi32 {{.*}} -> %[[DYNAMIC_TASKGROUP_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<?xi32>>>)
+! TASK: %[[DYNAMIC_TASKGROUP_DECL:.*]]:2 = hlfir.declare %[[DYNAMIC_TASKGROUP_ARG]]
+! TASK: omp.task in_reduction(byref @add_reduction_byref_box_Uxi32 %[[DYNAMIC_TASKGROUP_DECL]]#0 -> %[[DYNAMIC_TASK_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<?xi32>>>)
+! TASK: %[[DYNAMIC_TASK_DECL:.*]]:2 = hlfir.declare %[[DYNAMIC_TASK_ARG]]
+! TASK: %[[DYNAMIC_TASK_BOX:.*]] = fir.load %[[DYNAMIC_TASK_DECL]]#0
+! TASK: hlfir.designate %[[DYNAMIC_TASK_BOX]]
+
! TASK-DEFAULT-LABEL: func.func @_QPtask_default_firstprivate_full_section
! TASK-DEFAULT: omp.task in_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[FIRSTPRIVATE_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
! TASK-DEFAULT: %[[FIRSTPRIVATE_DECL:.*]]:2 = hlfir.declare %[[FIRSTPRIVATE_ARG]]
@@ -120,6 +128,15 @@ subroutine task_explicit_full_section(a)
!$omp end taskgroup
end subroutine
+subroutine task_dynamic_full_section(a)
+ integer :: a(:)
+ !$omp taskgroup task_reduction(+: a(lbound(a, 1):ubound(a, 1)))
+ !$omp task shared(a) in_reduction(+: a(lbound(a, 1):ubound(a, 1)))
+ a(lbound(a, 1):ubound(a, 1)) = a(lbound(a, 1):ubound(a, 1)) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
!--- task-default.f90
subroutine task_default_firstprivate_full_section(a)
integer :: a(-2:1)
>From 98e3ed548bf9aad80786b86b7ae487a5bd6fee37 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Thu, 20 Aug 2026 15:24:04 +0100
Subject: [PATCH 10/20] Diagnose shared partial taskloop reductions
Partial array sections in taskloop reductions are lowered as whole-array
reductions when their base is shared. The existing diagnostic checks
only privatized bases, so explicit shared data bypasses it.
Diagnose partial sections independently of base data sharing while
continuing to accept full-extent sections. Cover reduction and
in_reduction clauses, including user-defined reductions, with eager and
delayed privatization.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 18 ++++++++-
...ction-array-element-task-privatization.f90 | 38 +++++++++++++++++++
2 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 3a979032968fd..17a7e1acab894 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -180,6 +180,18 @@ static bool hasPrivatizedArrayReductionObject(
return false;
}
+static bool
+hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
+ semantics::SemanticsContext &semaCtx) {
+ for (const Object &object : reductionObjects) {
+ if (!object.ref() || isWholeArraySection(object, semaCtx))
+ continue;
+ if (evaluate::IsArraySection(*object.ref()))
+ return true;
+ }
+ return false;
+}
+
/// Structure holding the information needed to create and bind entry block
/// arguments associated to a single clause during OpenMP lowering.
struct ObjectEntryBlockArgsEntry {
@@ -4981,7 +4993,11 @@ static mlir::omp::TaskloopContextOp genStandaloneTaskloop(
TODO(loc,
"TASKLOOP construct with REDUCTION of an array element or section "
"whose base array is privatized");
-
+ if (hasPartialArrayReductionObject(inReductionObjects, semaCtx))
+ TODO(loc,
+ "TASKLOOP construct with IN_REDUCTION of a partial array section");
+ if (hasPartialArrayReductionObject(reductionObjects, semaCtx))
+ TODO(loc, "TASKLOOP construct with REDUCTION of a partial array section");
mlir::omp::LoopNestOperands loopNestClauseOps;
llvm::SmallVector<const semantics::Symbol *> iv;
genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 323241c3a65a6..0bc80a6a113ec 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -25,6 +25,14 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-udr-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASKLOOP-UDR-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-cross-scope-bounds.f90 2>&1 | FileCheck %s --check-prefix=TASK-CROSS-SCOPE-BOUNDS
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-cross-scope-bounds.f90 2>&1 | FileCheck %s --check-prefix=TASK-CROSS-SCOPE-BOUNDS
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-udr-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-udr-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-udr-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-udr-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -43,6 +51,8 @@
! EAGER-TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-UDR-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! TASK-CROSS-SCOPE-BOUNDS: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASKLOOP-UDR-SHARED-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of a partial array section
+! TASKLOOP-IN-SHARED-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of a partial array section
!--- task.f90
subroutine task_reduction_element(a)
@@ -103,6 +113,34 @@ subroutine taskloop_udr_section(a)
end do
end subroutine
+!--- taskloop-udr-shared-section.f90
+subroutine taskloop_udr_shared_section(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop reduction(myred : a(2:3))
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+!--- taskloop-in-shared-section.f90
+subroutine taskloop_in_reduction_shared_section(a)
+ integer :: a(4, 4), i
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop in_reduction(+: a(:, 2))
+ do i = 1, 1
+ a(:, 2) = a(:, 2) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
!--- taskloop-in.f90
subroutine taskloop_in_reduction_element(a, n)
integer :: a(4), n
>From 7bedde5695c8d719c3a1d57a0ded2cb25e242011 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Thu, 20 Aug 2026 15:33:07 +0100
Subject: [PATCH 11/20] Preserve supported array element reductions
Single element task reductions lower as whole-array reductions. Keep
those paths enabled when lowering succeeds.
Diagnose eager ordinary tasks and targets, where current lowering
otherwise produces invalid IR, and cover supported task, taskgroup, and
taskloop paths.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 28 ++++++-
...ction-array-element-task-privatization.f90 | 26 ++++++
.../OpenMP/reduction-array-element-shared.f90 | 83 +++++++++++++++++++
3 files changed, 136 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Lower/OpenMP/reduction-array-element-shared.f90
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 17a7e1acab894..a761b2c173566 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -192,6 +192,25 @@ hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
return false;
}
+static bool
+hasArrayElementReductionObject(llvm::ArrayRef<Object> reductionObjects) {
+ for (const Object &object : reductionObjects) {
+ if (!object.ref())
+ continue;
+ std::optional<evaluate::DataRef> dataRef =
+ evaluate::ExtractDataRef(*object.ref());
+ if (!dataRef)
+ continue;
+ const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u);
+ if (arrayRef && llvm::all_of(arrayRef->subscript(),
+ [](const evaluate::Subscript &subscript) {
+ return subscript.Rank() == 0;
+ }))
+ return true;
+ }
+ return false;
+}
+
/// Structure holding the information needed to create and bind entry block
/// arguments associated to a single clause during OpenMP lowering.
struct ObjectEntryBlockArgsEntry {
@@ -4050,6 +4069,9 @@ genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
"TARGET construct with IN_REDUCTION of a privatized variable");
}
+ if (hasArrayElementReductionObject(inReductionObjects))
+ TODO(loc, "TARGET construct with IN_REDUCTION of an array element");
+
// Collect symbols that have dynamic substring accesses
llvm::SmallPtrSet<const semantics::Symbol *, 8> symbolsWithDynamicSubstring;
collectSymbolsWithDynamicSubstring(semaCtx, eval,
@@ -4347,12 +4369,16 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
genTaskClauses(converter, semaCtx, symTable, stmtCtx, item->clauses, loc,
clauseOps, inReductionObjects);
- if (!enableDelayedPrivatization)
+ if (!enableDelayedPrivatization) {
+ if (hasArrayElementReductionObject(inReductionObjects))
+ TODO(loc, "TASK construct with IN_REDUCTION of an array element when "
+ "delayed privatization is disabled");
return genOpWithBody<mlir::omp::TaskOp>(
OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,
llvm::omp::Directive::OMPD_task)
.setClauses(&item->clauses),
queue, item, clauseOps);
+ }
DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,
lower::omp::isLastItemInQueue(item, queue),
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 0bc80a6a113ec..6e0067abcb5eb 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -33,6 +33,10 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/task-shared-element.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/task-shared-element.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -53,6 +57,8 @@
! TASK-CROSS-SCOPE-BOUNDS: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-UDR-SHARED-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of a partial array section
! TASKLOOP-IN-SHARED-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of a partial array section
+! EAGER-TASK-SHARED-ELEMENT: not yet implemented: TASK construct with IN_REDUCTION of an array element when delayed privatization is disabled
+! TARGET-ELEMENT: not yet implemented: TARGET construct with IN_REDUCTION of an array element
!--- task.f90
subroutine task_reduction_element(a)
@@ -64,6 +70,26 @@ subroutine task_reduction_element(a)
!$omp end taskgroup
end subroutine
+!--- task-shared-element.f90
+subroutine task_in_reduction_shared_element(a)
+ integer :: a(4)
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp task in_reduction(+: a(2))
+ a(2) = a(2) + 1
+ !$omp end task
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+!--- target-element.f90
+subroutine target_in_reduction_element(a)
+ integer :: a(4)
+ !$omp target in_reduction(+: a(2)) map(tofrom: a)
+ a(2) = a(2) + 1
+ !$omp end target
+end subroutine
+
!--- task-section.f90
subroutine task_reduction_section(a)
integer :: a(4)
diff --git a/flang/test/Lower/OpenMP/reduction-array-element-shared.f90 b/flang/test/Lower/OpenMP/reduction-array-element-shared.f90
new file mode 100644
index 0000000000000..c066453a93637
--- /dev/null
+++ b/flang/test/Lower/OpenMP/reduction-array-element-shared.f90
@@ -0,0 +1,83 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task.f90 | FileCheck %s --check-prefix=TASK --implicit-check-not="not yet implemented"
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskgroup.f90 | FileCheck %s --check-prefix=TASKGROUP --implicit-check-not="not yet implemented"
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not="not yet implemented"
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not="not yet implemented"
+
+! A single-element reduction is lowered as a reduction of its whole base array
+! when that path is otherwise supported. Check that the generated reduction
+! arguments therefore use the base array descriptor.
+
+! TASK-LABEL: func.func @_QPtask_element
+! TASK: omp.taskgroup task_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[TASKGROUP_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK: %[[TASKGROUP_DECL:.*]]:2 = hlfir.declare %[[TASKGROUP_ARG]]
+! TASK: omp.task in_reduction(byref @add_reduction_byref_box_4xi32 %[[TASKGROUP_DECL]]#0 -> %[[TASK_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK: %[[TASK_DECL:.*]]:2 = hlfir.declare %[[TASK_ARG]]
+! TASK: %[[TASK_BOX:.*]] = fir.load %[[TASK_DECL]]#0
+! TASK: hlfir.designate %[[TASK_BOX]]
+
+! TASKGROUP-LABEL: func.func @_QPtaskgroup_element
+! TASKGROUP: omp.taskgroup task_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[TASKGROUP_ONLY_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASKGROUP: %[[TASKGROUP_ONLY_DECL:.*]]:2 = hlfir.declare %[[TASKGROUP_ONLY_ARG]]
+! TASKGROUP: %[[TASKGROUP_ONLY_BOX:.*]] = fir.load %[[TASKGROUP_ONLY_DECL]]#0
+! TASKGROUP: hlfir.designate %[[TASKGROUP_ONLY_BOX]]
+
+! TASKLOOP-LABEL: func.func @_QPtaskloop_in_element
+! TASKLOOP: omp.taskgroup task_reduction(byref @add_reduction_byref_box_4xi32 {{.*}} -> %[[TASKLOOP_GROUP_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASKLOOP: %[[TASKLOOP_GROUP_DECL:.*]]:2 = hlfir.declare %[[TASKLOOP_GROUP_ARG]]
+! TASKLOOP: omp.taskloop.context in_reduction(byref @add_reduction_byref_box_4xi32 %[[TASKLOOP_GROUP_DECL]]#0 -> %[[TASKLOOP_ARG:arg[0-9]+]] : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASKLOOP: %[[TASKLOOP_DECL:.*]]:2 = hlfir.declare %[[TASKLOOP_ARG]]
+! TASKLOOP: %[[TASKLOOP_BOX:.*]] = fir.load %[[TASKLOOP_DECL]]#0
+! TASKLOOP: hlfir.designate %[[TASKLOOP_BOX]]
+
+! TASKLOOP-LABEL: func.func @_QPtaskloop_reduction_element
+! TASKLOOP: omp.taskloop.context {{.*}}reduction(byref @add_reduction_byref_box_4xi32
+
+!--- task.f90
+subroutine task_element(a)
+ integer :: a(4)
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskgroup task_reduction(+: a(2))
+ !$omp task in_reduction(+: a(2))
+ a(2) = a(2) + 1
+ !$omp end task
+ !$omp end taskgroup
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+!--- taskgroup.f90
+subroutine taskgroup_element(a)
+ integer :: a(4)
+ !$omp taskgroup task_reduction(+: a(2))
+ a(2) = a(2) + 1
+ !$omp end taskgroup
+end subroutine
+
+!--- taskloop.f90
+subroutine taskloop_in_element(a)
+ integer :: a(4), i
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskgroup task_reduction(+: a(2))
+ !$omp taskloop in_reduction(+: a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end taskgroup
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+subroutine taskloop_reduction_element(a)
+ integer :: a(4), i
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop reduction(+: a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
>From 00f3ad5723e9bc405a64d6caf60393e41c0948fa Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Thu, 20 Aug 2026 16:40:33 +0100
Subject: [PATCH 12/20] Diagnose eager task in_reduction
Eager task lowering creates in_reduction operands without corresponding
region arguments when delayed privatization is disabled. This produces
invalid MLIR.
Diagnose eager task in_reduction until lowering can bind its region
arguments. Move full-array-section coverage to the TODO test.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 4 ++++
.../reduction-array-element-task-privatization.f90 | 13 +++++++++++++
2 files changed, 17 insertions(+)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index a761b2c173566..6c0294a94b3d8 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4373,6 +4373,10 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
if (hasArrayElementReductionObject(inReductionObjects))
TODO(loc, "TASK construct with IN_REDUCTION of an array element when "
"delayed privatization is disabled");
+ if (!inReductionObjects.empty())
+ TODO(loc, "TASK construct with IN_REDUCTION when delayed "
+ "privatization is disabled");
+
return genOpWithBody<mlir::omp::TaskOp>(
OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,
llvm::omp::Directive::OMPD_task)
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 6e0067abcb5eb..5badf02aee97a 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -35,6 +35,8 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-in-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-SHARED-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/task-shared-element.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/task-shared-element.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/task-shared-full-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-FULL-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/task-shared-full-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-FULL-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
@@ -58,6 +60,7 @@
! TASKLOOP-UDR-SHARED-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of a partial array section
! TASKLOOP-IN-SHARED-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of a partial array section
! EAGER-TASK-SHARED-ELEMENT: not yet implemented: TASK construct with IN_REDUCTION of an array element when delayed privatization is disabled
+! EAGER-TASK-SHARED-FULL-SECTION: not yet implemented: TASK construct with IN_REDUCTION when delayed privatization is disabled
! TARGET-ELEMENT: not yet implemented: TARGET construct with IN_REDUCTION of an array element
!--- task.f90
@@ -82,6 +85,16 @@ subroutine task_in_reduction_shared_element(a)
!$omp end parallel
end subroutine
+!--- task-shared-full-section.f90
+subroutine task_in_reduction_shared_full_section(a)
+ integer :: a(4)
+ !$omp taskgroup task_reduction(+: a(:))
+ !$omp task shared(a) in_reduction(+: a(:))
+ a(:) = a(:) + 1
+ !$omp end task
+ !$omp end taskgroup
+end subroutine
+
!--- target-element.f90
subroutine target_in_reduction_element(a)
integer :: a(4)
>From 8e11e0df1e006cdd06a390f8c40696fc3b815c37 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 24 Aug 2026 11:21:00 +0100
Subject: [PATCH 13/20] Diagnose UDR array element task reductions
Task reduction array elements currently lower through their whole-array
descriptor. User-defined initializers and combiners can therefore modify
elements outside the listed reduction item.
Diagnose these cases for task, taskgroup, and taskloop constructs while
keeping supported predefined reductions unchanged.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 129 ++++++++++++++++--
...ction-array-element-task-privatization.f90 | 83 +++++++++++
2 files changed, 197 insertions(+), 15 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 6c0294a94b3d8..8ce3c9ab36dec 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -13,6 +13,7 @@
#include "flang/Lower/OpenMP.h"
#include "Atomic.h"
+#include "ClauseFinder.h"
#include "ClauseProcessor.h"
#include "DataSharingProcessor.h"
#include "Decomposer.h"
@@ -192,23 +193,100 @@ hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
return false;
}
+static bool isArrayElementReductionObject(const Object &object) {
+ return object.ref() && object.ref()->Rank() == 0 &&
+ evaluate::IsArrayElement(*object.ref(), /*intoSubstring=*/false);
+}
+
static bool
hasArrayElementReductionObject(llvm::ArrayRef<Object> reductionObjects) {
- for (const Object &object : reductionObjects) {
- if (!object.ref())
- continue;
- std::optional<evaluate::DataRef> dataRef =
- evaluate::ExtractDataRef(*object.ref());
- if (!dataRef)
- continue;
- const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u);
- if (arrayRef && llvm::all_of(arrayRef->subscript(),
- [](const evaluate::Subscript &subscript) {
- return subscript.Rank() == 0;
- }))
- return true;
- }
- return false;
+ return llvm::any_of(reductionObjects, isArrayElementReductionObject);
+}
+
+static bool isUserDefinedReductionOperator(
+ const clause::ReductionOperator &reductionOperator, const Object &object,
+ lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx) {
+ const semantics::Symbol *objectSymbol = object.sym();
+ const semantics::DeclTypeSpec *objectType =
+ objectSymbol ? objectSymbol->GetUltimate().GetType() : nullptr;
+ if (!objectType)
+ return false;
+
+ return common::visit(
+ common::visitors{
+ [&](const clause::DefinedOperator &definedOperator) {
+ return common::visit(
+ common::visitors{
+ [&](const clause::DefinedOperator::IntrinsicOperator &op) {
+ using IntrinsicOperator =
+ clause::DefinedOperator::IntrinsicOperator;
+ switch (op) {
+ case IntrinsicOperator::Add:
+ case IntrinsicOperator::Multiply:
+ case IntrinsicOperator::AND:
+ case IntrinsicOperator::OR:
+ case IntrinsicOperator::EQV:
+ case IntrinsicOperator::NEQV:
+ break;
+ default:
+ return false;
+ }
+
+ parser::CharBlock mangledName =
+ semantics::omp::MangledIntrinsicOperatorReductionName(
+ ReductionProcessor::toParserIntrinsicOperator(op),
+ semaCtx);
+ return semantics::omp::FindUserReductionSymbol(
+ converter.getCurrentScope(), mangledName,
+ objectType) != nullptr;
+ },
+ [&](const clause::DefinedOperator::DefinedOpName &op) {
+ const semantics::Symbol *operatorSymbol = op.v.sym();
+ return operatorSymbol &&
+ semantics::omp::FindOperatorUserReductionSymbol(
+ converter.getCurrentScope(), *operatorSymbol,
+ objectType);
+ },
+ },
+ definedOperator.u);
+ },
+ [&](const clause::ProcedureDesignator &procedureDesignator) {
+ const semantics::Symbol *symbol = procedureDesignator.v.sym();
+ return (symbol &&
+ symbol->GetUltimate()
+ .detailsIf<semantics::UserReductionDetails>()) ||
+ ReductionProcessor::findUserDefinedReductionForIntrinsic(
+ converter.getCurrentScope(), procedureDesignator,
+ objectType) != nullptr;
+ },
+ },
+ reductionOperator.u);
+}
+
+template <typename ReductionClause>
+static bool
+hasUserDefinedArrayElementReduction(const List<Clause> &clauses,
+ lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx) {
+ bool found = false;
+ ClauseFinder::findRepeatableClause<ReductionClause>(
+ clauses,
+ [&](const ReductionClause &reductionClause, const parser::CharBlock &) {
+ if (found)
+ return;
+
+ const auto &reductionOperators =
+ std::get<clause::ReductionOperatorList>(reductionClause.t);
+ assert(reductionOperators.size() == 1 &&
+ "expected one reduction operator");
+ const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
+ found = llvm::any_of(objects, [&](const Object &object) {
+ return isArrayElementReductionObject(object) &&
+ isUserDefinedReductionOperator(reductionOperators.front(),
+ object, converter, semaCtx);
+ });
+ });
+ return found;
}
/// Structure holding the information needed to create and bind entry block
@@ -4369,6 +4447,11 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
genTaskClauses(converter, semaCtx, symTable, stmtCtx, item->clauses, loc,
clauseOps, inReductionObjects);
+ if (hasUserDefinedArrayElementReduction<clause::InReduction>(
+ item->clauses, converter, semaCtx))
+ TODO(loc, "TASK construct with IN_REDUCTION of an array element using a "
+ "user-defined reduction");
+
if (!enableDelayedPrivatization) {
if (hasArrayElementReductionObject(inReductionObjects))
TODO(loc, "TASK construct with IN_REDUCTION of an array element when "
@@ -4420,6 +4503,12 @@ genTaskgroupOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
genTaskgroupClauses(converter, semaCtx, item->clauses, loc, clauseOps,
taskReductionObjects);
+ if (hasUserDefinedArrayElementReduction<clause::TaskReduction>(
+ item->clauses, converter, semaCtx))
+ TODO(loc,
+ "TASKGROUP construct with TASK_REDUCTION of an array element using a "
+ "user-defined reduction");
+
ObjectEntryBlockArgs taskgroupArgs;
taskgroupArgs.taskReduction.objects = taskReductionObjects;
taskgroupArgs.taskReduction.vars = clauseOps.taskReductionVars;
@@ -5012,6 +5101,16 @@ static mlir::omp::TaskloopContextOp genStandaloneTaskloop(
enableDelayedPrivatization, symTable);
dsp.processStep1(&taskloopClauseOps);
+ if (hasUserDefinedArrayElementReduction<clause::InReduction>(
+ item->clauses, converter, semaCtx))
+ TODO(loc,
+ "TASKLOOP construct with IN_REDUCTION of an array element using a "
+ "user-defined reduction");
+ if (hasUserDefinedArrayElementReduction<clause::Reduction>(
+ item->clauses, converter, semaCtx))
+ TODO(loc, "TASKLOOP construct with REDUCTION of an array element using a "
+ "user-defined reduction");
+
llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols =
enableDelayedPrivatization ? dsp.getDelayedPrivSymbols()
: dsp.getAllSymbolsToPrivatize().getArrayRef();
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 5badf02aee97a..0ebbd4bc73c26 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -37,6 +37,18 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/task-shared-element.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-ELEMENT
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/task-shared-full-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-FULL-SECTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/task-shared-full-section.f90 2>&1 | FileCheck %s --check-prefix=EAGER-TASK-SHARED-FULL-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASK-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASK-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskgroup-udr-element.f90 2>&1 | FileCheck %s --check-prefix=TASKGROUP-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskgroup-udr-element.f90 2>&1 | FileCheck %s --check-prefix=TASKGROUP-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 --enable-delayed-privatization=false -o - %t/taskloop-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-max-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-MAX-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-max-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-MAX-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-UDR-SHARED-ELEMENT
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
@@ -61,6 +73,11 @@
! TASKLOOP-IN-SHARED-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of a partial array section
! EAGER-TASK-SHARED-ELEMENT: not yet implemented: TASK construct with IN_REDUCTION of an array element when delayed privatization is disabled
! EAGER-TASK-SHARED-FULL-SECTION: not yet implemented: TASK construct with IN_REDUCTION when delayed privatization is disabled
+! TASK-UDR-SHARED-ELEMENT: not yet implemented: TASK construct with IN_REDUCTION of an array element using a user-defined reduction
+! TASKGROUP-UDR-ELEMENT: not yet implemented: TASKGROUP construct with TASK_REDUCTION of an array element using a user-defined reduction
+! TASKLOOP-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with REDUCTION of an array element using a user-defined reduction
+! TASKLOOP-MAX-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with REDUCTION of an array element using a user-defined reduction
+! TASKLOOP-IN-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element using a user-defined reduction
! TARGET-ELEMENT: not yet implemented: TARGET construct with IN_REDUCTION of an array element
!--- task.f90
@@ -95,6 +112,26 @@ subroutine task_in_reduction_shared_full_section(a)
!$omp end taskgroup
end subroutine
+!--- task-udr-shared-element.f90
+subroutine task_in_reduction_udr_shared_element(a)
+ integer :: a(4)
+ !$omp declare reduction(+: integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp task shared(a) in_reduction(+: a(2))
+ a(2) = a(2) + 1
+ !$omp end task
+end subroutine
+
+!--- taskgroup-udr-element.f90
+subroutine taskgroup_udr_element(a)
+ integer :: a(4)
+ !$omp declare reduction(+: integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp taskgroup task_reduction(+: a(2))
+ a(2) = a(2) + 1
+ !$omp end taskgroup
+end subroutine
+
!--- target-element.f90
subroutine target_in_reduction_element(a)
integer :: a(4)
@@ -167,6 +204,52 @@ subroutine taskloop_udr_shared_section(a)
!$omp end parallel
end subroutine
+!--- taskloop-udr-shared-element.f90
+subroutine taskloop_udr_shared_element(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop reduction(myred : a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+!--- taskloop-max-udr-shared-element.f90
+subroutine taskloop_max_udr_shared_element(a)
+ integer :: a(4), i
+ intrinsic :: max
+ !$omp declare reduction(max : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop reduction(max : a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+!--- taskloop-in-udr-shared-element.f90
+subroutine taskloop_in_reduction_udr_shared_element(a)
+ integer :: a(4), i
+ !$omp declare reduction(+: integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel shared(a)
+ !$omp single
+ !$omp taskloop in_reduction(+: a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
!--- taskloop-in-shared-section.f90
subroutine taskloop_in_reduction_shared_section(a)
integer :: a(4, 4), i
>From 9dfb7b64fd5baecfff5d9e2aae6c6663cfba3f4b Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 24 Aug 2026 11:23:47 +0100
Subject: [PATCH 14/20] Diagnose partial task reduction sections
Partial array sections lower as whole-array reductions even when their
base is shared. Existing checks tied the diagnostic to privatization and
therefore missed ordinary task and taskgroup constructs.
Diagnose partial sections independently of their base data-sharing
attribute.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 7 +++++++
...duction-array-element-task-privatization.f90 | 17 ++++++++++++++---
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 8ce3c9ab36dec..59d89d029b4e5 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4452,6 +4452,9 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
TODO(loc, "TASK construct with IN_REDUCTION of an array element using a "
"user-defined reduction");
+ if (hasPartialArrayReductionObject(inReductionObjects, semaCtx))
+ TODO(loc, "TASK construct with IN_REDUCTION of a partial array section");
+
if (!enableDelayedPrivatization) {
if (hasArrayElementReductionObject(inReductionObjects))
TODO(loc, "TASK construct with IN_REDUCTION of an array element when "
@@ -4509,6 +4512,10 @@ genTaskgroupOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
"TASKGROUP construct with TASK_REDUCTION of an array element using a "
"user-defined reduction");
+ if (hasPartialArrayReductionObject(taskReductionObjects, semaCtx))
+ TODO(loc,
+ "TASKGROUP construct with TASK_REDUCTION of a partial array section");
+
ObjectEntryBlockArgs taskgroupArgs;
taskgroupArgs.taskReduction.objects = taskReductionObjects;
taskgroupArgs.taskReduction.vars = clauseOps.taskReductionVars;
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 0ebbd4bc73c26..e8eaf00f274b7 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -49,6 +49,8 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-max-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-MAX-UDR-SHARED-ELEMENT
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-UDR-SHARED-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop-in-udr-shared-element.f90 2>&1 | FileCheck %s --check-prefix=TASKLOOP-IN-UDR-SHARED-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SHARED-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SHARED-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
@@ -59,8 +61,8 @@
! TASK: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
-! TASK-SECTION: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
-! TASK-RANK-TWO-SECTION: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASK-SECTION: not yet implemented: TASKGROUP construct with TASK_REDUCTION of a partial array section
+! TASK-RANK-TWO-SECTION: not yet implemented: TASKGROUP construct with TASK_REDUCTION of a partial array section
! TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-IN: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
@@ -68,7 +70,7 @@
! EAGER-TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-UDR-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
-! TASK-CROSS-SCOPE-BOUNDS: not yet implemented: TASK construct with IN_REDUCTION of an array element or section whose base array is privatized
+! TASK-CROSS-SCOPE-BOUNDS: not yet implemented: TASKGROUP construct with TASK_REDUCTION of a partial array section
! TASKLOOP-UDR-SHARED-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of a partial array section
! TASKLOOP-IN-SHARED-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of a partial array section
! EAGER-TASK-SHARED-ELEMENT: not yet implemented: TASK construct with IN_REDUCTION of an array element when delayed privatization is disabled
@@ -78,6 +80,7 @@
! TASKLOOP-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with REDUCTION of an array element using a user-defined reduction
! TASKLOOP-MAX-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with REDUCTION of an array element using a user-defined reduction
! TASKLOOP-IN-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element using a user-defined reduction
+! TASK-SHARED-SECTION: not yet implemented: TASK construct with IN_REDUCTION of a partial array section
! TARGET-ELEMENT: not yet implemented: TARGET construct with IN_REDUCTION of an array element
!--- task.f90
@@ -132,6 +135,14 @@ subroutine taskgroup_udr_element(a)
!$omp end taskgroup
end subroutine
+!--- task-shared-section.f90
+subroutine task_in_reduction_shared_section(a)
+ integer :: a(4)
+ !$omp task shared(a) in_reduction(+: a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end task
+end subroutine
+
!--- target-element.f90
subroutine target_in_reduction_element(a)
integer :: a(4)
>From 8573e0461f15630d5ddadd1ef5a87239ebef0138 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 24 Aug 2026 14:28:43 +0100
Subject: [PATCH 15/20] Diagnose partial task-modifier reductions
Task-modified reductions currently lower partial array sections through
their whole-array descriptor. A user-defined initializer or combiner can
therefore update elements outside the reduction item.
Diagnose partial sections on parallel and worksharing reduction-scoping
constructs while continuing to accept full-extent sections.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 18 ++++++
...ction-array-element-task-privatization.f90 | 57 +++++++++++++++++++
...-array-full-section-task-privatization.f90 | 57 +++++++++++++++++++
3 files changed, 132 insertions(+)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 59d89d029b4e5..934decbd67859 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -193,6 +193,16 @@ hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
return false;
}
+static void checkTaskModifierPartialArrayReduction(
+ mlir::Location loc, semantics::SemanticsContext &semaCtx,
+ mlir::omp::ReductionModifierAttr reductionMod,
+ llvm::ArrayRef<Object> reductionObjects) {
+ if (reductionMod &&
+ reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
+ hasPartialArrayReductionObject(reductionObjects, semaCtx))
+ TODO(loc, "REDUCTION with TASK modifier of a partial array section");
+}
+
static bool isArrayElementReductionObject(const Object &object) {
return object.ref() && object.ref()->Rank() == 0 &&
evaluate::IsArrayElement(*object.ref(), /*intoSubstring=*/false);
@@ -2543,6 +2553,8 @@ static void genParallelClauses(
cp.processProcBind(clauseOps);
cp.processReduction(loc, clauseOps, reductionObjects);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
+ reductionObjects);
}
static void genScanClauses(lower::AbstractConverter &converter,
@@ -2564,6 +2576,8 @@ genSectionsClauses(lower::AbstractConverter &converter,
cp.processAllocate(clauseOps);
cp.processNowait(clauseOps);
cp.processReduction(loc, clauseOps, reductionObjects);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
+ reductionObjects);
// TODO Support delayed privatization.
}
@@ -2664,6 +2678,8 @@ static void genScopeClauses(lower::AbstractConverter &converter,
cp.processAllocate(clauseOps);
cp.processNowait(clauseOps);
cp.processReduction(loc, clauseOps, reductionObjects);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
+ reductionObjects);
}
static void genSingleClauses(lower::AbstractConverter &converter,
@@ -2890,6 +2906,8 @@ static void genWsloopClauses(
cp.processOrder(clauseOps);
cp.processOrdered(clauseOps);
cp.processReduction(loc, clauseOps, reductionObjects, reductionVarCache);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
+ reductionObjects);
cp.processSchedule(stmtCtx, clauseOps);
cp.processLinear(clauseOps);
}
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index e8eaf00f274b7..8819d11b66c7c 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -53,6 +53,14 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-shared-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SHARED-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/target-element.f90 2>&1 | FileCheck %s --check-prefix=TARGET-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/parallel-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=PARALLEL-TASK-UDR-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/parallel-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=PARALLEL-TASK-UDR-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/sections-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=SECTIONS-TASK-UDR-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/sections-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=SECTIONS-TASK-UDR-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-TASK-UDR-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-TASK-UDR-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-SECTION
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -82,6 +90,10 @@
! TASKLOOP-IN-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element using a user-defined reduction
! TASK-SHARED-SECTION: not yet implemented: TASK construct with IN_REDUCTION of a partial array section
! TARGET-ELEMENT: not yet implemented: TARGET construct with IN_REDUCTION of an array element
+! PARALLEL-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
+! SECTIONS-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
+! SCOPE-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
+! DO-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
!--- task.f90
subroutine task_reduction_element(a)
@@ -309,3 +321,48 @@ subroutine inner(m)
!$omp end taskgroup
end subroutine
end subroutine
+
+!--- parallel-task-udr-section.f90
+subroutine parallel_task_udr_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel reduction(task, + : a(2:3))
+ !$omp target map(tofrom: a) in_reduction(+ : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end target
+ !$omp end parallel
+end subroutine
+
+!--- sections-task-udr-section.f90
+subroutine sections_task_udr_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp sections reduction(task, + : a(2:3))
+ !$omp section
+ a(2:3) = a(2:3) + 1
+ !$omp end sections
+end subroutine
+
+!--- scope-task-udr-section.f90
+subroutine scope_task_udr_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp scope reduction(task, + : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end scope
+end subroutine
+
+!--- do-task-udr-section.f90
+subroutine do_task_udr_section(a)
+ integer :: a(4), i
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp do reduction(task, + : a(2:3))
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ end do
+ !$omp end do
+end subroutine
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
index d30a71de4debc..ccaf4cc554316 100644
--- a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -3,6 +3,8 @@
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/task-default.f90 | FileCheck %s --check-prefix=TASK-DEFAULT --implicit-check-not="not yet implemented" --implicit-check-not=Ea_firstprivate --implicit-check-not=Ea_private
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not="not yet implemented" --implicit-check-not=Ea_firstprivate
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -mmlir --enable-delayed-privatization=false -o - %t/taskloop.f90 | FileCheck %s --check-prefix=TASKLOOP --implicit-check-not="not yet implemented" --implicit-check-not=Ea_firstprivate
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/task-modifier.f90 | FileCheck %s --check-prefix=TASK-MODIFIER --implicit-check-not="not yet implemented"
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/task-modifier.f90 | FileCheck %s --check-prefix=TASK-MODIFIER --implicit-check-not="not yet implemented"
! A full-extent section uses the same descriptor as its base array. Check that
! it is bound only to the reduction argument, rather than also being captured
@@ -100,6 +102,19 @@
! TASKLOOP-LABEL: func.func @_QPtaskloop_rank_two_explicit_full_section
! TASKLOOP: omp.taskloop.context {{.*}}reduction(byref @add_reduction_byref_box_4x4xi32
+! TASK-MODIFIER-LABEL: func.func @_QPparallel_task_udr_full_section
+! TASK-MODIFIER: omp.parallel reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-MODIFIER: omp.target {{.*}}in_reduction(byref @{{.*}} %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+
+! TASK-MODIFIER-LABEL: func.func @_QPsections_task_udr_full_section
+! TASK-MODIFIER: omp.sections reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+
+! TASK-MODIFIER-LABEL: func.func @_QPscope_task_udr_full_section
+! TASK-MODIFIER: omp.scope reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+
+! TASK-MODIFIER-LABEL: func.func @_QPdo_task_udr_full_section
+! TASK-MODIFIER: omp.wsloop {{.*}}reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+
!--- task.f90
subroutine task_full_section(a)
integer :: a(-2:1)
@@ -206,3 +221,45 @@ subroutine taskloop_rank_two_explicit_full_section(a)
!$omp end single
!$omp end parallel
end subroutine
+
+!--- task-modifier.f90
+subroutine parallel_task_udr_full_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel reduction(task, + : a(:))
+ !$omp target map(tofrom: a) in_reduction(+ : a(:))
+ a(:) = a(:) + 1
+ !$omp end target
+ !$omp end parallel
+end subroutine
+
+subroutine sections_task_udr_full_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp sections reduction(task, + : a(:))
+ !$omp section
+ a(:) = a(:) + 1
+ !$omp end sections
+end subroutine
+
+subroutine scope_task_udr_full_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp scope reduction(task, + : a(:))
+ a(:) = a(:) + 1
+ !$omp end scope
+end subroutine
+
+subroutine do_task_udr_full_section(a)
+ integer :: a(4), i
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp do reduction(task, + : a(:))
+ do i = 1, 1
+ a(:) = a(:) + i
+ end do
+ !$omp end do
+end subroutine
>From bf9bf23eb17be28beef5ebf3fd8b0950bc0ce5e7 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 25 Aug 2026 12:26:06 +0100
Subject: [PATCH 16/20] Diagnose task UDR array elements
Task-modified reductions currently lower user-defined reductions on
array elements through the whole-array descriptor. Their initializers
and combiners can therefore update elements outside the reduction item.
Diagnose these reductions on parallel and worksharing reduction-scoping
constructs while continuing to accept predefined array-element
reductions.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 21 +++++++
...ction-array-element-task-privatization.f90 | 55 +++++++++++++++++++
2 files changed, 76 insertions(+)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 934decbd67859..0f9e7fdff0060 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -299,6 +299,19 @@ hasUserDefinedArrayElementReduction(const List<Clause> &clauses,
return found;
}
+static void checkTaskModifierUserDefinedArrayElementReduction(
+ mlir::Location loc, lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx,
+ mlir::omp::ReductionModifierAttr reductionMod,
+ const List<Clause> &clauses) {
+ if (reductionMod &&
+ reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
+ hasUserDefinedArrayElementReduction<clause::Reduction>(clauses, converter,
+ semaCtx))
+ TODO(loc, "REDUCTION with TASK modifier of an array element using a "
+ "user-defined reduction");
+}
+
/// Structure holding the information needed to create and bind entry block
/// arguments associated to a single clause during OpenMP lowering.
struct ObjectEntryBlockArgsEntry {
@@ -2555,6 +2568,8 @@ static void genParallelClauses(
cp.processReduction(loc, clauseOps, reductionObjects);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
+ checkTaskModifierUserDefinedArrayElementReduction(
+ loc, converter, semaCtx, clauseOps.reductionMod, clauses);
}
static void genScanClauses(lower::AbstractConverter &converter,
@@ -2578,6 +2593,8 @@ genSectionsClauses(lower::AbstractConverter &converter,
cp.processReduction(loc, clauseOps, reductionObjects);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
+ checkTaskModifierUserDefinedArrayElementReduction(
+ loc, converter, semaCtx, clauseOps.reductionMod, clauses);
// TODO Support delayed privatization.
}
@@ -2680,6 +2697,8 @@ static void genScopeClauses(lower::AbstractConverter &converter,
cp.processReduction(loc, clauseOps, reductionObjects);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
+ checkTaskModifierUserDefinedArrayElementReduction(
+ loc, converter, semaCtx, clauseOps.reductionMod, clauses);
}
static void genSingleClauses(lower::AbstractConverter &converter,
@@ -2908,6 +2927,8 @@ static void genWsloopClauses(
cp.processReduction(loc, clauseOps, reductionObjects, reductionVarCache);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
+ checkTaskModifierUserDefinedArrayElementReduction(
+ loc, converter, semaCtx, clauseOps.reductionMod, clauses);
cp.processSchedule(stmtCtx, clauseOps);
cp.processLinear(clauseOps);
}
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 8819d11b66c7c..021beabde9bea 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -61,6 +61,14 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-TASK-UDR-SECTION
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-SECTION
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-section.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/parallel-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=PARALLEL-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/parallel-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=PARALLEL-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/sections-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SECTIONS-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/sections-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SECTIONS-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-ELEMENT
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -94,6 +102,10 @@
! SECTIONS-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
! SCOPE-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
! DO-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
+! PARALLEL-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
+! SECTIONS-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
+! SCOPE-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
+! DO-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
!--- task.f90
subroutine task_reduction_element(a)
@@ -366,3 +378,46 @@ subroutine do_task_udr_section(a)
end do
!$omp end do
end subroutine
+
+!--- parallel-task-udr-element.f90
+subroutine parallel_task_udr_element(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel reduction(task, + : a(2))
+ a(2) = a(2) + 1
+ !$omp end parallel
+end subroutine
+
+!--- sections-task-udr-element.f90
+subroutine sections_task_udr_element(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp sections reduction(task, + : a(2))
+ !$omp section
+ a(2) = a(2) + 1
+ !$omp end sections
+end subroutine
+
+!--- scope-task-udr-element.f90
+subroutine scope_task_udr_element(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp scope reduction(task, + : a(2))
+ a(2) = a(2) + 1
+ !$omp end scope
+end subroutine
+
+!--- do-task-udr-element.f90
+subroutine do_task_udr_element(a)
+ integer :: a(4), i
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp do reduction(task, + : a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end do
+end subroutine
>From 4320bbd7b18ad5c564858a6e706ae8eff50eb9a0 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 25 Aug 2026 12:27:24 +0100
Subject: [PATCH 17/20] Strengthen task reduction tests
Task-modifier full-section tests accepted any reduction declaration and
could not distinguish user-defined reductions from built-in ones.
Check exact UDR symbols and add a predefined max array-element control.
Assisted-by: Codex
---
...-array-full-section-task-privatization.f90 | 20 ++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90 b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
index ccaf4cc554316..c6993e2b37f8d 100644
--- a/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/reduction-array-full-section-task-privatization.f90
@@ -103,17 +103,20 @@
! TASKLOOP: omp.taskloop.context {{.*}}reduction(byref @add_reduction_byref_box_4x4xi32
! TASK-MODIFIER-LABEL: func.func @_QPparallel_task_udr_full_section
-! TASK-MODIFIER: omp.parallel reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
-! TASK-MODIFIER: omp.target {{.*}}in_reduction(byref @{{.*}} %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-MODIFIER: omp.parallel reduction(mod: task, byref @"_QQFparallel_task_udr_full_sectionop.+_byref_box_4xi32" %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-MODIFIER: omp.target {{.*}}in_reduction(byref @"_QQFparallel_task_udr_full_sectionop.+_byref_box_4xi32" %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
! TASK-MODIFIER-LABEL: func.func @_QPsections_task_udr_full_section
-! TASK-MODIFIER: omp.sections reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-MODIFIER: omp.sections reduction(mod: task, byref @"_QQFsections_task_udr_full_sectionop.+_byref_box_4xi32" %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
! TASK-MODIFIER-LABEL: func.func @_QPscope_task_udr_full_section
-! TASK-MODIFIER: omp.scope reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-MODIFIER: omp.scope reduction(mod: task, byref @"_QQFscope_task_udr_full_sectionop.+_byref_box_4xi32" %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
! TASK-MODIFIER-LABEL: func.func @_QPdo_task_udr_full_section
-! TASK-MODIFIER: omp.wsloop {{.*}}reduction(mod: task, byref @{{.*}} %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+! TASK-MODIFIER: omp.wsloop {{.*}}reduction(mod: task, byref @"_QQFdo_task_udr_full_sectionop.+_byref_box_4xi32" %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
+
+! TASK-MODIFIER-LABEL: func.func @_QPparallel_task_intrinsic_max_element
+! TASK-MODIFIER: omp.parallel reduction(mod: task, byref @max_byref_box_4xi32 %{{.*}} -> %{{.*}} : !fir.ref<!fir.box<!fir.array<4xi32>>>)
!--- task.f90
subroutine task_full_section(a)
@@ -263,3 +266,10 @@ subroutine do_task_udr_full_section(a)
end do
!$omp end do
end subroutine
+
+subroutine parallel_task_intrinsic_max_element(a)
+ integer :: a(4)
+ !$omp parallel reduction(task, max : a(2))
+ a(2) = max(a(2), 1)
+ !$omp end parallel
+end subroutine
>From 2ce84bfc47adcf9bdfa90f11884cc5570cfac092 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Sun, 6 Sep 2026 19:46:35 +0100
Subject: [PATCH 18/20] Diagnose UDR array element reductions
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 25 +++-----
...ction-array-element-task-privatization.f90 | 63 +++++++++++++++++--
2 files changed, 67 insertions(+), 21 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 0f9e7fdff0060..a318c77e70372 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -299,17 +299,12 @@ hasUserDefinedArrayElementReduction(const List<Clause> &clauses,
return found;
}
-static void checkTaskModifierUserDefinedArrayElementReduction(
+static void checkUserDefinedArrayElementReduction(
mlir::Location loc, lower::AbstractConverter &converter,
- semantics::SemanticsContext &semaCtx,
- mlir::omp::ReductionModifierAttr reductionMod,
- const List<Clause> &clauses) {
- if (reductionMod &&
- reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
- hasUserDefinedArrayElementReduction<clause::Reduction>(clauses, converter,
+ semantics::SemanticsContext &semaCtx, const List<Clause> &clauses) {
+ if (hasUserDefinedArrayElementReduction<clause::Reduction>(clauses, converter,
semaCtx))
- TODO(loc, "REDUCTION with TASK modifier of an array element using a "
- "user-defined reduction");
+ TODO(loc, "REDUCTION of an array element using a user-defined reduction");
}
/// Structure holding the information needed to create and bind entry block
@@ -2568,8 +2563,7 @@ static void genParallelClauses(
cp.processReduction(loc, clauseOps, reductionObjects);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
- checkTaskModifierUserDefinedArrayElementReduction(
- loc, converter, semaCtx, clauseOps.reductionMod, clauses);
+ checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
}
static void genScanClauses(lower::AbstractConverter &converter,
@@ -2593,8 +2587,7 @@ genSectionsClauses(lower::AbstractConverter &converter,
cp.processReduction(loc, clauseOps, reductionObjects);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
- checkTaskModifierUserDefinedArrayElementReduction(
- loc, converter, semaCtx, clauseOps.reductionMod, clauses);
+ checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
// TODO Support delayed privatization.
}
@@ -2697,8 +2690,7 @@ static void genScopeClauses(lower::AbstractConverter &converter,
cp.processReduction(loc, clauseOps, reductionObjects);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
- checkTaskModifierUserDefinedArrayElementReduction(
- loc, converter, semaCtx, clauseOps.reductionMod, clauses);
+ checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
}
static void genSingleClauses(lower::AbstractConverter &converter,
@@ -2927,8 +2919,7 @@ static void genWsloopClauses(
cp.processReduction(loc, clauseOps, reductionObjects, reductionVarCache);
checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
reductionObjects);
- checkTaskModifierUserDefinedArrayElementReduction(
- loc, converter, semaCtx, clauseOps.reductionMod, clauses);
+ checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
cp.processSchedule(stmtCtx, clauseOps);
cp.processLinear(clauseOps);
}
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 021beabde9bea..199ee499f0451 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -69,6 +69,14 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-TASK-UDR-ELEMENT
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-task-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-TASK-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/parallel-udr-element.f90 2>&1 | FileCheck %s --check-prefix=PARALLEL-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/parallel-udr-element.f90 2>&1 | FileCheck %s --check-prefix=PARALLEL-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/sections-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SECTIONS-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/sections-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SECTIONS-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=51 -o - %t/scope-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 -o - %t/scope-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/do-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-UDR-ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/do-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-UDR-ELEMENT
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -102,10 +110,14 @@
! SECTIONS-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
! SCOPE-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
! DO-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
-! PARALLEL-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
-! SECTIONS-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
-! SCOPE-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
-! DO-TASK-UDR-ELEMENT: not yet implemented: REDUCTION with TASK modifier of an array element using a user-defined reduction
+! PARALLEL-TASK-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! SECTIONS-TASK-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! SCOPE-TASK-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! DO-TASK-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! PARALLEL-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! SECTIONS-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! SCOPE-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! DO-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
!--- task.f90
subroutine task_reduction_element(a)
@@ -421,3 +433,46 @@ subroutine do_task_udr_element(a)
end do
!$omp end do
end subroutine
+
+!--- parallel-udr-element.f90
+subroutine parallel_udr_element(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel reduction(myred : a(2))
+ a(2) = a(2) + 1
+ !$omp end parallel
+end subroutine
+
+!--- sections-udr-element.f90
+subroutine sections_udr_element(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp sections reduction(myred : a(2))
+ !$omp section
+ a(2) = a(2) + 1
+ !$omp end sections
+end subroutine
+
+!--- scope-udr-element.f90
+subroutine scope_udr_element(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp scope reduction(myred : a(2))
+ a(2) = a(2) + 1
+ !$omp end scope
+end subroutine
+
+!--- do-udr-element.f90
+subroutine do_udr_element(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp do reduction(myred : a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end do
+end subroutine
>From eee2596f436d7773cce8a474edbdbf5d02d6c0f7 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Sun, 6 Sep 2026 19:57:04 +0100
Subject: [PATCH 19/20] Validate reduction modifiers per clause
Reduction lowering flattens repeated clauses into a single operand list
and operation-wide modifier. Applying that modifier to every reduction
object makes modifier diagnostics depend on clause order.
Inspect each clause's modifier and object list before flattening. Reject
different effective modifiers because the current MLIR representation
cannot encode them without changing semantics.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 12 +++
flang/lib/Lower/OpenMP/OpenMP.cpp | 48 ++++++----
...ction-array-element-task-privatization.f90 | 88 +++++++++++++++++++
3 files changed, 129 insertions(+), 19 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index b2f511c2c9cd1..c682061423d80 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -2119,6 +2119,18 @@ bool ClauseProcessor::processReduction(
llvm::SmallVectorImpl<Object> &outReductionObjects,
llvm::DenseMap<const semantics::Symbol *, mlir::Value> *reductionVarCache)
const {
+ std::optional<ReductionModifier> commonModifier;
+ findRepeatableClause<omp::clause::Reduction>(
+ [&](const omp::clause::Reduction &clause, const parser::CharBlock &) {
+ const auto &modifier =
+ std::get<std::optional<ReductionModifier>>(clause.t);
+ ReductionModifier effectiveModifier =
+ modifier.value_or(ReductionModifier::Default);
+ if (commonModifier && *commonModifier != effectiveModifier)
+ TODO(currentLocation, "REDUCTION clauses with different modifiers");
+ commonModifier = effectiveModifier;
+ });
+
return findRepeatableClause<omp::clause::Reduction>(
[&](const omp::clause::Reduction &clause, const parser::CharBlock &) {
llvm::SmallVector<mlir::Value> reductionVars;
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index a318c77e70372..2af14cda46eec 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -193,13 +193,27 @@ hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
return false;
}
-static void checkTaskModifierPartialArrayReduction(
- mlir::Location loc, semantics::SemanticsContext &semaCtx,
- mlir::omp::ReductionModifierAttr reductionMod,
- llvm::ArrayRef<Object> reductionObjects) {
- if (reductionMod &&
- reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
- hasPartialArrayReductionObject(reductionObjects, semaCtx))
+static void
+checkTaskModifierPartialArrayReduction(mlir::Location loc,
+ semantics::SemanticsContext &semaCtx,
+ const List<Clause> &clauses) {
+ bool found = false;
+ ClauseFinder::findRepeatableClause<clause::Reduction>(
+ clauses,
+ [&](const clause::Reduction &reductionClause, const parser::CharBlock &) {
+ if (found)
+ return;
+
+ using ReductionModifier = clause::Reduction::ReductionModifier;
+ const auto &modifier =
+ std::get<std::optional<ReductionModifier>>(reductionClause.t);
+ if (!modifier || *modifier != ReductionModifier::Task)
+ return;
+
+ const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
+ found = hasPartialArrayReductionObject(objects, semaCtx);
+ });
+ if (found)
TODO(loc, "REDUCTION with TASK modifier of a partial array section");
}
@@ -2560,10 +2574,9 @@ static void genParallelClauses(
cp.processNumThreads(stmtCtx, clauseOps);
cp.processProcBind(clauseOps);
- cp.processReduction(loc, clauseOps, reductionObjects);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
- reductionObjects);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
+ cp.processReduction(loc, clauseOps, reductionObjects);
}
static void genScanClauses(lower::AbstractConverter &converter,
@@ -2584,10 +2597,9 @@ genSectionsClauses(lower::AbstractConverter &converter,
ClauseProcessor cp(converter, semaCtx, clauses);
cp.processAllocate(clauseOps);
cp.processNowait(clauseOps);
- cp.processReduction(loc, clauseOps, reductionObjects);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
- reductionObjects);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
+ cp.processReduction(loc, clauseOps, reductionObjects);
// TODO Support delayed privatization.
}
@@ -2687,10 +2699,9 @@ static void genScopeClauses(lower::AbstractConverter &converter,
ClauseProcessor cp(converter, semaCtx, clauses);
cp.processAllocate(clauseOps);
cp.processNowait(clauseOps);
- cp.processReduction(loc, clauseOps, reductionObjects);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
- reductionObjects);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
+ cp.processReduction(loc, clauseOps, reductionObjects);
}
static void genSingleClauses(lower::AbstractConverter &converter,
@@ -2916,10 +2927,9 @@ static void genWsloopClauses(
cp.processNowait(clauseOps);
cp.processOrder(clauseOps);
cp.processOrdered(clauseOps);
- cp.processReduction(loc, clauseOps, reductionObjects, reductionVarCache);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauseOps.reductionMod,
- reductionObjects);
+ checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
+ cp.processReduction(loc, clauseOps, reductionObjects, reductionVarCache);
cp.processSchedule(stmtCtx, clauseOps);
cp.processLinear(clauseOps);
}
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 199ee499f0451..8e49626fce6f1 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -77,6 +77,16 @@
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 -o - %t/scope-udr-element.f90 2>&1 | FileCheck %s --check-prefix=SCOPE-UDR-ELEMENT
! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/do-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-UDR-ELEMENT
! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %t/do-udr-element.f90 2>&1 | FileCheck %s --check-prefix=DO-UDR-ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-first-section.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-FIRST-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-first-section.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-FIRST-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-last-section.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-LAST-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-last-section.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-LAST-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-first.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-FIRST
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-first.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-FIRST
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-last.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-LAST
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-mixed-task-last.f90 2>&1 | FileCheck %s --check-prefix=MIXED-TASK-LAST
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-equivalent-modifiers.f90 | FileCheck %s --check-prefix=EQUIVALENT-MODIFIERS --implicit-check-not="not yet implemented"
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-equivalent-modifiers.f90 | FileCheck %s --check-prefix=EQUIVALENT-MODIFIERS --implicit-check-not="not yet implemented"
! An array element or section in a task reduction and the implicitly
! firstprivate base array are represented by separate block arguments. Reject
@@ -118,6 +128,15 @@
! SECTIONS-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
! SCOPE-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
! DO-UDR-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! MIXED-TASK-FIRST-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
+! MIXED-TASK-LAST-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
+! MIXED-TASK-FIRST: not yet implemented: REDUCTION clauses with different modifiers
+! MIXED-TASK-LAST: not yet implemented: REDUCTION clauses with different modifiers
+
+! EQUIVALENT-MODIFIERS-LABEL: func.func @_QPequivalent_default_modifiers
+! EQUIVALENT-MODIFIERS: omp.wsloop {{.*}}reduction(mod: defaultmod
+! EQUIVALENT-MODIFIERS-LABEL: func.func @_QPequivalent_task_modifiers
+! EQUIVALENT-MODIFIERS: omp.wsloop {{.*}}reduction(mod: task
!--- task.f90
subroutine task_reduction_element(a)
@@ -476,3 +495,72 @@ subroutine do_udr_element(a)
end do
!$omp end do
end subroutine
+
+!--- do-mixed-task-first-section.f90
+subroutine do_mixed_task_first_section(a, x)
+ integer :: a(4), x, i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp do reduction(task, myred : a(2:3)) reduction(default, + : x)
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ x = x + i
+ end do
+ !$omp end do
+end subroutine
+
+!--- do-mixed-task-last-section.f90
+subroutine do_mixed_task_last_section(a, x)
+ integer :: a(4), x, i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp do reduction(default, + : x) reduction(task, myred : a(2:3))
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ x = x + i
+ end do
+ !$omp end do
+end subroutine
+
+!--- do-mixed-task-first.f90
+subroutine do_mixed_task_first(x, y)
+ integer :: x, y, i
+ !$omp do reduction(task, + : x) reduction(+ : y)
+ do i = 1, 1
+ x = x + i
+ y = y + i
+ end do
+ !$omp end do
+end subroutine
+
+!--- do-mixed-task-last.f90
+subroutine do_mixed_task_last(x, y)
+ integer :: x, y, i
+ !$omp do reduction(+ : y) reduction(task, + : x)
+ do i = 1, 1
+ x = x + i
+ y = y + i
+ end do
+ !$omp end do
+end subroutine
+
+!--- do-equivalent-modifiers.f90
+subroutine equivalent_default_modifiers(x, y)
+ integer :: x, y, i
+ !$omp do reduction(default, + : x) reduction(+ : y)
+ do i = 1, 1
+ x = x + i
+ y = y + i
+ end do
+ !$omp end do
+end subroutine
+
+subroutine equivalent_task_modifiers(x, y)
+ integer :: x, y, i
+ !$omp do reduction(task, + : x) reduction(task, + : y)
+ do i = 1, 1
+ x = x + i
+ y = y + i
+ end do
+ !$omp end do
+end subroutine
>From 3291d2b42a45bcde6107f2951af0e0dccec5ee93 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 7 Sep 2026 11:41:27 +0100
Subject: [PATCH 20/20] Validate UDR reduction subobjects centrally
User-defined reductions on array elements and partial sections are
lowered through the descriptor of the base array. Construct-specific
checks left some reduction consumers able to initialize and combine
the whole array.
Classify reduction objects in the shared clause processors before
lowering discards their designators. Preserve whole-array sections and
predefined reductions, and cover every reduction construct family.
Assisted-by: Codex
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 136 ++++++++++++
flang/lib/Lower/OpenMP/OpenMP.cpp | 147 -------------
...ction-array-element-task-privatization.f90 | 16 +-
.../Todo/reduction-udr-subobject-matrix.f90 | 193 ++++++++++++++++++
4 files changed, 337 insertions(+), 155 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/Todo/reduction-udr-subobject-matrix.f90
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index c682061423d80..2ad45f7328117 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -21,6 +21,7 @@
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Support/InternalNames.h"
+#include "flang/Semantics/openmp-utils.h"
#include "flang/Semantics/tools.h"
#include "flang/Utils/OpenMP.h"
#include "llvm/Frontend/OpenMP/OMP.h.inc"
@@ -1705,11 +1706,132 @@ getObjectsSyms(llvm::ArrayRef<Object> objects) {
return syms;
}
+enum class UserDefinedReductionSubobject {
+ None,
+ ArrayElement,
+ PartialArraySection,
+};
+
+static bool isUserDefinedReductionOperator(
+ const omp::clause::ReductionOperator &reductionOperator,
+ const Object &object, lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx) {
+ const semantics::Symbol *objectSymbol = object.sym();
+ const semantics::DeclTypeSpec *objectType =
+ objectSymbol ? objectSymbol->GetUltimate().GetType() : nullptr;
+ if (!objectType)
+ return false;
+
+ return common::visit(
+ common::visitors{
+ [&](const omp::clause::DefinedOperator &definedOperator) {
+ return common::visit(
+ common::visitors{
+ [&](const omp::clause::DefinedOperator::IntrinsicOperator
+ &op) {
+ using IntrinsicOperator =
+ omp::clause::DefinedOperator::IntrinsicOperator;
+ switch (op) {
+ case IntrinsicOperator::Add:
+ case IntrinsicOperator::Multiply:
+ case IntrinsicOperator::AND:
+ case IntrinsicOperator::OR:
+ case IntrinsicOperator::EQV:
+ case IntrinsicOperator::NEQV:
+ break;
+ default:
+ return false;
+ }
+
+ parser::CharBlock mangledName =
+ semantics::omp::MangledIntrinsicOperatorReductionName(
+ ReductionProcessor::toParserIntrinsicOperator(op),
+ semaCtx);
+ return semantics::omp::FindUserReductionSymbol(
+ converter.getCurrentScope(), mangledName,
+ objectType) != nullptr;
+ },
+ [&](const omp::clause::DefinedOperator::DefinedOpName &op) {
+ const semantics::Symbol *operatorSymbol = op.v.sym();
+ return operatorSymbol &&
+ semantics::omp::FindOperatorUserReductionSymbol(
+ converter.getCurrentScope(), *operatorSymbol,
+ objectType);
+ },
+ },
+ definedOperator.u);
+ },
+ [&](const omp::clause::ProcedureDesignator &procedureDesignator) {
+ const semantics::Symbol *symbol = procedureDesignator.v.sym();
+ return (symbol &&
+ symbol->GetUltimate()
+ .detailsIf<semantics::UserReductionDetails>()) ||
+ ReductionProcessor::findUserDefinedReductionForIntrinsic(
+ converter.getCurrentScope(), procedureDesignator,
+ objectType) != nullptr;
+ },
+ },
+ reductionOperator.u);
+}
+
+template <typename ReductionClause>
+static UserDefinedReductionSubobject
+getUserDefinedReductionSubobject(const ReductionClause &clause,
+ lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx) {
+ // ReductionProcessor receives only base symbols, so classify unsupported
+ // subobjects before lowering discards their designators.
+ const auto &reductionOperators =
+ std::get<omp::clause::ReductionOperatorList>(clause.t);
+ assert(reductionOperators.size() == 1 && "expected one reduction operator");
+
+ for (const Object &object : std::get<omp::ObjectList>(clause.t)) {
+ if (!object.ref())
+ continue;
+
+ UserDefinedReductionSubobject subobject =
+ UserDefinedReductionSubobject::None;
+ if (object.ref()->Rank() == 0 &&
+ evaluate::IsArrayElement(*object.ref(), /*intoSubstring=*/false))
+ subobject = UserDefinedReductionSubobject::ArrayElement;
+ else if (evaluate::IsArraySection(*object.ref()) &&
+ !isWholeArraySection(object, semaCtx))
+ subobject = UserDefinedReductionSubobject::PartialArraySection;
+ if (subobject != UserDefinedReductionSubobject::None &&
+ isUserDefinedReductionOperator(reductionOperators.front(), object,
+ converter, semaCtx))
+ return subobject;
+ }
+ return UserDefinedReductionSubobject::None;
+}
+
+template <typename ReductionClause>
+static void checkUserDefinedReductionSubobject(
+ mlir::Location currentLocation, llvm::StringRef clauseName,
+ const ReductionClause &clause, lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx) {
+ switch (getUserDefinedReductionSubobject(clause, converter, semaCtx)) {
+ case UserDefinedReductionSubobject::ArrayElement:
+ TODO(currentLocation,
+ llvm::Twine(clauseName) +
+ " of an array element using a user-defined reduction");
+ case UserDefinedReductionSubobject::PartialArraySection:
+ TODO(currentLocation,
+ llvm::Twine(clauseName) +
+ " of a partial array section using a user-defined reduction");
+ case UserDefinedReductionSubobject::None:
+ return;
+ }
+ llvm_unreachable("invalid user-defined reduction subobject");
+}
+
bool ClauseProcessor::processInReduction(
mlir::Location currentLocation, mlir::omp::InReductionClauseOps &result,
llvm::SmallVectorImpl<Object> &outReductionObjects) const {
return findRepeatableClause<omp::clause::InReduction>(
[&](const omp::clause::InReduction &clause, const parser::CharBlock &) {
+ checkUserDefinedReductionSubobject(currentLocation, "IN_REDUCTION",
+ clause, converter, semaCtx);
llvm::SmallVector<mlir::Value> inReductionVars;
llvm::SmallVector<bool> inReduceVarByRef;
llvm::SmallVector<mlir::Attribute> inReductionDeclSymbols;
@@ -2126,6 +2248,18 @@ bool ClauseProcessor::processReduction(
std::get<std::optional<ReductionModifier>>(clause.t);
ReductionModifier effectiveModifier =
modifier.value_or(ReductionModifier::Default);
+ const omp::ObjectList &objects = std::get<omp::ObjectList>(clause.t);
+ if (effectiveModifier == ReductionModifier::Task &&
+ llvm::any_of(objects, [&](const Object &object) {
+ return object.ref() && evaluate::IsArraySection(*object.ref()) &&
+ !isWholeArraySection(object, semaCtx);
+ }))
+ TODO(currentLocation,
+ "REDUCTION with TASK modifier of a partial array section");
+
+ checkUserDefinedReductionSubobject(currentLocation, "REDUCTION", clause,
+ converter, semaCtx);
+
if (commonModifier && *commonModifier != effectiveModifier)
TODO(currentLocation, "REDUCTION clauses with different modifiers");
commonModifier = effectiveModifier;
@@ -2169,6 +2303,8 @@ bool ClauseProcessor::processTaskReduction(
llvm::SmallVectorImpl<Object> &outReductionObjects) const {
return findRepeatableClause<omp::clause::TaskReduction>(
[&](const omp::clause::TaskReduction &clause, const parser::CharBlock &) {
+ checkUserDefinedReductionSubobject(currentLocation, "TASK_REDUCTION",
+ clause, converter, semaCtx);
llvm::SmallVector<mlir::Value> taskReductionVars;
llvm::SmallVector<bool> taskReduceVarByRef;
llvm::SmallVector<mlir::Attribute> taskReductionDeclSymbols;
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 2af14cda46eec..6eb662bf9593b 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -193,30 +193,6 @@ hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
return false;
}
-static void
-checkTaskModifierPartialArrayReduction(mlir::Location loc,
- semantics::SemanticsContext &semaCtx,
- const List<Clause> &clauses) {
- bool found = false;
- ClauseFinder::findRepeatableClause<clause::Reduction>(
- clauses,
- [&](const clause::Reduction &reductionClause, const parser::CharBlock &) {
- if (found)
- return;
-
- using ReductionModifier = clause::Reduction::ReductionModifier;
- const auto &modifier =
- std::get<std::optional<ReductionModifier>>(reductionClause.t);
- if (!modifier || *modifier != ReductionModifier::Task)
- return;
-
- const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
- found = hasPartialArrayReductionObject(objects, semaCtx);
- });
- if (found)
- TODO(loc, "REDUCTION with TASK modifier of a partial array section");
-}
-
static bool isArrayElementReductionObject(const Object &object) {
return object.ref() && object.ref()->Rank() == 0 &&
evaluate::IsArrayElement(*object.ref(), /*intoSubstring=*/false);
@@ -227,100 +203,6 @@ hasArrayElementReductionObject(llvm::ArrayRef<Object> reductionObjects) {
return llvm::any_of(reductionObjects, isArrayElementReductionObject);
}
-static bool isUserDefinedReductionOperator(
- const clause::ReductionOperator &reductionOperator, const Object &object,
- lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx) {
- const semantics::Symbol *objectSymbol = object.sym();
- const semantics::DeclTypeSpec *objectType =
- objectSymbol ? objectSymbol->GetUltimate().GetType() : nullptr;
- if (!objectType)
- return false;
-
- return common::visit(
- common::visitors{
- [&](const clause::DefinedOperator &definedOperator) {
- return common::visit(
- common::visitors{
- [&](const clause::DefinedOperator::IntrinsicOperator &op) {
- using IntrinsicOperator =
- clause::DefinedOperator::IntrinsicOperator;
- switch (op) {
- case IntrinsicOperator::Add:
- case IntrinsicOperator::Multiply:
- case IntrinsicOperator::AND:
- case IntrinsicOperator::OR:
- case IntrinsicOperator::EQV:
- case IntrinsicOperator::NEQV:
- break;
- default:
- return false;
- }
-
- parser::CharBlock mangledName =
- semantics::omp::MangledIntrinsicOperatorReductionName(
- ReductionProcessor::toParserIntrinsicOperator(op),
- semaCtx);
- return semantics::omp::FindUserReductionSymbol(
- converter.getCurrentScope(), mangledName,
- objectType) != nullptr;
- },
- [&](const clause::DefinedOperator::DefinedOpName &op) {
- const semantics::Symbol *operatorSymbol = op.v.sym();
- return operatorSymbol &&
- semantics::omp::FindOperatorUserReductionSymbol(
- converter.getCurrentScope(), *operatorSymbol,
- objectType);
- },
- },
- definedOperator.u);
- },
- [&](const clause::ProcedureDesignator &procedureDesignator) {
- const semantics::Symbol *symbol = procedureDesignator.v.sym();
- return (symbol &&
- symbol->GetUltimate()
- .detailsIf<semantics::UserReductionDetails>()) ||
- ReductionProcessor::findUserDefinedReductionForIntrinsic(
- converter.getCurrentScope(), procedureDesignator,
- objectType) != nullptr;
- },
- },
- reductionOperator.u);
-}
-
-template <typename ReductionClause>
-static bool
-hasUserDefinedArrayElementReduction(const List<Clause> &clauses,
- lower::AbstractConverter &converter,
- semantics::SemanticsContext &semaCtx) {
- bool found = false;
- ClauseFinder::findRepeatableClause<ReductionClause>(
- clauses,
- [&](const ReductionClause &reductionClause, const parser::CharBlock &) {
- if (found)
- return;
-
- const auto &reductionOperators =
- std::get<clause::ReductionOperatorList>(reductionClause.t);
- assert(reductionOperators.size() == 1 &&
- "expected one reduction operator");
- const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
- found = llvm::any_of(objects, [&](const Object &object) {
- return isArrayElementReductionObject(object) &&
- isUserDefinedReductionOperator(reductionOperators.front(),
- object, converter, semaCtx);
- });
- });
- return found;
-}
-
-static void checkUserDefinedArrayElementReduction(
- mlir::Location loc, lower::AbstractConverter &converter,
- semantics::SemanticsContext &semaCtx, const List<Clause> &clauses) {
- if (hasUserDefinedArrayElementReduction<clause::Reduction>(clauses, converter,
- semaCtx))
- TODO(loc, "REDUCTION of an array element using a user-defined reduction");
-}
-
/// Structure holding the information needed to create and bind entry block
/// arguments associated to a single clause during OpenMP lowering.
struct ObjectEntryBlockArgsEntry {
@@ -2574,8 +2456,6 @@ static void genParallelClauses(
cp.processNumThreads(stmtCtx, clauseOps);
cp.processProcBind(clauseOps);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
- checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
cp.processReduction(loc, clauseOps, reductionObjects);
}
@@ -2597,8 +2477,6 @@ genSectionsClauses(lower::AbstractConverter &converter,
ClauseProcessor cp(converter, semaCtx, clauses);
cp.processAllocate(clauseOps);
cp.processNowait(clauseOps);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
- checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
cp.processReduction(loc, clauseOps, reductionObjects);
// TODO Support delayed privatization.
}
@@ -2699,8 +2577,6 @@ static void genScopeClauses(lower::AbstractConverter &converter,
ClauseProcessor cp(converter, semaCtx, clauses);
cp.processAllocate(clauseOps);
cp.processNowait(clauseOps);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
- checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
cp.processReduction(loc, clauseOps, reductionObjects);
}
@@ -2927,8 +2803,6 @@ static void genWsloopClauses(
cp.processNowait(clauseOps);
cp.processOrder(clauseOps);
cp.processOrdered(clauseOps);
- checkTaskModifierPartialArrayReduction(loc, semaCtx, clauses);
- checkUserDefinedArrayElementReduction(loc, converter, semaCtx, clauses);
cp.processReduction(loc, clauseOps, reductionObjects, reductionVarCache);
cp.processSchedule(stmtCtx, clauseOps);
cp.processLinear(clauseOps);
@@ -4487,11 +4361,6 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
genTaskClauses(converter, semaCtx, symTable, stmtCtx, item->clauses, loc,
clauseOps, inReductionObjects);
- if (hasUserDefinedArrayElementReduction<clause::InReduction>(
- item->clauses, converter, semaCtx))
- TODO(loc, "TASK construct with IN_REDUCTION of an array element using a "
- "user-defined reduction");
-
if (hasPartialArrayReductionObject(inReductionObjects, semaCtx))
TODO(loc, "TASK construct with IN_REDUCTION of a partial array section");
@@ -4546,12 +4415,6 @@ genTaskgroupOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
genTaskgroupClauses(converter, semaCtx, item->clauses, loc, clauseOps,
taskReductionObjects);
- if (hasUserDefinedArrayElementReduction<clause::TaskReduction>(
- item->clauses, converter, semaCtx))
- TODO(loc,
- "TASKGROUP construct with TASK_REDUCTION of an array element using a "
- "user-defined reduction");
-
if (hasPartialArrayReductionObject(taskReductionObjects, semaCtx))
TODO(loc,
"TASKGROUP construct with TASK_REDUCTION of a partial array section");
@@ -5148,16 +5011,6 @@ static mlir::omp::TaskloopContextOp genStandaloneTaskloop(
enableDelayedPrivatization, symTable);
dsp.processStep1(&taskloopClauseOps);
- if (hasUserDefinedArrayElementReduction<clause::InReduction>(
- item->clauses, converter, semaCtx))
- TODO(loc,
- "TASKLOOP construct with IN_REDUCTION of an array element using a "
- "user-defined reduction");
- if (hasUserDefinedArrayElementReduction<clause::Reduction>(
- item->clauses, converter, semaCtx))
- TODO(loc, "TASKLOOP construct with REDUCTION of an array element using a "
- "user-defined reduction");
-
llvm::ArrayRef<const semantics::Symbol *> privatizedSymbols =
enableDelayedPrivatization ? dsp.getDelayedPrivSymbols()
: dsp.getAllSymbolsToPrivatize().getArrayRef();
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90 b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
index 8e49626fce6f1..e4361dcd2d8c7 100644
--- a/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
+++ b/flang/test/Lower/OpenMP/Todo/reduction-array-element-task-privatization.f90
@@ -103,17 +103,17 @@
! EAGER-TASKLOOP-REDUCTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-IN-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element or section whose base array is privatized
! EAGER-TASKLOOP-REDUCTION-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
-! EAGER-TASKLOOP-UDR-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of an array element or section whose base array is privatized
-! TASK-CROSS-SCOPE-BOUNDS: not yet implemented: TASKGROUP construct with TASK_REDUCTION of a partial array section
-! TASKLOOP-UDR-SHARED-SECTION: not yet implemented: TASKLOOP construct with REDUCTION of a partial array section
+! EAGER-TASKLOOP-UDR-SECTION: not yet implemented: REDUCTION of a partial array section using a user-defined reduction
+! TASK-CROSS-SCOPE-BOUNDS: not yet implemented: TASK_REDUCTION of a partial array section using a user-defined reduction
+! TASKLOOP-UDR-SHARED-SECTION: not yet implemented: REDUCTION of a partial array section using a user-defined reduction
! TASKLOOP-IN-SHARED-SECTION: not yet implemented: TASKLOOP construct with IN_REDUCTION of a partial array section
! EAGER-TASK-SHARED-ELEMENT: not yet implemented: TASK construct with IN_REDUCTION of an array element when delayed privatization is disabled
! EAGER-TASK-SHARED-FULL-SECTION: not yet implemented: TASK construct with IN_REDUCTION when delayed privatization is disabled
-! TASK-UDR-SHARED-ELEMENT: not yet implemented: TASK construct with IN_REDUCTION of an array element using a user-defined reduction
-! TASKGROUP-UDR-ELEMENT: not yet implemented: TASKGROUP construct with TASK_REDUCTION of an array element using a user-defined reduction
-! TASKLOOP-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with REDUCTION of an array element using a user-defined reduction
-! TASKLOOP-MAX-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with REDUCTION of an array element using a user-defined reduction
-! TASKLOOP-IN-UDR-SHARED-ELEMENT: not yet implemented: TASKLOOP construct with IN_REDUCTION of an array element using a user-defined reduction
+! TASK-UDR-SHARED-ELEMENT: not yet implemented: IN_REDUCTION of an array element using a user-defined reduction
+! TASKGROUP-UDR-ELEMENT: not yet implemented: TASK_REDUCTION of an array element using a user-defined reduction
+! TASKLOOP-UDR-SHARED-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! TASKLOOP-MAX-UDR-SHARED-ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! TASKLOOP-IN-UDR-SHARED-ELEMENT: not yet implemented: IN_REDUCTION of an array element using a user-defined reduction
! TASK-SHARED-SECTION: not yet implemented: TASK construct with IN_REDUCTION of a partial array section
! TARGET-ELEMENT: not yet implemented: TARGET construct with IN_REDUCTION of an array element
! PARALLEL-TASK-UDR-SECTION: not yet implemented: REDUCTION with TASK modifier of a partial array section
diff --git a/flang/test/Lower/OpenMP/Todo/reduction-udr-subobject-matrix.f90 b/flang/test/Lower/OpenMP/Todo/reduction-udr-subobject-matrix.f90
new file mode 100644
index 0000000000000..77599627c5b46
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/reduction-udr-subobject-matrix.f90
@@ -0,0 +1,193 @@
+! RUN: split-file %s %t
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/parallel-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/parallel-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/do-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/sections-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/sections-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/scope-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/simd-element.f90 2>&1 | FileCheck %s --check-prefix=ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/simd-element.f90 2>&1 | FileCheck %s --check-prefix=ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/simd-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/simd-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/teams-element.f90 2>&1 | FileCheck %s --check-prefix=ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/teams-element.f90 2>&1 | FileCheck %s --check-prefix=ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/teams-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/teams-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/loop-element.f90 2>&1 | FileCheck %s --check-prefix=ELEMENT
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/loop-element.f90 2>&1 | FileCheck %s --check-prefix=ELEMENT
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/loop-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/loop-section.f90 2>&1 | FileCheck %s --check-prefix=SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/in-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=IN-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/in-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=IN-SECTION
+! RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/task-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SECTION
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/task-reduction-section.f90 2>&1 | FileCheck %s --check-prefix=TASK-SECTION
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/supported.f90 | FileCheck %s --check-prefix=SUPPORTED --implicit-check-not="not yet implemented"
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %t/supported.f90 | FileCheck %s --check-prefix=SUPPORTED --implicit-check-not="not yet implemented"
+
+! All constructs that lower a REDUCTION clause call the shared reduction
+! processor. Together with the existing taskloop and element tests, these cases
+! form the complete construct-by-subobject matrix.
+
+! ELEMENT: not yet implemented: REDUCTION of an array element using a user-defined reduction
+! SECTION: not yet implemented: REDUCTION of a partial array section using a user-defined reduction
+! IN-SECTION: not yet implemented: IN_REDUCTION of a partial array section using a user-defined reduction
+! TASK-SECTION: not yet implemented: TASK_REDUCTION of a partial array section using a user-defined reduction
+
+! SUPPORTED-LABEL: func.func @_QPwhole_section
+! SUPPORTED-LABEL: func.func @_QPpredefined_element
+! SUPPORTED-LABEL: func.func @_QPpredefined_section
+
+!--- parallel-section.f90
+subroutine parallel_section(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel reduction(myred : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end parallel
+end subroutine
+
+!--- do-section.f90
+subroutine do_section(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp do reduction(myred : a(2:3))
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ end do
+ !$omp end do
+end subroutine
+
+!--- sections-section.f90
+subroutine sections_section(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp sections reduction(myred : a(2:3))
+ !$omp section
+ a(2:3) = a(2:3) + 1
+ !$omp end sections
+end subroutine
+
+!--- scope-section.f90
+subroutine scope_section(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp scope reduction(myred : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end scope
+end subroutine
+
+!--- simd-element.f90
+subroutine simd_element(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp simd reduction(myred : a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+end subroutine
+
+!--- simd-section.f90
+subroutine simd_section(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp simd reduction(myred : a(2:3))
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ end do
+end subroutine
+
+!--- teams-element.f90
+subroutine teams_element(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp teams reduction(myred : a(2))
+ a(2) = a(2) + 1
+ !$omp end teams
+end subroutine
+
+!--- teams-section.f90
+subroutine teams_section(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp teams reduction(myred : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end teams
+end subroutine
+
+!--- loop-element.f90
+subroutine loop_element(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp loop reduction(myred : a(2))
+ do i = 1, 1
+ a(2) = a(2) + i
+ end do
+ !$omp end loop
+end subroutine
+
+!--- loop-section.f90
+subroutine loop_section(a)
+ integer :: a(4), i
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp loop reduction(myred : a(2:3))
+ do i = 1, 1
+ a(2:3) = a(2:3) + i
+ end do
+ !$omp end loop
+end subroutine
+
+!--- in-reduction-section.f90
+subroutine in_reduction_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp task shared(a) in_reduction(+ : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end task
+end subroutine
+
+!--- task-reduction-section.f90
+subroutine task_reduction_section(a)
+ integer :: a(4)
+ !$omp declare reduction(+ : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp taskgroup task_reduction(+ : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end taskgroup
+end subroutine
+
+!--- supported.f90
+subroutine whole_section(a)
+ integer :: a(4)
+ !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 1)
+ !$omp parallel reduction(myred : a(:))
+ a = a + 1
+ !$omp end parallel
+end subroutine
+
+subroutine predefined_element(a)
+ integer :: a(4)
+ !$omp parallel reduction(+ : a(2))
+ a(2) = a(2) + 1
+ !$omp end parallel
+end subroutine
+
+subroutine predefined_section(a)
+ integer :: a(4)
+ !$omp parallel reduction(+ : a(2:3))
+ a(2:3) = a(2:3) + 1
+ !$omp end parallel
+end subroutine
More information about the flang-commits
mailing list