Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions flang/include/flang/Parser/parse-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4435,15 +4435,15 @@ 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]
//
// 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]
Expand Down
2 changes: 2 additions & 0 deletions flang/include/flang/Semantics/openmp-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions flang/lib/Lower/OpenMP/Clauses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions flang/lib/Parser/openmp-parsers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>(
Expand Down
193 changes: 192 additions & 1 deletion flang/lib/Semantics/check-omp-structure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -2017,6 +2021,193 @@ 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)
-> const parser::OmpDirectiveSpecification & {
return y.BeginDir();
},
[&](const parser::OpenMPLoopConstruct &y)
-> const parser::OmpDirectiveSpecification & {
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).
Expand Down
1 change: 1 addition & 0 deletions flang/lib/Semantics/check-omp-structure.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
41 changes: 41 additions & 0 deletions flang/lib/Semantics/openmp-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions flang/test/Parser/OpenMP/taskgraph.f90
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions flang/test/Semantics/OpenMP/graph-id.f90
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions flang/test/Semantics/OpenMP/graph-reset.f90
Original file line number Diff line number Diff line change
@@ -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
Loading