From 95131ec8fe9e1041a7b110dcd48cd4dd06143857 Mon Sep 17 00:00:00 2001 From: Leandro Lupori Date: Fri, 2 Feb 2024 16:31:20 -0300 Subject: [PATCH] [flang][OpenMP] Add support for copyprivate Add initial handling of OpenMP copyprivate clause in Flang. When lowering copyprivate, Flang generates the copy function needed by each variable and builds the appropriate omp.single's CopyPrivateVarList. This is patch 3 of 4, to add support for COPYPRIVATE in Flang. Original PR: https://github.com/llvm/llvm-project/pull/73128 --- flang/include/flang/Lower/AbstractConverter.h | 3 + flang/lib/Lower/Bridge.cpp | 137 ++++++++------- flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 156 +++++++++++++++++ flang/lib/Lower/OpenMP/ClauseProcessor.h | 4 + flang/lib/Lower/OpenMP/OpenMP.cpp | 17 +- flang/test/Lower/OpenMP/Todo/copyprivate.f90 | 13 -- flang/test/Lower/OpenMP/copyprivate.f90 | 164 ++++++++++++++++++ 7 files changed, 418 insertions(+), 76 deletions(-) delete mode 100644 flang/test/Lower/OpenMP/Todo/copyprivate.f90 create mode 100644 flang/test/Lower/OpenMP/copyprivate.f90 diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h index 796933a4eb5f6..e2af59e0aaa19 100644 --- a/flang/include/flang/Lower/AbstractConverter.h +++ b/flang/include/flang/Lower/AbstractConverter.h @@ -121,6 +121,9 @@ class AbstractConverter { const Fortran::semantics::Symbol &sym, mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) = 0; + virtual void copyVar(mlir::Location loc, mlir::Value dst, + mlir::Value src) = 0; + /// For a given symbol, check if it is present in the inner-most /// level of the symbol map. virtual bool isPresentShallowLookup(Fortran::semantics::Symbol &sym) = 0; diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index 2d7f748cefa2d..83555e7cd82e7 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -744,6 +744,11 @@ class FirConverter : public Fortran::lower::AbstractConverter { }); } + void copyVar(mlir::Location loc, mlir::Value dst, + mlir::Value src) override final { + copyVarHLFIR(loc, dst, src); + } + void copyHostAssociateVar( const Fortran::semantics::Symbol &sym, mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) override final { @@ -778,64 +783,7 @@ class FirConverter : public Fortran::lower::AbstractConverter { rhs_sb = &hsb; } - mlir::Location loc = genLocation(sym.name()); - - if (lowerToHighLevelFIR()) { - hlfir::Entity lhs{lhs_sb->getAddr()}; - hlfir::Entity rhs{rhs_sb->getAddr()}; - // Temporary_lhs is set to true in hlfir.assign below to avoid user - // assignment to be used and finalization to be called on the LHS. - // This may or may not be correct but mimics the current behaviour - // without HLFIR. - auto copyData = [&](hlfir::Entity l, hlfir::Entity r) { - // Dereference RHS and load it if trivial scalar. - r = hlfir::loadTrivialScalar(loc, *builder, r); - builder->create( - loc, r, l, - /*isWholeAllocatableAssignment=*/false, - /*keepLhsLengthInAllocatableAssignment=*/false, - /*temporary_lhs=*/true); - }; - if (lhs.isAllocatable()) { - // Deep copy allocatable if it is allocated. - // Note that when allocated, the RHS is already allocated with the LHS - // shape for copy on entry in createHostAssociateVarClone. - // For lastprivate, this assumes that the RHS was not reallocated in - // the OpenMP region. - lhs = hlfir::derefPointersAndAllocatables(loc, *builder, lhs); - mlir::Value addr = hlfir::genVariableRawAddress(loc, *builder, lhs); - mlir::Value isAllocated = builder->genIsNotNullAddr(loc, addr); - builder->genIfThen(loc, isAllocated) - .genThen([&]() { - // Copy the DATA, not the descriptors. - copyData(lhs, rhs); - }) - .end(); - } else if (lhs.isPointer()) { - // Set LHS target to the target of RHS (do not copy the RHS - // target data into the LHS target storage). - auto loadVal = builder->create(loc, rhs); - builder->create(loc, loadVal, lhs); - } else { - // Non ALLOCATABLE/POINTER variable. Simple DATA copy. - copyData(lhs, rhs); - } - } else { - fir::ExtendedValue lhs = symBoxToExtendedValue(*lhs_sb); - fir::ExtendedValue rhs = symBoxToExtendedValue(*rhs_sb); - mlir::Type symType = genType(sym); - if (auto seqTy = symType.dyn_cast()) { - Fortran::lower::StatementContext stmtCtx; - Fortran::lower::createSomeArrayAssignment(*this, lhs, rhs, localSymbols, - stmtCtx); - stmtCtx.finalizeAndReset(); - } else if (lhs.getBoxOf()) { - fir::factory::CharacterExprHelper{*builder, loc}.createAssign(lhs, rhs); - } else { - auto loadVal = builder->create(loc, fir::getBase(rhs)); - builder->create(loc, loadVal, fir::getBase(lhs)); - } - } + copyVar(sym, *lhs_sb, *rhs_sb); if (copyAssignIP && copyAssignIP->isSet() && sym.test(Fortran::semantics::Symbol::Flag::OmpLastPrivate)) { @@ -1093,6 +1041,79 @@ class FirConverter : public Fortran::lower::AbstractConverter { return true; } + void copyVar(const Fortran::semantics::Symbol &sym, + const Fortran::lower::SymbolBox &lhs_sb, + const Fortran::lower::SymbolBox &rhs_sb) { + mlir::Location loc = genLocation(sym.name()); + if (lowerToHighLevelFIR()) + copyVarHLFIR(loc, lhs_sb.getAddr(), rhs_sb.getAddr()); + else + copyVarFIR(loc, sym, lhs_sb, rhs_sb); + } + + void copyVarHLFIR(mlir::Location loc, mlir::Value dst, mlir::Value src) { + assert(lowerToHighLevelFIR()); + hlfir::Entity lhs{dst}; + hlfir::Entity rhs{src}; + // Temporary_lhs is set to true in hlfir.assign below to avoid user + // assignment to be used and finalization to be called on the LHS. + // This may or may not be correct but mimics the current behaviour + // without HLFIR. + auto copyData = [&](hlfir::Entity l, hlfir::Entity r) { + // Dereference RHS and load it if trivial scalar. + r = hlfir::loadTrivialScalar(loc, *builder, r); + builder->create( + loc, r, l, + /*isWholeAllocatableAssignment=*/false, + /*keepLhsLengthInAllocatableAssignment=*/false, + /*temporary_lhs=*/true); + }; + if (lhs.isAllocatable()) { + // Deep copy allocatable if it is allocated. + // Note that when allocated, the RHS is already allocated with the LHS + // shape for copy on entry in createHostAssociateVarClone. + // For lastprivate, this assumes that the RHS was not reallocated in + // the OpenMP region. + lhs = hlfir::derefPointersAndAllocatables(loc, *builder, lhs); + mlir::Value addr = hlfir::genVariableRawAddress(loc, *builder, lhs); + mlir::Value isAllocated = builder->genIsNotNullAddr(loc, addr); + builder->genIfThen(loc, isAllocated) + .genThen([&]() { + // Copy the DATA, not the descriptors. + copyData(lhs, rhs); + }) + .end(); + } else if (lhs.isPointer()) { + // Set LHS target to the target of RHS (do not copy the RHS + // target data into the LHS target storage). + auto loadVal = builder->create(loc, rhs); + builder->create(loc, loadVal, lhs); + } else { + // Non ALLOCATABLE/POINTER variable. Simple DATA copy. + copyData(lhs, rhs); + } + } + + void copyVarFIR(mlir::Location loc, const Fortran::semantics::Symbol &sym, + const Fortran::lower::SymbolBox &lhs_sb, + const Fortran::lower::SymbolBox &rhs_sb) { + assert(!lowerToHighLevelFIR()); + fir::ExtendedValue lhs = symBoxToExtendedValue(lhs_sb); + fir::ExtendedValue rhs = symBoxToExtendedValue(rhs_sb); + mlir::Type symType = genType(sym); + if (auto seqTy = symType.dyn_cast()) { + Fortran::lower::StatementContext stmtCtx; + Fortran::lower::createSomeArrayAssignment(*this, lhs, rhs, localSymbols, + stmtCtx); + stmtCtx.finalizeAndReset(); + } else if (lhs.getBoxOf()) { + fir::factory::CharacterExprHelper{*builder, loc}.createAssign(lhs, rhs); + } else { + auto loadVal = builder->create(loc, fir::getBase(rhs)); + builder->create(loc, loadVal, fir::getBase(lhs)); + } + } + /// Map a block argument to a result or dummy symbol. This is not the /// definitive mapping. The specification expression have not been lowered /// yet. The final mapping will be done using this pre-mapping in diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index 4e3951492fb65..a41f8312a28c9 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -609,6 +609,162 @@ bool ClauseProcessor::processCopyin() const { return hasCopyin; } +/// Class that extracts information from the specified type. +class TypeInfo { +public: + TypeInfo(mlir::Type ty) { typeScan(ty); } + + // Returns the length of character types. + std::optional getCharLength() const { + return charLen; + } + + // Returns the shape of array types. + const llvm::SmallVector &getShape() const { return shape; } + + // Is the type inside a box? + bool isBox() const { return inBox; } + +private: + void typeScan(mlir::Type type); + + std::optional charLen; + llvm::SmallVector shape; + bool inBox = false; +}; + +void TypeInfo::typeScan(mlir::Type ty) { + if (auto sty = mlir::dyn_cast(ty)) { + assert(shape.empty() && !sty.getShape().empty()); + shape = llvm::SmallVector(sty.getShape()); + typeScan(sty.getEleTy()); + } else if (auto bty = mlir::dyn_cast(ty)) { + inBox = true; + typeScan(bty.getEleTy()); + } else if (auto cty = mlir::dyn_cast(ty)) { + charLen = cty.getLen(); + } else if (auto hty = mlir::dyn_cast(ty)) { + typeScan(hty.getEleTy()); + } else if (auto pty = mlir::dyn_cast(ty)) { + typeScan(pty.getEleTy()); + } else { + // The scan ends when reaching any built-in or record type. + assert(ty.isIntOrIndexOrFloat() || mlir::isa(ty) || + mlir::isa(ty) || mlir::isa(ty)); + } +} + +// Create a function that performs a copy between two variables, compatible +// with their types and attributes. +static mlir::func::FuncOp +createCopyFunc(mlir::Location loc, Fortran::lower::AbstractConverter &converter, + mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) { + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + mlir::ModuleOp module = builder.getModule(); + mlir::Type eleTy = mlir::cast(varType).getEleTy(); + TypeInfo typeInfo(eleTy); + std::string copyFuncName = + fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy"); + + if (auto decl = module.lookupSymbol(copyFuncName)) + return decl; + + // create function + mlir::OpBuilder::InsertionGuard guard(builder); + mlir::OpBuilder modBuilder(module.getBodyRegion()); + llvm::SmallVector argsTy = {varType, varType}; + auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {}); + mlir::func::FuncOp funcOp = + modBuilder.create(loc, copyFuncName, funcType); + funcOp.setVisibility(mlir::SymbolTable::Visibility::Private); + builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy, + {loc, loc}); + builder.setInsertionPointToStart(&funcOp.getRegion().back()); + // generate body + fir::FortranVariableFlagsAttr attrs; + if (varAttrs != fir::FortranVariableFlagsEnum::None) + attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs); + llvm::SmallVector typeparams; + if (typeInfo.getCharLength().has_value()) { + mlir::Value charLen = builder.createIntegerConstant( + loc, builder.getCharacterLengthType(), *typeInfo.getCharLength()); + typeparams.push_back(charLen); + } + mlir::Value shape; + if (!typeInfo.isBox() && !typeInfo.getShape().empty()) { + llvm::SmallVector extents; + for (auto extent : typeInfo.getShape()) + extents.push_back( + builder.createIntegerConstant(loc, builder.getIndexType(), extent)); + shape = builder.create(loc, extents); + } + auto declDst = builder.create(loc, funcOp.getArgument(0), + copyFuncName + "_dst", shape, + typeparams, attrs); + auto declSrc = builder.create(loc, funcOp.getArgument(1), + copyFuncName + "_src", shape, + typeparams, attrs); + converter.copyVar(loc, declDst.getBase(), declSrc.getBase()); + builder.create(loc); + return funcOp; +} + +bool ClauseProcessor::processCopyPrivate( + mlir::Location currentLocation, + llvm::SmallVectorImpl ©PrivateVars, + llvm::SmallVectorImpl ©PrivateFuncs) const { + auto addCopyPrivateVar = [&](Fortran::semantics::Symbol *sym) { + mlir::Value symVal = converter.getSymbolAddress(*sym); + auto declOp = symVal.getDefiningOp(); + if (!declOp) + fir::emitFatalError(currentLocation, + "COPYPRIVATE is supported only in HLFIR mode"); + symVal = declOp.getBase(); + mlir::Type symType = symVal.getType(); + fir::FortranVariableFlagsEnum attrs = + declOp.getFortranAttrs().has_value() + ? *declOp.getFortranAttrs() + : fir::FortranVariableFlagsEnum::None; + mlir::Value cpVar = symVal; + + // CopyPrivate variables must be passed by reference. However, in the case + // of assumed shapes/vla the type is not a !fir.ref, but a !fir.box. + // In these cases to retrieve the appropriate !fir.ref> to + // access the data we need we must perform an alloca and then store to it + // and retrieve the data from the new alloca. + if (mlir::isa(symType)) { + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + auto alloca = builder.create(currentLocation, symType); + builder.create(currentLocation, symVal, alloca); + cpVar = alloca; + } + + copyPrivateVars.push_back(cpVar); + mlir::func::FuncOp funcOp = + createCopyFunc(currentLocation, converter, cpVar.getType(), attrs); + copyPrivateFuncs.push_back(mlir::SymbolRefAttr::get(funcOp)); + }; + + bool hasCopyPrivate = findRepeatableClause( + [&](const ClauseTy::Copyprivate *copyPrivateClause, + const Fortran::parser::CharBlock &) { + const Fortran::parser::OmpObjectList &ompObjectList = + copyPrivateClause->v; + for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) { + Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject); + if (const auto *commonDetails = + sym->detailsIf()) { + for (const auto &mem : commonDetails->objects()) + addCopyPrivateVar(&*mem); + break; + } + addCopyPrivateVar(sym); + } + }); + + return hasCopyPrivate; +} + bool ClauseProcessor::processDepend( llvm::SmallVectorImpl &dependTypeOperands, llvm::SmallVectorImpl &dependOperands) const { diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h index 312255112605e..11aff0be25053 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.h +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h @@ -95,6 +95,10 @@ class ClauseProcessor { processAllocate(llvm::SmallVectorImpl &allocatorOperands, llvm::SmallVectorImpl &allocateOperands) const; bool processCopyin() const; + bool processCopyPrivate( + mlir::Location currentLocation, + llvm::SmallVectorImpl ©PrivateVars, + llvm::SmallVectorImpl ©PrivateFuncs) const; bool processDepend(llvm::SmallVectorImpl &dependTypeOperands, llvm::SmallVectorImpl &dependOperands) const; bool diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 3aefad6cf0ec1..abd17139b95d6 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -25,6 +25,7 @@ #include "flang/Optimizer/Builder/BoxValue.h" #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Builder/Todo.h" +#include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Optimizer/HLFIR/HLFIROps.h" #include "flang/Parser/parse-tree.h" #include "flang/Semantics/openmp-directive-sets.h" @@ -639,21 +640,26 @@ genSingleOp(Fortran::lower::AbstractConverter &converter, const Fortran::parser::OmpClauseList &endClauseList) { llvm::SmallVector allocateOperands, allocatorOperands; llvm::SmallVector copyPrivateVars; + llvm::SmallVector copyPrivateFuncs; mlir::UnitAttr nowaitAttr; ClauseProcessor cp(converter, semaCtx, beginClauseList); cp.processAllocate(allocatorOperands, allocateOperands); - cp.processTODO( - currentLocation, llvm::omp::Directive::OMPD_single); - ClauseProcessor(converter, semaCtx, endClauseList).processNowait(nowaitAttr); + ClauseProcessor ecp(converter, semaCtx, endClauseList); + ecp.processNowait(nowaitAttr); + ecp.processCopyPrivate(currentLocation, copyPrivateVars, copyPrivateFuncs); return genOpWithBody( OpWithBodyGenInfo(converter, semaCtx, currentLocation, eval) .setGenNested(genNested) .setClauses(&beginClauseList), allocateOperands, allocatorOperands, copyPrivateVars, - /*copyPrivateFuncs=*/nullptr, nowaitAttr); + copyPrivateFuncs.empty() + ? nullptr + : mlir::ArrayAttr::get(converter.getFirOpBuilder().getContext(), + copyPrivateFuncs), + nowaitAttr); } static mlir::omp::TaskOp @@ -1681,7 +1687,8 @@ genOMP(Fortran::lower::AbstractConverter &converter, for (const auto &clause : endClauseList.v) { mlir::Location clauseLocation = converter.genLocation(clause.source); - if (!std::get_if(&clause.u)) + if (!std::get_if(&clause.u) && + !std::get_if(&clause.u)) TODO(clauseLocation, "OpenMP Block construct clause"); } diff --git a/flang/test/Lower/OpenMP/Todo/copyprivate.f90 b/flang/test/Lower/OpenMP/Todo/copyprivate.f90 deleted file mode 100644 index 0d871427ce60f..0000000000000 --- a/flang/test/Lower/OpenMP/Todo/copyprivate.f90 +++ /dev/null @@ -1,13 +0,0 @@ -! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s -! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s - -! CHECK: not yet implemented: OpenMP Block construct clause -subroutine sb - integer, save :: a - !$omp threadprivate(a) - !$omp parallel - !$omp single - a = 3 - !$omp end single copyprivate(a) - !$omp end parallel -end subroutine diff --git a/flang/test/Lower/OpenMP/copyprivate.f90 b/flang/test/Lower/OpenMP/copyprivate.f90 new file mode 100644 index 0000000000000..9b76a996ef3e1 --- /dev/null +++ b/flang/test/Lower/OpenMP/copyprivate.f90 @@ -0,0 +1,164 @@ +! Test COPYPRIVATE. +! RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s + +!CHECK-DAG: func private @_copy_i64(%{{.*}}: !fir.ref, %{{.*}}: !fir.ref) +!CHECK-DAG: func private @_copy_f32(%{{.*}}: !fir.ref, %{{.*}}: !fir.ref) +!CHECK-DAG: func private @_copy_f64(%{{.*}}: !fir.ref, %{{.*}}: !fir.ref) +!CHECK-DAG: func private @_copy_z32(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_z64(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_l32(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_l64(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_c8x3(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_c8x8(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_c16x8(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) + +!CHECK-DAG: func private @_copy_box_Uxi32(%{{.*}}: !fir.ref>>, %{{.*}}: !fir.ref>>) +!CHECK-DAG: func private @_copy_10xi32(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_3x4xi32(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_10xf32(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_3x4xz32(%{{.*}}: !fir.ref>>, %{{.*}}: !fir.ref>>) +!CHECK-DAG: func private @_copy_10xl32(%{{.*}}: !fir.ref>>, %{{.*}}: !fir.ref>>) +!CHECK-DAG: func private @_copy_3xc8x8(%{{.*}}: !fir.ref>>, %{{.*}}: !fir.ref>>) +!CHECK-DAG: func private @_copy_3xc16x5(%{{.*}}: !fir.ref>>, %{{.*}}: !fir.ref>>) + +!CHECK-DAG: func private @_copy_rec__QFtest_dtTdt(%{{.*}}: !fir.ref>, %{{.*}}: !fir.ref>) +!CHECK-DAG: func private @_copy_box_heap_Uxi32(%{{.*}}: !fir.ref>>>, %{{.*}}: !fir.ref>>>) +!CHECK-DAG: func private @_copy_box_heap_i32(%{{.*}}: !fir.ref>>, %{{.*}}: !fir.ref>>) +!CHECK-DAG: func private @_copy_box_ptr_i32(%{{.*}}: !fir.ref>>, %{{.*}}: !fir.ref>>) +!CHECK-DAG: func private @_copy_box_ptr_Uxf32(%{{.*}}: !fir.ref>>>, %{{.*}}: !fir.ref>>>) +!CHECK-DAG: func private @_copy_box_heap_Uxc8x5(%{{.*}}: !fir.ref>>>>, %{{.*}}: !fir.ref>>>>) +!CHECK-DAG: func private @_copy_box_ptr_Uxc8x9(%{{.*}}: !fir.ref>>>>, %{{.*}}: !fir.ref>>>>) + +!CHECK-LABEL: func private @_copy_i32( +!CHECK-SAME: %[[ARG0:.*]]: !fir.ref, %[[ARG1:.*]]: !fir.ref) { +!CHECK-NEXT: %[[DST:.*]]:2 = hlfir.declare %[[ARG0]] {uniq_name = "_copy_i32_dst"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK-NEXT: %[[SRC:.*]]:2 = hlfir.declare %[[ARG1]] {uniq_name = "_copy_i32_src"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK-NEXT: %[[SRC_VAL:.*]] = fir.load %[[SRC]]#0 : !fir.ref +!CHECK-NEXT: hlfir.assign %[[SRC_VAL]] to %[[DST]]#0 temporary_lhs : i32, !fir.ref +!CHECK-NEXT: return +!CHECK-NEXT: } + +!CHECK-LABEL: func @_QPtest_tp +!CHECK: omp.parallel +!CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_tpEi"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[J:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_tpEj"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[K:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_tpEk"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: omp.single copyprivate(%[[I]]#0 -> @_copy_i32 : !fir.ref, %[[J]]#0 -> @_copy_i32 : !fir.ref, %[[K]]#0 -> @_copy_f32 : !fir.ref) +subroutine test_tp() + integer, save :: i, j + !$omp threadprivate(i, j) + real :: k + + k = 33.3 + !$omp parallel firstprivate(k) + !$omp single + i = 11 + j = 22 + !$omp end single copyprivate(i, j, k) + !$omp end parallel +end subroutine + +!CHECK-LABEL: func @_QPtest_scalar +!CHECK: omp.parallel +!CHECK: %[[I1:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEi1"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[I2:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEi2"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[I3:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEi3"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[R1:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEr1"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[R2:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEr2"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!CHECK: %[[C1:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEc1"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[C2:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEc2"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[L1:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEl1"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[L2:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEl2"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[S1:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEs1"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[S2:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEs2"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[S3:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_scalarEs3"} : (!fir.ref>, index) -> (!fir.ref>, !fir.ref>) +!CHECK: omp.single copyprivate(%[[I1]]#0 -> @_copy_i32 : !fir.ref, %[[I2]]#0 -> @_copy_i64 : !fir.ref, %[[I3]]#0 -> @_copy_i64 : !fir.ref, %[[R1]]#0 -> @_copy_f32 : !fir.ref, %[[R2]]#0 -> @_copy_f64 : !fir.ref, %[[C1]]#0 -> @_copy_z32 : !fir.ref>, %[[C2]]#0 -> @_copy_z64 : !fir.ref>, %[[L1]]#0 -> @_copy_l32 : !fir.ref>, %[[L2]]#0 -> @_copy_l64 : !fir.ref>, %[[S1]]#0 -> @_copy_c8x3 : !fir.ref>, %[[S2]]#0 -> @_copy_c8x8 : !fir.ref>, %[[S3]]#0 -> @_copy_c16x8 : !fir.ref>) +subroutine test_scalar() + integer(4) :: i1 + integer(8) :: i2, i3 + real(4) :: r1 + real(8) :: r2 + complex(4) :: c1 + complex(8) :: c2 + logical(4) :: l1 + logical(8) :: l2 + character(kind=1, len=3) :: s1 + character(kind=1, len=8) :: s2 + character(kind=2, len=8) :: s3 + + !$omp parallel private(i1, i2, i3, r1, r2, c1, c2, l1, l2, s1, s2, s3) + !$omp single + !$omp end single copyprivate(i1, i2, i3, r1, r2, c1, c2, l1, l2, s1, s2, s3) + !$omp end parallel +end subroutine + +!CHECK-LABEL: func @_QPtest_array +!CHECK: omp.parallel +!CHECK: %[[A:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_arrayEa"} : (!fir.ref>, !fir.shape<1>) -> (!fir.box>, !fir.ref>) +!CHECK: %[[I1:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_arrayEi1"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[I2:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_arrayEi2"} : (!fir.ref>, !fir.shape<2>) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[I3:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_arrayEi3"} : (!fir.ref>, !fir.shape<1>) -> (!fir.box>, !fir.ref>) +!CHECK: %[[R1:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_arrayEr1"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) +!CHECK: %[[C1:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_arrayEc1"} : (!fir.ref>>, !fir.shape<2>) -> (!fir.ref>>, !fir.ref>>) +!CHECK: %[[L1:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {uniq_name = "_QFtest_arrayEl1"} : (!fir.ref>>, !fir.shape<1>) -> (!fir.ref>>, !fir.ref>>) +!CHECK: %[[S1:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFtest_arrayEs1"} : (!fir.ref>>, !fir.shape<1>, index) -> (!fir.ref>>, !fir.ref>>) +!CHECK: %[[S2:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFtest_arrayEs2"} : (!fir.ref>>, !fir.shape<1>, index) -> (!fir.ref>>, !fir.ref>>) +!CHECK: %[[A_REF:.*]] = fir.alloca !fir.box> +!CHECK: fir.store %[[A]]#0 to %[[A_REF]] : !fir.ref>> +!CHECK: %[[I3_REF:.*]] = fir.alloca !fir.box> +!CHECK: fir.store %[[I3]]#0 to %[[I3_REF]] : !fir.ref>> +!CHECK: omp.single copyprivate(%[[A_REF]] -> @_copy_box_Uxi32 : !fir.ref>>, %[[I1]]#0 -> @_copy_10xi32 : !fir.ref>, %[[I2]]#0 -> @_copy_3x4xi32 : !fir.ref>, %[[I3_REF]] -> @_copy_box_Uxi32 : !fir.ref>>, %[[R1]]#0 -> @_copy_10xf32 : !fir.ref>, %[[C1]]#0 -> @_copy_3x4xz32 : !fir.ref>>, %[[L1]]#0 -> @_copy_10xl32 : !fir.ref>>, %[[S1]]#0 -> @_copy_3xc8x8 : !fir.ref>>, %[[S2]]#0 -> @_copy_3xc16x5 : !fir.ref>>) +subroutine test_array(a, n) + integer :: a(:), n + integer :: i1(10), i2(3, 4), i3(n) + real :: r1(10) + complex :: c1(3, 4) + logical :: l1(10) + character(8) :: s1(3) + character(kind=2, len=5) :: s2(3) + + !$omp parallel private(a, i1, i2, i3, r1, c1, l1, s1, s2) + !$omp single + !$omp end single copyprivate(a, i1, i2, i3, r1, c1, l1, s1, s2) + !$omp end parallel +end subroutine + +!CHECK-LABEL: func @_QPtest_dt +!CHECK: omp.parallel +!CHECK: %[[T:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtest_dtEt"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) +!CHECK: omp.single copyprivate(%[[T]]#0 -> @_copy_rec__QFtest_dtTdt : !fir.ref>) +subroutine test_dt() + type dt + integer :: i + real :: r + end type + type(dt) :: t + + !$omp parallel private(t) + !$omp single + !$omp end single copyprivate(t) + !$omp end parallel +end subroutine + +!CHECK-LABEL: func @_QPtest_attr +!CHECK: omp.parallel +!CHECK: %[[I1:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest_attrEi1"} : (!fir.ref>>>) -> (!fir.ref>>>, !fir.ref>>>) +!CHECK: %[[I2:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest_attrEi2"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) +!CHECK: %[[I3:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest_attrEi3"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) +!CHECK: %[[R1:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest_attrEr1"} : (!fir.ref>>>) -> (!fir.ref>>>, !fir.ref>>>) +!CHECK: %[[C1:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest_attrEc1"} : (!fir.ref>>>>) -> (!fir.ref>>>>, !fir.ref>>>>) +!CHECK: %[[C2:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtest_attrEc2"} : (!fir.ref>>>>) -> (!fir.ref>>>>, !fir.ref>>>>) +!CHECK: omp.single copyprivate(%[[I1]]#0 -> @_copy_box_heap_Uxi32 : !fir.ref>>>, %[[I2:.*]]#0 -> @_copy_box_heap_i32 : !fir.ref>>, %[[I3]]#0 -> @_copy_box_ptr_i32 : !fir.ref>>, %[[R1]]#0 -> @_copy_box_ptr_Uxf32 : !fir.ref>>>, %[[C1]]#0 -> @_copy_box_heap_Uxc8x5 : !fir.ref>>>>, %[[C2]]#0 -> @_copy_box_ptr_Uxc8x9 : !fir.ref>>>>) +subroutine test_attr() + integer, allocatable :: i1(:) + integer, allocatable :: i2 + integer, pointer :: i3 + real, pointer :: r1(:) + character(kind=1, len=5), allocatable :: c1(:) + character(kind=1, len=9), pointer :: c2(:) + + !$omp parallel private(i1, i2, i3, r1, c1, c2) + !$omp single + !$omp end single copyprivate(i1, i2, i3, r1, c1, c2) + !$omp end parallel +end subroutine