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