Skip to content

Commit e8e8b6a

Browse files
authored
[flang][OpenMP] Move ALLOCATE + privatize check to semantic checks (#192792)
Move the check from symbol resolution to semantic checks. The check now seems to be more accurate, catching some cases that were not detected before.
1 parent 1d6c9b8 commit e8e8b6a

7 files changed

Lines changed: 126 additions & 44 deletions

File tree

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,34 @@ template <typename T> constexpr auto addr_if(const std::optional<T> &x) {
3636
return x ? &*x : nullptr;
3737
}
3838

39+
namespace detail {
40+
struct DirectiveSpecificationScope {
41+
using ODS = OmpDirectiveSpecification;
42+
template <typename T> static const ODS &GetODS(const T &x) {
43+
if constexpr ( //
44+
std::is_base_of_v<OmpBlockConstruct, T> ||
45+
std::is_same_v<OpenMPLoopConstruct, T> ||
46+
std::is_same_v<OpenMPSectionsConstruct, T>) {
47+
return x.BeginDir();
48+
} else if constexpr (WrapperTrait<T>) {
49+
return GetODS(x.v);
50+
} else if constexpr (UnionTrait<T>) {
51+
return std::visit(
52+
[](auto &&s) -> decltype(auto) { return GetODS(s); }, x.u);
53+
} else {
54+
static_assert(std::is_same_v<OpenMPSectionConstruct, T>);
55+
llvm_unreachable("This function does not work for SECTION");
56+
}
57+
}
58+
static inline const ODS &GetODS(const ODS &x) { return x; }
59+
};
60+
} // namespace detail
61+
62+
const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
63+
const OpenMPConstruct &x);
64+
const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
65+
const OpenMPDeclarativeConstruct &x);
66+
3967
namespace detail {
4068
struct DirectiveNameScope {
4169
static OmpDirectiveName MakeName(CharBlock source = {},

flang/lib/Parser/openmp-utils.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@
2525

2626
namespace Fortran::parser::omp {
2727

28+
const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
29+
const OpenMPConstruct &x) {
30+
return std::visit(
31+
[](auto &&s) -> decltype(auto) {
32+
return detail::DirectiveSpecificationScope::GetODS(s);
33+
},
34+
x.u);
35+
}
36+
37+
const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
38+
const OpenMPDeclarativeConstruct &x) {
39+
return std::visit(
40+
[](auto &&s) -> decltype(auto) {
41+
return detail::DirectiveSpecificationScope::GetODS(s);
42+
},
43+
x.u);
44+
}
45+
2846
std::string GetUpperName(llvm::omp::Clause id, unsigned version) {
2947
llvm::StringRef name{llvm::omp::getOpenMPClauseName(id, version)};
3048
return parser::ToUpperCaseLetters(name);

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

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,16 @@ class OmpUnitedTaskDesignatorChecker {
336336
SemanticsContext &context_;
337337
};
338338

339+
bool OmpStructureChecker::IsAllowedClause(llvm::omp::Clause clauseId) {
340+
// Do not do clause checks while processing METADIRECTIVE.
341+
// See comment in CheckAllowedClause.
342+
if (GetDirectiveNest(ContextSelectorNest) > 0) {
343+
return true;
344+
}
345+
return llvm::omp::isAllowedClauseForDirective(
346+
GetContext().directive, clauseId, context_.langOptions().OpenMPVersion);
347+
}
348+
339349
bool OmpStructureChecker::CheckAllowedClause(llvmOmpClause clause) {
340350
// Do not do clause checks while processing METADIRECTIVE.
341351
// Context selectors can contain clauses that are not given as a part
@@ -772,6 +782,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
772782
return CheckDirectiveSpelling(source, id);
773783
});
774784
parser::Walk(x, visitor);
785+
if (GetOmpDirectiveName(x).v != llvm::omp::Directive::OMPD_section) {
786+
dirStack_.push_back(&GetOmpDirectiveSpecification(x));
787+
}
775788

776789
// Simd Construct with Ordered Construct Nesting check
777790
// We cannot use CurrentDirectiveIsNested() here because
@@ -788,12 +801,15 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
788801
}
789802
}
790803

791-
void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) {
804+
void OmpStructureChecker::Leave(const parser::OpenMPConstruct &x) {
792805
for (const auto &[sym, source] : deferredNonVariables_) {
793806
context_.SayWithDecl(
794807
*sym, source, "'%s' must be a variable"_err_en_US, sym->name());
795808
}
796809
deferredNonVariables_.clear();
810+
if (GetOmpDirectiveName(x).v != llvm::omp::Directive::OMPD_section) {
811+
dirStack_.pop_back();
812+
}
797813
}
798814

799815
void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
@@ -803,11 +819,13 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
803819
});
804820
parser::Walk(x, visitor);
805821

