[flang-commits] [flang] 27afb36 - [flang][OpenMP] Initial support the lowering of copyin clause

via flang-commits flang-commits at lists.llvm.org
Fri Jun 24 00:35:03 PDT 2022


Author: Peixin-Qiao
Date: 2022-06-24T15:33:09+08:00
New Revision: 27afb362b1e85dac21744b95ed9b48f7e9fd016c

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

LOG: [flang][OpenMP] Initial support the lowering of copyin clause

This supports the lowering of copyin clause initially. The pointer,
allocatable, common block, polymorphic varaibles will be supported
later.

This also includes the following changes:

1. Resolve the COPYIN clause and make the entity as host associated.

2. Fix collectSymbolSet by adding one option to control collecting the
   symbol itself or ultimate symbol of it so that it can be used
   explicitly differentiate the host and associated variables in
   host-association.

3. Add one helper function `lookupOneLevelUpSymbol` to differentiate the
   usage of host and associated variables explicitly. The previous
   lowering of firstprivate depends on the order of
   `createHostAssociateVarClone` and `lookupSymbol` of host symbol. With
   this fix, this dependence is removed.

4. Reuse `copyHostAssociateVar` for copying operation of COPYIN clause.

Reviewed By: kiranchandramohan, NimishMishra

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

Added: 
    flang/test/Lower/OpenMP/copyin.f90

Modified: 
    flang/include/flang/Lower/AbstractConverter.h
    flang/include/flang/Lower/SymbolMap.h
    flang/lib/Lower/Bridge.cpp
    flang/lib/Lower/OpenMP.cpp
    flang/lib/Lower/SymbolMap.cpp
    flang/lib/Semantics/resolve-directives.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index 525d4e4473639..b00414fffd5ee 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -103,11 +103,13 @@ class AbstractConverter {
 
   virtual void copyHostAssociateVar(const Fortran::semantics::Symbol &sym) = 0;
 
-  /// Collect the set of symbols flagged as \p flag in \p eval region.
+  /// Collect the set of ultimate symbols of symbols with \p flag in \p eval
+  /// region if \p isUltimateSymbol is true. Otherwise, collect the set of
+  /// symbols with \p flag.
   virtual void collectSymbolSet(
       pft::Evaluation &eval,
       llvm::SetVector<const Fortran::semantics::Symbol *> &symbolSet,
-      Fortran::semantics::Symbol::Flag flag) = 0;
+      Fortran::semantics::Symbol::Flag flag, bool isUltimateSymbol = true) = 0;
 
   //===--------------------------------------------------------------------===//
   // Expressions

diff  --git a/flang/include/flang/Lower/SymbolMap.h b/flang/include/flang/Lower/SymbolMap.h
index 98f4e3cbe486b..165776f3d7c0e 100644
--- a/flang/include/flang/Lower/SymbolMap.h
+++ b/flang/include/flang/Lower/SymbolMap.h
@@ -302,6 +302,13 @@ class SymMap {
     return shallowLookupSymbol(*sym);
   }
 
