Skip to content

Commit 322dd63

Browse files
authored
[flang][OpenMP] Refactor/update semantic checks for ALLOCATE directive (#164420)
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 e6af0a4 commit 322dd63

File tree

13 files changed

+175
-120
lines changed

13 files changed

+175
-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: 134 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,95 @@ 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)) {
1692-
context_.Say(source,
1693-
"List item '%s' in ALLOCATE directive must not have POINTER "
1694-
"attribute"_err_en_US,
1695-
source.ToString());
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 maybeHasPredefinedAllocator{[&](const parser::OmpClause *calloc) {
1721+
// Return "true" if the ALLOCATOR clause was provided with an argument
1722+
// that is either a prefdefined allocator, or a run-time value.
1723+
// Otherwise return "false".
1724+
if (!calloc) {
1725+
return false;
1726+
}
1727+
auto *allocator{std::get_if<parser::OmpClause::Allocator>(&calloc->u)};
1728+
if (auto val{ToInt64(GetEvaluateExpr(DEREF(allocator).v))}) {
1729+
// Predefined allocators (defined in OpenMP 6.0 20.8.1):
1730+
// omp_null_allocator = 0,
1731+
// omp_default_mem_alloc = 1,
1732+
// omp_large_cap_mem_alloc = 2,
1733+
// omp_const_mem_alloc = 3,
1734+
// omp_high_bw_mem_alloc = 4,
1735+
// omp_low_lat_mem_alloc = 5,
1736+
// omp_cgroup_mem_alloc = 6,
1737+
// omp_pteam_mem_alloc = 7,
1738+
// omp_thread_mem_alloc = 8
1739+
return *val >= 0 && *val <= 8;
16961740
}
1697-
if (IsDummy(*symbol)) {
1741+
return true;
1742+
}};
1743+
1744+
const auto *allocator{FindClause(llvm::omp::Clause::OMPC_allocator)};
1745+
if (InTargetRegion()) {
1746+
bool hasDynAllocators{
1747+
HasRequires(llvm::omp::Clause::OMPC_dynamic_allocators)};
1748+
if (!allocator && !hasDynAllocators) {
16981749
context_.Say(source,
1699-
"List item '%s' in ALLOCATE directive must not be a dummy "
1700-
"argument"_err_en_US,
1701-
source.ToString());
1750+
"An ALLOCATE directive in a TARGET region must specify an ALLOCATOR clause or REQUIRES(DYNAMIC_ALLOCATORS) must be specified"_err_en_US);
1751+
}
1752+
}
1753+
1754+
auto maybePredefined{maybeHasPredefinedAllocator(allocator)};
1755+
1756+
for (auto &[symbol, source] : symbols) {
1757+
if (!inExecutableAllocate_) {
1758+
if (symbol->owner() != thisScope) {
1759+
context_.Say(source,
1760+
"A list item on a declarative ALLOCATE must be declared in the same scope in which the directive appears"_err_en_US);
1761+
}
1762+
if (IsPointer(*symbol) || IsAllocatable(*symbol)) {
1763+
context_.Say(source,
1764+
"A list item in a declarative ALLOCATE cannot have the ALLOCATABLE or POINTER attribute"_err_en_US);
1765+
}
17021766
}
17031767
if (symbol->GetUltimate().has<AssocEntityDetails>()) {
17041768
context_.Say(source,
1705-
"List item '%s' in ALLOCATE directive must not be an associate "
1706-
"name"_err_en_US,
1707-
source.ToString());
1769+
"A list item in a declarative ALLOCATE cannot be an associate name"_err_en_US);
1770+
}
1771+
if (symbol->attrs().test(Attr::SAVE) || IsCommonBlock(*symbol)) {
1772+
if (!allocator) {
1773+
context_.Say(source,
1774+
"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);
1775+
} else if (!maybePredefined) {
1776+
context_.Say(source,
1777+
"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);
1778+
}
1779+
}
1780+
if (FindCommonBlockContaining(*symbol)) {
1781+
context_.Say(source,
1782+
"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);
17081783
}
17091784
}
1710-
CheckVarIsNotPartOfAnotherVar(dir.source, objectList);
1785+
CheckVarIsNotPartOfAnotherVar(source, objects);
17111786
}
17121787

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

@@ -2069,6 +2154,7 @@ void OmpStructureChecker::CheckNameInAllocateStmt(
20692154
}
20702155

20712156
void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
2157+
inExecutableAllocate_ = true;
20722158
const auto &dir{std::get<parser::Verbatim>(x.t)};
20732159
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
20742160

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

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-
20992167
const auto &allocateStmt =
21002168
std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement;
21012169
if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
@@ -2112,18 +2180,34 @@ void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
21122180
}
21132181

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

21212185
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);
2186+
parser::OmpObjectList empty{std::list<parser::OmpObject>{}};
2187+
auto &objects{[&]() -> const parser::OmpObjectList & {
2188+
if (auto &objects{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
2189+
return *objects;
2190+
} else {
2191+
return empty;
2192+
}
2193+
}()};
2194+
auto &clauses{std::get<parser::OmpClauseList>(x.t)};
2195+
CheckAllocateDirective(
2196+
std::get<parser::Verbatim>(x.t).source, objects, clauses);
2197+
2198+
if (const auto &subDirs{
2199+
std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
2200+
x.t)}) {
2201+
for (const auto &dalloc : *subDirs) {
2202+
const auto &dir{std::get<parser::Verbatim>(x.t)};
2203+
const auto &clauses{std::get<parser::OmpClauseList>(dalloc.t)};
2204+
const auto &objects{std::get<parser::OmpObjectList>(dalloc.t)};
2205+
CheckAllocateDirective(dir.source, objects, clauses);
2206+
}
2207+
}
2208+
21262209
dirContext_.pop_back();
2210+
inExecutableAllocate_ = false;
21272211
}
21282212

