1 //===-- lib/Semantics/canonicalize-acc.cpp --------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "canonicalize-acc.h" 10 #include "flang/Parser/parse-tree-visitor.h" 11 #include "flang/Semantics/tools.h" 12 13 // After Loop Canonicalization, rewrite OpenACC parse tree to make OpenACC 14 // Constructs more structured which provide explicit scopes for later 15 // structural checks and semantic analysis. 16 // 1. move structured DoConstruct into 17 // OpenACCLoopConstruct. Compilation will not proceed in case of errors 18 // after this pass. 19 // 2. move structured DoConstruct into OpenACCCombinedConstruct. Move 20 // AccEndCombinedConstruct into OpenACCCombinedConstruct if present. 21 // Compilation will not proceed in case of errors after this pass. 22 namespace Fortran::semantics { 23 24 using namespace parser::literals; 25 26 class CanonicalizationOfAcc { 27 public: 28 template <typename T> bool Pre(T &) { return true; } 29 template <typename T> void Post(T &) {} 30 CanonicalizationOfAcc(parser::Messages &messages) : messages_{messages} {} 31 32 void Post(parser::Block &block) { 33 for (auto it{block.begin()}; it != block.end(); ++it) { 34 if (auto *accLoop{parser::Unwrap<parser::OpenACCLoopConstruct>(*it)}) { 35 RewriteOpenACCLoopConstruct(*accLoop, block, it); 36 } else if (auto *accCombined{ 37 parser::Unwrap<parser::OpenACCCombinedConstruct>(*it)}) { 38 RewriteOpenACCCombinedConstruct(*accCombined, block, it); 39 } else if (auto *endDir{ 40 parser::Unwrap<parser::AccEndCombinedDirective>(*it)}) { 41 // Unmatched AccEndCombinedDirective 42 messages_.Say(endDir->v.source, 43 "The %s directive must follow the DO loop associated with the " 44 "loop construct"_err_en_US, 45 parser::ToUpperCaseLetters(endDir->v.source.ToString())); 46 } 47 } // Block list 48 } 49 50 private: 51 // Check constraint in 2.9.7 52 // If there are n tile sizes in the list, the loop construct must be 53 // immediately followed by n tightly-nested loops. 54 template <typename C, typename D> 55 void CheckTileClauseRestriction(const C &x) { 56 const auto &beginLoopDirective = std::get<D>(x.t); 57 const auto &accClauseList = 58 std::get<parser::AccClauseList>(beginLoopDirective.t); 59 for (const auto &clause : accClauseList.v) { 60 if (const auto *tileClause = 61 std::get_if<parser::AccClause::Tile>(&clause.u)) { 62 const parser::AccTileExprList &tileExprList = tileClause->v; 63 const std::list<parser::AccTileExpr> &listTileExpr = tileExprList.v; 64 std::size_t tileArgNb = listTileExpr.size(); 65 66 const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; 67 if (outer->IsDoConcurrent()) { 68 return; // Tile is not allowed on DO CONURRENT 69 } 70 for (const parser::DoConstruct *loop{&*outer}; loop && tileArgNb > 0; 71 --tileArgNb) { 72 const auto &block{std::get<parser::Block>(loop->t)}; 73 const auto it{block.begin()}; 74 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 75 : nullptr; 76 } 77 78 if (tileArgNb > 0) { 79 messages_.Say(beginLoopDirective.source, 80 "The loop construct with the TILE clause must be followed by %d " 81 "tightly-nested loops"_err_en_US, 82 listTileExpr.size()); 83 } 84 } 85 } 86 } 87 88 // Check constraint on line 1835 in Section 2.9 89 // A tile and collapse clause may not appear on loop that is associated with 90 // do concurrent. 91 template <typename C, typename D> 92 void CheckDoConcurrentClauseRestriction(const C &x) { 93 const auto &doCons{std::get<std::optional<parser::DoConstruct>>(x.t)}; 94 if (!doCons->IsDoConcurrent()) { 95 return; 96 } 97 const auto &beginLoopDirective = std::get<D>(x.t); 98 const auto &accClauseList = 99 std::get<parser::AccClauseList>(beginLoopDirective.t); 100 for (const auto &clause : accClauseList.v) { 101 if (std::holds_alternative<parser::AccClause::Collapse>(clause.u) || 102 std::holds_alternative<parser::AccClause::Tile>(clause.u)) { 103 messages_.Say(beginLoopDirective.source, 104 "TILE and COLLAPSE clause may not appear on loop construct " 105 "associated with DO CONCURRENT"_err_en_US); 106 } 107 } 108 } 109 110 void RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct &x, 111 parser::Block &block, parser::Block::iterator it) { 112 // Check the sequence of DoConstruct in the same iteration 113 // 114 // Original: 115 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct 116 // ACCBeginLoopDirective 117 // ExecutableConstruct -> DoConstruct 118 // 119 // After rewriting: 120 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct 121 // AccBeginLoopDirective 122 // DoConstruct 123 parser::Block::iterator nextIt; 124 auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 125 auto &dir{std::get<parser::AccLoopDirective>(beginDir.t)}; 126 127 nextIt = it; 128 if (++nextIt != block.end()) { 129 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) { 130 if (doCons->GetLoopControl()) { 131 // move DoConstruct 132 std::get<std::optional<parser::DoConstruct>>(x.t) = 133 std::move(*doCons); 134 nextIt = block.erase(nextIt); 135 } else { 136 messages_.Say(dir.source, 137 "DO loop after the %s directive must have loop control"_err_en_US, 138 parser::ToUpperCaseLetters(dir.source.ToString())); 139 } 140 141 CheckDoConcurrentClauseRestriction<parser::OpenACCLoopConstruct, 142 parser::AccBeginLoopDirective>(x); 143 CheckTileClauseRestriction<parser::OpenACCLoopConstruct, 144 parser::AccBeginLoopDirective>(x); 145 146 return; // found do-loop 147 } 148 } 149 messages_.Say(dir.source, 150 "A DO loop must follow the %s directive"_err_en_US, 151 parser::ToUpperCaseLetters(dir.source.ToString())); 152 } 153 154 void RewriteOpenACCCombinedConstruct(parser::OpenACCCombinedConstruct &x, 155 parser::Block &block, parser::Block::iterator it) { 156 // Check the sequence of DoConstruct in the same iteration 157 // 158 // Original: 159 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct 160 // ACCBeginCombinedDirective 161 // ExecutableConstruct -> DoConstruct 162 // ExecutableConstruct -> AccEndCombinedDirective (if available) 163 // 164 // After rewriting: 165 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct 166 // ACCBeginCombinedDirective 167 // DoConstruct 168 // AccEndCombinedDirective (if available) 169 parser::Block::iterator nextIt; 170 auto &beginDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; 171 auto &dir{std::get<parser::AccCombinedDirective>(beginDir.t)}; 172 173 nextIt = it; 174 if (++nextIt != block.end()) { 175 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) { 176 if (doCons->GetLoopControl()) { 177 // move DoConstruct 178 std::get<std::optional<parser::DoConstruct>>(x.t) = 179 std::move(*doCons); 180 nextIt = block.erase(nextIt); 181 // try to match AccEndCombinedDirective 182 if (nextIt != block.end()) { 183 if (auto *endDir{ 184 parser::Unwrap<parser::AccEndCombinedDirective>(*nextIt)}) { 185 std::get<std::optional<parser::AccEndCombinedDirective>>(x.t) = 186 std::move(*endDir); 187 block.erase(nextIt); 188 } 189 } 190 } else { 191 messages_.Say(dir.source, 192 "DO loop after the %s directive must have loop control"_err_en_US, 193 parser::ToUpperCaseLetters(dir.source.ToString())); 194 } 195 196 CheckDoConcurrentClauseRestriction<parser::OpenACCCombinedConstruct, 197 parser::AccBeginCombinedDirective>(x); 198 CheckTileClauseRestriction<parser::OpenACCCombinedConstruct, 199 parser::AccBeginCombinedDirective>(x); 200 201 return; // found do-loop 202 } 203 } 204 messages_.Say(dir.source, 205 "A DO loop must follow the %s directive"_err_en_US, 206 parser::ToUpperCaseLetters(dir.source.ToString())); 207 } 208 209 parser::Messages &messages_; 210 }; 211 212 bool CanonicalizeAcc(parser::Messages &messages, parser::Program &program) { 213 CanonicalizationOfAcc acc{messages}; 214 Walk(program, acc); 215 return !messages.AnyFatalError(); 216 } 217 } // namespace Fortran::semantics 218