+  /// Find `symbol` and return its value if it appears in the one level up map
+  /// such as for the host variable in host-association in OpenMP code.
+  SymbolBox lookupOneLevelUpSymbol(semantics::SymbolRef sym);
+  SymbolBox lookupOneLevelUpSymbol(const semantics::Symbol *sym) {
+    return lookupOneLevelUpSymbol(*sym);
+  }
+
   /// Add a new binding from the ac-do-variable `var` to `value`.
   void pushImpliedDoBinding(AcDoVar var, mlir::Value value) {
     impliedDoStack.emplace_back(var, value);

diff  --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index facd6640a2fee..b7d180ed73207 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -481,16 +481,20 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     assert(sym.has<Fortran::semantics::HostAssocDetails>() &&
            "No host-association found");
     const Fortran::semantics::Symbol &hsym = sym.GetUltimate();
-    Fortran::lower::SymbolBox hsb = lookupSymbol(hsym);
+    Fortran::lower::SymbolBox hsb = lookupOneLevelUpSymbol(hsym);
+    assert(hsb && "Host symbol box not found");
     fir::ExtendedValue hexv = getExtendedValue(hsb);
 
-    // 2) Create a copy that will mask the original.
-    createHostAssociateVarClone(sym);
-    Fortran::lower::SymbolBox sb = lookupSymbol(sym);
+    // 2) Fetch the copied one that will mask the original.
+    Fortran::lower::SymbolBox sb = shallowLookupSymbol(sym);
+    assert(sb && "Host-associated symbol box not found");
+    assert(hsb.getAddr() != sb.getAddr() &&
+           "Host and associated symbol boxes are the same");
     fir::ExtendedValue exv = getExtendedValue(sb);
 
     // 3) Perform the assignment.
-    mlir::Location loc = genLocation(sym.name());
+    builder->setInsertionPointAfter(fir::getBase(exv).getDefiningOp());
+    mlir::Location loc = getCurrentLocation();
     mlir::Type symType = genType(sym);
     if (auto seqTy = symType.dyn_cast<fir::SequenceType>()) {
       Fortran::lower::StatementContext stmtCtx;
@@ -514,11 +518,13 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   void collectSymbolSet(
       Fortran::lower::pft::Evaluation &eval,
       llvm::SetVector<const Fortran::semantics::Symbol *> &symbolSet,
-      Fortran::semantics::Symbol::Flag flag) override final {
+      Fortran::semantics::Symbol::Flag flag,
+      bool isUltimateSymbol) override final {
     auto addToList = [&](const Fortran::semantics::Symbol &sym) {
-      const Fortran::semantics::Symbol &ultimate = sym.GetUltimate();
-      if (ultimate.test(flag))
-        symbolSet.insert(&ultimate);
+      const Fortran::semantics::Symbol &symbol =
+          isUltimateSymbol ? sym.GetUltimate() : sym;
+      if (symbol.test(flag))
+        symbolSet.insert(&symbol);
     };
     Fortran::lower::pft::visitAllSymbols(eval, addToList);
   }
@@ -609,6 +615,15 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     return {};
   }
 
+  /// Find the symbol in one level up of symbol map such as for host-association
+  /// in OpenMP code or return null.
+  Fortran::lower::SymbolBox
+  lookupOneLevelUpSymbol(const Fortran::semantics::Symbol &sym) {
+    if (Fortran::lower::SymbolBox v = localSymbols.lookupOneLevelUpSymbol(sym))
+      return v;
+    return {};
+  }
+
   /// Add the symbol to the local map and return `true`. If the symbol is
   /// already in the map and \p forced is `false`, the map is not updated.
   /// Instead the value `false` is returned.

diff  --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp
index a895e6fea37e8..c60fb711f55d0 100644
--- a/flang/lib/Lower/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP.cpp
@@ -69,12 +69,11 @@ static void createPrivateVarSyms(Fortran::lower::AbstractConverter &converter,
     // variables) happen separately, for everything else privatize here.
     if (sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined))
       continue;
+    bool success = converter.createHostAssociateVarClone(*sym);
+    (void)success;
+    assert(success && "Privatization failed due to existing binding");
     if constexpr (std::is_same_v<T, Fortran::parser::OmpClause::Firstprivate>) {
       converter.copyHostAssociateVar(*sym);
-    } else {
-      bool success = converter.createHostAssociateVarClone(*sym);
-      (void)success;
-      assert(success && "Privatization failed due to existing binding");
     }
   }
 }
@@ -161,9 +160,10 @@ static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter,
   };
 
   llvm::SetVector<const Fortran::semantics::Symbol *> threadprivateSyms;
