Skip to content

Commit fc9b35d

Browse files
committed
[flang][OpenMP] Refactor/update semantic checks for ALLOCATE directive
OpenMP 5.0 and 5.1 allowed the ALLOCATE directive to appear in two forms, declarative and executable. The syntax of an individual directive was the same in both cases, but the semantic restrictions were slightly different. - Update the semantic checks to reflect the different restrictions, gather them in a single function. - Improve test for the presence of a TARGET region, add a check for REQUIRES directive. - Update tests.
1 parent 6acfa6d commit fc9b35d

File tree

13 files changed

+173
-120
lines changed

13 files changed

+173
-120
lines changed

flang/include/flang/Semantics/openmp-utils.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
#ifndef FORTRAN_SEMANTICS_OPENMP_UTILS_H
1414
#define FORTRAN_SEMANTICS_OPENMP_UTILS_H
1515

16+
#include "flang/Common/indirection.h"
1617
#include "flang/Evaluate/type.h"
1718
#include "flang/Parser/char-block.h"
1819
#include "flang/Parser/parse-tree.h"
20+
#include "flang/Parser/tools.h"
1921
#include "flang/Semantics/tools.h"
2022

2123
#include "llvm/ADT/ArrayRef.h"
@@ -74,7 +76,11 @@ bool IsVarOrFunctionRef(const MaybeExpr &expr);
7476
bool IsMapEnteringType(parser::OmpMapType::Value type);
7577
bool IsMapExitingType(parser::OmpMapType::Value type);
7678

77-
std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr);
79+
MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr);
80+
template <typename T> MaybeExpr GetEvaluateExpr(const T &inp) {
81+
return GetEvaluateExpr(parser::UnwrapRef<parser::Expr>(inp));
82+
}
83+
7884
std::optional<evaluate::DynamicType> GetDynamicType(
7985
const parser::Expr &parserExpr);
8086

flang/lib/Semantics/check-omp-structure.cpp

Lines changed: 131 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,36 @@ bool OmpStructureChecker::IsNestedInDirective(llvm::omp::Directive directive) {
480480
return false;
481481
}
482482

