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