-  converter.collectSymbolSet(
-      eval, threadprivateSyms,
-      Fortran::semantics::Symbol::Flag::OmpThreadprivate);
+  converter.collectSymbolSet(eval, threadprivateSyms,
+                             Fortran::semantics::Symbol::Flag::OmpThreadprivate,
+                             /*isUltimateSymbol=*/false);
+  std::set<Fortran::semantics::SourceName> threadprivateSymNames;
 
   // For a COMMON block, the ThreadprivateOp is generated for itself instead of
   // its members, so only bind the value of the new copied ThreadprivateOp
@@ -173,6 +173,11 @@ static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter,
   for (std::size_t i = 0; i < threadprivateSyms.size(); i++) {
     auto sym = threadprivateSyms[i];
     mlir::Value symThreadprivateValue;
+    // The variable may be used more than once, and each reference has one
+    // symbol with the same name. Only do once for references of one variable.
+    if (threadprivateSymNames.find(sym->name()) != threadprivateSymNames.end())
+      continue;
+    threadprivateSymNames.insert(sym->name());
     if (const Fortran::semantics::Symbol *common =
             Fortran::semantics::FindCommonBlockContaining(sym->GetUltimate())) {
       mlir::Value commonThreadprivateValue;
@@ -198,6 +203,36 @@ static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter,
   firOpBuilder.restoreInsertionPoint(insPt);
 }
 
+static void
+genCopyinClause(Fortran::lower::AbstractConverter &converter,
+                const Fortran::parser::OmpClauseList &opClauseList) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();
+  firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
+  bool hasCopyin = false;
+  for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
+    if (const auto &copyinClause =
+            std::get_if<Fortran::parser::OmpClause::Copyin>(&clause.u)) {
+      hasCopyin = true;
+      const Fortran::parser::OmpObjectList &ompObjectList = copyinClause->v;
+      for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
+        Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
+        if (sym->has<Fortran::semantics::CommonBlockDetails>())
+          TODO(converter.getCurrentLocation(), "common block in Copyin clause");
+        if (Fortran::semantics::IsAllocatableOrPointer(sym->GetUltimate()))
+          TODO(converter.getCurrentLocation(),
+               "pointer or allocatable variables in Copyin clause");
+        assert(sym->has<Fortran::semantics::HostAssocDetails>() &&
+               "No host-association found");
+        converter.copyHostAssociateVar(*sym);
+      }
+    }
+  }
+  if (hasCopyin)
+    firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation());
+  firOpBuilder.restoreInsertionPoint(insPt);
+}
+
 static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
                           Fortran::lower::AbstractConverter &converter,
                           llvm::SmallVectorImpl<Value> &operands) {
@@ -343,8 +378,11 @@ createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter,
   if (clauses && !outerCombined)
     privatizeVars(converter, *clauses);
 
-  if (std::is_same_v<Op, omp::ParallelOp>)
+  if (std::is_same_v<Op, omp::ParallelOp>) {
     threadPrivatizeVars(converter, eval);
+    if (clauses)
+      genCopyinClause(converter, *clauses);
+  }
 }
 
 static void genOMP(Fortran::lower::AbstractConverter &converter,
@@ -490,7 +528,6 @@ createCombinedParallelOp(Fortran::lower::AbstractConverter &converter,
       std::get<Fortran::parser::OmpClauseList>(directive.t);
   // TODO: Handle the following clauses
   // 1. default
-  // 2. copyin
   // Note: rest of the clauses are handled when the inner operation is created
   for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
     if (const auto &ifClause =
@@ -570,8 +607,9 @@ genOMP(Fortran::lower::AbstractConverter &converter,
                         allocateOperands);
     } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) ||
                std::get_if<Fortran::parser::OmpClause::Firstprivate>(
-                   &clause.u)) {
-      // Privatisation clauses are handled elsewhere.
+                   &clause.u) ||
+               std::get_if<Fortran::parser::OmpClause::Copyin>(&clause.u)) {
+      // Privatisation and copyin clauses are handled elsewhere.
       continue;
     } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) {
       // Nothing needs to be done for threads clause.

diff  --git a/flang/lib/Lower/SymbolMap.cpp b/flang/lib/Lower/SymbolMap.cpp
index 414c4f0f5c9e9..c081c8aa1e7af 100644
--- a/flang/lib/Lower/SymbolMap.cpp
+++ b/flang/lib/Lower/SymbolMap.cpp
@@ -51,6 +51,25 @@ Fortran::lower::SymbolBox Fortran::lower::SymMap::shallowLookupSymbol(
   return SymbolBox::None{};
 }
 
+/// Skip one level when looking up the symbol. The use case is such as looking
+/// up the host variable symbol box by skipping the associated level in
+/// host-association in OpenMP code.
+Fortran::lower::SymbolBox Fortran::lower::SymMap::lookupOneLevelUpSymbol(
+    Fortran::semantics::SymbolRef symRef) {
+  Fortran::semantics::SymbolRef sym = symRef.get().GetUltimate();
+  auto jmap = symbolMapStack.rbegin();
+  auto jend = symbolMapStack.rend();
+  if (jmap == jend)
+    return SymbolBox::None{};
+  // Skip one level in symbol map stack.
+  for (++jmap; jmap != jend; ++jmap) {
+    auto iter = jmap->find(&*sym);
+    if (iter != jmap->end())
+      return iter->second;
+  }
+  return SymbolBox::None{};
+}
+
 mlir::Value
 Fortran::lower::SymMap::lookupImpliedDo(Fortran::lower::SymMap::AcDoVar var) {
   for (auto [marker, binding] : llvm::reverse(impliedDoStack))

diff  --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 99e59f7fc7af8..bb42638a308ee 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -476,7 +476,8 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
   static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
       Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
       Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
-      Symbol::Flag::OmpReduction, Symbol::Flag::OmpCriticalLock};
+      Symbol::Flag::OmpReduction, Symbol::Flag::OmpCriticalLock,
+      Symbol::Flag::OmpCopyIn};
 
   static constexpr Symbol::Flags ompFlagsRequireMark{
       Symbol::Flag::OmpThreadprivate};
