[flang-commits] [flang] f4accbf - [flang][OpenMP] Support privatization for single construct

Peixin Qiao via flang-commits flang-commits at lists.llvm.org
Wed Oct 5 05:23:39 PDT 2022


Author: Peixin Qiao
Date: 2022-10-05T20:22:33+08:00
New Revision: f4accbf55f4d0fcd6d7cc6f7632a0e4b69c9f3dd

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

LOG: [flang][OpenMP] Support privatization for single construct

This supports the lowering of private and firstprivate clauses in single
construct. The alloca ops are emitted in the entry block according to
https://llvm.org/docs/Frontend/PerformanceTips.html#use-of-allocas, and
the load/store ops are emitted in the single region. The data race
problem is handled in OMPIRBuilder. That is, the barrier is emitted in
OMPIRBuilder.

Co-authored-by: Nimish Mishra <neelam.nimish at gmail.com>

Reviewed By: kiranchandramohan

Differential Revision: https://reviews.llvm.org/D128596

Added: 
    

Modified: 
    flang/include/flang/Lower/AbstractConverter.h
    flang/lib/Lower/Bridge.cpp
    flang/lib/Lower/OpenMP.cpp
    flang/test/Lower/OpenMP/single.f90

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index 5a2a52335f6ce..6ab649ccf020a 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -18,6 +18,7 @@
 #include "flang/Lower/PFTDefs.h"
 #include "flang/Optimizer/Builder/BoxValue.h"
 #include "flang/Semantics/symbol.h"
+#include "mlir/IR/Builders.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/Operation.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -103,8 +104,9 @@ class AbstractConverter {
   virtual bool
   createHostAssociateVarClone(const Fortran::semantics::Symbol &sym) = 0;
 
-  virtual void copyHostAssociateVar(const Fortran::semantics::Symbol &sym,
-                                    mlir::Block *lastPrivBlock = nullptr) = 0;
+  virtual void copyHostAssociateVar(
+      const Fortran::semantics::Symbol &sym,
+      mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) = 0;
 
   /// Collect the set of symbols with \p flag in \p eval
   /// region if \p collectSymbols is true. Likewise, collect the

diff  --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 3748f72c1645f..bd1bddcc987b9 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -516,10 +516,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     return bindIfNewSymbol(sym, exv);
   }
 
-  // FIXME: Generalize this function, so that lastPrivBlock can be removed
-  void
-  copyHostAssociateVar(const Fortran::semantics::Symbol &sym,
-                       mlir::Block *lastPrivBlock = nullptr) override final {
+  void copyHostAssociateVar(
+      const Fortran::semantics::Symbol &sym,
+      mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) override final {
     // 1) Fetch the original copy of the variable.
     assert(sym.has<Fortran::semantics::HostAssocDetails>() &&
            "No host-association found");
@@ -537,13 +536,14 @@ class FirConverter : public Fortran::lower::AbstractConverter {
 
     // 3) Perform the assignment.
     mlir::OpBuilder::InsertPoint insPt = builder->saveInsertionPoint();
-    if (lastPrivBlock)
-      builder->setInsertionPointToStart(lastPrivBlock);
+    if (copyAssignIP && copyAssignIP->isSet())
+      builder->restoreInsertionPoint(*copyAssignIP);
     else
       builder->setInsertionPointAfter(fir::getBase(exv).getDefiningOp());
 
     fir::ExtendedValue lhs, rhs;
-    if (lastPrivBlock) {
+    if (copyAssignIP && copyAssignIP->isSet() &&
+        sym.test(Fortran::semantics::Symbol::Flag::OmpLastPrivate)) {
       // lastprivate case
       lhs = hexv;
       rhs = exv;
@@ -568,7 +568,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
       builder->create<fir::StoreOp>(loc, loadVal, fir::getBase(lhs));
     }
 
-    if (lastPrivBlock)
+    if (copyAssignIP && copyAssignIP->isSet() &&
+        sym.test(Fortran::semantics::Symbol::Flag::OmpLastPrivate))
       builder->restoreInsertionPoint(insPt);
   }
 

diff  --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp
index cf7b54d2c1dab..31709d68ee8ac 100644
--- a/flang/lib/Lower/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP.cpp
@@ -61,10 +61,11 @@ getOmpObjectSymbol(const Fortran::parser::OmpObject &ompObject) {
   return sym;
 }
 
-static void
-privatizeSymbol(Fortran::lower::AbstractConverter &converter,
-                const Fortran::semantics::Symbol *sym,
-                [[maybe_unused]] mlir::Block *lastPrivBlock = nullptr) {
+template <typename Op>
+static void privatizeSymbol(
+    Op &op, Fortran::lower::AbstractConverter &converter,
+    const Fortran::semantics::Symbol *sym,
+    [[maybe_unused]] mlir::OpBuilder::InsertPoint *lastPrivIP = nullptr) {
   // Privatization for symbols which are pre-determined (like loop index
   // variables) happen separately, for everything else privatize here.
   if (sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined))
@@ -72,10 +73,20 @@ privatizeSymbol(Fortran::lower::AbstractConverter &converter,
   bool success = converter.createHostAssociateVarClone(*sym);
   (void)success;
   assert(success && "Privatization failed due to existing binding");
-  if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate))
-    converter.copyHostAssociateVar(*sym);
+  if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate)) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    mlir::OpBuilder::InsertPoint firstPrivIP, insPt;
+    if (mlir::isa<mlir::omp::SingleOp>(op)) {
+      insPt = firOpBuilder.saveInsertionPoint();
+      firOpBuilder.setInsertionPointToStart(&op.getRegion().front());
+      firstPrivIP = firOpBuilder.saveInsertionPoint();
+    }
+    converter.copyHostAssociateVar(*sym, &firstPrivIP);
+    if (mlir::isa<mlir::omp::SingleOp>(op))
+      firOpBuilder.restoreInsertionPoint(insPt);
+  }
   if (sym->test(Fortran::semantics::Symbol::Flag::OmpLastPrivate))