21292213
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
@@ -188,10 +188,12 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
188188
const parser::CharBlock &, const OmpDirectiveSet &);
189189
bool IsCloselyNestedRegion(const OmpDirectiveSet &set);
190190
bool IsNestedInDirective(llvm::omp::Directive directive);
191+
bool InTargetRegion();
191192
void HasInvalidTeamsNesting(
192193
const llvm::omp::Directive &dir, const parser::CharBlock &source);
193194
void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
194195
void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
196+
bool HasRequires(llvm::omp::Clause req);
195197
// specific clause related
196198
void CheckAllowedMapTypes(
197199
parser::OmpMapType::Value, llvm::ArrayRef<parser::OmpMapType::Value>);
@@ -261,6 +263,9 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
261263
bool CheckTargetBlockOnlyTeams(const parser::Block &);
262264
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
263265
void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
266+
void CheckAllocateDirective(parser::CharBlock source,
267+
const parser::OmpObjectList &objects,
268+
const parser::OmpClauseList &clauses);
264269

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

386+
bool inExecutableAllocate_{false};
381387
parser::CharBlock visitedAtomicSource_;
382388
SymbolSourceMap deferredNonVariables_;
383389

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
@@ -3094,26 +3094,6 @@ void OmpAttributeVisitor::ResolveOmpDesignator(
30943094
AddAllocateName(name);
30953095
}
30963096
}
3097-
if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective &&
3098-
IsAllocatable(*symbol) &&
3099-
!IsNestedInDirective(llvm::omp::Directive::OMPD_allocate)) {
3100-
context_.Say(designator.source,
3101-
"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);
3102-
}
3103-
bool checkScope{ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective};
3104-
// In 5.1 the scope check only applies to declarative allocate.
3105-
if (version == 50 && !checkScope) {
3106-
checkScope = ompFlag == Symbol::Flag::OmpExecutableAllocateDirective;
3107-
}
3108-
if (checkScope) {
3109-
if (omp::GetScopingUnit(GetContext().scope) !=
3110-
omp::GetScopingUnit(symbol->GetUltimate().owner())) {
3111-
context_.Say(designator.source, // 2.15.3
3112-
"List items must be declared in the same scoping unit in which the %s directive appears"_err_en_US,
3113-
parser::ToUpperCaseLetters(
3114-
llvm::omp::getOpenMPDirectiveName(directive, version)));
3115-
}
3116-
}
31173097
if (ompFlag == Symbol::Flag::OmpReduction) {
31183098
// Using variables inside of a namelist in OpenMP reductions
31193099
// 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)