[flang] [llvm] [flang][OpenMP] Semantic checks for TASKGRAPH (PR #160115)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 22 07:10:17 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-parser
Author: Krzysztof Parzyszek (kparzysz)
<details>
<summary>Changes</summary>
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.
---
Patch is 22.22 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/160115.diff
12 Files Affected:
- (modified) flang/include/flang/Parser/parse-tree.h (+2-2)
- (modified) flang/include/flang/Semantics/openmp-utils.h (+2)
- (modified) flang/lib/Lower/OpenMP/Clauses.cpp (+2-2)
- (modified) flang/lib/Parser/openmp-parsers.cpp (+2-2)
- (modified) flang/lib/Semantics/check-omp-structure.cpp (+195-6)
- (modified) flang/lib/Semantics/check-omp-structure.h (+1)
- (modified) flang/lib/Semantics/openmp-utils.cpp (+41)
- (modified) flang/test/Parser/OpenMP/taskgraph.f90 (+2-2)
- (added) flang/test/Semantics/OpenMP/graph-id.f90 (+13)
- (added) flang/test/Semantics/OpenMP/graph-reset.f90 (+15)
- (added) flang/test/Semantics/OpenMP/taskgraph.f90 (+117)
- (modified) llvm/include/llvm/Frontend/OpenMP/ClauseT.h (+9-10)
``````````diff
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
---...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/160115
More information about the llvm-commits
mailing list