822+
dirStack_.push_back(&GetOmpDirectiveSpecification(x));
806823
EnterDirectiveNest(DeclarativeNest);
807824
}
808825

809826
void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeConstruct &x) {
810827
ExitDirectiveNest(DeclarativeNest);
828+
dirStack_.pop_back();
811829
}
812830

813831
void OmpStructureChecker::AddEndDirectiveClauses(
@@ -2010,6 +2028,50 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Allocate &x) {
20102028
}
20112029
}
20122030
}
2031+
2032+
// If the clause is not allowed, don't diagnose specific problems with it.
2033+
if (!IsAllowedClause(llvm::omp::Clause::OMPC_allocate)) {
2034+
return;
2035+
}
2036+
2037+
llvm::omp::Directive dirId{GetContext().directive};
2038+
// For any list item that is specified in the allocate clause on a directive
2039+
// other than the allocators directive, a data-sharing attribute clause that
2040+
// may create a private copy of that list item must be specified on the same
2041+
// directive.
2042+
if (dirId != llvm::omp::Directive::OMPD_allocators &&
2043+
dirId != llvm::omp::Directive::OMPD_section) {
2044+
static llvm::omp::Clause privatizingDSAClauses[] = {
2045+
llvm::omp::Clause::OMPC_private,
2046+
llvm::omp::Clause::OMPC_firstprivate,
2047+
llvm::omp::Clause::OMPC_lastprivate,
2048+
llvm::omp::Clause::OMPC_is_device_ptr,
2049+
llvm::omp::Clause::OMPC_use_device_ptr,
2050+
};
2051+
const parser::OmpDirectiveSpecification &spec{*dirStack_.back()};
2052+
2053+
std::set<const Symbol *> privatized;
2054+
for (auto dsaClause : privatizingDSAClauses) {
2055+
if (auto *found{parser::omp::FindClause(spec, dsaClause)}) {
2056+
for (auto &object : GetOmpObjectList(*found)->v) {
2057+
if (auto *symbol{GetObjectSymbol(object)}) {
2058+
privatized.insert(symbol);
2059+
}
2060+
}
2061+
}
2062+
}
2063+
2064+
for (auto &object : GetOmpObjectList(x)->v) {
2065+
if (auto *symbol{GetObjectSymbol(object)}) {
2066+
if (!privatized.count(symbol)) {
2067+
context_.Say(
2068+
GetObjectSource(object).value_or(GetContext().clauseSource),
2069+
"The ALLOCATE clause requires that '%s' must be listed in a private data-sharing attribute clause on the same directive"_err_en_US,
2070+
symbol->name());
2071+
}
2072+
}
2073+
}
2074+
}
20132075
}
20142076

