diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h --- a/flang/include/flang/Lower/AbstractConverter.h +++ b/flang/include/flang/Lower/AbstractConverter.h @@ -107,7 +107,7 @@ virtual void collectSymbolSet( pft::Evaluation &eval, llvm::SetVector &symbolSet, - Fortran::semantics::Symbol::Flag flag) = 0; + Fortran::semantics::Symbol::Flag flag, bool isUltimateSymbol = true) = 0; //===--------------------------------------------------------------------===// // Expressions diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -514,11 +514,13 @@ void collectSymbolSet( Fortran::lower::pft::Evaluation &eval, llvm::SetVector &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); } diff --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp --- a/flang/lib/Lower/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP.cpp @@ -79,24 +79,86 @@ } } +static void genDefaultClause( + Fortran::lower::AbstractConverter &converter, + Fortran::lower::pft::Evaluation &eval, + const Fortran::parser::OmpDefaultClause::Type &ompDefaultClauseType) { + llvm::SetVector symbols; + if (ompDefaultClauseType == Fortran::parser::OmpDefaultClause::Type::Private) + converter.collectSymbolSet( + eval, symbols, Fortran::semantics::Symbol::Flag::OmpPrivate, false); + else if (ompDefaultClauseType == + Fortran::parser::OmpDefaultClause::Type::Firstprivate) + converter.collectSymbolSet( + eval, symbols, Fortran::semantics::Symbol::Flag::OmpFirstPrivate, + false); + else + return; + for (const Fortran::semantics::Symbol *sym : symbols) { + // 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)) + continue; + if (ompDefaultClauseType == + Fortran::parser::OmpDefaultClause::Type::Private) { + bool success = converter.createHostAssociateVarClone(*sym); + (void)success; + assert(success && "Privatization failed due to existing binding"); + } else if (ompDefaultClauseType == + Fortran::parser::OmpDefaultClause::Type::Firstprivate) { + converter.copyHostAssociateVar(*sym); + } + } +} + static void privatizeVars(Fortran::lower::AbstractConverter &converter, - const Fortran::parser::OmpClauseList &opClauseList) { + const Fortran::parser::OmpClauseList &opClauseList, + Fortran::lower::pft::Evaluation &eval) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); + Fortran::parser::OmpDefaultClause::Type ompDefaultClauseType; auto insPt = firOpBuilder.saveInsertionPoint(); firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); bool hasFirstPrivateOp = false; + for (const Fortran::parser::OmpClause &clause : opClauseList.v) { + if (const auto &defaultClause = + std::get_if(&clause.u)) { + const auto &ompDefaultClause{defaultClause->v}; + ompDefaultClauseType = ompDefaultClause.v; + } + } + for (const Fortran::parser::OmpClause &clause : opClauseList.v) { if (const auto &privateClause = std::get_if(&clause.u)) { - createPrivateVarSyms(converter, privateClause); + if (ompDefaultClauseType != + Fortran::parser::OmpDefaultClause::Type:: + Private) // if default clause is 'private', symbols with + // OmpPrivate flag shall be dealt with while dealing with + // default clause; hence skip privatization here. + createPrivateVarSyms(converter, privateClause); } else if (const auto &firstPrivateClause = std::get_if( &clause.u)) { - createPrivateVarSyms(converter, firstPrivateClause); + if (ompDefaultClauseType != + Fortran::parser::OmpDefaultClause::Type:: + Firstprivate) // if default clause is 'firstprivate', symbols with + // OmpFirstPrivate flag shall be dealt with while + // dealing with default clause; hence skip + // privatization here + createPrivateVarSyms(converter, firstPrivateClause); hasFirstPrivateOp = true; } } - if (hasFirstPrivateOp) + genDefaultClause(converter, eval, ompDefaultClauseType); + + if (hasFirstPrivateOp || + (!hasFirstPrivateOp && + ompDefaultClauseType == + Fortran::parser::OmpDefaultClause::Type::Firstprivate)) + // If a separate `firstprivate()` clause is detected, insert a + // `omp.barrier`. If a separate `firstprivate()` clause is absent but + // default-clause is + // firstprivate, insert a `omp.barrier` nevertheless firOpBuilder.create(converter.getCurrentLocation()); firOpBuilder.restoreInsertionPoint(insPt); } @@ -341,7 +403,7 @@ // Handle privatization. Do not privatize if this is the outer operation. if (clauses && !outerCombined) - privatizeVars(converter, *clauses); + privatizeVars(converter, *clauses, eval); if (std::is_same_v) threadPrivatizeVars(converter, eval); @@ -576,6 +638,10 @@ } else if (std::get_if(&clause.u)) { // Nothing needs to be done for threads clause. continue; + } else if (std::get_if(&clause.u)) { + continue; + } else if (std::get_if(&clause.u)) { + continue; } else if (const auto &finalClause = std::get_if(&clause.u)) { mlir::Value finalVal = fir::getBase(converter.genExprValue( diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -1485,6 +1485,21 @@ } } } + if (GetContext().defaultDSA == semantics::Symbol::Flag::OmpPrivate && + !HasDataSharingAttributeObject(*name.symbol)) { + name.symbol = DeclarePrivateAccessEntity( + *name.symbol, semantics::Symbol::Flag::OmpPrivate, currScope()); + AddToContextObjectWithDSA( + *name.symbol, semantics::Symbol::Flag::OmpPrivate); + } else if (GetContext().defaultDSA == + semantics::Symbol::Flag::OmpFirstPrivate && + !HasDataSharingAttributeObject(*name.symbol)) { + name.symbol = DeclarePrivateAccessEntity( + *name.symbol, semantics::Symbol::Flag::OmpFirstPrivate, currScope()); + AddToContextObjectWithDSA( + *name.symbol, semantics::Symbol::Flag::OmpPrivate); + } + } // within OpenMP construct } @@ -1585,6 +1600,8 @@ CheckMultipleAppearances(*name, *symbol, ompFlag); } if (privateDataSharingAttributeFlags.test(ompFlag)) { + AddDataSharingAttributeObject( + common::Reference(*symbol)); CheckObjectInNamelist(*name, *symbol, ompFlag); } diff --git a/flang/test/Lower/OpenMP/default-clause.f90 b/flang/test/Lower/OpenMP/default-clause.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/OpenMP/default-clause.f90 @@ -0,0 +1,165 @@ +! This test checks lowering of OpenMP parallel directive +! with `DEFAULT` clause present. + +! RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | FileCheck %s +! RUN: bbc -fopenmp -emit-fir %s -o - | FileCheck %s --check-prefix=FIRDialect + + +!CHECK: func @_QQmain() { +!CHECK: %[[W:.*]] = fir.alloca i32 {bindc_name = "w", uniq_name = "_QFEw"} +!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"} +!CHECK: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"} +!CHECK: %[[Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFEz"} +!CHECK: omp.parallel { +!CHECK: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!CHECK: %{{.*}} = fir.load %[[X]] : !fir.ref +!CHECK: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!CHECK: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!CHECK: %[[PRIVATE_W:.*]] = fir.alloca i32 {bindc_name = "w", pinned, uniq_name = "_QFEw"} +!CHECK: omp.barrier +!CHECK: %{{.*}} = arith.constant 2 : i32 +!CHECK: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!CHECK: %{{.*}} = arith.muli %{{.*}}, %{{.*}} : i32 +!CHECK: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!CHECK: %{{.*}} = fir.load %[[PRIVATE_W]] : !fir.ref +!CHECK: %{{.*}} = arith.constant 45 : i32 +!CHECK: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i32 +!CHECK: fir.store %{{.*}} to %[[Z]] : !fir.ref +!CHECK: omp.terminator +!CHECK: } +!FIRDialect: func @_QQmain() { +!FIRDialect: %[[W:.*]] = fir.alloca i32 {bindc_name = "w", uniq_name = "_QFEw"} +!FIRDialect: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"} +!FIRDialect: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"} +!FIRDialect: %[[Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFEz"} +!FIRDialect: omp.parallel { +!FIRDialect: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!FIRDialect: %{{.*}} = fir.load %[[X]] : !fir.ref +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!FIRDialect: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!FIRDialect: %[[PRIVATE_W:.*]] = fir.alloca i32 {bindc_name = "w", pinned, uniq_name = "_QFEw"} +!FIRDialect: omp.barrier +!FIRDialect: %{{.*}} = arith.constant 2 : i32 +!FIRDialect: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!FIRDialect: %{{.*}} = arith.muli %{{.*}}, %{{.*}} : i32 +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!FIRDialect: %{{.*}} = fir.load %[[PRIVATE_W]] : !fir.ref +!FIRDialect: %{{.*}} = arith.constant 45 : i32 +!FIRDialect: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i32 +!FIRDialect: fir.store %{{.*}} to %[[Z]] : !fir.ref +!FIRDialect: omp.terminator +!FIRDialect: } +program default_clause_lowering + integer :: x, y, z, w, i, j, k + + !$omp parallel default(private) firstprivate(x) shared(z) + x = y * 2 + z = w + 45 + !$omp end parallel + +!CHECK: omp.parallel { +!CHECK: %{{.*}} = fir.load %[[Y]] : !fir.ref +!CHECK: fir.store %{{.*}} to %[[X]] : !fir.ref +!CHECK: omp.terminator +!CHECK: } +!FIRDialect: omp.parallel { +!FIRDialect: %{{.*}} = fir.load %[[Y]] : !fir.ref +!FIRDialect: fir.store %{{.*}} to %[[X]] : !fir.ref +!FIRDialect: omp.terminator +!FIRDialect: } + !$omp parallel default(shared) + x = y + !$omp end parallel + +!CHECK: omp.parallel { +!CHECK: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!CHECK: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!CHECK: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!CHECK: fir.store {{.*}} to %[[PRIVATE_X]] : !fir.ref +!CHECK: omp.terminator +!CHECK: } +!FIRDialect: omp.parallel { +!FIRDialect: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!FIRDialect: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!FIRDialect: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!FIRDialect: fir.store {{.*}} to %[[PRIVATE_X]] : !fir.ref +!FIRDialect: omp.terminator +!FIRDialect: } + !$omp parallel default(none) private(x, y) + x = y + !$omp end parallel + +!CHECK: omp.parallel { +!CHECK: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!CHECK: %{{.*}} = fir.load %[[Y]] : !fir.ref +!CHECK: fir.store %{{.*}} to %[[PRIVATE_Y]] : !fir.ref +!CHECK: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!CHECK: %{{.*}} = fir.load %[[X]] : !fir.ref +!CHECK: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!CHECK: omp.barrier +!CHECK: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!CHECK: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!CHECK: omp.terminator +!CHECK: } +!FIRDialect: omp.parallel { +!FIRDialect: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!FIRDialect: %{{.*}} = fir.load %[[Y]] : !fir.ref +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_Y]] : !fir.ref +!FIRDialect: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!FIRDialect: %{{.*}} = fir.load %[[X]] : !fir.ref +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!FIRDialect: omp.barrier +!FIRDialect: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!FIRDialect: omp.terminator +!FIRDialect: } + !$omp parallel default(firstprivate) firstprivate(y) + x = y + !$omp end parallel + +!CHECK: omp.parallel { +!CHECK: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!CHECK: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!CHECK: %{{.*}} = fir.load %[[Y]] : !fir.ref +!CHECK: fir.store %{{.*}} to %[[PRIVATE_Y]] : !fir.ref +!CHECK: %[[PRIVATE_W:.*]] = fir.alloca i32 {bindc_name = "w", pinned, uniq_name = "_QFEw"} +!CHECK: %{{.*}} = fir.load %[[W]] : !fir.ref +!CHECK: fir.store %{{.*}} to %[[PRIVATE_W]] : !fir.ref +!CHECK: omp.barrier +!CHECK: %{{.*}} = arith.constant 2 : i32 +!CHECK: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!CHECK: %{{.*}} = arith.muli %{{.*}}, %{{.*}} : i32 +!CHECK: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!CHECK: %{{.*}} = fir.load %[[PRIVATE_W]] : !fir.ref +!CHECK: %{{.*}} = arith.constant 45 : i32 +!CHECK: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i32 +!CHECK: fir.store %{{.*}} to %[[Z]] : !fir.ref +!CHECK: omp.terminator +!CHECK: } +!FIRDialect: omp.parallel { +!FIRDialect: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFEx"} +!FIRDialect: %[[PRIVATE_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFEy"} +!FIRDialect: %{{.*}} = fir.load %[[Y]] : !fir.ref +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_Y]] : !fir.ref +!FIRDialect: %[[PRIVATE_W:.*]] = fir.alloca i32 {bindc_name = "w", pinned, uniq_name = "_QFEw"} +!FIRDialect: %{{.*}} = fir.load %[[W]] : !fir.ref +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_W]] : !fir.ref +!FIRDialect: omp.barrier +!FIRDialect: %{{.*}} = arith.constant 2 : i32 +!FIRDialect: %{{.*}} = fir.load %[[PRIVATE_Y]] : !fir.ref +!FIRDialect: %{{.*}} = arith.muli %{{.*}}, %{{.*}} : i32 +!FIRDialect: fir.store %{{.*}} to %[[PRIVATE_X]] : !fir.ref +!FIRDialect: %{{.*}} = fir.load %[[PRIVATE_W]] : !fir.ref +!FIRDialect: %{{.*}} = arith.constant 45 : i32 +!FIRDialect: %{{.*}} = arith.addi %{{.*}}, %{{.*}} : i32 +!FIRDialect: fir.store %{{.*}} to %[[Z]] : !fir.ref +!FIRDialect: omp.terminator +!FIRDialect: } +!FIRDialect: return +!FIRDialect: } + + !$omp parallel default(firstprivate) private(x) shared(z) + x = y * 2 + z = w + 45 + !$omp end parallel +end program default_clause_lowering