@@ -580,6 +581,10 @@ Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity(
   if (object.owner() != currScope()) {
     auto &symbol{MakeAssocSymbol(object.name(), object, scope)};
     symbol.set(flag);
+    if (flag == Symbol::Flag::OmpCopyIn) {
+      // The symbol in copyin clause must be threadprivate entity.
+      symbol.set(Symbol::Flag::OmpThreadprivate);
+    }
     return &symbol;
   } else {
     object.set(flag);

diff  --git a/flang/test/Lower/OpenMP/copyin.f90 b/flang/test/Lower/OpenMP/copyin.f90
new file mode 100644
index 0000000000000..cabfb147d2dc2
--- /dev/null
+++ b/flang/test/Lower/OpenMP/copyin.f90
@@ -0,0 +1,206 @@
+! This test checks lowering of `COPYIN` clause.
+! RUN: bbc -fopenmp -emit-fir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPcopyin_scalar_array() {
+! CHECK:         %[[VAL_0:.*]] = fir.address_of(@_QFcopyin_scalar_arrayEx1) : !fir.ref<i32>
+! CHECK:         %[[VAL_1:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<i32> -> !fir.ref<i32>
+! CHECK:         %[[VAL_2:.*]] = fir.address_of(@_QFcopyin_scalar_arrayEx2) : !fir.ref<!fir.array<10xi64>>
+! CHECK:         %[[VAL_3:.*]] = arith.constant 10 : index
+! CHECK:         %[[VAL_4:.*]] = omp.threadprivate %[[VAL_2]] : !fir.ref<!fir.array<10xi64>> -> !fir.ref<!fir.array<10xi64>>
+! CHECK:         omp.parallel   {
+! CHECK:           %[[VAL_5:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<i32> -> !fir.ref<i32>
+! CHECK:           %[[VAL_6:.*]] = fir.load %[[VAL_1]] : !fir.ref<i32>
+! CHECK:           fir.store %[[VAL_6]] to %[[VAL_5]] : !fir.ref<i32>
+! CHECK:           %[[VAL_7:.*]] = omp.threadprivate %[[VAL_2]] : !fir.ref<!fir.array<10xi64>> -> !fir.ref<!fir.array<10xi64>>
+! CHECK:           %[[VAL_8:.*]] = fir.shape %[[VAL_3]] : (index) -> !fir.shape<1>
+! CHECK:           %[[VAL_9:.*]] = fir.array_load %[[VAL_7]](%[[VAL_8]]) : (!fir.ref<!fir.array<10xi64>>, !fir.shape<1>) -> !fir.array<10xi64>
+! CHECK:           %[[VAL_10:.*]] = fir.shape %[[VAL_3]] : (index) -> !fir.shape<1>
+! CHECK:           %[[VAL_11:.*]] = fir.array_load %[[VAL_4]](%[[VAL_10]]) : (!fir.ref<!fir.array<10xi64>>, !fir.shape<1>) -> !fir.array<10xi64>
+! CHECK:           %[[VAL_12:.*]] = arith.constant 1 : index
+! CHECK:           %[[VAL_13:.*]] = arith.constant 0 : index
+! CHECK:           %[[VAL_14:.*]] = arith.subi %[[VAL_3]], %[[VAL_12]] : index
+! CHECK:           %[[VAL_15:.*]] = fir.do_loop %[[VAL_16:.*]] = %[[VAL_13]] to %[[VAL_14]] step %[[VAL_12]] unordered iter_args(%[[VAL_17:.*]] = %[[VAL_9]]) -> (!fir.array<10xi64>) {
+! CHECK:             %[[VAL_18:.*]] = fir.array_fetch %[[VAL_11]], %[[VAL_16]] : (!fir.array<10xi64>, index) -> i64
+! CHECK:             %[[VAL_19:.*]] = fir.array_update %[[VAL_17]], %[[VAL_18]], %[[VAL_16]] : (!fir.array<10xi64>, i64, index) -> !fir.array<10xi64>
+! CHECK:             fir.result %[[VAL_19]] : !fir.array<10xi64>
+! CHECK:           }
+! CHECK:           fir.array_merge_store %[[VAL_9]], %[[VAL_20:.*]] to %[[VAL_7]] : !fir.array<10xi64>, !fir.array<10xi64>, !fir.ref<!fir.array<10xi64>>
+! CHECK:           omp.barrier
+! CHECK:           fir.call @_QPsub1(%[[VAL_5]], %[[VAL_7]]) : (!fir.ref<i32>, !fir.ref<!fir.array<10xi64>>) -> ()
+! CHECK:           omp.terminator
+! CHECK:         }
+! CHECK:         return
+! CHECK:       }
+
+subroutine copyin_scalar_array()
+  integer(kind=4), save :: x1
+  integer(kind=8), save :: x2(10)
+  !$omp threadprivate(x1, x2)
+
+  !$omp parallel copyin(x1) copyin(x2)
+    call sub1(x1, x2)
+  !$omp end parallel
+
+end
+
+! CHECK-LABEL: func.func @_QPcopyin_char_chararray() {
+! CHECK:         %[[VAL_0:.*]] = fir.address_of(@_QFcopyin_char_chararrayEx3) : !fir.ref<!fir.char<1,5>>
+! CHECK:         %[[VAL_1:.*]] = arith.constant 5 : index
+! CHECK:         %[[VAL_2:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<!fir.char<1,5>> -> !fir.ref<!fir.char<1,5>>
+! CHECK:         %[[VAL_3:.*]] = fir.address_of(@_QFcopyin_char_chararrayEx4) : !fir.ref<!fir.array<10x!fir.char<1,5>>>
+! CHECK:         %[[VAL_4:.*]] = arith.constant 5 : index
+! CHECK:         %[[VAL_5:.*]] = arith.constant 10 : index
+! CHECK:         %[[VAL_6:.*]] = omp.threadprivate %[[VAL_3]] : !fir.ref<!fir.array<10x!fir.char<1,5>>> -> !fir.ref<!fir.array<10x!fir.char<1,5>>>
+! CHECK:         omp.parallel   {
+! CHECK:           %[[VAL_7:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<!fir.char<1,5>> -> !fir.ref<!fir.char<1,5>>
+! CHECK:           %[[VAL_8:.*]] = arith.constant 1 : i64
+! CHECK:           %[[VAL_9:.*]] = fir.convert %[[VAL_1]] : (index) -> i64
+! CHECK:           %[[VAL_10:.*]] = arith.muli %[[VAL_8]], %[[VAL_9]] : i64
+! CHECK:           %[[VAL_11:.*]] = arith.constant false
+! CHECK:           %[[VAL_12:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.char<1,5>>) -> !fir.ref<i8>
+! CHECK:           %[[VAL_13:.*]] = fir.convert %[[VAL_2]] : (!fir.ref<!fir.char<1,5>>) -> !fir.ref<i8>
+! CHECK:           fir.call @llvm.memmove.p0.p0.i64(%[[VAL_12]], %[[VAL_13]], %[[VAL_10]], %[[VAL_11]]) : (!fir.ref<i8>, !fir.ref<i8>, i64, i1) -> ()
+! CHECK:           %[[VAL_14:.*]] = omp.threadprivate %[[VAL_3]] : !fir.ref<!fir.array<10x!fir.char<1,5>>> -> !fir.ref<!fir.array<10x!fir.char<1,5>>>
+! CHECK:           %[[VAL_15:.*]] = fir.shape %[[VAL_5]] : (index) -> !fir.shape<1>
+! CHECK:           %[[VAL_16:.*]] = fir.array_load %[[VAL_14]](%[[VAL_15]]) : (!fir.ref<!fir.array<10x!fir.char<1,5>>>, !fir.shape<1>) -> !fir.array<10x!fir.char<1,5>>
+! CHECK:           %[[VAL_17:.*]] = fir.shape %[[VAL_5]] : (index) -> !fir.shape<1>
+! CHECK:           %[[VAL_18:.*]] = fir.array_load %[[VAL_6]](%[[VAL_17]]) : (!fir.ref<!fir.array<10x!fir.char<1,5>>>, !fir.shape<1>) -> !fir.array<10x!fir.char<1,5>>
+! CHECK:           %[[VAL_19:.*]] = arith.constant 1 : index
+! CHECK:           %[[VAL_20:.*]] = arith.constant 0 : index
+! CHECK:           %[[VAL_21:.*]] = arith.subi %[[VAL_5]], %[[VAL_19]] : index
+! CHECK:           %[[VAL_22:.*]] = fir.do_loop %[[VAL_23:.*]] = %[[VAL_20]] to %[[VAL_21]] step %[[VAL_19]] unordered iter_args(%[[VAL_24:.*]] = %[[VAL_16]]) -> (!fir.array<10x!fir.char<1,5>>) {
+! CHECK:             %[[VAL_25:.*]] = fir.array_access %[[VAL_18]], %[[VAL_23]] : (!fir.array<10x!fir.char<1,5>>, index) -> !fir.ref<!fir.char<1,5>>
+! CHECK:             %[[VAL_26:.*]] = fir.array_access %[[VAL_24]], %[[VAL_23]] : (!fir.array<10x!fir.char<1,5>>, index) -> !fir.ref<!fir.char<1,5>>
+! CHECK:             %[[VAL_27:.*]] = arith.constant 5 : index
+! CHECK:             %[[VAL_28:.*]] = arith.constant 1 : i64
+! CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_27]] : (index) -> i64
+! CHECK:             %[[VAL_30:.*]] = arith.muli %[[VAL_28]], %[[VAL_29]] : i64
+! CHECK:             %[[VAL_31:.*]] = arith.constant false
+! CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_26]] : (!fir.ref<!fir.char<1,5>>) -> !fir.ref<i8>
+! CHECK:             %[[VAL_33:.*]] = fir.convert %[[VAL_25]] : (!fir.ref<!fir.char<1,5>>) -> !fir.ref<i8>
+! CHECK:             fir.call @llvm.memmove.p0.p0.i64(%[[VAL_32]], %[[VAL_33]], %[[VAL_30]], %[[VAL_31]]) : (!fir.ref<i8>, !fir.ref<i8>, i64, i1) -> ()
+! CHECK:             %[[VAL_34:.*]] = fir.array_amend %[[VAL_24]], %[[VAL_26]] : (!fir.array<10x!fir.char<1,5>>, !fir.ref<!fir.char<1,5>>) -> !fir.array<10x!fir.char<1,5>>
+! CHECK:             fir.result %[[VAL_34]] : !fir.array<10x!fir.char<1,5>>
+! CHECK:           }
+! CHECK:           fir.array_merge_store %[[VAL_16]], %[[VAL_35:.*]] to %[[VAL_14]] : !fir.array<10x!fir.char<1,5>>, !fir.array<10x!fir.char<1,5>>, !fir.ref<!fir.array<10x!fir.char<1,5>>>
+! CHECK:           omp.barrier
+! CHECK:           %[[VAL_36:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<!fir.char<1,5>>) -> !fir.ref<!fir.char<1,?>>
+! CHECK:           %[[VAL_37:.*]] = fir.emboxchar %[[VAL_36]], %[[VAL_1]] : (!fir.ref<!fir.char<1,?>>, index) -> !fir.boxchar<1>
+! CHECK:           %[[VAL_38:.*]] = fir.convert %[[VAL_14]] : (!fir.ref<!fir.array<10x!fir.char<1,5>>>) -> !fir.ref<!fir.char<1,?>>
+! CHECK:           %[[VAL_39:.*]] = fir.emboxchar %[[VAL_38]], %[[VAL_4]] : (!fir.ref<!fir.char<1,?>>, index) -> !fir.boxchar<1>
+! CHECK:           fir.call @_QPsub2(%[[VAL_37]], %[[VAL_39]]) : (!fir.boxchar<1>, !fir.boxchar<1>) -> ()
+! CHECK:           omp.terminator
+! CHECK:         }
+! CHECK:         return
+! CHECK:       }
+
+subroutine copyin_char_chararray()
+  character(5), save :: x3, x4(10)
+  !$omp threadprivate(x3, x4)
+
+  !$omp parallel copyin(x3) copyin(x4)
+    call sub2(x3, x4)
+  !$omp end parallel
+
+end
+
+! CHECK-LABEL: func.func @_QPcopyin_derived_type() {
+! CHECK:         %[[VAL_0:.*]] = fir.address_of(@_QFcopyin_derived_typeEx5) : !fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>>
+! CHECK:         %[[VAL_1:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>> -> !fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>>
+! CHECK:         omp.parallel   {
+! CHECK:           %[[VAL_2:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>> -> !fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>>
+! CHECK:           %[[VAL_3:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>>
+! CHECK:           fir.store %[[VAL_3]] to %[[VAL_2]] : !fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>>
+! CHECK:           omp.barrier
+! CHECK:           fir.call @_QPsub3(%[[VAL_2]]) : (!fir.ref<!fir.type<_QFcopyin_derived_typeTmy_type{t_i:i32,t_arr:!fir.array<5xi32>}>>) -> ()
+! CHECK:           omp.terminator
+! CHECK:         }
+! CHECK:         return
+! CHECK:       }
+
+subroutine copyin_derived_type()
+  type my_type
+    integer :: t_i
+    integer :: t_arr(5)
+  end type my_type
+  type(my_type), save :: x5
+  !$omp threadprivate(x5)
+
+  !$omp parallel copyin(x5)
+    call sub3(x5)
+  !$omp end parallel
+
+end
+
+! CHECK-LABEL: func.func @_QPcombined_parallel_worksharing_loop() {
+! CHECK:         %[[VAL_0:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFcombined_parallel_worksharing_loopEi"}
+! CHECK:         %[[VAL_1:.*]] = fir.address_of(@_QFcombined_parallel_worksharing_loopEx6) : !fir.ref<i32>
+! CHECK:         %[[VAL_2:.*]] = omp.threadprivate %[[VAL_1]] : !fir.ref<i32> -> !fir.ref<i32>
+! CHECK:         omp.parallel   {
+! CHECK:           %[[VAL_3:.*]] = fir.alloca i32 {adapt.valuebyref, pinned}
+! CHECK:           %[[VAL_4:.*]] = omp.threadprivate %[[VAL_1]] : !fir.ref<i32> -> !fir.ref<i32>
+! CHECK:           %[[VAL_5:.*]] = fir.load %[[VAL_2]] : !fir.ref<i32>
+! CHECK:           fir.store %[[VAL_5]] to %[[VAL_4]] : !fir.ref<i32>
+! CHECK:           omp.barrier
+! CHECK:           %[[VAL_6:.*]] = arith.constant 1 : i32
+! CHECK:           %[[VAL_7:.*]] = fir.load %[[VAL_4]] : !fir.ref<i32>
+! CHECK:           %[[VAL_8:.*]] = arith.constant 1 : i32
+! CHECK:           omp.wsloop   for  (%[[VAL_9:.*]]) : i32 = (%[[VAL_6]]) to (%[[VAL_7]]) inclusive step (%[[VAL_8]]) {
+! CHECK:             fir.store %[[VAL_9]] to %[[VAL_3]] : !fir.ref<i32>
+! CHECK:             fir.call @_QPsub4(%[[VAL_4]]) : (!fir.ref<i32>) -> ()
+! CHECK:             omp.yield
+! CHECK:           }
+! CHECK:           omp.terminator
+! CHECK:         }
+! CHECK:         return
+! CHECK:       }
+
+subroutine combined_parallel_worksharing_loop()
+  integer, save :: x6
+  !$omp threadprivate(x6)
+
+  !$omp parallel do copyin(x6)
+    do i=1, x6
+      call sub4(x6)
+    end do
+  !$omp end parallel do
+
+end
+
+! CHECK-LABEL: func.func @_QPcombined_parallel_sections() {
+! CHECK:         %[[VAL_0:.*]] = fir.address_of(@_QFcombined_parallel_sectionsEx7) : !fir.ref<i32>
+! CHECK:         %[[VAL_1:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<i32> -> !fir.ref<i32>
+! CHECK:         omp.parallel   {
+! CHECK:           %[[VAL_2:.*]] = omp.threadprivate %[[VAL_0]] : !fir.ref<i32> -> !fir.ref<i32>
+! CHECK:           %[[VAL_3:.*]] = fir.load %[[VAL_1]] : !fir.ref<i32>
+! CHECK:           fir.store %[[VAL_3]] to %[[VAL_2]] : !fir.ref<i32>
+! CHECK:           omp.barrier
+! CHECK:           omp.sections   {
+! CHECK:             omp.section {
+! CHECK:               fir.call @_QPsub5(%[[VAL_2]]) : (!fir.ref<i32>) -> ()
+! CHECK:               omp.terminator
+! CHECK:             }
+! CHECK:             omp.section {
+! CHECK:               fir.call @_QPsub6(%[[VAL_2]]) : (!fir.ref<i32>) -> ()
+! CHECK:               omp.terminator
+! CHECK:             }
+! CHECK:             omp.terminator
+! CHECK:           }
+! CHECK:           omp.terminator
+! CHECK:         }
+! CHECK:         return
+! CHECK:       }
+
+subroutine combined_parallel_sections()
+  integer, save :: x7
+  !$omp threadprivate(x7)
+
+  !$omp parallel sections copyin(x7)
+    !$omp section
+      call sub5(x7)
+    !$omp section
+      call sub6(x7)
+  !$omp end parallel sections
+
+end


        


More information about the flang-commits mailing list