20152077
void OmpStructureChecker::Enter(const parser::OpenMPDeclareMapperConstruct &x) {

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
9393

9494
void Enter(const parser::OpenMPConstruct &);
9595
void Leave(const parser::OpenMPConstruct &);
96-
void Enter(const parser::OpenMPInteropConstruct &);
97-
void Leave(const parser::OpenMPInteropConstruct &);
9896
void Enter(const parser::OpenMPDeclarativeConstruct &);
9997
void Leave(const parser::OpenMPDeclarativeConstruct &);
10098

@@ -112,6 +110,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
112110
void Leave(const parser::OpenMPAssumeConstruct &);
113111
void Enter(const parser::OpenMPDeclarativeAssumes &);
114112
void Leave(const parser::OpenMPDeclarativeAssumes &);
113+
void Enter(const parser::OpenMPInteropConstruct &);
114+
void Leave(const parser::OpenMPInteropConstruct &);
115115
void Enter(const parser::OmpBlockConstruct &);
116116
void Leave(const parser::OmpBlockConstruct &);
117117
void Leave(const parser::OmpBeginDirective &);
@@ -267,6 +267,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
267267
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
268268

269269
// check-omp-structure.cpp
270+
bool IsAllowedClause(llvm::omp::Clause clauseId);
270271
bool CheckAllowedClause(llvmOmpClause clause);
271272
void CheckVariableListItem(const SymbolSourceMap &symbols);
272273
void CheckDirectiveSpelling(
@@ -404,6 +405,10 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
404405
std::vector<LoopConstruct> loopStack_;
405406
// Scopes for scoping units.
406407
std::vector<const Scope *> scopeStack_;
408+
// Stack of directive specifications (except for SECTION).
409+
// This is to allow visitor functions to see all specified clauses, since
410+
// they are only recorded in DirContext as they are processed.
411+
std::vector<const parser::OmpDirectiveSpecification *> dirStack_;
407412

408413
enum class PartKind : int {
409414
// There are also other "parts", such as internal-subprogram-part, etc,

flang/lib/Semantics/resolve-directives.cpp

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
10821082
Symbol::Flags dataCopyingAttributeFlags{
10831083
Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate};
10841084

1085-
std::vector<const parser::Name *> allocateNames_; // on one directive
10861085
UnorderedSymbolSet privateDataSharingAttributeObjects_; // on one directive
10871086
UnorderedSymbolSet stmtFunctionExprSymbols_;
10881087
std::multimap<const parser::Label,
@@ -1101,11 +1100,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
11011100
};
11021101
std::vector<PartKind> partStack_;
11031102

1104-
void AddAllocateName(const parser::Name *&object) {
1105-
allocateNames_.push_back(object);
1106-
}
1107-
void ClearAllocateNames() { allocateNames_.clear(); }
1108-
11091103
void AddPrivateDataSharingAttributeObjects(SymbolRef object) {
11101104
privateDataSharingAttributeObjects_.insert(object);
11111105
}
@@ -2006,35 +2000,10 @@ bool OmpAttributeVisitor::Pre(const parser::OmpBlockConstruct &x) {
20062000
IssueNonConformanceWarning(dirId, dirSpec.source, 52);
20072001
ClearDataSharingAttributeObjects();
20082002
ClearPrivateDataSharingAttributeObjects();
2009-
ClearAllocateNames();
20102003
return true;
20112004
}
20122005

20132006
void OmpAttributeVisitor::Post(const parser::OmpBlockConstruct &x) {
2014-
const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
2015-
llvm::omp::Directive dirId{dirSpec.DirId()};
2016-
unsigned version{context_.langOptions().OpenMPVersion};
2017-
2018-
if (llvm::omp::isPrivatizingConstruct(dirId, version)) {
2019-
bool hasPrivate;
2020-
for (const auto *allocName : allocateNames_) {
2021-
hasPrivate = false;
2022-
for (auto privateObj : privateDataSharingAttributeObjects_) {
2023-
const Symbol &symbolPrivate{*privateObj};
2024-
if (allocName->source == symbolPrivate.name()) {
2025-
hasPrivate = true;
2026-
break;
2027-
}
2028-
}
2029-
if (!hasPrivate) {
2030-
context_.Say(allocName->source,
2031-
"The ALLOCATE clause requires that '%s' must be listed in a "
2032-
"private "
2033-
"data-sharing attribute clause on the same directive"_err_en_US,
2034-
allocName->ToString());
2035-
}
2036-
}
2037-
}
20382007
PopContext();
20392008
}
20402009

@@ -2931,10 +2900,6 @@ void OmpAttributeVisitor::ResolveOmpDesignator(
29312900
if (privateDataSharingAttributeFlags.test(ompFlag)) {
29322901
CheckObjectIsPrivatizable(*name, *symbol, ompFlag);
29332902
}
2934-
2935-
if (ompFlag == Symbol::Flag::OmpAllocate) {
2936-
AddAllocateName(name);
2937-
}
29382903
}
29392904
// Save the original symbol. For privatizing clauses, ensure enclosing
29402905
// constructs properly capture the variable.

flang/test/Lower/OpenMP/sections.f90

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
!CHECK: %[[CONST_1:.*]] = arith.constant 4 : i64
1313
!CHECK: %[[PRIVATE_ETA:.*]] = fir.alloca f32 {bindc_name = "eta", pinned, uniq_name = "_QFEeta"}
1414
!CHECK: %[[PRIVATE_ETA_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_ETA]] {uniq_name = "_QFEeta"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
15+
!CHECK: %[[PRIVATE_COUNT:.*]] = fir.alloca i32 {bindc_name = "count", pinned, uniq_name = "_QFEcount"}
16+
!CHECK: %[[PRIVATE_COUNT_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_COUNT]] {uniq_name = "_QFEcount"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
1517
!CHECK: %[[PRIVATE_DOUBLE_COUNT:.*]] = fir.alloca i32 {bindc_name = "double_count", pinned, uniq_name = "_QFEdouble_count"}
1618
!CHECK: %[[PRIVATE_DOUBLE_COUNT_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_DOUBLE_COUNT]] {uniq_name = "_QFEdouble_count"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
1719
!CHECK: omp.sections allocate(%[[CONST_1]] : i64 -> %[[COUNT_DECL]]#0 : !fir.ref<i32>) {
1820
!CHECK: omp.section {
1921
!CHECK: %[[CONST5:.*]] = arith.constant 5 : i32
20-
!CHECK: hlfir.assign %[[CONST5]] to %[[COUNT_DECL]]#0 : i32, !fir.ref<i32>
21-
!CHECK: %[[TEMP_COUNT:.*]] = fir.load %[[COUNT_DECL]]#0 : !fir.ref<i32>
22+
!CHECK: hlfir.assign %[[CONST5]] to %[[PRIVATE_COUNT_DECL]]#0 : i32, !fir.ref<i32>
23+
!CHECK: %[[TEMP_COUNT:.*]] = fir.load %[[PRIVATE_COUNT_DECL]]#0 : !fir.ref<i32>
2224
!CHECK: %[[TEMP_DOUBLE_COUNT:.*]] = fir.load %[[PRIVATE_DOUBLE_COUNT_DECL]]#0 : !fir.ref<i32>
2325
!CHECK: %[[RESULT:.*]] = arith.muli %[[TEMP_COUNT]], %[[TEMP_DOUBLE_COUNT]] : i32
2426
!CHECK: %[[RESULT_CONVERT:.*]] = fir.convert %[[RESULT]] : (i32) -> f32
@@ -37,13 +39,13 @@
3739
!CHECK: %[[CONST:.*]] = arith.constant 7.000000e+00 : f32
3840
!CHECK: %[[RESULT:.*]] = arith.subf %[[TEMP]], %[[CONST]] {{.*}}: f32
3941
!CHECK: hlfir.assign %[[RESULT]] to %[[PRIVATE_ETA_DECL]]#0 : f32, !fir.ref<f32>
40-
!CHECK: %[[TEMP_COUNT1:.*]] = fir.load %[[COUNT_DECL]]#0 : !fir.ref<i32>
42+
!CHECK: %[[TEMP_COUNT1:.*]] = fir.load %[[PRIVATE_COUNT_DECL]]#0 : !fir.ref<i32>
4143
!CHECK: %[[TEMP_COUNT:.*]] = fir.convert %[[TEMP_COUNT1]] : (i32) -> f32
4244
!CHECK: %[[TEMP_ETA:.*]] = fir.load %[[PRIVATE_ETA_DECL]]#0 : !fir.ref<f32>
4345
!CHECK: %[[TEMP_COUNT2:.*]] = arith.mulf %[[TEMP_COUNT]], %[[TEMP_ETA]] {{.*}}: f32
4446
!CHECK: %[[RESULT:.*]] = fir.convert %[[TEMP_COUNT2]] : (f32) -> i32
45-
!CHECK: hlfir.assign %[[RESULT]] to %[[COUNT_DECL]]#0 : i32, !fir.ref<i32>
46-
!CHECK: %[[TEMP_COUNT3:.*]] = fir.load %[[COUNT_DECL]]#0 : !fir.ref<i32>
47+
!CHECK: hlfir.assign %[[RESULT]] to %[[PRIVATE_COUNT_DECL]]#0 : i32, !fir.ref<i32>
48+
!CHECK: %[[TEMP_COUNT3:.*]] = fir.load %[[PRIVATE_COUNT_DECL]]#0 : !fir.ref<i32>
4749
!CHECK: %[[TEMP_COUNT4:.*]] = fir.convert %[[TEMP_COUNT3]] : (i32) -> f32
4850
!CHECK: %[[TEMP_ETA:.*]] = fir.load %[[PRIVATE_ETA_DECL]]#0 : !fir.ref<f32>
4951
!CHECK: %[[TEMP_COUNT5:.*]] = arith.subf %[[TEMP_COUNT4]], %[[TEMP_ETA]] {{.*}}: f32
@@ -62,7 +64,7 @@
6264
program sample
6365
use omp_lib
6466
integer :: count = 0, double_count = 1
65-
!$omp sections private (eta, double_count) allocate(omp_high_bw_mem_alloc: count)
67+
!$omp sections private (eta, count, double_count) allocate(omp_high_bw_mem_alloc: count)
6668
!$omp section
6769
count = 1 + 4
6870
eta = count * double_count

flang/test/Semantics/OpenMP/taskgroup01.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
!$omp end parallel
2222

2323
!$omp parallel private(xyz)
24+
!ERROR: The ALLOCATE clause requires that 'xyz' must be listed in a private data-sharing attribute clause on the same directive
2425
!$omp taskgroup allocate(xyz)
2526
!$omp task
2627
print *, "The "
28+
!ERROR: The ALLOCATE clause requires that 'abc' must be listed in a private data-sharing attribute clause on the same directive
2729
!$omp taskgroup allocate(omp_large_cap_mem_space: abc)
2830
!$omp task
2931
print *, "almighty sun"

0 commit comments

Comments
 (0)