-    converter.copyHostAssociateVar(*sym, lastPrivBlock);
+    converter.copyHostAssociateVar(*sym, lastPrivIP);
 }
 
 template <typename Op>
@@ -96,7 +107,7 @@ static bool privatizeVars(Op &op, Fortran::lower::AbstractConverter &converter,
       };
   // We need just one ICmpOp for multiple LastPrivate clauses.
   mlir::arith::CmpIOp cmpOp;
-  mlir::Block *lastPrivBlock = nullptr;
+  mlir::OpBuilder::InsertPoint lastPrivIP;
   bool hasLastPrivateOp = false;
   for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
     if (const auto &privateClause =
@@ -155,7 +166,8 @@ static bool privatizeVars(Op &op, Fortran::lower::AbstractConverter &converter,
         }
         mlir::scf::IfOp ifOp = firOpBuilder.create<mlir::scf::IfOp>(
             wsLoopOp->getLoc(), cmpOp, /*else*/ false);
-        lastPrivBlock = &ifOp.getThenRegion().front();
+        firOpBuilder.setInsertionPointToStart(&ifOp.getThenRegion().front());
+        lastPrivIP = firOpBuilder.saveInsertionPoint();
       } else {
         TODO(converter.getCurrentLocation(),
              "lastprivate clause in constructs other than worksharing-loop");
@@ -207,7 +219,7 @@ static bool privatizeVars(Op &op, Fortran::lower::AbstractConverter &converter,
   else
     firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
   for (auto sym : privatizedSymbols) {
-    privatizeSymbol(converter, sym, lastPrivBlock);
+    privatizeSymbol(op, converter, sym, &lastPrivIP);
     if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate) &&
         sym->test(Fortran::semantics::Symbol::Flag::OmpLastPrivate))
       needBarrier = true;
@@ -217,7 +229,7 @@ static bool privatizeVars(Op &op, Fortran::lower::AbstractConverter &converter,
     if (!symbolsInNestedRegions.contains(sym) &&
         !symbolsInParentRegions.contains(sym) &&
         !privatizedSymbols.contains(sym))
-      privatizeSymbol(converter, sym);
+      privatizeSymbol(op, converter, sym);
 
   // Emit implicit barrier to synchronize threads and avoid data races on
   // initialization of firstprivate variables and post-update of lastprivate
@@ -825,7 +837,8 @@ genOMP(Fortran::lower::AbstractConverter &converter,
   } else if (blockDirective.v == llvm::omp::OMPD_single) {
     auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
         currentLocation, allocateOperands, allocatorOperands, nowaitAttr);
-    createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval);
+    createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval,
+                                  &opClauseList);
   } else if (blockDirective.v == llvm::omp::OMPD_ordered) {
     auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>(
         currentLocation, /*simd=*/nullptr);
@@ -839,7 +852,7 @@ genOMP(Fortran::lower::AbstractConverter &converter,
         allocatorOperands);
     createBodyOfOp(taskOp, converter, currentLocation, eval, &opClauseList);
   } else if (blockDirective.v == llvm::omp::OMPD_taskgroup) {
-      // TODO: Add task_reduction support
+    // TODO: Add task_reduction support
     auto taskGroupOp = firOpBuilder.create<mlir::omp::TaskGroupOp>(
         currentLocation, /*task_reduction_vars=*/ValueRange(),
         /*task_reductions=*/nullptr, allocateOperands, allocatorOperands);

diff  --git a/flang/test/Lower/OpenMP/single.f90 b/flang/test/Lower/OpenMP/single.f90
index b655d78075bdd..fa34753910c59 100644
--- a/flang/test/Lower/OpenMP/single.f90
+++ b/flang/test/Lower/OpenMP/single.f90
@@ -1,25 +1,25 @@
-!RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | FileCheck %s --check-prefixes="FIRDialect,OMPDialect"
-!RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | fir-opt --cfg-conversion | fir-opt --fir-to-llvm-ir | FileCheck %s --check-prefixes="LLVMDialect,OMPDialect"
+!RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | FileCheck %s
+!RUN: bbc -emit-fir -fopenmp %s -o - | FileCheck %s
 
 !===============================================================================
 ! Single construct
 !===============================================================================
 
-!FIRDialect-LABEL: func @_QPomp_single
-!FIRDialect-SAME: (%[[x:.*]]: !fir.ref<i32> {fir.bindc_name = "x"})
+!CHECK-LABEL: func @_QPomp_single
+!CHECK-SAME: (%[[x:.*]]: !fir.ref<i32> {fir.bindc_name = "x"})
 subroutine omp_single(x)
   integer, intent(inout) :: x
-  !OMPDialect: omp.parallel
+  !CHECK: omp.parallel
   !$omp parallel
-  !OMPDialect: omp.single
+  !CHECK: omp.single
   !$omp single
-    !FIRDialect: %[[xval:.*]] = fir.load %[[x]] : !fir.ref<i32>
-    !FIRDialect: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32
-    !FIRDialect: fir.store %[[res]] to %[[x]] : !fir.ref<i32>
+    !CHECK: %[[xval:.*]] = fir.load %[[x]] : !fir.ref<i32>
+    !CHECK: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32
+    !CHECK: fir.store %[[res]] to %[[x]] : !fir.ref<i32>
     x = x + 12
-  !OMPDialect: omp.terminator
+  !CHECK: omp.terminator
   !$omp end single
-  !OMPDialect: omp.terminator
+  !CHECK: omp.terminator
   !$omp end parallel
 end subroutine omp_single
 
@@ -27,21 +27,21 @@ end subroutine omp_single
 ! Single construct with nowait
 !===============================================================================
 
-!FIRDialect-LABEL: func @_QPomp_single_nowait
-!FIRDialect-SAME: (%[[x:.*]]: !fir.ref<i32> {fir.bindc_name = "x"})
+!CHECK-LABEL: func @_QPomp_single_nowait
+!CHECK-SAME: (%[[x:.*]]: !fir.ref<i32> {fir.bindc_name = "x"})
 subroutine omp_single_nowait(x)
   integer, intent(inout) :: x
-  !OMPDialect: omp.parallel
+  !CHECK: omp.parallel
   !$omp parallel
-  !OMPDialect: omp.single nowait
+  !CHECK: omp.single nowait
   !$omp single
-    !FIRDialect: %[[xval:.*]] = fir.load %[[x]] : !fir.ref<i32>
-    !FIRDialect: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32
-    !FIRDialect: fir.store %[[res]] to %[[x]] : !fir.ref<i32>
+    !CHECK: %[[xval:.*]] = fir.load %[[x]] : !fir.ref<i32>
+    !CHECK: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32
+    !CHECK: fir.store %[[res]] to %[[x]] : !fir.ref<i32>
     x = x + 12
-  !OMPDialect: omp.terminator
+  !CHECK: omp.terminator
   !$omp end single nowait
-  !OMPDialect: omp.terminator
+  !CHECK: omp.terminator
   !$omp end parallel
 end subroutine omp_single_nowait
 
@@ -49,21 +49,73 @@ end subroutine omp_single_nowait
 ! Single construct with allocate
 !===============================================================================
 
-!FIRDialect-LABEL: func @_QPsingle_allocate
+!CHECK-LABEL: func @_QPsingle_allocate
 subroutine single_allocate()
   use omp_lib
   integer :: x
-  !OMPDialect: omp.parallel {
+  !CHECK: omp.parallel {
   !$omp parallel
-  !OMPDialect: omp.single allocate(
-  !FIRDialect: %{{.+}} : i32 -> %{{.+}} : !fir.ref<i32>
-  !LLVMDialect: %{{.+}} : i32 -> %{{.+}} : !llvm.ptr<i32>
-  !OMPDialect: ) {
+  !CHECK: omp.single allocate(%{{.+}} : i32 -> %{{.+}} : !fir.ref<i32>) {
   !$omp single allocate(omp_high_bw_mem_alloc: x) private(x)
-  !FIRDialect: arith.addi
+  !CHECK: arith.addi
   x = x + 12
-  !OMPDialect: omp.terminator
+  !CHECK: omp.terminator
   !$omp end single
-  !OMPDialect: omp.terminator
+  !CHECK: omp.terminator
   !$omp end parallel
 end subroutine single_allocate
+
+!===============================================================================
+! Single construct with private/firstprivate
+!===============================================================================
+
+! CHECK-LABEL: func.func @_QPsingle_privatization(
+! CHECK-SAME:                                     %[[VAL_0:.*]]: !fir.ref<f32> {fir.bindc_name = "x"},
+! CHECK-SAME:                                     %[[VAL_1:.*]]: !fir.ref<f64> {fir.bindc_name = "y"}) {
+! CHECK:         %[[VAL_2:.*]] = fir.alloca f32 {bindc_name = "x", pinned, uniq_name = "_QFsingle_privatizationEx"}
+! CHECK:         %[[VAL_3:.*]] = fir.alloca f64 {bindc_name = "y", pinned, uniq_name = "_QFsingle_privatizationEy"}
+! CHECK:         omp.single   {
+! CHECK:           %[[VAL_4:.*]] = fir.load %[[VAL_1]] : !fir.ref<f64>
+! CHECK:           fir.store %[[VAL_4]] to %[[VAL_3]] : !fir.ref<f64>
+! CHECK:           fir.call @_QPbar(%[[VAL_2]], %[[VAL_3]]) : (!fir.ref<f32>, !fir.ref<f64>) -> ()
+! CHECK:           omp.terminator
+! CHECK:         }
+! CHECK:         return
+! CHECK:       }
+
+subroutine single_privatization(x, y)
+  real :: x
+  real(8) :: y
+
+  !$omp single private(x) firstprivate(y)
+  call bar(x, y)
+  !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_privatization2(
+! CHECK-SAME:                                      %[[VAL_0:.*]]: !fir.ref<f32> {fir.bindc_name = "x"},
+! CHECK-SAME:                                      %[[VAL_1:.*]]: !fir.ref<f64> {fir.bindc_name = "y"}) {
+! CHECK:         omp.parallel   {
+! CHECK:           %[[VAL_2:.*]] = fir.alloca f32 {bindc_name = "x", pinned, uniq_name = "_QFsingle_privatization2Ex"}
+! CHECK:           %[[VAL_3:.*]] = fir.alloca f64 {bindc_name = "y", pinned, uniq_name = "_QFsingle_privatization2Ey"}
+! CHECK:           omp.single   {
+! CHECK:             %[[VAL_4:.*]] = fir.load %[[VAL_1]] : !fir.ref<f64>
+! CHECK:             fir.store %[[VAL_4]] to %[[VAL_3]] : !fir.ref<f64>
+! CHECK:             fir.call @_QPbar(%[[VAL_2]], %[[VAL_3]]) : (!fir.ref<f32>, !fir.ref<f64>) -> ()
+! CHECK:             omp.terminator
+! CHECK:           }
+! CHECK:           omp.terminator
+! CHECK:         }
+! CHECK:         return
+! CHECK:       }
+
+subroutine single_privatization2(x, y)
+  real :: x
+  real(8) :: y
+
+  !$omp parallel
+  !$omp single private(x) firstprivate(y)
+  call bar(x, y)
+  !$omp end single
+  !$omp end parallel
+end subroutine


        


More information about the flang-commits mailing list