Skip to content

Commit 89d79b6

Browse files
authored
[flang][OpenMP] Semantic checks for TASKGRAPH (#160115)
This verifies the "structural" restrictions on constructs encountered in a TASKGRAPH construct. There are also restrictions that apply to list items, specifically in the following contexts: - a list item on a clause on a replayable construct, - data-sharing attributes for a variable on a replayable construct. These restrictions are not verified, because that would require knowing which clauses (on a potential compound directive) apply to the task- generating construct of interest. This information is not available during semantic checks.
1 parent 87e1da3 commit 89d79b6

File tree

12 files changed

+399
-19
lines changed

12 files changed

+399
-19
lines changed

flang/include/flang/Parser/parse-tree.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4442,15 +4442,15 @@ struct OmpGrainsizeClause {
44424442
// graph_id-clause ->
44434443
// GRAPH_ID(graph-id-value) // since 6.0
44444444
struct OmpGraphIdClause {
4445-
WRAPPER_CLASS_BOILERPLATE(OmpGraphIdClause, common::Indirection<Expr>);
4445+
WRAPPER_CLASS_BOILERPLATE(OmpGraphIdClause, ScalarIntExpr);
44464446
};
44474447

44484448
// Ref: [6.0:438-439]
44494449
//
44504450
// graph_reset-clause ->
44514451
// GRAPH_RESET[(graph-reset-expression)] // since 6.0
44524452
struct OmpGraphResetClause {
4453-
WRAPPER_CLASS_BOILERPLATE(OmpGraphResetClause, common::Indirection<Expr>);
4453+
WRAPPER_CLASS_BOILERPLATE(OmpGraphResetClause, ScalarLogicalExpr);
44544454
};
44554455

44564456
// Ref: [5.0:234-242], [5.1:266-275], [5.2:299], [6.0:472-473]

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr);
7575
std::optional<evaluate::DynamicType> GetDynamicType(
7676
const parser::Expr &parserExpr);
7777

78+
std::optional<bool> GetLogicalValue(const SomeExpr &expr);
79+
7880
std::optional<bool> IsContiguous(
7981
SemanticsContext &semaCtx, const parser::OmpObject &object);
8082

flang/lib/Lower/OpenMP/Clauses.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,6 @@ MAKE_EMPTY_CLASS(Capture, Capture);
221221
MAKE_EMPTY_CLASS(Compare, Compare);
222222
MAKE_EMPTY_CLASS(DynamicAllocators, DynamicAllocators);
223223
MAKE_EMPTY_CLASS(Full, Full);
224-
MAKE_EMPTY_CLASS(GraphId, GraphId);
225-
MAKE_EMPTY_CLASS(GraphReset, GraphReset);
226224
MAKE_EMPTY_CLASS(Inbranch, Inbranch);
227225
MAKE_EMPTY_CLASS(Mergeable, Mergeable);
228226
MAKE_EMPTY_CLASS(Nogroup, Nogroup);
@@ -258,6 +256,8 @@ MAKE_EMPTY_CLASS(Groupprivate, Groupprivate);
258256

259257
MAKE_INCOMPLETE_CLASS(AdjustArgs, AdjustArgs);
260258
MAKE_INCOMPLETE_CLASS(AppendArgs, AppendArgs);
259+
MAKE_INCOMPLETE_CLASS(GraphId, GraphId);
260+
MAKE_INCOMPLETE_CLASS(GraphReset, GraphReset);
261261
MAKE_INCOMPLETE_CLASS(Replayable, Replayable);
262262
MAKE_INCOMPLETE_CLASS(Transparent, Transparent);
263263

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -824,9 +824,9 @@ TYPE_PARSER(construct<OmpFailClause>(
824824
"RELEASE" >> pure(common::OmpMemoryOrderType::Release) ||
825825
"SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst)))
826826

827-
TYPE_PARSER(construct<OmpGraphIdClause>(expr))
827+
TYPE_PARSER(construct<OmpGraphIdClause>(scalarIntExpr))
828828

829-
TYPE_PARSER(construct<OmpGraphResetClause>(expr))
829+
TYPE_PARSER(construct<OmpGraphResetClause>(scalarLogicalExpr))
830830

831831
// 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD)
832832
TYPE_PARSER(construct<OmpProcBindClause>(

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

Lines changed: 192 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "flang/Common/idioms.h"
1616
#include "flang/Common/indirection.h"
1717
#include "flang/Common/visit.h"
18+
#include "flang/Evaluate/fold.h"
1819
#include "flang/Evaluate/tools.h"
1920
#include "flang/Evaluate/type.h"
2021
#include "flang/Parser/char-block.h"
@@ -1046,7 +1047,10 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAssumes &) {
10461047
dirContext_.pop_back();
10471048
}
10481049

1049-
void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &) {
1050+
void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &x) {
1051+
if (GetContext().directive == llvm::omp::Directive::OMPD_taskgraph) {
1052+
CheckTaskgraph(x);
1053+
}
10501054
if (GetDirectiveNest(TargetBlockOnlyTeams)) {
10511055
ExitDirectiveNest(TargetBlockOnlyTeams);
10521056
}
@@ -2036,6 +2040,193 @@ void OmpStructureChecker::CheckTargetUpdate() {
20362040
}
20372041
}
20382042

2043+
namespace {
2044+
struct TaskgraphVisitor {
2045+
TaskgraphVisitor(SemanticsContext &context) : context_(context) {}
2046+
2047+
template <typename T> bool Pre(const T &) { return true; }
2048+
template <typename T> void Post(const T &) {}
2049+
2050+
bool Pre(const parser::OpenMPConstruct &x) {
2051+
parser::OmpDirectiveName name{GetOmpDirectiveName(x)};
2052+
llvm::ArrayRef<llvm::omp::Directive> leafs{getLeafConstructsOrSelf(name.v)};
2053+
2054+
if (!IsTaskGenerating(leafs)) {
2055+
context_.Say(name.source,
2056+
"Only task-generating constructs are allowed inside TASKGRAPH region"_err_en_US);
2057+
// Only visit top-level constructs.
2058+
return false;
2059+
}
2060+
2061+
const parser::OmpDirectiveSpecification &dirSpec{GetDirSpec(x)};
2062+
2063+
// Most restrictions apply to replayable constructs. All constructs are
2064+
// replayable unless REPLAYABLE(false) is present.
2065+
bool isReplayable{IsReplayable(dirSpec)};
2066+
const parser::OmpClause *nogroup{nullptr};
2067+
2068+
for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
2069+
switch (clause.Id()) {
2070+
case llvm::omp::Clause::OMPC_transparent:
2071+
if (isReplayable) {
2072+
CheckTransparent(clause);
2073+
}
2074+
break;
2075+
case llvm::omp::Clause::OMPC_detach:
2076+
if (isReplayable) {
2077+
context_.Say(clause.source,
2078+
"Detachable replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
2079+
}
2080+
break;
2081+
case llvm::omp::Clause::OMPC_if:
2082+
if (isReplayable) {
2083+
CheckIf(clause, leafs);
2084+
}
2085+
break;
2086+
case llvm::omp::Clause::OMPC_nogroup:
2087+
nogroup = &clause;
2088+
break;
2089+
default:
2090+
break;
2091+
}
2092+
}
2093+
2094+
unsigned version{context_.langOptions().OpenMPVersion};
2095+
bool allowsNogroup{llvm::omp::isAllowedClauseForDirective(
2096+
leafs[0], llvm::omp::Clause::OMPC_nogroup, version)};
2097+
2098+
if (allowsNogroup) {
2099+
if (!nogroup) {
2100+
context_.Say(dirSpec.source,
2101+
"The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP"_err_en_US);
2102+
}
2103+
}
2104+
2105+
// Only visit top-level constructs.
2106+
return false;
2107+
}
2108+
2109+
private:
2110+
const parser::OmpDirectiveSpecification &GetDirSpec(
2111+
const parser::OpenMPConstruct &x) const {
2112+
return common::visit(
2113+
common::visitors{
2114+
[&](const parser::OmpBlockConstruct &y)
2115+
-> const parser::OmpDirectiveSpecification & {
2116+
return y.BeginDir();
2117+
},
2118+
[&](const parser::OpenMPLoopConstruct &y)
2119+
-> const parser::OmpDirectiveSpecification & {
2120+
return y.BeginDir();
2121+
},
2122+
[&](const parser::OpenMPStandaloneConstruct &y)
2123+
-> const parser::OmpDirectiveSpecification & {
2124+
return std::get<parser::OpenMPSimpleStandaloneConstruct>(y.u).v;
2125+
},
2126+
[&](const auto &) -> const parser::OmpDirectiveSpecification & {
2127+
llvm_unreachable("Invalid construct");
2128+
},
2129+
},
2130+
x.u);
2131+
}
2132+
2133+
bool IsTaskGenerating(llvm::ArrayRef<llvm::omp::Directive> leafs) const {
2134+
const static llvm::omp::Directive taskGen[] = {
2135+
llvm::omp::Directive::OMPD_target,
2136+
llvm::omp::Directive::OMPD_target_data,
2137+
llvm::omp::Directive::OMPD_target_enter_data,
2138+
llvm::omp::Directive::OMPD_target_exit_data,
2139+
llvm::omp::Directive::OMPD_target_update,
2140+
llvm::omp::Directive::OMPD_task,
2141+
llvm::omp::Directive::OMPD_taskloop,
2142+
};
2143+
return llvm::all_of(leafs,
2144+
[](llvm::omp::Directive d) { return llvm::is_contained(taskGen, d); });
2145+
}
2146+
2147+
bool IsReplayable(const parser::OmpDirectiveSpecification &dirSpec) const {
2148+
for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
2149+
if (clause.Id() != llvm::omp::Clause::OMPC_replayable) {
2150+
continue;
2151+
}
2152+
if (auto &repl{std::get<parser::OmpClause::Replayable>(clause.u).v}) {
2153+
// Scalar<Logical<Constant<indirection<Expr>>>>
2154+
const parser::Expr &parserExpr{repl->v.thing.thing.thing.value()};
2155+
if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
2156+
return GetLogicalValue(*expr).value_or(true);
2157+
}
2158+
}
2159+
break;
2160+
}
2161+
return true;
2162+
}
2163+
2164+
void CheckTransparent(const parser::OmpClause &clause) const {
2165+
bool isTransparent{true};
2166+
if (auto &transp{std::get<parser::OmpClause::Transparent>(clause.u).v}) {
2167+
// Scalar<Integer<indirection<Expr>>>
2168+
const parser::Expr &parserExpr{transp->v.thing.thing.value()};
2169+
if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
2170+
// If the argument is omp_not_impex (defined as 0), then
2171+
// the task is not transparent, otherwise it is.
2172+
const int64_t omp_not_impex{0};
2173+
if (auto &&val{evaluate::ToInt64(*expr)}) {
2174+
isTransparent = *val != omp_not_impex;
2175+
}
2176+
}
2177+
}
2178+
if (isTransparent) {
2179+
context_.Say(clause.source,
2180+
"Transparent replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
2181+
}
2182+
}
2183+
2184+
void CheckIf(const parser::OmpClause &clause,
2185+
llvm::ArrayRef<llvm::omp::Directive> leafs) const {
2186+
// The only constructs that can generate undeferred tasks (via IF clause)
2187+
// are TASK and TASKLOOP.
2188+
if (leafs[0] != llvm::omp::Directive::OMPD_task &&
2189+
leafs[0] != llvm::omp::Directive::OMPD_taskloop) {
2190+
return;
2191+
}
2192+
2193+
auto &&ifc{std::get<parser::OmpClause::If>(clause.u)};
2194+
// Check if there is a directive-name-modifier first.
2195+
auto &modifiers{OmpGetModifiers(ifc.v)};
2196+
if (auto *dnm{OmpGetUniqueModifier<parser::OmpDirectiveNameModifier>(
2197+
modifiers)}) {
2198+
llvm::omp::Directive sub{dnm->v};
2199+
auto subLeafs{llvm::omp::getLeafConstructsOrSelf(sub)};
2200+
// Only interested in the outermost constructs. The body of the created
2201+
// task is not a part of the TASKGRAPH region.
2202+
if (subLeafs[0] != leafs[0]) {
2203+
return;
2204+
}
2205+
}
2206+
// Scalar<Logical<indirection<Expr>>>
2207+
auto &parserExpr{
2208+
std::get<parser::ScalarLogicalExpr>(ifc.v.t).thing.thing.value()};
2209+
if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
2210+
// If the value is known to be false, an undeferred task will be
2211+
// generated.
2212+
if (!GetLogicalValue(*expr).value_or(true)) {
2213+
context_.Say(clause.source,
2214+
"Undeferred replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
2215+
}
2216+
}
2217+
}
2218+
2219+
SemanticsContext &context_;
2220+
};
2221+
} // namespace
2222+
2223+
void OmpStructureChecker::CheckTaskgraph(const parser::OmpBlockConstruct &x) {
2224+
const parser::Block &block{std::get<parser::Block>(x.t)};
2225+
2226+
TaskgraphVisitor visitor{context_};
2227+
parser::Walk(block, visitor);
2228+
}
2229+
20392230
void OmpStructureChecker::CheckTaskDependenceType(
20402231
const parser::OmpTaskDependenceType::Value &x) {
20412232
// Common checks for task-dependence-type (DEPEND and UPDATE clauses).

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ class OmpStructureChecker
304304
void CheckSIMDNest(const parser::OpenMPConstruct &x);
305305
void CheckTargetNest(const parser::OpenMPConstruct &x);
306306
void CheckTargetUpdate();
307+
void CheckTaskgraph(const parser::OmpBlockConstruct &x);
307308
void CheckDependenceType(const parser::OmpDependenceType::Value &x);
308309
void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x);
309310
std::optional<llvm::omp::Directive> GetCancelType(

flang/lib/Semantics/openmp-utils.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "flang/Semantics/openmp-utils.h"
1414

15+
#include "flang/Common/Fortran-consts.h"
1516
#include "flang/Common/indirection.h"
1617
#include "flang/Common/reference.h"
1718
#include "flang/Common/visit.h"
@@ -195,6 +196,46 @@ std::optional<evaluate::DynamicType> GetDynamicType(
195196
}
196197
}
197198

199+
namespace {
200+
struct LogicalConstantVistor : public evaluate::Traverse<LogicalConstantVistor,
201+
std::optional<bool>, false> {
202+
using Result = std::optional<bool>;
203+
using Base = evaluate::Traverse<LogicalConstantVistor, Result, false>;
204+
LogicalConstantVistor() : Base(*this) {}
205+
206+
Result Default() const { return std::nullopt; }
207+
208+
using Base::operator();
209+
210+
template <typename T> //
211+
Result operator()(const evaluate::Constant<T> &x) const {
212+
if constexpr (T::category == common::TypeCategory::Logical) {
213+
return llvm::transformOptional(
214+
x.GetScalarValue(), [](auto &&v) { return v.IsTrue(); });
215+
} else {
216+
return std::nullopt;
217+
}
218+
}
219+
220+
template <typename... Rs> //
221+
Result Combine(Result &&result, Rs &&...results) const {
222+
if constexpr (sizeof...(results) == 0) {
223+
return result;
224+
} else {
225+
if (result.has_value()) {
226+
return result;
227+
} else {
228+
return Combine(std::move(results)...);
229+
}
230+
}
231+
}
232+
};
233+
} // namespace
234+
235+
std::optional<bool> GetLogicalValue(const SomeExpr &expr) {
236+
return LogicalConstantVistor{}(expr);
237+
}
238+
198239
namespace {
199240
struct ContiguousHelper {
200241
ContiguousHelper(SemanticsContext &context)

flang/test/Parser/OpenMP/taskgraph.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ subroutine f01(x, y)
5050
!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OmpBlockConstruct
5151
!PARSE-TREE: | OmpBeginDirective
5252
!PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = taskgraph
53-
!PARSE-TREE: | | OmpClauseList -> OmpClause -> GraphId -> OmpGraphIdClause -> Expr = 'x'
53+
!PARSE-TREE: | | OmpClauseList -> OmpClause -> GraphId -> OmpGraphIdClause -> Scalar -> Integer -> Expr = 'x'
5454
!PARSE-TREE: | | | Designator -> DataRef -> Name = 'x'
55-
!PARSE-TREE: | | OmpClause -> GraphReset -> OmpGraphResetClause -> Expr = 'y'
55+
!PARSE-TREE: | | OmpClause -> GraphReset -> OmpGraphResetClause -> Scalar -> Logical -> Expr = 'y'
5656
!PARSE-TREE: | | | Designator -> DataRef -> Name = 'y'
5757
!PARSE-TREE: | | Flags = None
5858
!PARSE-TREE: | Block
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
2+
3+
subroutine f00
4+
!ERROR: Must have INTEGER type, but is CHARACTER(KIND=1,LEN=8_8)
5+
!$omp taskgraph graph_id("my graph")
6+
!$omp end taskgraph
7+
end
8+
9+
subroutine f01
10+
!ERROR: At most one GRAPH_ID clause can appear on the TASKGRAPH directive
11+
!$omp taskgraph graph_id(1) graph_id(2)
12+
!$omp end taskgraph
13+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
2+
3+
subroutine f00(x)
4+
integer :: x(*)
5+
!ERROR: Whole assumed-size array 'x' may not appear here without subscripts
6+
!ERROR: Must have LOGICAL type, but is INTEGER(4)
7+
!$omp taskgraph graph_reset(x)
8+
!$omp end taskgraph
9+
end
10+
11+
subroutine f01
12+
!ERROR: At most one GRAPH_RESET clause can appear on the TASKGRAPH directive
13+
!$omp taskgraph graph_reset(.true.) graph_reset(.false.)
14+
!$omp end taskgraph
15+
end

0 commit comments

Comments
 (0)