aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Semantics
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Semantics')
-rw-r--r--flang/lib/Semantics/check-cuda.cpp3
-rw-r--r--flang/lib/Semantics/check-omp-structure.cpp302
-rw-r--r--flang/lib/Semantics/check-omp-structure.h41
-rw-r--r--flang/lib/Semantics/openmp-utils.cpp2
-rw-r--r--flang/lib/Semantics/resolve-directives.cpp20
5 files changed, 286 insertions, 82 deletions
diff --git a/flang/lib/Semantics/check-cuda.cpp b/flang/lib/Semantics/check-cuda.cpp
index 3d2db6a..caa9bdd 100644
--- a/flang/lib/Semantics/check-cuda.cpp
+++ b/flang/lib/Semantics/check-cuda.cpp
@@ -131,6 +131,9 @@ struct FindHostArray
return (*this)(x.base());
}
Result operator()(const Symbol &symbol) const {
+ if (symbol.IsFuncResult()) {
+ return nullptr;
+ }
if (const auto *details{
symbol.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()}) {
if (details->IsArray() &&
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index be10669..4141630 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -61,6 +61,124 @@ namespace Fortran::semantics {
using namespace Fortran::semantics::omp;
using namespace Fortran::parser::omp;
+OmpStructureChecker::OmpStructureChecker(SemanticsContext &context)
+ : DirectiveStructureChecker(context,
+#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
+#include "llvm/Frontend/OpenMP/OMP.inc"
+ ) {
+ scopeStack_.push_back(&context.globalScope());
+}
+
+bool OmpStructureChecker::Enter(const parser::MainProgram &x) {
+ using StatementProgramStmt = parser::Statement<parser::ProgramStmt>;
+ if (auto &stmt{std::get<std::optional<StatementProgramStmt>>(x.t)}) {
+ scopeStack_.push_back(stmt->statement.v.symbol->scope());
+ } else {
+ for (const Scope &scope : context_.globalScope().children()) {
+ // There can only be one main program.
+ if (scope.kind() == Scope::Kind::MainProgram) {
+ scopeStack_.push_back(&scope);
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::MainProgram &x) {
+ scopeStack_.pop_back();
+}
+
+bool OmpStructureChecker::Enter(const parser::BlockData &x) {
+ // The BLOCK DATA name is optional, so we need to look for the
+ // corresponding scope in the global scope.
+ auto &stmt{std::get<parser::Statement<parser::BlockDataStmt>>(x.t)};
+ if (auto &name{stmt.statement.v}) {
+ scopeStack_.push_back(name->symbol->scope());
+ } else {
+ for (const Scope &scope : context_.globalScope().children()) {
+ if (scope.kind() == Scope::Kind::BlockData) {
+ if (scope.symbol()->name().empty()) {
+ scopeStack_.push_back(&scope);
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::BlockData &x) {
+ scopeStack_.pop_back();
+}
+
+bool OmpStructureChecker::Enter(const parser::Module &x) {
+ auto &stmt{std::get<parser::Statement<parser::ModuleStmt>>(x.t)};
+ const Symbol *sym{stmt.statement.v.symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::Module &x) {
+ scopeStack_.pop_back();
+}
+
+bool OmpStructureChecker::Enter(const parser::Submodule &x) {
+ auto &stmt{std::get<parser::Statement<parser::SubmoduleStmt>>(x.t)};
+ const Symbol *sym{std::get<parser::Name>(stmt.statement.t).symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::Submodule &x) {
+ scopeStack_.pop_back();
+}
+
+// Function/subroutine subprogram nodes don't appear in INTERFACEs, but
+// the subprogram/end statements do.
+bool OmpStructureChecker::Enter(const parser::SubroutineStmt &x) {
+ const Symbol *sym{std::get<parser::Name>(x.t).symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::EndSubroutineStmt &x) {
+ scopeStack_.pop_back();
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::FunctionStmt &x) {
+ const Symbol *sym{std::get<parser::Name>(x.t).symbol};
+ scopeStack_.push_back(sym->scope());
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::EndFunctionStmt &x) {
+ scopeStack_.pop_back();
+ return true;
+}
+
+bool OmpStructureChecker::Enter(const parser::BlockConstruct &x) {
+ auto &specPart{std::get<parser::BlockSpecificationPart>(x.t)};
+ auto &execPart{std::get<parser::Block>(x.t)};
+ if (auto &&source{parser::GetSource(specPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ } else if (auto &&source{parser::GetSource(execPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ }
+ return true;
+}
+
+void OmpStructureChecker::Leave(const parser::BlockConstruct &x) {
+ auto &specPart{std::get<parser::BlockSpecificationPart>(x.t)};
+ auto &execPart{std::get<parser::Block>(x.t)};
+ if (auto &&source{parser::GetSource(specPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ } else if (auto &&source{parser::GetSource(execPart)}) {
+ scopeStack_.push_back(&context_.FindScope(*source));
+ }
+}
+
// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
#define CHECK_SIMPLE_CLAUSE(X, Y) \
void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
@@ -362,6 +480,36 @@ bool OmpStructureChecker::IsNestedInDirective(llvm::omp::Directive directive) {
return false;
}
+bool OmpStructureChecker::InTargetRegion() {
+ if (IsNestedInDirective(llvm::omp::Directive::OMPD_target)) {
+ // Return true even for device_type(host).
+ return true;
+ }
+ for (const Scope *scope : llvm::reverse(scopeStack_)) {
+ if (const auto *symbol{scope->symbol()}) {
+ if (symbol->test(Symbol::Flag::OmpDeclareTarget)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool OmpStructureChecker::HasRequires(llvm::omp::Clause req) {
+ const Scope &unit{GetProgramUnit(*scopeStack_.back())};
+ return common::visit(
+ [&](const auto &details) {
+ if constexpr (std::is_convertible_v<decltype(details),
+ const WithOmpDeclarative &>) {
+ if (auto *reqs{details.ompRequires()}) {
+ return reqs->test(req);
+ }
+ }
+ return false;
+ },
+ DEREF(unit.symbol()).details());
+}
+
void OmpStructureChecker::CheckVariableListItem(
const SymbolSourceMap &symbols) {
for (auto &[symbol, source] : symbols) {
@@ -1562,40 +1710,95 @@ void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {
dirContext_.pop_back();
}
-void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
- isPredefinedAllocator = true;
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
- PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
- SymbolSourceMap currSymbols;
- GetSymbolsInObjectList(objectList, currSymbols);
- for (auto &[symbol, source] : currSymbols) {
- if (IsPointer(*symbol)) {
- context_.Say(source,
- "List item '%s' in ALLOCATE directive must not have POINTER "
- "attribute"_err_en_US,
- source.ToString());
+void OmpStructureChecker::CheckAllocateDirective(parser::CharBlock source,
+ const parser::OmpObjectList &objects,
+ const parser::OmpClauseList &clauses) {
+ const Scope &thisScope{context_.FindScope(source)};
+ SymbolSourceMap symbols;
+ GetSymbolsInObjectList(objects, symbols);
+
+ auto maybeHasPredefinedAllocator{[&](const parser::OmpClause *calloc) {
+ // Return "true" if the ALLOCATOR clause was provided with an argument
+ // that is either a prefdefined allocator, or a run-time value.
+ // Otherwise return "false".
+ if (!calloc) {
+ return false;
}
- if (IsDummy(*symbol)) {
+ auto *allocator{std::get_if<parser::OmpClause::Allocator>(&calloc->u)};
+ if (auto val{ToInt64(GetEvaluateExpr(DEREF(allocator).v))}) {
+ // Predefined allocators (defined in OpenMP 6.0 20.8.1):
+ // omp_null_allocator = 0,
+ // omp_default_mem_alloc = 1,
+ // omp_large_cap_mem_alloc = 2,
+ // omp_const_mem_alloc = 3,
+ // omp_high_bw_mem_alloc = 4,
+ // omp_low_lat_mem_alloc = 5,
+ // omp_cgroup_mem_alloc = 6,
+ // omp_pteam_mem_alloc = 7,
+ // omp_thread_mem_alloc = 8
+ return *val >= 0 && *val <= 8;
+ }
+ return true;
+ }};
+
+ const auto *allocator{FindClause(llvm::omp::Clause::OMPC_allocator)};
+ if (InTargetRegion()) {
+ bool hasDynAllocators{
+ HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)};
+ if (!allocator && !hasDynAllocators) {
context_.Say(source,
- "List item '%s' in ALLOCATE directive must not be a dummy "
- "argument"_err_en_US,
- source.ToString());
+ "An ALLOCATE directive in a TARGET region must specify an ALLOCATOR clause or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US);
+ }
+ }
+
+ auto maybePredefined{maybeHasPredefinedAllocator(allocator)};
+
+ for (auto &[symbol, source] : symbols) {
+ if (!inExecutableAllocate_) {
+ if (symbol->owner() != thisScope) {
+ context_.Say(source,
+ "A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears"_err_en_US);
+ }
+ if (IsPointer(*symbol) || IsAllocatable(*symbol)) {
+ context_.Say(source,
+ "A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute"_err_en_US);
+ }
}
if (symbol->GetUltimate().has<AssocEntityDetails>()) {
context_.Say(source,
- "List item '%s' in ALLOCATE directive must not be an associate "
- "name"_err_en_US,
- source.ToString());
+ "A list item in a declarative ALLOCATE cannot be an associate name"_err_en_US);
+ }
+ if (symbol->attrs().test(Attr::SAVE) || IsCommonBlock(*symbol)) {
+ if (!allocator) {
+ context_.Say(source,
+ "If a list item is a named common block or has SAVE attribute, an ALLOCATOR clause must be present with a predefined allocator"_err_en_US);
+ } else if (!maybePredefined) {
+ context_.Say(source,
+ "If a list item is a named common block or has SAVE attribute, only a predefined allocator may be used on the ALLOCATOR clause"_err_en_US);
+ }
+ }
+ if (FindCommonBlockContaining(*symbol)) {
+ context_.Say(source,
+ "A variable that is part of a common block may not be specified as a list item in an ALLOCATE directive, except implicitly via the named common block"_err_en_US);
}
}
- CheckVarIsNotPartOfAnotherVar(dir.source, objectList);
+ CheckVarIsNotPartOfAnotherVar(source, objects);
}
-void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
+void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
- CheckPredefinedAllocatorRestriction(dir.source, objectList);
+ PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
+ if (!inExecutableAllocate_) {
+ const auto &dir{std::get<parser::Verbatim>(x.t)};
+ const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
+ const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
+
+ isPredefinedAllocator = true;
+ CheckAllocateDirective(dir.source, objectList, clauseList);
+ }
dirContext_.pop_back();
}
@@ -1951,6 +2154,7 @@ void OmpStructureChecker::CheckNameInAllocateStmt(
}
void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
+ inExecutableAllocate_ = true;
const auto &dir{std::get<parser::Verbatim>(x.t)};
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
@@ -1960,24 +2164,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
"The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);
}
- bool hasAllocator = false;
- // TODO: Investigate whether searching the clause list can be done with
- // parser::Unwrap instead of the following loop
- const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
- for (const auto &clause : clauseList.v) {
- if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) {
- hasAllocator = true;
- }
- }
-
- if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) {
- // TODO: expand this check to exclude the case when a requires
- // directive with the dynamic_allocators clause is present
- // in the same compilation unit (OMP5.0 2.11.3).
- context_.Say(x.source,
- "ALLOCATE directives that appear in a TARGET region must specify an allocator clause"_err_en_US);
- }
-
const auto &allocateStmt =
std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement;
if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
@@ -1994,18 +2180,34 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
}
isPredefinedAllocator = true;
- const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
- if (objectList) {
- CheckVarIsNotPartOfAnotherVar(dir.source, *objectList);
- }
}
void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
- if (objectList)
- CheckPredefinedAllocatorRestriction(dir.source, *objectList);
+ parser::OmpObjectList empty{std::list<parser::OmpObject>{}};
+ auto &objects{[&]() -> const parser::OmpObjectList & {
+ if (auto &objects{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
+ return *objects;
+ } else {
+ return empty;
+ }
+ }()};
+ auto &clauses{std::get<parser::OmpClauseList>(x.t)};
+ CheckAllocateDirective(
+ std::get<parser::Verbatim>(x.t).source, objects, clauses);
+
+ if (const auto &subDirs{
+ std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
+ x.t)}) {
+ for (const auto &dalloc : *subDirs) {
+ const auto &dir{std::get<parser::Verbatim>(x.t)};
+ const auto &clauses{std::get<parser::OmpClauseList>(dalloc.t)};
+ const auto &objects{std::get<parser::OmpObjectList>(dalloc.t)};
+ CheckAllocateDirective(dir.source, objects, clauses);
+ }
+ }
+
dirContext_.pop_back();
+ inExecutableAllocate_ = false;
}
void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index b3fd6c8..7426559 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -56,21 +56,32 @@ using SymbolSourceMap = std::multimap<const Symbol *, parser::CharBlock>;
using DirectivesClauseTriple = std::multimap<llvm::omp::Directive,
std::pair<llvm::omp::Directive, const OmpClauseSet>>;
-class OmpStructureChecker
- : public DirectiveStructureChecker<llvm::omp::Directive, llvm::omp::Clause,
- parser::OmpClause, llvm::omp::Clause_enumSize> {
+using OmpStructureCheckerBase = DirectiveStructureChecker<llvm::omp::Directive,
+ llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;
+
+class OmpStructureChecker : public OmpStructureCheckerBase {
public:
- using Base = DirectiveStructureChecker<llvm::omp::Directive,
- llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;
+ using Base = OmpStructureCheckerBase;
+
+ OmpStructureChecker(SemanticsContext &context);
- OmpStructureChecker(SemanticsContext &context)
- : DirectiveStructureChecker(context,
-#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
-#include "llvm/Frontend/OpenMP/OMP.inc"
- ) {
- }
using llvmOmpClause = const llvm::omp::Clause;
+ bool Enter(const parser::MainProgram &);
+ void Leave(const parser::MainProgram &);
+ bool Enter(const parser::BlockData &);
+ void Leave(const parser::BlockData &);
+ bool Enter(const parser::Module &);
+ void Leave(const parser::Module &);
+ bool Enter(const parser::Submodule &);
+ void Leave(const parser::Submodule &);
+ bool Enter(const parser::SubroutineStmt &);
+ bool Enter(const parser::EndSubroutineStmt &);
+ bool Enter(const parser::FunctionStmt &);
+ bool Enter(const parser::EndFunctionStmt &);
+ bool Enter(const parser::BlockConstruct &);
+ void Leave(const parser::BlockConstruct &);
+
void Enter(const parser::OpenMPConstruct &);
void Leave(const parser::OpenMPConstruct &);
void Enter(const parser::OpenMPInteropConstruct &);
@@ -177,10 +188,12 @@ private:
const parser::CharBlock &, const OmpDirectiveSet &);
bool IsCloselyNestedRegion(const OmpDirectiveSet &set);
bool IsNestedInDirective(llvm::omp::Directive directive);
+ bool InTargetRegion();
void HasInvalidTeamsNesting(
const llvm::omp::Directive &dir, const parser::CharBlock &source);
void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
+ bool HasRequires(llvm::omp::Clause req);
// specific clause related
void CheckAllowedMapTypes(
parser::OmpMapType::Value, llvm::ArrayRef<parser::OmpMapType::Value>);
@@ -250,6 +263,9 @@ private:
bool CheckTargetBlockOnlyTeams(const parser::Block &);
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
+ void CheckAllocateDirective(parser::CharBlock source,
+ const parser::OmpObjectList &objects,
+ const parser::OmpClauseList &clauses);
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
void CheckIteratorModifier(const parser::OmpIterator &x);
@@ -367,12 +383,15 @@ private:
};
int directiveNest_[LastType + 1] = {0};
+ bool inExecutableAllocate_{false};
parser::CharBlock visitedAtomicSource_;
SymbolSourceMap deferredNonVariables_;
using LoopConstruct = std::variant<const parser::DoConstruct *,
const parser::OpenMPLoopConstruct *>;
std::vector<LoopConstruct> loopStack_;
+ // Scopes for scoping units.
+ std::vector<const Scope *> scopeStack_;
};
/// Find a duplicate entry in the range, and return an iterator to it.
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 292e73b..cc55bb4 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -218,7 +218,7 @@ bool IsMapExitingType(parser::OmpMapType::Value type) {
}
}
-std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr) {
+MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr) {
const parser::TypedExpr &typedExpr{parserExpr.typedExpr};
// ForwardOwningPointer typedExpr
// `- GenericExprWrapper ^.get()
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index c410bd4..196755e 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -3094,26 +3094,6 @@ void OmpAttributeVisitor::ResolveOmpDesignator(
AddAllocateName(name);
}
}
- if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective &&
- IsAllocatable(*symbol) &&
- !IsNestedInDirective(llvm::omp::Directive::OMPD_allocate)) {
- context_.Say(designator.source,
- "List items specified in the ALLOCATE directive must not have the ALLOCATABLE attribute unless the directive is associated with an ALLOCATE statement"_err_en_US);
- }
- bool checkScope{ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective};
- // In 5.1 the scope check only applies to declarative allocate.
- if (version == 50 && !checkScope) {
- checkScope = ompFlag == Symbol::Flag::OmpExecutableAllocateDirective;
- }
- if (checkScope) {
- if (omp::GetScopingUnit(GetContext().scope) !=
- omp::GetScopingUnit(symbol->GetUltimate().owner())) {
- context_.Say(designator.source, // 2.15.3
- "List items must be declared in the same scoping unit in which the %s directive appears"_err_en_US,
- parser::ToUpperCaseLetters(
- llvm::omp::getOpenMPDirectiveName(directive, version)));
- }
- }
if (ompFlag == Symbol::Flag::OmpReduction) {
// Using variables inside of a namelist in OpenMP reductions
// is allowed by the standard, but is not allowed for