483+
bool OmpStructureChecker::InTargetRegion() {
484+
if (IsNestedInDirective(llvm::omp::Directive::OMPD_target)) {
485+
// Return true even for device_type(host).
486+
return true;
487+
}
488+
for (const Scope *scope : llvm::reverse(scopeStack_)) {
489+
if (const auto *symbol{scope->symbol()}) {
490+
if (symbol->test(Symbol::Flag::OmpDeclareTarget)) {
491+
return true;
492+
}
493+
}
494+
}
495+
return false;
496+
}
497+
498+
bool OmpStructureChecker::HasRequires(llvm::omp::Clause req) {
499+
const Scope &unit{GetProgramUnit(*scopeStack_.back())};
500+
return common::visit(
501+
[&](const auto &details) {
502+
if constexpr (std::is_convertible_v<decltype(details),
503+
const WithOmpDeclarative &>) {
504+
if (auto *reqs{details.ompRequires()}) {
505+
return reqs->test(req);
506+
}
507+
}
508+
return false;
509+
},
510+
DEREF(unit.symbol()).details());
511+
}
512+
483513
void OmpStructureChecker::CheckVariableListItem(
484514
const SymbolSourceMap &symbols) {
485515
for (auto &[symbol, source] : symbols) {
@@ -1680,40 +1710,92 @@ void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {
16801710
dirContext_.pop_back();
16811711
}
16821712

1683-
void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
1684-
isPredefinedAllocator = true;
1685-
const auto &dir{std::get<parser::Verbatim>(x.t)};
1686-
const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
1687-
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
1688-
SymbolSourceMap currSymbols;
1689-
GetSymbolsInObjectList(objectList, currSymbols);
1690-
for (auto &[symbol, source] : currSymbols) {
1691-
if (IsPointer(*symbol)) {
1713+
void OmpStructureChecker::CheckAllocateDirective(parser::CharBlock source,
1714+
const parser::OmpObjectList &objects,
1715+
const parser::OmpClauseList &clauses) {
1716+
const Scope &thisScope{context_.FindScope(source)};
1717+
SymbolSourceMap symbols;
1718+
GetSymbolsInObjectList(objects, symbols);
1719+
1720+
auto hasPredefinedAllocator{[&](const parser::OmpClause *c) {
1721+
if (!c) {
1722+
return std::make_optional(false);
1723+
}
1724+
auto *allocator{std::get_if<parser::OmpClause::Allocator>(&c->u)};
1725+
if (auto val{ToInt64(GetEvaluateExpr(allocator->v))}) {
1726+
// Predefined allocators:
1727+
// omp_null_allocator = 0,
1728+
// omp_default_mem_alloc = 1,
1729+
// omp_large_cap_mem_alloc = 2,
1730+
// omp_const_mem_alloc = 3,
1731+
// omp_high_bw_mem_alloc = 4,
1732+
// omp_low_lat_mem_alloc = 5,
1733+
// omp_cgroup_mem_alloc = 6,
1734+
// omp_pteam_mem_alloc = 7,
1735+
// omp_thread_mem_alloc = 8
1736+
return std::make_optional(*val >= 0 && *val <= 8);
1737+
}
1738+
return std::optional<bool>{};
1739+
}};
1740+
1741+
const auto *allocator{FindClause(llvm::omp::Clause::OMPC_allocator)};
1742+
if (InTargetRegion()) {
1743+
bool hasDynAllocators{
1744+
HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)};
1745+
if (!allocator && !hasDynAllocators) {
16921746
context_.Say(source,
1693-
"List item '%s' in ALLOCATE directive must not have POINTER "
1694-
"attribute"_err_en_US,
1695-
source.ToString());
1747+
"An ALLOCATE directive in a TARGET region must specify an ALLOCATOR clause or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US);
16961748
}
1697-
if (IsDummy(*symbol)) {
1698-
context_.Say(source,
1699-
"List item '%s' in ALLOCATE directive must not be a dummy "
1700-
"argument"_err_en_US,
1701-
source.ToString());
1749+
}
1750+
1751+
bool isPredefined{hasPredefinedAllocator(allocator).value_or(false)};
1752+
1753+
for (auto &[symbol, source] : symbols) {
1754+
if (!inExecutableAllocate_) {
1755+
if (symbol->owner() != thisScope) {
1756+
context_.Say(source,
1757+
"A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears"_err_en_US);
1758+
}
1759+
if (IsPointer(*symbol) || IsAllocatable(*symbol)) {
1760+
context_.Say(source,
1761+
"A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute"_err_en_US);
1762+
}
17021763
}
17031764
if (symbol->GetUltimate().has<AssocEntityDetails>()) {
17041765
context_.Say(source,
1705-
"List item '%s' in ALLOCATE directive must not be an associate "
1706-
"name"_err_en_US,
1707-
source.ToString());
1766+
"A list item in a declarative ALLOCATE cannot be an associate name"_err_en_US);
1767+
}
1768+
if (symbol->attrs().test(Attr::SAVE) || IsCommonBlock(*symbol)) {
1769+
if (!allocator) {
1770+
context_.Say(source,
1771+
"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);
1772+
} else if (!isPredefined) {
1773+
context_.Say(source,
1774+
"If a list item is a named common block or has SAVE attribute, only a predefined allocator may be used on the ALLOCATOR clause"_warn_en_US);
1775+
}
1776+
}
1777+
if (FindCommonBlockContaining(*symbol)) {
1778+
context_.Say(source,
1779+
"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);
17081780
}
17091781
}
1710-
CheckVarIsNotPartOfAnotherVar(dir.source, objectList);
1782+
CheckVarIsNotPartOfAnotherVar(source, objects);
17111783
}
17121784

1713-
void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
1785+
void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
17141786
const auto &dir{std::get<parser::Verbatim>(x.t)};
1715-
const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
1716-
CheckPredefinedAllocatorRestriction(dir.source, objectList);
1787+
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
1788+
}
1789+
1790+
void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
1791+
if (!inExecutableAllocate_) {
1792+
const auto &dir{std::get<parser::Verbatim>(x.t)};
1793+
const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
1794+
const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
1795+
1796+
isPredefinedAllocator = true;
1797+
CheckAllocateDirective(dir.source, objectList, clauseList);
1798+
}
17171799
dirContext_.pop_back();
17181800
}
17191801

@@ -2069,6 +2151,7 @@ void OmpStructureChecker::CheckNameInAllocateStmt(
20692151
}
20702152

20712153
void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
2154+
inExecutableAllocate_ = true;
20722155
const auto &dir{std::get<parser::Verbatim>(x.t)};
20732156
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
20742157

@@ -2078,24 +2161,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
20782161
"The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);
20792162
}
20802163

2081-
bool hasAllocator = false;
2082-
// TODO: Investigate whether searching the clause list can be done with
2083-
// parser::Unwrap instead of the following loop
2084-
const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
2085-
for (const auto &clause : clauseList.v) {
2086-
if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) {
2087-
hasAllocator = true;
2088-
}
2089-
}
2090-
2091-
if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) {
2092-
// TODO: expand this check to exclude the case when a requires
2093-
// directive with the dynamic_allocators clause is present
2094-
// in the same compilation unit (OMP5.0 2.11.3).
2095-
context_.Say(x.source,
2096-
"ALLOCATE directives that appear in a TARGET region must specify an allocator clause"_err_en_US);
2097-
}
2098-
20992164
const auto &allocateStmt =
21002165
std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement;
21012166
if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
@@ -2112,18 +2177,34 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
21122177
}
21132178

