-
Notifications
You must be signed in to change notification settings - Fork 14.8k
[flang][OpenMP] Semantic checks for TASKGRAPH #160115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
cc: @dreachem |
@llvm/pr-subscribers-flang-openmp Author: Krzysztof Parzyszek (kparzysz) ChangesThis 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:
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:
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]
|
@llvm/pr-subscribers-flang-semantics Author: Krzysztof Parzyszek (kparzysz) ChangesThis 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:
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:
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]
|
@llvm/pr-subscribers-flang-fir-hlfir Author: Krzysztof Parzyszek (kparzysz) ChangesThis 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:
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:
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]
|
@llvm/pr-subscribers-flang-parser Author: Krzysztof Parzyszek (kparzysz) ChangesThis 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:
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:
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]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, maybe remove the seemingly unrelated changes.
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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes?
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/22999 Here is the relevant piece of the build log for the reference
|
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: