[flang] [llvm] [Flang][OpenMP] Add Lowering support for lastprivate in taskloops (PR #166584)

Jack Styles via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 5 08:36:56 PST 2025


https://github.com/Stylie777 created https://github.com/llvm/llvm-project/pull/166584

lastprivate support already exists within the DataSharingProcessor,
so this can be extended to include the use of taskloops to enable
AST->FIR lowering. The related error message has also been updated
to indicate that taskloops are now supported.

>From 19291b778cff940252decf96a00cd18fdb5c3114 Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Wed, 5 Nov 2025 14:25:07 +0000
Subject: [PATCH 1/2] [Flang][OpenMP] Add Lowering support for taskloop
 reductions

Support for lowering the Reduction clause support already exists,
so we can extend the support for taskloop to include reduction.

As support for Reduction in taskloop was only added in OpenMP 5.0,
the use of the Clause has been restricted to that version of OpenMP
or greater.
---
 flang/lib/Lower/OpenMP/OpenMP.cpp             | 20 ++++++++-----
 .../Lower/OpenMP/Todo/taskloop-reduction.f90  | 13 --------
 .../test/Lower/OpenMP/taskloop-reduction.f90  | 30 +++++++++++++++++++
 llvm/include/llvm/Frontend/OpenMP/OMP.td      | 21 +++++++------
 4 files changed, 52 insertions(+), 32 deletions(-)
 delete mode 100644 flang/test/Lower/OpenMP/Todo/taskloop-reduction.f90
 create mode 100644 flang/test/Lower/OpenMP/taskloop-reduction.f90

diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index ad456d89bc432..51170a39d272b 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1763,21 +1763,22 @@ static void genTaskgroupClauses(
   cp.processTaskReduction(loc, clauseOps, taskReductionSyms);
 }
 
-static void genTaskloopClauses(lower::AbstractConverter &converter,
-                               semantics::SemanticsContext &semaCtx,
-                               lower::StatementContext &stmtCtx,
-                               const List<Clause> &clauses, mlir::Location loc,
-                               mlir::omp::TaskloopOperands &clauseOps) {
+static void genTaskloopClauses(
+    lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,
+    lower::StatementContext &stmtCtx, const List<Clause> &clauses,
+    mlir::Location loc, mlir::omp::TaskloopOperands &clauseOps,
+    llvm::SmallVectorImpl<const semantics::Symbol *> &taskReductionSyms) {
 
   ClauseProcessor cp(converter, semaCtx, clauses);
   cp.processGrainsize(stmtCtx, clauseOps);
   cp.processNumTasks(stmtCtx, clauseOps);
+  cp.processReduction(loc, clauseOps, taskReductionSyms);
 
   cp.processTODO<clause::Allocate, clause::Collapse, clause::Default,
                  clause::Final, clause::If, clause::InReduction,
                  clause::Lastprivate, clause::Mergeable, clause::Nogroup,
-                 clause::Priority, clause::Reduction, clause::Shared,
-                 clause::Untied>(loc, llvm::omp::Directive::OMPD_taskloop);
+                 clause::Priority, clause::Shared, clause::Untied>(
+      loc, llvm::omp::Directive::OMPD_taskloop);
 }
 
 static void genTaskwaitClauses(lower::AbstractConverter &converter,
@@ -2979,8 +2980,9 @@ static mlir::omp::TaskloopOp genStandaloneTaskloop(
     lower::pft::Evaluation &eval, mlir::Location loc,
     const ConstructQueue &queue, ConstructQueue::const_iterator item) {
   mlir::omp::TaskloopOperands taskloopClauseOps;
+  llvm::SmallVector<const semantics::Symbol *> taskReductionSyms;
   genTaskloopClauses(converter, semaCtx, stmtCtx, item->clauses, loc,
-                     taskloopClauseOps);
+                     taskloopClauseOps, taskReductionSyms);
   DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,
                            /*shouldCollectPreDeterminedSymbols=*/true,
                            enableDelayedPrivatization, symTable);
@@ -2994,6 +2996,8 @@ static mlir::omp::TaskloopOp genStandaloneTaskloop(
   EntryBlockArgs taskloopArgs;
   taskloopArgs.priv.syms = dsp.getDelayedPrivSymbols();
   taskloopArgs.priv.vars = taskloopClauseOps.privateVars;
+  taskloopArgs.reduction.syms = taskReductionSyms;
+  taskloopArgs.reduction.vars = taskloopClauseOps.reductionVars;
 
   auto taskLoopOp = genWrapperOp<mlir::omp::TaskloopOp>(
       converter, loc, taskloopClauseOps, taskloopArgs);
diff --git a/flang/test/Lower/OpenMP/Todo/taskloop-reduction.f90 b/flang/test/Lower/OpenMP/Todo/taskloop-reduction.f90
deleted file mode 100644
index 0c16bd227257f..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/taskloop-reduction.f90
+++ /dev/null
@@ -1,13 +0,0 @@
-! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
-! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
-
-! CHECK: not yet implemented: Unhandled clause REDUCTION in TASKLOOP construct
-subroutine omp_taskloop_reduction()
-   integer x
-   x = 0
-   !$omp taskloop reduction(+:x)
-   do i = 1, 100
-      x = x + 1
-   end do
-   !$omp end taskloop
-end subroutine omp_taskloop_reduction
diff --git a/flang/test/Lower/OpenMP/taskloop-reduction.f90 b/flang/test/Lower/OpenMP/taskloop-reduction.f90
new file mode 100644
index 0000000000000..4185a927366e7
--- /dev/null
+++ b/flang/test/Lower/OpenMP/taskloop-reduction.f90
@@ -0,0 +1,30 @@
+! This test checks the lowering of the reduction clause in the taskloop construct
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
+! RUN %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=45 -o - %s 2>&1 | FileCheck %s --check-prefix=CHECK-VERSION
+
+! CHECK-VERSION: error: REDUCTION clause is not allowed on directive TASKLOOP in OpenMP v4.5, try -fopenmp-version=50
+
+! CHECK-LABEL: omp.private
+! CHECK-SAME: {type = private} @[[I_PRIVATE:.*]] : i32
+
+! CHECK-LABEL: func.func @_QPtest_reduction()
+! CHECK: %[[ALLOCA_A:.*]] = fir.alloca !fir.array<10xi32> {bindc_name = "a", uniq_name = "_QFtest_reductionEa"}
+! CHECK: %[[DECLARE_A:.*]]:2 = hlfir.declare %[[ALLOCA_A]](%2) {uniq_name = "_QFtest_reductionEa"} : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xi32>>, !fir.ref<!fir.array<10xi32>>)
+! CHECK: %[[ALLOCA_I:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFtest_reductionEi"}
+! CHECK: %[[DECLARE_I:.*]]:2 = hlfir.declare %[[ALLOCA_I]] {uniq_name = "_QFtest_reductionEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[ALLOCA_SUM_I:.*]] = fir.alloca i32 {bindc_name = "sum_i", uniq_name = "_QFtest_reductionEsum_i"}
+! CHECK: %[[DECLARE_SUM_I:.*]]:2 = hlfir.declare %[[ALLOCA_SUM_I]] {uniq_name = "_QFtest_reductionEsum_i"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+
+subroutine test_reduction
+    integer :: i, a(10), sum_i
+
+    ! CHECK: omp.taskloop
+    ! CHECK-SAME: private(@[[I_PRIVATE]] %[[DECLARE_I]]#0 -> %arg0 : !fir.ref<i32>) reduction(@add_reduction_i32 %[[DECLARE_SUM_I]]#0 -> %arg1 : !fir.ref<i32>) {
+    !$omp taskloop reduction (+:sum_i)
+    do i = 1,10
+        sum_i = sum_i + i
+    end do
+    !$omp end taskloop
+
+end subroutine
\ No newline at end of file
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 208609f64f418..0afae8a013bd6 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -1284,17 +1284,16 @@ def OMP_TaskGroup : Directive<[Spelling<"taskgroup">]> {
   let category = CA_Executable;
 }
 def OMP_TaskLoop : Directive<[Spelling<"taskloop">]> {
-  let allowedClauses = [
-    VersionedClause<OMPC_Allocate>,
-    VersionedClause<OMPC_FirstPrivate>,
-    VersionedClause<OMPC_InReduction>,
-    VersionedClause<OMPC_LastPrivate>,
-    VersionedClause<OMPC_Mergeable>,
-    VersionedClause<OMPC_NoGroup>,
-    VersionedClause<OMPC_Private>,
-    VersionedClause<OMPC_Reduction>,
-    VersionedClause<OMPC_Shared>,
-    VersionedClause<OMPC_Untied>,
+  let allowedClauses = [VersionedClause<OMPC_Allocate>,
+                        VersionedClause<OMPC_FirstPrivate>,
+                        VersionedClause<OMPC_InReduction>,
+                        VersionedClause<OMPC_LastPrivate>,
+                        VersionedClause<OMPC_Mergeable>,
+                        VersionedClause<OMPC_NoGroup>,
+                        VersionedClause<OMPC_Private>,
+                        VersionedClause<OMPC_Reduction, 50>,
+                        VersionedClause<OMPC_Shared>,
+                        VersionedClause<OMPC_Untied>,
   ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Collapse>,

>From b5d77cc8fa37e1eb8791565cd2f65ba7385786f9 Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Wed, 5 Nov 2025 15:43:19 +0000
Subject: [PATCH 2/2] [Flang][OpenMP] Add Lowering support for lastprivate in
 taskloops

lastprivate support already exists within the DataSharingProcessor,
so this can be extended to include the use of taskloops to enable
AST->FIR lowering. The related error message has also been updated
to indicate that taskloops are now supported.
---
 .../lib/Lower/OpenMP/DataSharingProcessor.cpp |  5 ++--
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  4 +--
 .../Lower/OpenMP/taskloop-lastprivate.f90     | 25 +++++++++++++++++++
 3 files changed, 30 insertions(+), 4 deletions(-)
 create mode 100644 flang/test/Lower/OpenMP/taskloop-lastprivate.f90

diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 146a252b049ec..83c2eda0a2dc7 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -342,7 +342,8 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
   if (!hasLastPrivate)
     return;
 
-  if (mlir::isa<mlir::omp::WsloopOp>(op) || mlir::isa<mlir::omp::SimdOp>(op)) {
+  if (mlir::isa<mlir::omp::WsloopOp>(op) || mlir::isa<mlir::omp::SimdOp>(op) ||
+      mlir::isa<mlir::omp::TaskloopOp>(op)) {
     mlir::omp::LoopRelatedClauseOps result;
     llvm::SmallVector<const semantics::Symbol *> iv;
     collectLoopRelatedInfo(converter, converter.getCurrentLocation(), eval,
@@ -408,7 +409,7 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
   } else {
     TODO(converter.getCurrentLocation(),
          "lastprivate clause in constructs other than "
-         "simd/worksharing-loop");
+         "simd/worksharing-loop/taskloop");
   }
 }
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 51170a39d272b..8c56480390e58 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1776,8 +1776,8 @@ static void genTaskloopClauses(
 
   cp.processTODO<clause::Allocate, clause::Collapse, clause::Default,
                  clause::Final, clause::If, clause::InReduction,
-                 clause::Lastprivate, clause::Mergeable, clause::Nogroup,
-                 clause::Priority, clause::Shared, clause::Untied>(
+                 clause::Mergeable, clause::Nogroup, clause::Priority,
+                 clause::Shared, clause::Untied>(
       loc, llvm::omp::Directive::OMPD_taskloop);
 }
 
diff --git a/flang/test/Lower/OpenMP/taskloop-lastprivate.f90 b/flang/test/Lower/OpenMP/taskloop-lastprivate.f90
new file mode 100644
index 0000000000000..a3562a971bb59
--- /dev/null
+++ b/flang/test/Lower/OpenMP/taskloop-lastprivate.f90
@@ -0,0 +1,25 @@
+! Test the lastprivate clause when used with the taskloop construct
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=45 %s -o - 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=45 %s -o - 2>&1 | FileCheck %s
+
+! CHECK-LABEL: omp.private
+! CHECK-SAME: {type = private} @[[I_PRIVATE:.*]] : i32
+! CHECK-LABEL: omp.private
+! CHECK-SAME: {type = private} @[[LAST_I_PRIVATE:.*]] : i32
+
+! CHECK: %[[ALLOCA_I:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFlastprivateEi"}
+! CHECK: %[[DECLARE_I:.*]]:2 = hlfir.declare %[[ALLOCA_I]] {uniq_name = "_QFlastprivateEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[ALLOCA_LAST_I:.*]] = fir.alloca i32 {bindc_name = "last_i", uniq_name = "_QFlastprivateElast_i"}
+! CHECK: %[[DECLARE_LAST_I:.*]]:2 = hlfir.declare %[[ALLOCA_LAST_I]] {uniq_name = "_QFlastprivateElast_i"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+
+subroutine lastprivate()
+   integer :: i, last_i
+
+   ! CHECK: omp.taskloop
+   ! CHECK-SAME: private(@[[LAST_I_PRIVATE]] %[[DECLARE_LAST_I]]#0 -> %arg0, @[[I_PRIVATE]] %[[DECLARE_I]]#0 -> %arg1 : !fir.ref<i32>, !fir.ref<i32>) {
+   !$omp taskloop lastprivate(last_i)
+   do i=1,10
+       last_i = i
+   end do
+   !$omp end taskloop
+end
\ No newline at end of file



More information about the llvm-commits mailing list