21142179
isPredefinedAllocator = true;
2115-
const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
2116-
if (objectList) {
2117-
CheckVarIsNotPartOfAnotherVar(dir.source, *objectList);
2118-
}
21192180
}
21202181

21212182
void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) {
2122-
const auto &dir{std::get<parser::Verbatim>(x.t)};
2123-
const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
2124-
if (objectList)
2125-
CheckPredefinedAllocatorRestriction(dir.source, *objectList);
2183+
parser::OmpObjectList empty{std::list<parser::OmpObject>{}};
2184+
auto &objects{[&]() -> const parser::OmpObjectList & {
2185+
if (auto &objects{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
2186+
return *objects;
2187+
} else {
2188+
return empty;
2189+
}
2190+
}()};
2191+
auto &clauses{std::get<parser::OmpClauseList>(x.t)};
2192+
CheckAllocateDirective(
2193+
std::get<parser::Verbatim>(x.t).source, objects, clauses);
2194+
2195+
if (const auto &subDirs{
2196+
std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
2197+
x.t)}) {
2198+
for (const auto &dalloc : *subDirs) {
2199+
const auto &dir{std::get<parser::Verbatim>(x.t)};
2200+
const auto &clauses{std::get<parser::OmpClauseList>(dalloc.t)};
2201+
const auto &objects{std::get<parser::OmpObjectList>(dalloc.t)};
2202+
CheckAllocateDirective(dir.source, objects, clauses);
2203+
}
2204+
}
2205+
21262206
dirContext_.pop_back();
2207+
inExecutableAllocate_ = false;
21272208
}
21282209

21292210
void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {

flang/lib/Semantics/check-omp-structure.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,12 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
189189
const parser::CharBlock &, const OmpDirectiveSet &);
190190
bool IsCloselyNestedRegion(const OmpDirectiveSet &set);
191191
bool IsNestedInDirective(llvm::omp::Directive directive);
192+
bool InTargetRegion();
192193
void HasInvalidTeamsNesting(
193194
const llvm::omp::Directive &dir, const parser::CharBlock &source);
194195
void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
195196
void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
197+
bool HasRequires(llvm::omp::Clause req);
196198
// specific clause related
197199
void CheckAllowedMapTypes(
198200
parser::OmpMapType::Value, llvm::ArrayRef<parser::OmpMapType::Value>);
@@ -262,6 +264,9 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
262264
bool CheckTargetBlockOnlyTeams(const parser::Block &);
263265
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
264266
void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
267+
void CheckAllocateDirective(parser::CharBlock source,
268+
const parser::OmpObjectList &objects,
269+
const parser::OmpClauseList &clauses);
265270

266271
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
267272
void CheckIteratorModifier(const parser::OmpIterator &x);
@@ -379,6 +384,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
379384
};
380385
int directiveNest_[LastType + 1] = {0};
381386

387+
bool inExecutableAllocate_{false};
382388
parser::CharBlock visitedAtomicSource_;
383389
SymbolSourceMap deferredNonVariables_;
384390

flang/lib/Semantics/openmp-utils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ bool IsMapExitingType(parser::OmpMapType::Value type) {
218218
}
219219
}
220220

