[llvm-branch-commits] [flang] [flang][OpenMP] Support tasks' implicit firstprivate DSA (PR #85989)
Leandro Lupori via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Apr 24 12:21:08 PDT 2024
https://github.com/luporl updated https://github.com/llvm/llvm-project/pull/85989
>From 94301e00239b789cf90d5291b1d733f0f2baab6c Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Mon, 11 Mar 2024 16:47:47 -0300
Subject: [PATCH 1/2] [flang][OpenMP] Support tasks' implicit firstprivate DSA
Handle implicit firstprivate DSAs on task generating constructs.
Fixes https://github.com/llvm/llvm-project/issues/64480
---
flang/include/flang/Semantics/symbol.h | 3 +-
.../lib/Lower/OpenMP/DataSharingProcessor.cpp | 114 +++++++-
flang/lib/Lower/OpenMP/DataSharingProcessor.h | 11 +-
flang/lib/Semantics/resolve-directives.cpp | 85 +++++-
.../test/Lower/OpenMP/FIR/default-clause.f90 | 3 +-
.../Lower/OpenMP/default-clause-byref.f90 | 4 +-
flang/test/Lower/OpenMP/default-clause.f90 | 4 +-
flang/test/Lower/OpenMP/implicit-dsa.f90 | 275 ++++++++++++++++++
flang/test/Semantics/OpenMP/implicit-dsa.f90 | 158 ++++++++++
flang/test/Semantics/OpenMP/symbol08.f90 | 2 +-
10 files changed, 617 insertions(+), 42 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/implicit-dsa.f90
create mode 100644 flang/test/Semantics/OpenMP/implicit-dsa.f90
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 67153ffb3be9f6..34ac674614a695 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -740,7 +740,8 @@ class Symbol {
OmpCommonBlock, OmpReduction, OmpAligned, OmpNontemporal, OmpAllocate,
OmpDeclarativeAllocateDirective, OmpExecutableAllocateDirective,
OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction,
- OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined);
+ OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined,
+ OmpImplicit);
using Flags = common::EnumSet<Flag, Flag_enumSize>;
const Scope &owner() const { return *owner_; }
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 90c7e46dd183e3..792b3341ef03cb 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -26,8 +26,10 @@ namespace omp {
void DataSharingProcessor::processStep1() {
collectSymbolsForPrivatization();
collectDefaultSymbols();
+ collectImplicitSymbols();
privatize();
defaultPrivatize();
+ implicitPrivatize();
insertBarrier();
}
@@ -268,16 +270,94 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
firOpBuilder.restoreInsertionPoint(localInsPt);
}
+static const Fortran::parser::CharBlock *
+getSource(const Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::lower::pft::Evaluation &eval) {
+ const Fortran::parser::CharBlock *source = nullptr;
+
+ auto ompConsVisit = [&](const Fortran::parser::OpenMPConstruct &x) {
+ std::visit(Fortran::common::visitors{
+ [&](const Fortran::parser::OpenMPSectionsConstruct &x) {
+ source = &std::get<0>(x.t).source;
+ },
+ [&](const Fortran::parser::OpenMPLoopConstruct &x) {
+ source = &std::get<0>(x.t).source;
+ },
+ [&](const Fortran::parser::OpenMPBlockConstruct &x) {
+ source = &std::get<0>(x.t).source;
+ },
+ [&](const Fortran::parser::OpenMPCriticalConstruct &x) {
+ source = &std::get<0>(x.t).source;
+ },
+ [&](const Fortran::parser::OpenMPAtomicConstruct &x) {
+ std::visit([&](const auto &x) { source = &x.source; },
+ x.u);
+ },
+ [&](const auto &x) { source = &x.source; },
+ },
+ x.u);
+ };
+
+ eval.visit(Fortran::common::visitors{
+ [&](const Fortran::parser::OpenMPConstruct &x) { ompConsVisit(x); },
+ [&](const Fortran::parser::OpenMPDeclarativeConstruct &x) {
+ source = &x.source;
+ },
+ [&](const Fortran::parser::OmpEndLoopDirective &x) {
+ source = &x.source;
+ },
+ [&](const auto &x) {},
+ });
+
+ return source;
+}
+
void DataSharingProcessor::collectSymbols(
- Fortran::semantics::Symbol::Flag flag) {
- converter.collectSymbolSet(eval, defaultSymbols, flag,
+ Fortran::semantics::Symbol::Flag flag,
+ llvm::SetVector<const Fortran::semantics::Symbol *> &symbols) {
+ // Collect all scopes associated with 'eval'.
+ llvm::SetVector<const Fortran::semantics::Scope *> clauseScopes;
+ std::function<void(const Fortran::semantics::Scope *)> collectScopes =
+ [&](const Fortran::semantics::Scope *scope) {
+ clauseScopes.insert(scope);
+ for (const Fortran::semantics::Scope &child : scope->children())
+ collectScopes(&child);
+ };
+ const Fortran::parser::CharBlock *source =
+ clauses.empty() ? getSource(semaCtx, eval) : &clauses.front().source;
+ const Fortran::semantics::Scope *curScope = nullptr;
+ if (source && !source->empty()) {
+ curScope = &semaCtx.FindScope(*source);
+ collectScopes(curScope);
+ }
+ // Collect all symbols referenced in the evaluation being processed,
+ // that matches 'flag'.
+ llvm::SetVector<const Fortran::semantics::Symbol *> allSymbols;
+ converter.collectSymbolSet(eval, allSymbols, flag,
/*collectSymbols=*/true,
/*collectHostAssociatedSymbols=*/true);
- for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) {
+ llvm::SetVector<const Fortran::semantics::Symbol *> symbolsInNestedRegions;
+ for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations())
if (e.hasNestedEvaluations() && !e.isConstruct())
converter.collectSymbolSet(e, symbolsInNestedRegions, flag,
/*collectSymbols=*/true,
/*collectHostAssociatedSymbols=*/false);
+ // Filter-out symbols that must not be privatized.
+ bool collectImplicit = flag == Fortran::semantics::Symbol::Flag::OmpImplicit;
+ auto isPrivatizable = [](const Fortran::semantics::Symbol &sym) -> bool {
+ return !Fortran::semantics::IsProcedure(sym) &&
+ !sym.GetUltimate().has<Fortran::semantics::DerivedTypeDetails>() &&
+ !sym.GetUltimate().has<Fortran::semantics::NamelistDetails>();
+ };
+ for (const auto *sym : allSymbols) {
+ assert(curScope && "couldn't find current scope");
+ if (isPrivatizable(*sym) && !symbolsInNestedRegions.contains(sym) &&
+ !privatizedSymbols.contains(sym) &&
+ !sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined) &&
+ (collectImplicit ||
+ !sym->test(Fortran::semantics::Symbol::Flag::OmpImplicit)) &&
+ clauseScopes.contains(&sym->owner()))
+ symbols.insert(sym);
}
}
@@ -286,13 +366,22 @@ void DataSharingProcessor::collectDefaultSymbols() {
if (const auto *defaultClause =
std::get_if<omp::clause::Default>(&clause.u)) {
if (defaultClause->v == omp::clause::Default::Type::Private)
- collectSymbols(Fortran::semantics::Symbol::Flag::OmpPrivate);
+ collectSymbols(Fortran::semantics::Symbol::Flag::OmpPrivate,
+ defaultSymbols);
else if (defaultClause->v == omp::clause::Default::Type::Firstprivate)
- collectSymbols(Fortran::semantics::Symbol::Flag::OmpFirstPrivate);
+ collectSymbols(Fortran::semantics::Symbol::Flag::OmpFirstPrivate,
+ defaultSymbols);
}
}
}
+void DataSharingProcessor::collectImplicitSymbols() {
+ // There will be no implicit symbols when a default clause is present.
+ if (defaultSymbols.empty())
+ collectSymbols(Fortran::semantics::Symbol::Flag::OmpImplicit,
+ implicitSymbols);
+}
+
void DataSharingProcessor::privatize() {
for (const Fortran::semantics::Symbol *sym : privatizedSymbols) {
if (const auto *commonDet =
@@ -318,14 +407,13 @@ void DataSharingProcessor::copyLastPrivatize(mlir::Operation *op) {
}
void DataSharingProcessor::defaultPrivatize() {
- for (const Fortran::semantics::Symbol *sym : defaultSymbols) {
- if (!Fortran::semantics::IsProcedure(*sym) &&
- !sym->GetUltimate().has<Fortran::semantics::DerivedTypeDetails>() &&
- !sym->GetUltimate().has<Fortran::semantics::NamelistDetails>() &&
- !symbolsInNestedRegions.contains(sym) &&
- !privatizedSymbols.contains(sym))
- doPrivatize(sym);
- }
+ for (const Fortran::semantics::Symbol *sym : defaultSymbols)
+ doPrivatize(sym);
+}
+
+void DataSharingProcessor::implicitPrivatize() {
+ for (const Fortran::semantics::Symbol *sym : implicitSymbols)
+ doPrivatize(sym);
}
void DataSharingProcessor::doPrivatize(const Fortran::semantics::Symbol *sym) {
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.h b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
index 002bb119c0b916..e5b695f3f1e77f 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.h
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
@@ -49,8 +49,9 @@ class DataSharingProcessor {
// Symbols in private, firstprivate, and/or lastprivate clauses.
llvm::SetVector<const Fortran::semantics::Symbol *> privatizedSymbols;
llvm::SetVector<const Fortran::semantics::Symbol *> defaultSymbols;
- llvm::SetVector<const Fortran::semantics::Symbol *> symbolsInNestedRegions;
+ llvm::SetVector<const Fortran::semantics::Symbol *> implicitSymbols;
Fortran::lower::AbstractConverter &converter;
+ Fortran::semantics::SemanticsContext &semaCtx;
fir::FirOpBuilder &firOpBuilder;
omp::List<omp::Clause> clauses;
Fortran::lower::pft::Evaluation &eval;
@@ -59,15 +60,19 @@ class DataSharingProcessor {
DelayedPrivatizationInfo delayedPrivatizationInfo;
bool needBarrier();
- void collectSymbols(Fortran::semantics::Symbol::Flag flag);
+ void
+ collectSymbols(Fortran::semantics::Symbol::Flag flag,
+ llvm::SetVector<const Fortran::semantics::Symbol *> &symbols);
void collectOmpObjectListSymbol(
const omp::ObjectList &objects,
llvm::SetVector<const Fortran::semantics::Symbol *> &symbolSet);
void collectSymbolsForPrivatization();
void insertBarrier();
void collectDefaultSymbols();
+ void collectImplicitSymbols();
void privatize();
void defaultPrivatize();
+ void implicitPrivatize();
void doPrivatize(const Fortran::semantics::Symbol *sym);
void copyLastPrivatize(mlir::Operation *op);
void insertLastPrivateCompare(mlir::Operation *op);
@@ -86,7 +91,7 @@ class DataSharingProcessor {
Fortran::lower::pft::Evaluation &eval,
bool useDelayedPrivatization = false,
Fortran::lower::SymMap *symTable = nullptr)
- : hasLastPrivateOp(false), converter(converter),
+ : hasLastPrivateOp(false), converter(converter), semaCtx(semaCtx),
firOpBuilder(converter.getFirOpBuilder()),
clauses(omp::makeList(opClauseList, semaCtx)), eval(eval),
useDelayedPrivatization(useDelayedPrivatization), symTable(symTable) {}
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 6d58013b87d298..57ba556b9d57bf 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2012,34 +2012,87 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
}
}
}
- std::vector<Symbol *> defaultDSASymbols;
+
+ // Implicitly determined DSAs
+ // OMP 5.2 5.1.1 - Variables Referenced in a Construct
+ Symbol *lastDeclSymbol = nullptr;
+ std::optional<Symbol::Flag> prevDSA;
for (int dirDepth{0}; dirDepth < (int)dirContext_.size(); ++dirDepth) {
DirContext &dirContext = dirContext_[dirDepth];
- bool hasDataSharingAttr{false};
+ std::optional<Symbol::Flag> dsa;
+
for (auto symMap : dirContext.objectWithDSA) {
// if the `symbol` already has a data-sharing attribute
if (symMap.first->name() == name.symbol->name()) {
- hasDataSharingAttr = true;
+ dsa = symMap.second;
break;
}
}
- if (hasDataSharingAttr) {
- if (defaultDSASymbols.size())
- symbol = &MakeAssocSymbol(symbol->name(), *defaultDSASymbols.back(),
+
+ // When handling each implicit rule, either a new private symbol is
+ // declared or the last declared symbol is used.
+ // In the latter case, it's necessary to insert a new symbol in the scope
+ // being processed, associated with the last declared symbol, to avoid
+ // "inheriting" the enclosing context's symbol and its flags.
+ auto declNewSymbol = [&](Symbol::Flag flag) {
+ Symbol *hostSymbol =
+ lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
+ lastDeclSymbol = DeclarePrivateAccessEntity(
+ *hostSymbol, flag, context_.FindScope(dirContext.directiveSource));
+ return lastDeclSymbol;
+ };
+ auto useLastDeclSymbol = [&]() {
+ if (lastDeclSymbol)
+ MakeAssocSymbol(symbol->name(), *lastDeclSymbol,
context_.FindScope(dirContext.directiveSource));
+ };
+
+ if (dsa.has_value()) {
+ useLastDeclSymbol();
+ prevDSA = dsa;
continue;
}
- if (dirContext.defaultDSA == semantics::Symbol::Flag::OmpPrivate ||
- dirContext.defaultDSA == semantics::Symbol::Flag::OmpFirstPrivate) {
- Symbol *hostSymbol = defaultDSASymbols.size() ? defaultDSASymbols.back()
- : &symbol->GetUltimate();
- defaultDSASymbols.push_back(
- DeclarePrivateAccessEntity(*hostSymbol, dirContext.defaultDSA,
- context_.FindScope(dirContext.directiveSource)));
- } else if (defaultDSASymbols.size())
- symbol = &MakeAssocSymbol(symbol->name(), *defaultDSASymbols.back(),
- context_.FindScope(dirContext.directiveSource));
+ bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive);
+ bool targetDir = llvm::omp::allTargetSet.test(dirContext.directive);
+ bool parallelDir = llvm::omp::allParallelSet.test(dirContext.directive);
+
+ if (dirContext.defaultDSA == Symbol::Flag::OmpPrivate ||
+ dirContext.defaultDSA == Symbol::Flag::OmpFirstPrivate ||
+ dirContext.defaultDSA == Symbol::Flag::OmpShared) {
+ // 1) default
+ // Allowed only with parallel, teams and task generating constructs.
+ assert(parallelDir || taskGenDir ||
+ llvm::omp::allTeamsSet.test(dirContext.directive));
+ if (dirContext.defaultDSA != Symbol::Flag::OmpShared)
+ declNewSymbol(dirContext.defaultDSA);
+ else
+ useLastDeclSymbol();
+ dsa = dirContext.defaultDSA;
+ } else if (parallelDir) {
+ // 2) parallel -> shared
+ useLastDeclSymbol();
+ dsa = Symbol::Flag::OmpShared;
+ } else if (!taskGenDir && !targetDir) {
+ // 3) enclosing context
+ useLastDeclSymbol();
+ dsa = prevDSA;
+ } else if (targetDir) {
+ // TODO 4) not mapped target variable -> firstprivate
+ dsa = prevDSA;
+ } else if (taskGenDir) {
+ // TODO 5) dummy arg in orphaned taskgen construct -> firstprivate
+ if (prevDSA == Symbol::Flag::OmpShared) {
+ // 6) shared in enclosing context -> shared
+ useLastDeclSymbol();
+ dsa = Symbol::Flag::OmpShared;
+ } else {
+ // 7) firstprivate
+ dsa = Symbol::Flag::OmpFirstPrivate;
+ declNewSymbol(*dsa)->set(Symbol::Flag::OmpImplicit);
+ }
+ }
+ prevDSA = dsa;
}
} // within OpenMP construct
}
diff --git a/flang/test/Lower/OpenMP/FIR/default-clause.f90 b/flang/test/Lower/OpenMP/FIR/default-clause.f90
index 8d131c5d69b16a..bc825f0e9dba3f 100644
--- a/flang/test/Lower/OpenMP/FIR/default-clause.f90
+++ b/flang/test/Lower/OpenMP/FIR/default-clause.f90
@@ -213,11 +213,10 @@ subroutine nested_default_clause_tests
!CHECK: omp.terminator
!CHECK: }
!CHECK: omp.parallel {
-!CHECK: %[[PRIVATE_INNER_Z:.*]] = fir.alloca i32 {bindc_name = "z", pinned, uniq_name = "_QFnested_default_clause_testsEz"}
!CHECK: %[[PRIVATE_INNER_W:.*]] = fir.alloca i32 {bindc_name = "w", pinned, uniq_name = "_QFnested_default_clause_testsEw"}
!CHECK: %[[PRIVATE_INNER_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFnested_default_clause_testsEx"}
!CHECK: %[[temp_1:.*]] = fir.load %[[PRIVATE_INNER_X]] : !fir.ref<i32>
-!CHECK: %[[temp_2:.*]] = fir.load %[[PRIVATE_INNER_Z]] : !fir.ref<i32>
+!CHECK: %[[temp_2:.*]] = fir.load %[[PRIVATE_Z]] : !fir.ref<i32>
!CHECK: %[[result:.*]] = arith.addi %[[temp_1]], %[[temp_2]] : i32
!CHECK: fir.store %[[result]] to %[[PRIVATE_INNER_W]] : !fir.ref<i32>
!CHECK: omp.terminator
diff --git a/flang/test/Lower/OpenMP/default-clause-byref.f90 b/flang/test/Lower/OpenMP/default-clause-byref.f90
index 86da354910a8ef..f0a117b4c6c2f8 100644
--- a/flang/test/Lower/OpenMP/default-clause-byref.f90
+++ b/flang/test/Lower/OpenMP/default-clause-byref.f90
@@ -240,14 +240,12 @@ subroutine nested_default_clause_tests
!CHECK: omp.terminator
!CHECK: }
!CHECK: omp.parallel {
-!CHECK: %[[PRIVATE_INNER_Z:.*]] = fir.alloca i32 {bindc_name = "z", pinned, uniq_name = "_QFnested_default_clause_testsEz"}
-!CHECK: %[[PRIVATE_INNER_Z_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_INNER_Z]] {uniq_name = "_QFnested_default_clause_testsEz"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
!CHECK: %[[PRIVATE_INNER_W:.*]] = fir.alloca i32 {bindc_name = "w", pinned, uniq_name = "_QFnested_default_clause_testsEw"}
!CHECK: %[[PRIVATE_INNER_W_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_INNER_W]] {uniq_name = "_QFnested_default_clause_testsEw"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
!CHECK: %[[PRIVATE_INNER_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFnested_default_clause_testsEx"}
!CHECK: %[[PRIVATE_INNER_X_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_INNER_X]] {uniq_name = "_QFnested_default_clause_testsEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
!CHECK: %[[TEMP_1:.*]] = fir.load %[[PRIVATE_INNER_X_DECL]]#0 : !fir.ref<i32>
-!CHECK: %[[TEMP_2:.*]] = fir.load %[[PRIVATE_INNER_Z_DECL]]#0 : !fir.ref<i32>
+!CHECK: %[[TEMP_2:.*]] = fir.load %[[PRIVATE_Z_DECL]]#0 : !fir.ref<i32>
!CHECK: %[[RESULT:.*]] = arith.addi %{{.*}}, %{{.*}} : i32
!CHECK: hlfir.assign %[[RESULT]] to %[[PRIVATE_INNER_W_DECL]]#0 : i32, !fir.ref<i32>
!CHECK: omp.terminator
diff --git a/flang/test/Lower/OpenMP/default-clause.f90 b/flang/test/Lower/OpenMP/default-clause.f90
index f69b5e607d3561..c4e6c7532026c7 100644
--- a/flang/test/Lower/OpenMP/default-clause.f90
+++ b/flang/test/Lower/OpenMP/default-clause.f90
@@ -254,14 +254,12 @@ subroutine nested_default_clause_test1
!CHECK: omp.terminator
!CHECK: }
!CHECK: omp.parallel {
-!CHECK: %[[PRIVATE_INNER_Z:.*]] = fir.alloca i32 {bindc_name = "z", pinned, uniq_name = "_QFnested_default_clause_test2Ez"}
-!CHECK: %[[PRIVATE_INNER_Z_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_INNER_Z]] {uniq_name = "_QFnested_default_clause_test2Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
!CHECK: %[[PRIVATE_INNER_W:.*]] = fir.alloca i32 {bindc_name = "w", pinned, uniq_name = "_QFnested_default_clause_test2Ew"}
!CHECK: %[[PRIVATE_INNER_W_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_INNER_W]] {uniq_name = "_QFnested_default_clause_test2Ew"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
!CHECK: %[[PRIVATE_INNER_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFnested_default_clause_test2Ex"}
!CHECK: %[[PRIVATE_INNER_X_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_INNER_X]] {uniq_name = "_QFnested_default_clause_test2Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
!CHECK: %[[TEMP_1:.*]] = fir.load %[[PRIVATE_INNER_X_DECL]]#0 : !fir.ref<i32>
-!CHECK: %[[TEMP_2:.*]] = fir.load %[[PRIVATE_INNER_Z_DECL]]#0 : !fir.ref<i32>
+!CHECK: %[[TEMP_2:.*]] = fir.load %[[PRIVATE_Z_DECL]]#0 : !fir.ref<i32>
!CHECK: %[[RESULT:.*]] = arith.addi %{{.*}}, %{{.*}} : i32
!CHECK: hlfir.assign %[[RESULT]] to %[[PRIVATE_INNER_W_DECL]]#0 : i32, !fir.ref<i32>
!CHECK: omp.terminator
diff --git a/flang/test/Lower/OpenMP/implicit-dsa.f90 b/flang/test/Lower/OpenMP/implicit-dsa.f90
new file mode 100644
index 00000000000000..0f67d5bfd194f9
--- /dev/null
+++ b/flang/test/Lower/OpenMP/implicit-dsa.f90
@@ -0,0 +1,275 @@
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+! Checks lowering of OpenMP variables with implicitly determined DSAs.
+
+! Basic cases.
+!CHECK-LABEL: func @_QPimplicit_dsa_test1
+!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFimplicit_dsa_test1Ex"}
+!CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFimplicit_dsa_test1Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFimplicit_dsa_test1Ey"}
+!CHECK: %[[Y_DECL:.*]]:2 = hlfir.declare %[[Y]] {uniq_name = "_QFimplicit_dsa_test1Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFimplicit_dsa_test1Ez"}
+!CHECK: %[[Z_DECL:.*]]:2 = hlfir.declare %[[Z]] {uniq_name = "_QFimplicit_dsa_test1Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.task {
+!CHECK-NEXT: %[[PRIV_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFimplicit_dsa_test1Ey"}
+!CHECK-NEXT: %[[PRIV_Y_DECL:.*]]:2 = hlfir.declare %[[PRIV_Y]] {uniq_name = "_QFimplicit_dsa_test1Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[PRIV_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test1Ex"}
+!CHECK-NEXT: %[[PRIV_X_DECL:.*]]:2 = hlfir.declare %[[PRIV_X]] {uniq_name = "_QFimplicit_dsa_test1Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP]] to %[[PRIV_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK-NOT: fir.alloca
+!CHECK: }
+!CHECK: omp.task {
+!CHECK-NOT: fir.alloca
+!CHECK: }
+subroutine implicit_dsa_test1
+ integer :: x, y, z
+
+ !$omp task private(y) shared(z)
+ x = y + z
+ !$omp end task
+
+ !$omp task default(shared)
+ x = y + z
+ !$omp end task
+end subroutine
+
+! Nested task with implicit firstprivate DSA variable.
+!CHECK-LABEL: func @_QPimplicit_dsa_test2
+!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFimplicit_dsa_test2Ex"}
+!CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFimplicit_dsa_test2Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.task {
+!CHECK: omp.task {
+!CHECK: %[[PRIV_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test2Ex"}
+!CHECK: %[[PRIV_X_DECL:.*]]:2 = hlfir.declare %[[PRIV_X]] {uniq_name = "_QFimplicit_dsa_test2Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[TEMP:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
+!CHECK: hlfir.assign %[[TEMP]] to %[[PRIV_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: }
+subroutine implicit_dsa_test2
+ integer :: x
+
+ !$omp task
+ !$omp task
+ x = 1
+ !$omp end task
+ !$omp end task
+end subroutine
+
+! Nested tasks with implicit shared DSA variables.
+!CHECK-LABEL: func @_QPimplicit_dsa_test3
+!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFimplicit_dsa_test3Ex"}
+!CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFimplicit_dsa_test3Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFimplicit_dsa_test3Ey"}
+!CHECK: %[[Y_DECL:.*]]:2 = hlfir.declare %[[Y]] {uniq_name = "_QFimplicit_dsa_test3Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFimplicit_dsa_test3Ez"}
+!CHECK: %[[Z_DECL:.*]]:2 = hlfir.declare %[[Z]] {uniq_name = "_QFimplicit_dsa_test3Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.parallel {
+!CHECK: omp.task {
+!CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+!CHECK: hlfir.assign %[[ONE]] to %[[X_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+!CHECK: hlfir.assign %[[ONE]] to %[[Y_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: omp.task {
+!CHECK: %[[PRIV_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test3Ex"}
+!CHECK: %[[PRIV_X_DECL:.*]]:2 = hlfir.declare %[[PRIV_X]] {uniq_name = "_QFimplicit_dsa_test3Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[TEMP:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
+!CHECK: hlfir.assign %[[TEMP]] to %[[PRIV_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+!CHECK: hlfir.assign %[[ONE]] to %[[PRIV_X_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+!CHECK: hlfir.assign %[[ONE]] to %[[Z_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: }
+subroutine implicit_dsa_test3
+ integer :: x, y, z
+
+ !$omp parallel
+ !$omp task
+ x = 1
+ y = 1
+ !$omp end task
+
+ !$omp task firstprivate(x)
+ x = 1
+ z = 1
+ !$omp end task
+ !$omp end parallel
+end subroutine
+
+! Task with implicit firstprivate DSA variables, enclosed in private context.
+!CHECK-LABEL: func @_QPimplicit_dsa_test4
+!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFimplicit_dsa_test4Ex"}
+!CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFimplicit_dsa_test4Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFimplicit_dsa_test4Ey"}
+!CHECK: %[[Y_DECL:.*]]:2 = hlfir.declare %[[Y]] {uniq_name = "_QFimplicit_dsa_test4Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFimplicit_dsa_test4Ez"}
+!CHECK: %[[Z_DECL:.*]]:2 = hlfir.declare %[[Z]] {uniq_name = "_QFimplicit_dsa_test4Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.parallel {
+!CHECK: %[[PRIV_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test4Ex"}
+!CHECK: %[[PRIV_X_DECL:.*]]:2 = hlfir.declare %[[PRIV_X]] {uniq_name = "_QFimplicit_dsa_test4Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[PRIV_Z:.*]] = fir.alloca i32 {bindc_name = "z", pinned, uniq_name = "_QFimplicit_dsa_test4Ez"}
+!CHECK: %[[PRIV_Z_DECL:.*]]:2 = hlfir.declare %[[PRIV_Z]] {uniq_name = "_QFimplicit_dsa_test4Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[PRIV_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFimplicit_dsa_test4Ey"}
+!CHECK: %[[PRIV_Y_DECL:.*]]:2 = hlfir.declare %[[PRIV_Y]] {uniq_name = "_QFimplicit_dsa_test4Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.task {
+!CHECK-NEXT: %[[PRIV2_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test4Ex"}
+!CHECK-NEXT: %[[PRIV2_X_DECL:.*]]:2 = hlfir.declare %[[PRIV2_X]] {uniq_name = "_QFimplicit_dsa_test4Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP:.*]] = fir.load %[[PRIV_X_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP]] to %[[PRIV2_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK-NEXT: %[[PRIV2_Z:.*]] = fir.alloca i32 {bindc_name = "z", pinned, uniq_name = "_QFimplicit_dsa_test4Ez"}
+!CHECK-NEXT: %[[PRIV2_Z_DECL:.*]]:2 = hlfir.declare %[[PRIV2_Z]] {uniq_name = "_QFimplicit_dsa_test4Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP2:.*]] = fir.load %[[PRIV_Z_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP2]] to %[[PRIV2_Z_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: %[[ZERO:.*]] = arith.constant 0 : i32
+!CHECK-NEXT: hlfir.assign %[[ZERO]] to %[[PRIV2_X_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+!CHECK-NEXT: hlfir.assign %[[ONE]] to %[[PRIV2_Z_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: omp.task {
+!CHECK-NEXT: %[[PRIV2_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test4Ex"}
+!CHECK-NEXT: %[[PRIV2_X_DECL:.*]]:2 = hlfir.declare %[[PRIV2_X]] {uniq_name = "_QFimplicit_dsa_test4Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP:.*]] = fir.load %[[PRIV_X_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP]] to %[[PRIV2_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK-NEXT: %[[PRIV2_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFimplicit_dsa_test4Ey"}
+!CHECK-NEXT: %[[PRIV2_Y_DECL:.*]]:2 = hlfir.declare %[[PRIV2_Y]] {uniq_name = "_QFimplicit_dsa_test4Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP2:.*]] = fir.load %[[PRIV_Y_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP2]] to %[[PRIV2_Y_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+!CHECK-NEXT: hlfir.assign %[[ONE]] to %[[PRIV2_X_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: %[[ZERO:.*]] = arith.constant 0 : i32
+!CHECK-NEXT: hlfir.assign %[[ZERO]] to %[[PRIV2_Z_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: }
+subroutine implicit_dsa_test4
+ integer :: x, y, z
+
+ !$omp parallel default(private)
+ !$omp task
+ x = 0
+ z = 1
+ !$omp end task
+
+ !$omp task
+ x = 1
+ y = 0
+ !$omp end task
+ !$omp end parallel
+end subroutine
+
+! Inner parallel using implicit firstprivate symbol.
+!CHECK-LABEL: func @_QPimplicit_dsa_test5
+!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFimplicit_dsa_test5Ex"}
+!CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFimplicit_dsa_test5Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.parallel {
+!CHECK: %[[PRIV_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test5Ex"}
+!CHECK: %[[PRIV_X_DECL:.*]]:2 = hlfir.declare %[[PRIV_X]] {uniq_name = "_QFimplicit_dsa_test5Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.task {
+!CHECK: %[[PRIV2_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test5Ex"}
+!CHECK: %[[PRIV2_X_DECL:.*]]:2 = hlfir.declare %[[PRIV2_X]] {uniq_name = "_QFimplicit_dsa_test5Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[TEMP:.*]] = fir.load %[[PRIV_X_DECL]]#0 : !fir.ref<i32>
+!CHECK: hlfir.assign %[[TEMP]] to %[[PRIV2_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: omp.parallel {
+!CHECK: %[[ONE:.*]] = arith.constant 1 : i32
+!CHECK: hlfir.assign %[[ONE]] to %[[PRIV2_X_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: }
+!CHECK: }
+subroutine implicit_dsa_test5
+ integer :: x
+
+ !$omp parallel default(private)
+ !$omp task
+ !$omp parallel
+ x = 1
+ !$omp end parallel
+ !$omp end task
+ !$omp end parallel
+end subroutine
+
+! Constructs nested inside a task with implicit DSA variables.
+!CHECK-LABEL: func @_QPimplicit_dsa_test6
+!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFimplicit_dsa_test6Ex"}
+!CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFimplicit_dsa_test6Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFimplicit_dsa_test6Ey"}
+!CHECK: %[[Y_DECL:.*]]:2 = hlfir.declare %[[Y]] {uniq_name = "_QFimplicit_dsa_test6Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFimplicit_dsa_test6Ez"}
+!CHECK: %[[Z_DECL:.*]]:2 = hlfir.declare %[[Z]] {uniq_name = "_QFimplicit_dsa_test6Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.task {
+!CHECK-NEXT: %[[PRIV_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test6Ex"}
+!CHECK-NEXT: %[[PRIV_X_DECL:.*]]:2 = hlfir.declare %[[PRIV_X]] {uniq_name = "_QFimplicit_dsa_test6Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP]] to %[[PRIV_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK-NEXT: %[[PRIV_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFimplicit_dsa_test6Ey"}
+!CHECK-NEXT: %[[PRIV_Y_DECL:.*]]:2 = hlfir.declare %[[PRIV_Y]] {uniq_name = "_QFimplicit_dsa_test6Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP2:.*]] = fir.load %[[Y_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP2]] to %[[PRIV_Y_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK-NEXT: %[[PRIV_Z:.*]] = fir.alloca i32 {bindc_name = "z", pinned, uniq_name = "_QFimplicit_dsa_test6Ez"}
+!CHECK-NEXT: %[[PRIV_Z_DECL:.*]]:2 = hlfir.declare %[[PRIV_Z]] {uniq_name = "_QFimplicit_dsa_test6Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP3:.*]] = fir.load %[[Z_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP3]] to %[[PRIV_Z_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: omp.parallel {
+!CHECK: %[[PRIV2_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test6Ex"}
+!CHECK: %[[PRIV2_X_DECL:.*]]:2 = hlfir.declare %[[PRIV2_X]] {uniq_name = "_QFimplicit_dsa_test6Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NOT: hlfir.assign
+!CHECK: %[[PRIV2_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFimplicit_dsa_test6Ey"}
+!CHECK: %[[PRIV2_Y_DECL:.*]]:2 = hlfir.declare %[[PRIV2_Y]] {uniq_name = "_QFimplicit_dsa_test6Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NOT: hlfir.assign
+!CHECK: hlfir.assign %{{.*}} to %[[PRIV2_X_DECL]]
+!CHECK: }
+!CHECK: omp.parallel {
+!CHECK-NEXT: %[[PRIV3_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test6Ex"}
+!CHECK-NEXT: %[[PRIV3_X_DECL:.*]]:2 = hlfir.declare %[[PRIV3_X]] {uniq_name = "_QFimplicit_dsa_test6Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP4:.*]] = fir.load %[[PRIV_X_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP4]] to %[[PRIV3_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK-NEXT: %[[PRIV3_Z:.*]] = fir.alloca i32 {bindc_name = "z", pinned, uniq_name = "_QFimplicit_dsa_test6Ez"}
+!CHECK-NEXT: %[[PRIV3_Z_DECL:.*]]:2 = hlfir.declare %[[PRIV3_Z]] {uniq_name = "_QFimplicit_dsa_test6Ez"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP5:.*]] = fir.load %[[PRIV_Z_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP5]] to %[[PRIV3_Z_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: hlfir.assign %{{.*}} to %[[PRIV_Y_DECL]]#0 : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: }
+subroutine implicit_dsa_test6
+ integer :: x, y, z
+
+ !$omp task
+ !$omp parallel default(private)
+ x = y
+ !$omp end parallel
+
+ !$omp parallel default(firstprivate) shared(y)
+ y = x + z
+ !$omp end parallel
+ !$omp end task
+end subroutine
+
+! Test taskgroup - it uses the same scope as task.
+!CHECK-LABEL: func @_QPimplicit_dsa_test7
+!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFimplicit_dsa_test7Ex"}
+!CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFimplicit_dsa_test7Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFimplicit_dsa_test7Ey"}
+!CHECK: %[[Y_DECL:.*]]:2 = hlfir.declare %[[Y]] {uniq_name = "_QFimplicit_dsa_test7Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.task {
+!CHECK: omp.taskgroup {
+!CHECK-NEXT: %[[PRIV_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFimplicit_dsa_test7Ex"}
+!CHECK-NEXT: %[[PRIV_X_DECL:.*]]:2 = hlfir.declare %[[PRIV_X]] {uniq_name = "_QFimplicit_dsa_test7Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP]] to %[[PRIV_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK-NEXT: %[[PRIV_Y:.*]] = fir.alloca i32 {bindc_name = "y", pinned, uniq_name = "_QFimplicit_dsa_test7Ey"}
+!CHECK-NEXT: %[[PRIV_Y_DECL:.*]]:2 = hlfir.declare %[[PRIV_Y]] {uniq_name = "_QFimplicit_dsa_test7Ey"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK-NEXT: %[[TEMP2:.*]] = fir.load %[[Y_DECL]]#0 : !fir.ref<i32>
+!CHECK-NEXT: hlfir.assign %[[TEMP2]] to %[[PRIV_Y_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
+!CHECK: }
+!CHECK: }
+subroutine implicit_dsa_test7
+ integer :: x, y
+
+ !$omp task
+ !$omp taskgroup
+ x = y
+ !$omp end taskgroup
+ !$omp end task
+end subroutine
+
+! TODO Test taskloop
diff --git a/flang/test/Semantics/OpenMP/implicit-dsa.f90 b/flang/test/Semantics/OpenMP/implicit-dsa.f90
new file mode 100644
index 00000000000000..92d2421d06f971
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/implicit-dsa.f90
@@ -0,0 +1,158 @@
+! RUN: %python %S/../test_symbols.py %s %flang_fc1 -fopenmp
+
+! Test symbols generated in block constructs that have implicitly
+! determined DSAs.
+
+! Basic cases.
+!DEF: /implicit_dsa_test1 (Subroutine) Subprogram
+subroutine implicit_dsa_test1
+ !DEF: /implicit_dsa_test1/i ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test1/x ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test1/y ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test1/z ObjectEntity INTEGER(4)
+ integer i, x, y, z
+
+ !$omp task private(y) shared(z)
+ !DEF: /implicit_dsa_test1/OtherConstruct1/x (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ !DEF: /implicit_dsa_test1/OtherConstruct1/y (OmpPrivate) HostAssoc INTEGER(4)
+ !REF: /implicit_dsa_test1/z
+ x = y + z
+ !$omp end task
+
+ !$omp task default(shared)
+ !REF: /implicit_dsa_test1/x
+ !REF: /implicit_dsa_test1/y
+ !REF: /implicit_dsa_test1/z
+ x = y + z
+ !$omp end task
+
+ !$omp taskloop
+ !DEF: /implicit_dsa_test1/OtherConstruct3/i (OmpPrivate, OmpPreDetermined) HostAssoc INTEGER(4)
+ do i = 0, 10
+ !DEF: /implicit_dsa_test1/OtherConstruct3/x (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ !DEF: /implicit_dsa_test1/OtherConstruct3/y (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ !REF: /implicit_dsa_test1/OtherConstruct3/i
+ x = y + i
+ end do
+ !$omp end taskloop
+end subroutine
+
+! Nested task with implicit firstprivate DSA variable.
+!DEF: /implicit_dsa_test2 (Subroutine) Subprogram
+subroutine implicit_dsa_test2
+ !DEF: /implicit_dsa_test2/x ObjectEntity INTEGER(4)
+ integer x
+
+ !$omp task
+ !$omp task
+ !DEF: /implicit_dsa_test2/OtherConstruct1/OtherConstruct1/x (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ x = 1
+ !$omp end task
+ !$omp end task
+end subroutine
+
+! Nested tasks with implicit shared DSA variables.
+!DEF: /implicit_dsa_test3 (Subroutine) Subprogram
+subroutine implicit_dsa_test3
+ !DEF: /implicit_dsa_test3/x ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test3/y ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test3/z ObjectEntity INTEGER(4)
+ integer x, y, z
+
+ !$omp parallel
+ !$omp task
+ !REF: /implicit_dsa_test3/x
+ x = 1
+ !REF: /implicit_dsa_test3/y
+ y = 1
+ !$omp end task
+
+ !$omp task firstprivate(x)
+ !DEF: /implicit_dsa_test3/OtherConstruct1/OtherConstruct2/x (OmpFirstPrivate) HostAssoc INTEGER(4)
+ x = 1
+ !REF: /implicit_dsa_test3/z
+ z = 1
+ !$omp end task
+ !$omp end parallel
+end subroutine
+
+! Task with implicit firstprivate DSA variables, enclosed in private context.
+!DEF: /implicit_dsa_test4 (Subroutine) Subprogram
+subroutine implicit_dsa_test4
+ !DEF: /implicit_dsa_test4/x ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test4/y ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test4/z ObjectEntity INTEGER(4)
+ integer x, y, z
+
+ !$omp parallel default(private)
+ !$omp task
+ !DEF: /implicit_dsa_test4/OtherConstruct1/OtherConstruct1/x (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ x = 0
+ !DEF: /implicit_dsa_test4/OtherConstruct1/OtherConstruct1/z (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ z = 1
+ !$omp end task
+
+ !$omp task
+ !DEF: /implicit_dsa_test4/OtherConstruct1/OtherConstruct2/x (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ x = 1
+ !DEF: /implicit_dsa_test4/OtherConstruct1/OtherConstruct2/y (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ y = 0
+ !$omp end task
+ !$omp end parallel
+end subroutine
+
+! Inner parallel using implicit firstprivate symbol.
+!DEF: /implicit_dsa_test5 (Subroutine) Subprogram
+subroutine implicit_dsa_test5
+ !DEF: /implicit_dsa_test5/x ObjectEntity INTEGER(4)
+ integer x
+
+ !$omp parallel default(private)
+ !$omp task
+ !$omp parallel
+ !DEF: /implicit_dsa_test5/OtherConstruct1/OtherConstruct1/OtherConstruct1/x HostAssoc INTEGER(4)
+ x = 1
+ !$omp end parallel
+ !$omp end task
+ !$omp end parallel
+end subroutine
+
+! Constructs nested inside a task with implicit DSA variables.
+!DEF: /implicit_dsa_test6 (Subroutine) Subprogram
+subroutine implicit_dsa_test6
+ !DEF: /implicit_dsa_test6/x ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test6/y ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test6/z ObjectEntity INTEGER(4)
+ integer x, y, z
+
+ !$omp task
+ !$omp parallel default(private)
+ !DEF: /implicit_dsa_test6/OtherConstruct1/OtherConstruct1/x (OmpPrivate) HostAssoc INTEGER(4)
+ !DEF: /implicit_dsa_test6/OtherConstruct1/OtherConstruct1/y (OmpPrivate) HostAssoc INTEGER(4)
+ x = y
+ !$omp end parallel
+
+ !$omp parallel default(firstprivate) shared(y)
+ !DEF: /implicit_dsa_test6/OtherConstruct1/OtherConstruct2/y HostAssoc INTEGER(4)
+ !DEF: /implicit_dsa_test6/OtherConstruct1/OtherConstruct2/x (OmpFirstPrivate) HostAssocINTEGER(4)
+ !DEF: /implicit_dsa_test6/OtherConstruct1/OtherConstruct2/z (OmpFirstPrivate) HostAssocINTEGER(4)
+ y = x + z
+ !$omp end parallel
+ !$omp end task
+end subroutine
+
+! Test taskgroup - it uses the same scope as task.
+!DEF: /implicit_dsa_test7 (Subroutine) Subprogram
+subroutine implicit_dsa_test7
+ !DEF: /implicit_dsa_test7/x ObjectEntity INTEGER(4)
+ !DEF: /implicit_dsa_test7/y ObjectEntity INTEGER(4)
+ integer x, y
+
+ !$omp task
+ !$omp taskgroup
+ !DEF: /implicit_dsa_test7/OtherConstruct1/x (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ !DEF: /implicit_dsa_test7/OtherConstruct1/y (OmpFirstPrivate, OmpImplicit) HostAssoc INTEGER(4)
+ x = y
+ !$omp end taskgroup
+ !$omp end task
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/symbol08.f90 b/flang/test/Semantics/OpenMP/symbol08.f90
index 50f34b736cdb77..3af85af74ee97c 100644
--- a/flang/test/Semantics/OpenMP/symbol08.f90
+++ b/flang/test/Semantics/OpenMP/symbol08.f90
@@ -94,7 +94,7 @@ subroutine test_taskloop
!DEF: /test_taskloop/OtherConstruct1/j (OmpPrivate) HostAssoc INTEGER(4)
!REF: /test_taskloop/OtherConstruct1/i
do j=1,i
- !REF: /test_taskloop/a
+ !DEF: /test_taskloop/OtherConstruct1/a (OmpFirstPrivate, OmpImplicit) HostAssoc REAL(4)
!REF: /test_taskloop/OtherConstruct1/j
!REF: /test_taskloop/OtherConstruct1/i
a(j,i) = 3.14
>From b35c3ddd0787d30eca16bcc646cc46de781db765 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Wed, 24 Apr 2024 16:18:48 -0300
Subject: [PATCH 2/2] Explain why new symbols are needed when inheriting
---
flang/lib/Semantics/resolve-directives.cpp | 25 ++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 57ba556b9d57bf..f699151c33ce0e 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2032,8 +2032,29 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
// When handling each implicit rule, either a new private symbol is
// declared or the last declared symbol is used.
// In the latter case, it's necessary to insert a new symbol in the scope
- // being processed, associated with the last declared symbol, to avoid
- // "inheriting" the enclosing context's symbol and its flags.
+ // being processed, associated with the last declared symbol.
+ // This captures the fact that, although we are using the last declared
+ // symbol, its DSA could be different in this scope.
+ // Also, because of how symbols are collected in lowering, not inserting
+ // a new symbol in this scope could lead to the conclusion that the
+ // symbol was declared in this construct, which would result in wrong
+ // privatization code being generated.
+ // Consider the following example:
+ //
+ // !$omp parallel default(private) ! p1
+ // !$omp parallel default(private) shared(x) ! p2
+ // x = 10
+ // !$omp end parallel
+ // !$omp end parallel
+ //
+ // If a new x symbol was not inserted in the inner parallel construct
+ // (p2), it would use the x symbol definition from the enclosing scope.
+ // Then, when p2's default symbols were collected in lowering, the x
+ // symbol from the outer parallel construct (p1) would be collected, as
+ // it would have the private flag set (note that symbols that don't have
+ // any private flag are considered as shared).
+ // This would make x appear to be defined in p2, causing it to be
+ // privatized in p2 and its privatization in p1 to be skipped.
auto declNewSymbol = [&](Symbol::Flag flag) {
Symbol *hostSymbol =
lastDeclSymbol ? lastDeclSymbol : &symbol->GetUltimate();
More information about the llvm-branch-commits
mailing list