[flang] [llvm] [flang][OpenMP] Semantic checks for TASKGRAPH (PR #160115)
Krzysztof Parzyszek via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 22 07:09:39 PDT 2025
https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/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.
>From 4062bb7972bd574242ffcffabca98b485895857b Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 10 Sep 2025 14:50:01 -0500
Subject: [PATCH] [flang][OpenMP] Semantic checks for TASKGRAPH
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.
---
flang/include/flang/Parser/parse-tree.h | 4 +-
flang/include/flang/Semantics/openmp-utils.h | 2 +
flang/lib/Lower/OpenMP/Clauses.cpp | 4 +-
flang/lib/Parser/openmp-parsers.cpp | 4 +-
flang/lib/Semantics/check-omp-structure.cpp | 201 ++++++++++++++++++-
flang/lib/Semantics/check-omp-structure.h | 1 +
flang/lib/Semantics/openmp-utils.cpp | 41 ++++
flang/test/Parser/OpenMP/taskgraph.f90 | 4 +-
flang/test/Semantics/OpenMP/graph-id.f90 | 13 ++
flang/test/Semantics/OpenMP/graph-reset.f90 | 15 ++
flang/test/Semantics/OpenMP/taskgraph.f90 | 117 +++++++++++
llvm/include/llvm/Frontend/OpenMP/ClauseT.h | 19 +-
12 files changed, 401 insertions(+), 24 deletions(-)
create mode 100644 flang/test/Semantics/OpenMP/graph-id.f90
create mode 100644 flang/test/Semantics/OpenMP/graph-reset.f90
create mode 100644 flang/test/Semantics/OpenMP/taskgraph.f90
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index bd0debe297916..78504276726fd 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4435,7 +4435,7 @@ struct OmpGrainsizeClause {
// graph_id-clause ->
// GRAPH_ID(graph-id-value) // since 6.0
struct OmpGraphIdClause {
- WRAPPER_CLASS_BOILERPLATE(OmpGraphIdClause, common::Indirection<Expr>);
+ WRAPPER_CLASS_BOILERPLATE(OmpGraphIdClause, ScalarIntExpr);
};
// Ref: [6.0:438-439]
@@ -4443,7 +4443,7 @@ struct OmpGraphIdClause {
// graph_reset-clause ->
// GRAPH_RESET[(graph-reset-expression)] // since 6.0
struct OmpGraphResetClause {
- WRAPPER_CLASS_BOILERPLATE(OmpGraphResetClause, common::Indirection<Expr>);
+ WRAPPER_CLASS_BOILERPLATE(OmpGraphResetClause, ScalarLogicalExpr);
};
// Ref: [5.0:234-242], [5.1:266-275], [5.2:299], [6.0:472-473]
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 68318d6093a1e..e7b0ab11825ec 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -74,6 +74,8 @@ std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr);
std::optional<evaluate::DynamicType> GetDynamicType(
const parser::Expr &parserExpr);
+std::optional<bool> GetLogicalValue(const SomeExpr &expr);
+
std::optional<bool> IsContiguous(
SemanticsContext &semaCtx, const parser::OmpObject &object);
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index 42b62413f4a26..48b90ccea2f2a 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -221,8 +221,6 @@ MAKE_EMPTY_CLASS(Capture, Capture);
MAKE_EMPTY_CLASS(Compare, Compare);
MAKE_EMPTY_CLASS(DynamicAllocators, DynamicAllocators);
MAKE_EMPTY_CLASS(Full, Full);
-MAKE_EMPTY_CLASS(GraphId, GraphId);
-MAKE_EMPTY_CLASS(GraphReset, GraphReset);
MAKE_EMPTY_CLASS(Inbranch, Inbranch);
MAKE_EMPTY_CLASS(Mergeable, Mergeable);
MAKE_EMPTY_CLASS(Nogroup, Nogroup);
@@ -258,6 +256,8 @@ MAKE_EMPTY_CLASS(Groupprivate, Groupprivate);
MAKE_INCOMPLETE_CLASS(AdjustArgs, AdjustArgs);
MAKE_INCOMPLETE_CLASS(AppendArgs, AppendArgs);
+MAKE_INCOMPLETE_CLASS(GraphId, GraphId);
+MAKE_INCOMPLETE_CLASS(GraphReset, GraphReset);
MAKE_INCOMPLETE_CLASS(Replayable, Replayable);
MAKE_INCOMPLETE_CLASS(Transparent, Transparent);
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 8ab9905123135..f87bc1757d7f7 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -824,9 +824,9 @@ TYPE_PARSER(construct<OmpFailClause>(
"RELEASE" >> pure(common::OmpMemoryOrderType::Release) ||
"SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst)))
-TYPE_PARSER(construct<OmpGraphIdClause>(expr))
+TYPE_PARSER(construct<OmpGraphIdClause>(scalarIntExpr))
-TYPE_PARSER(construct<OmpGraphResetClause>(expr))
+TYPE_PARSER(construct<OmpGraphResetClause>(scalarLogicalExpr))
// 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD)
TYPE_PARSER(construct<OmpProcBindClause>(
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index c39daef6b0ea9..73aea2f714eb3 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -15,6 +15,7 @@
#include "flang/Common/idioms.h"
#include "flang/Common/indirection.h"
#include "flang/Common/visit.h"
+#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/tools.h"
#include "flang/Evaluate/type.h"
#include "flang/Parser/char-block.h"
@@ -990,21 +991,21 @@ void OmpStructureChecker::Enter(const parser::OmpBlockConstruct &x) {
EnterDirectiveNest(TargetBlockOnlyTeams);
}
break;
- case llvm::omp::OMPD_workshare:
- case llvm::omp::OMPD_parallel_workshare:
+ case llvm::omp::Directive::OMPD_workshare:
+ case llvm::omp::Directive::OMPD_parallel_workshare:
CheckWorkshareBlockStmts(block, beginSpec.source);
HasInvalidWorksharingNesting(
beginSpec.source, llvm::omp::nestedWorkshareErrSet);
break;
- case llvm::omp::OMPD_workdistribute:
+ case llvm::omp::Directive::OMPD_workdistribute:
if (!CurrentDirectiveIsNested()) {
context_.Say(beginSpec.source,
"A WORKDISTRIBUTE region must be nested inside TEAMS region only."_err_en_US);
}
CheckWorkdistributeBlockStmts(block, beginSpec.source);
break;
- case llvm::omp::OMPD_teams_workdistribute:
- case llvm::omp::OMPD_target_teams_workdistribute:
+ case llvm::omp::Directive::OMPD_teams_workdistribute:
+ case llvm::omp::Directive::OMPD_target_teams_workdistribute:
CheckWorkdistributeBlockStmts(block, beginSpec.source);
break;
case llvm::omp::Directive::OMPD_scope:
@@ -1057,7 +1058,10 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAssumes &) {
dirContext_.pop_back();
}
-void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &) {
+void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &x) {
+ if (GetContext().directive == llvm::omp::Directive::OMPD_taskgraph) {
+ CheckTaskgraph(x);
+ }
if (GetDirectiveNest(TargetBlockOnlyTeams)) {
ExitDirectiveNest(TargetBlockOnlyTeams);
}
@@ -2017,6 +2021,191 @@ void OmpStructureChecker::CheckTargetUpdate() {
}
}
+namespace {
+struct TaskgraphVisitor {
+ TaskgraphVisitor(SemanticsContext &context) : context_(context) {}
+
+ template <typename T> bool Pre(const T &) { return true; }
+ template <typename T> void Post(const T &) {}
+
+ bool Pre(const parser::OpenMPConstruct &x) {
+ parser::OmpDirectiveName name{GetOmpDirectiveName(x)};
+ llvm::ArrayRef<llvm::omp::Directive> leafs{getLeafConstructsOrSelf(name.v)};
+
+ if (!IsTaskGenerating(leafs)) {
+ context_.Say(name.source,
+ "Only task-generating constructs are allowed inside TASKGRAPH region"_err_en_US);
+ // Only visit top-level constructs.
+ return false;
+ }
+
+ const parser::OmpDirectiveSpecification &dirSpec{GetDirSpec(x)};
+
+ // Most restrictions apply to replayable constructs. All constructs are
+ // replayable unless REPLAYABLE(false) is present.
+ bool isReplayable{IsReplayable(dirSpec)};
+ const parser::OmpClause *nogroup{nullptr};
+
+ for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+ switch (clause.Id()) {
+ case llvm::omp::Clause::OMPC_transparent:
+ if (isReplayable) {
+ CheckTransparent(clause);
+ }
+ break;
+ case llvm::omp::Clause::OMPC_detach:
+ if (isReplayable) {
+ context_.Say(clause.source,
+ "Detachable replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+ }
+ break;
+ case llvm::omp::Clause::OMPC_if:
+ if (isReplayable) {
+ CheckIf(clause, leafs);
+ }
+ break;
+ case llvm::omp::Clause::OMPC_nogroup:
+ nogroup = &clause;
+ break;
+ default:
+ break;
+ }
+ }
+
+ unsigned version{context_.langOptions().OpenMPVersion};
+ bool allowsNogroup{llvm::omp::isAllowedClauseForDirective(
+ leafs[0], llvm::omp::Clause::OMPC_nogroup, version)};
+
+ if (allowsNogroup) {
+ if (!nogroup) {
+ context_.Say(dirSpec.source,
+ "The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP"_err_en_US);
+ }
+ }
+
+ // Only visit top-level constructs.
+ return false;
+ }
+
+private:
+ const parser::OmpDirectiveSpecification &GetDirSpec(
+ const parser::OpenMPConstruct &x) const {
+ return common::visit(
+ common::visitors{
+ [&](const parser::OmpBlockConstruct &y) -> decltype(auto) {
+ return y.BeginDir();
+ },
+ [&](const parser::OpenMPLoopConstruct &y) -> decltype(auto) {
+ return y.BeginDir();
+ },
+ [&](const parser::OpenMPStandaloneConstruct &y)
+ -> const parser::OmpDirectiveSpecification & {
+ return std::get<parser::OpenMPSimpleStandaloneConstruct>(y.u).v;
+ },
+ [&](const auto &) -> const parser::OmpDirectiveSpecification & {
+ llvm_unreachable("Invalid construct");
+ },
+ },
+ x.u);
+ }
+
+ bool IsTaskGenerating(llvm::ArrayRef<llvm::omp::Directive> leafs) const {
+ const static llvm::omp::Directive taskGen[] = {
+ llvm::omp::Directive::OMPD_target,
+ llvm::omp::Directive::OMPD_target_data,
+ llvm::omp::Directive::OMPD_target_enter_data,
+ llvm::omp::Directive::OMPD_target_exit_data,
+ llvm::omp::Directive::OMPD_target_update,
+ llvm::omp::Directive::OMPD_task,
+ llvm::omp::Directive::OMPD_taskloop,
+ };
+ return llvm::all_of(leafs,
+ [](llvm::omp::Directive d) { return llvm::is_contained(taskGen, d); });
+ }
+
+ bool IsReplayable(const parser::OmpDirectiveSpecification &dirSpec) const {
+ for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+ if (clause.Id() != llvm::omp::Clause::OMPC_replayable) {
+ continue;
+ }
+ if (auto &repl{std::get<parser::OmpClause::Replayable>(clause.u).v}) {
+ // Scalar<Logical<Constant<indirection<Expr>>>>
+ const parser::Expr &parserExpr{repl->v.thing.thing.thing.value()};
+ if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+ return GetLogicalValue(*expr).value_or(true);
+ }
+ }
+ break;
+ }
+ return true;
+ }
+
+ void CheckTransparent(const parser::OmpClause &clause) const {
+ bool isTransparent{true};
+ if (auto &transp{std::get<parser::OmpClause::Transparent>(clause.u).v}) {
+ // Scalar<Integer<indirection<Expr>>>
+ const parser::Expr &parserExpr{transp->v.thing.thing.value()};
+ if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+ // If the argument is omp_not_impex (defined as 0), then
+ // the task is not transparent, otherwise it is.
+ const int64_t omp_not_impex{0};
+ if (auto &&val{evaluate::ToInt64(*expr)}) {
+ isTransparent = *val != omp_not_impex;
+ }
+ }
+ }
+ if (isTransparent) {
+ context_.Say(clause.source,
+ "Transparent replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+ }
+ }
+
+ void CheckIf(const parser::OmpClause &clause,
+ llvm::ArrayRef<llvm::omp::Directive> leafs) const {
+ // The only constructs that can generate undeferred tasks (via IF clause)
+ // are TASK and TASKLOOP.
+ if (leafs[0] != llvm::omp::Directive::OMPD_task &&
+ leafs[0] != llvm::omp::Directive::OMPD_taskloop) {
+ return;
+ }
+
+ auto &&ifc{std::get<parser::OmpClause::If>(clause.u)};
+ // Check if there is a directive-name-modifier first.
+ auto &modifiers{OmpGetModifiers(ifc.v)};
+ if (auto *dnm{OmpGetUniqueModifier<parser::OmpDirectiveNameModifier>(
+ modifiers)}) {
+ llvm::omp::Directive sub{dnm->v};
+ auto subLeafs{llvm::omp::getLeafConstructsOrSelf(sub)};
+ // Only interested in the outermost constructs. The body of the created
+ // task is not a part of the TASKGRAPH region.
+ if (subLeafs[0] != leafs[0]) {
+ return;
+ }
+ }
+ // Scalar<Logical<indirection<Expr>>>
+ auto &parserExpr{
+ std::get<parser::ScalarLogicalExpr>(ifc.v.t).thing.thing.value()};
+ if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+ // If the value is known to be false, an undeferred task will be
+ // generated.
+ if (!GetLogicalValue(*expr).value_or(true)) {
+ context_.Say(clause.source,
+ "Undeferred replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+ }
+ }
+ }
+
+ SemanticsContext &context_;
+};
+} // namespace
+
+void OmpStructureChecker::CheckTaskgraph(const parser::OmpBlockConstruct &x) {
+ const parser::Block &block{std::get<parser::Block>(x.t)};
+
+ TaskgraphVisitor visitor{context_};
+ parser::Walk(block, visitor);
+}
+
void OmpStructureChecker::CheckTaskDependenceType(
const parser::OmpTaskDependenceType::Value &x) {
// Common checks for task-dependence-type (DEPEND and UPDATE clauses).
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index ce074f5f3f86e..a22c80f0a3c3f 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -299,6 +299,7 @@ class OmpStructureChecker
void CheckSIMDNest(const parser::OpenMPConstruct &x);
void CheckTargetNest(const parser::OpenMPConstruct &x);
void CheckTargetUpdate();
+ void CheckTaskgraph(const parser::OmpBlockConstruct &x);
void CheckDependenceType(const parser::OmpDependenceType::Value &x);
void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x);
std::optional<llvm::omp::Directive> GetCancelType(
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 2980f827d3ef3..ef04f0337ba12 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -12,6 +12,7 @@
#include "flang/Semantics/openmp-utils.h"
+#include "flang/Common/Fortran-consts.h"
#include "flang/Common/indirection.h"
#include "flang/Common/reference.h"
#include "flang/Common/visit.h"
@@ -187,6 +188,46 @@ std::optional<evaluate::DynamicType> GetDynamicType(
}
}
+namespace {
+struct LogicalConstantVistor : public evaluate::Traverse<LogicalConstantVistor,
+ std::optional<bool>, false> {
+ using Result = std::optional<bool>;
+ using Base = evaluate::Traverse<LogicalConstantVistor, Result, false>;
+ LogicalConstantVistor() : Base(*this) {}
+
+ Result Default() const { return std::nullopt; }
+
+ using Base::operator();
+
+ template <typename T> //
+ Result operator()(const evaluate::Constant<T> &x) const {
+ if constexpr (T::category == common::TypeCategory::Logical) {
+ return llvm::transformOptional(
+ x.GetScalarValue(), [](auto &&v) { return v.IsTrue(); });
+ } else {
+ return std::nullopt;
+ }
+ }
+
+ template <typename... Rs> //
+ Result Combine(Result &&result, Rs &&...results) const {
+ if constexpr (sizeof...(results) == 0) {
+ return result;
+ } else {
+ if (result.has_value()) {
+ return result;
+ } else {
+ return Combine(std::move(results)...);
+ }
+ }
+ }
+};
+} // namespace
+
+std::optional<bool> GetLogicalValue(const SomeExpr &expr) {
+ return LogicalConstantVistor{}(expr);
+}
+
namespace {
struct ContiguousHelper {
ContiguousHelper(SemanticsContext &context)
diff --git a/flang/test/Parser/OpenMP/taskgraph.f90 b/flang/test/Parser/OpenMP/taskgraph.f90
index 7fcbae4227508..fa9994f41345e 100644
--- a/flang/test/Parser/OpenMP/taskgraph.f90
+++ b/flang/test/Parser/OpenMP/taskgraph.f90
@@ -50,9 +50,9 @@ subroutine f01(x, y)
!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OmpBlockConstruct
!PARSE-TREE: | OmpBeginDirective
!PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = taskgraph
-!PARSE-TREE: | | OmpClauseList -> OmpClause -> GraphId -> OmpGraphIdClause -> Expr = 'x'
+!PARSE-TREE: | | OmpClauseList -> OmpClause -> GraphId -> OmpGraphIdClause -> Scalar -> Integer -> Expr = 'x'
!PARSE-TREE: | | | Designator -> DataRef -> Name = 'x'
-!PARSE-TREE: | | OmpClause -> GraphReset -> OmpGraphResetClause -> Expr = 'y'
+!PARSE-TREE: | | OmpClause -> GraphReset -> OmpGraphResetClause -> Scalar -> Logical -> Expr = 'y'
!PARSE-TREE: | | | Designator -> DataRef -> Name = 'y'
!PARSE-TREE: | | Flags = None
!PARSE-TREE: | Block
diff --git a/flang/test/Semantics/OpenMP/graph-id.f90 b/flang/test/Semantics/OpenMP/graph-id.f90
new file mode 100644
index 0000000000000..64ce447ce587f
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/graph-id.f90
@@ -0,0 +1,13 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+subroutine f00
+ !ERROR: Must have INTEGER type, but is CHARACTER(KIND=1,LEN=8_8)
+ !$omp taskgraph graph_id("my graph")
+ !$omp end taskgraph
+end
+
+subroutine f01
+ !ERROR: At most one GRAPH_ID clause can appear on the TASKGRAPH directive
+ !$omp taskgraph graph_id(1) graph_id(2)
+ !$omp end taskgraph
+end
diff --git a/flang/test/Semantics/OpenMP/graph-reset.f90 b/flang/test/Semantics/OpenMP/graph-reset.f90
new file mode 100644
index 0000000000000..4ff9b3d11902b
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/graph-reset.f90
@@ -0,0 +1,15 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+subroutine f00(x)
+ integer :: x(*)
+ !ERROR: Whole assumed-size array 'x' may not appear here without subscripts
+ !ERROR: Must have LOGICAL type, but is INTEGER(4)
+ !$omp taskgraph graph_reset(x)
+ !$omp end taskgraph
+end
+
+subroutine f01
+ !ERROR: At most one GRAPH_RESET clause can appear on the TASKGRAPH directive
+ !$omp taskgraph graph_reset(.true.) graph_reset(.false.)
+ !$omp end taskgraph
+end
diff --git a/flang/test/Semantics/OpenMP/taskgraph.f90 b/flang/test/Semantics/OpenMP/taskgraph.f90
new file mode 100644
index 0000000000000..e45ef46c3bef2
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/taskgraph.f90
@@ -0,0 +1,117 @@
+!RUN: %python %S/../test_errors.py %s %flang %openmp_flags -fopenmp -fopenmp-version=60
+
+module m
+use omp_lib
+
+implicit none
+! Not in omp_lib yet.
+integer, parameter :: omp_not_impex = 0
+integer, parameter :: omp_import = 1
+integer, parameter :: omp_export = 2
+integer, parameter :: omp_impex = 3
+
+contains
+
+subroutine f00
+ !$omp taskgraph
+ !ERROR: Only task-generating constructs are allowed inside TASKGRAPH region
+ !$omp parallel
+ !$omp end parallel
+ !$omp end taskgraph
+end
+
+subroutine f01
+ !$omp taskgraph
+ !$omp task
+ !Non-task-generating constructs are ok if contained in an encountered task.
+ !No diagnostic expected.
+ !$omp parallel
+ !$omp end parallel
+ !$omp end task
+ !$omp end taskgraph
+end
+
+subroutine f02
+ !$omp taskgraph
+ !ERROR: Transparent replayable tasks are not allowed in a TASKGRAPH region
+ !$omp task transparent
+ !$omp end task
+ !$omp end taskgraph
+
+ !$omp taskgraph
+ !Not a transparent task.
+ !No diagnostic expected.
+ !$omp task transparent(omp_not_impex)
+ !$omp end task
+ !$omp end taskgraph
+
+ !$omp taskgraph
+ !Ok: transparent, but not replayable task.
+ !No diagnostic expected.
+ !$omp task replayable(.false.) transparent
+ !$omp end task
+ !$omp end taskgraph
+end
+
+subroutine f03
+ integer(kind=omp_event_handle_kind) :: event
+
+ !$omp taskgraph
+ !ERROR: Detachable replayable tasks are not allowed in a TASKGRAPH region
+ !$omp task detach(event)
+ !$omp end task
+ !$omp end taskgraph
+
+ !$omp taskgraph
+ !Ok: task is detachable, but not replayable.
+ !No diagnostic expected
+ !$omp task detach(event) replayable(.false.)
+ !$omp end task
+ !$omp end taskgraph
+end
+
+subroutine f04
+ !$omp taskgraph
+ !ERROR: Undeferred replayable tasks are not allowed in a TASKGRAPH region
+ !$omp task if(.false.)
+ !$omp end task
+ !$omp end taskgraph
+
+ !$omp taskgraph
+ !Ok: task is undeferred, but not replayable.
+ !No diagnostic expected.
+ !$omp task if(.false.) replayable(.false.)
+ !$omp end task
+ !$omp end taskgraph
+end
+
+subroutine f05
+ integer :: i
+
+ !$omp taskgraph
+ !ERROR: The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP
+ !$omp taskloop
+ do i = 1, 10
+ enddo
+ !$omp end taskloop
+ !$omp end taskgraph
+
+ !$omp taskgraph
+ !This also applies to non-replayable constructs
+ !ERROR: The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP
+ !$omp taskloop replayable(.false.)
+ do i = 1, 10
+ enddo
+ !$omp end taskloop
+ !$omp end taskgraph
+
+ !$omp taskgraph
+ !No diagnostic expected.
+ !$omp taskloop replayable(.false.) nogroup
+ do i = 1, 10
+ enddo
+ !$omp end taskloop
+ !$omp end taskgraph
+end
+
+end module
diff --git a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
index 1ed23eed1571d..1ade9ce0c3a7d 100644
--- a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
+++ b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
@@ -663,13 +663,13 @@ struct GrainsizeT {
// [6.0:438] `graph_id` clause
template <typename T, typename I, typename E> //
struct GraphIdT {
- using EmptyTrait = std::true_type;
+ using IncompleteTrait = std::true_type;
};
// [6.0:438] `graph_reset` clause
template <typename T, typename I, typename E> //
struct GraphResetT {
- using EmptyTrait = std::true_type;
+ using IncompleteTrait = std::true_type;
};
// V5.2: [5.4.9] `has_device_addr` clause
@@ -1278,11 +1278,10 @@ using ExtensionClausesT =
template <typename T, typename I, typename E>
using EmptyClausesT = std::variant<
AcqRelT<T, I, E>, AcquireT<T, I, E>, CaptureT<T, I, E>, CompareT<T, I, E>,
- DynamicAllocatorsT<T, I, E>, FullT<T, I, E>, GraphIdT<T, I, E>,
- GraphResetT<T, I, E>, InbranchT<T, I, E>, MergeableT<T, I, E>,
- NogroupT<T, I, E>, NoOpenmpRoutinesT<T, I, E>, NoOpenmpT<T, I, E>,
- NoParallelismT<T, I, E>, NotinbranchT<T, I, E>, NowaitT<T, I, E>,
- ReadT<T, I, E>, RelaxedT<T, I, E>, ReleaseT<T, I, E>,
+ DynamicAllocatorsT<T, I, E>, FullT<T, I, E>, InbranchT<T, I, E>,
+ MergeableT<T, I, E>, NogroupT<T, I, E>, NoOpenmpRoutinesT<T, I, E>,
+ NoOpenmpT<T, I, E>, NoParallelismT<T, I, E>, NotinbranchT<T, I, E>,
+ NowaitT<T, I, E>, ReadT<T, I, E>, RelaxedT<T, I, E>, ReleaseT<T, I, E>,
ReverseOffloadT<T, I, E>, SeqCstT<T, I, E>, SimdT<T, I, E>,
ThreadsT<T, I, E>, UnifiedAddressT<T, I, E>, UnifiedSharedMemoryT<T, I, E>,
UnknownT<T, I, E>, UntiedT<T, I, E>, UseT<T, I, E>, WeakT<T, I, E>,
@@ -1290,9 +1289,9 @@ using EmptyClausesT = std::variant<
template <typename T, typename I, typename E>
using IncompleteClausesT =
- std::variant<AdjustArgsT<T, I, E>, AppendArgsT<T, I, E>, MatchT<T, I, E>,
- OtherwiseT<T, I, E>, ReplayableT<T, I, E>,
- TransparentT<T, I, E>, WhenT<T, I, E>>;
+ std::variant<AdjustArgsT<T, I, E>, AppendArgsT<T, I, E>, GraphIdT<T, I, E>,
+ GraphResetT<T, I, E>, MatchT<T, I, E>, OtherwiseT<T, I, E>,
+ ReplayableT<T, I, E>, TransparentT<T, I, E>, WhenT<T, I, E>>;
template <typename T, typename I, typename E>
using TupleClausesT =
More information about the llvm-commits
mailing list