221-
std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr) {
221+
MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr) {
222222
const parser::TypedExpr &typedExpr{parserExpr.typedExpr};
223223
// ForwardOwningPointer typedExpr
224224
// `- GenericExprWrapper ^.get()

flang/lib/Semantics/resolve-directives.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3107,26 +3107,6 @@ void OmpAttributeVisitor::ResolveOmpDesignator(
31073107
AddAllocateName(name);
31083108
}
31093109
}
3110-
if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective &&
3111-
IsAllocatable(*symbol) &&
3112-
!IsNestedInDirective(llvm::omp::Directive::OMPD_allocate)) {
3113-
context_.Say(designator.source,
3114-
"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);
3115-
}
3116-
bool checkScope{ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective};
3117-
// In 5.1 the scope check only applies to declarative allocate.
3118-
if (version == 50 && !checkScope) {
3119-
checkScope = ompFlag == Symbol::Flag::OmpExecutableAllocateDirective;
3120-
}
3121-
if (checkScope) {
3122-
if (omp::GetScopingUnit(GetContext().scope) !=
3123-
omp::GetScopingUnit(symbol->GetUltimate().owner())) {
3124-
context_.Say(designator.source, // 2.15.3
3125-
"List items must be declared in the same scoping unit in which the %s directive appears"_err_en_US,
3126-
parser::ToUpperCaseLetters(
3127-
llvm::omp::getOpenMPDirectiveName(directive, version)));
3128-
}
3129-
}
31303110
if (ompFlag == Symbol::Flag::OmpReduction) {
31313111
// Using variables inside of a namelist in OpenMP reductions
31323112
// is allowed by the standard, but is not allowed for

flang/test/Semantics/OpenMP/allocate01.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ subroutine sema()
1515
integer :: a, b
1616
real, dimension (:,:), allocatable :: darray
1717

18-
!ERROR: List items must be declared in the same scoping unit in which the ALLOCATE directive appears
18+
!ERROR: A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears
1919
!$omp allocate(y)
2020
print *, a
2121

flang/test/Semantics/OpenMP/allocate04.f90

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@ subroutine allocate(z)
1414
type(c_ptr), pointer :: p
1515
integer :: x, y, z
1616

17-
associate (a => x)
18-
!$omp allocate(x) allocator(omp_default_mem_alloc)
19-
2017
!ERROR: PRIVATE clause is not allowed on the ALLOCATE directive
2118
!$omp allocate(y) private(y)
22-
!ERROR: List item 'z' in ALLOCATE directive must not be a dummy argument
23-
!$omp allocate(z)
24-
!ERROR: List item 'p' in ALLOCATE directive must not have POINTER attribute
19+
!ERROR: A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute
2520
!$omp allocate(p)
26-
!ERROR: List item 'a' in ALLOCATE directive must not be an associate name
21+
22+
associate (a => x)
23+
block
24+
!ERROR: A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears
25+
!$omp allocate(x) allocator(omp_default_mem_alloc)
26+
27+
!ERROR: A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears
28+
!ERROR: A list item in a declarative ALLOCATE cannot be an associate name
2729
!$omp allocate(a)
30+
end block
2831
end associate
2932
end subroutine allocate

flang/test/Semantics/OpenMP/allocate05.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ subroutine allocate()
1818
!$omp end target
1919

2020
!$omp target
21-
!ERROR: ALLOCATE directives that appear in a TARGET region must specify an allocator clause
21+
!ERROR: An ALLOCATE directive in a TARGET region must specify an ALLOCATOR clause or REQUIRES(DYNAMIC_ALLOCATORS) must be specified
2222
!$omp allocate
2323
allocate ( darray(a, b) )
2424
!$omp end target

flang/test/Semantics/OpenMP/allocate06.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ subroutine allocate()
1111
integer :: a, b, x
1212
real, dimension (:,:), allocatable :: darray
1313

14-
!ERROR: List items specified in the ALLOCATE directive must not have the ALLOCATABLE attribute unless the directive is associated with an ALLOCATE statement
14+
!ERROR: A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute
1515
!$omp allocate(darray) allocator(omp_default_mem_alloc)
1616

1717
!$omp allocate(darray) allocator(omp_default_mem_alloc)

0 commit comments

Comments
 (0)