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 for (const parser::DoConstruct *loop{&*outer}; loop && tileArgNb > 0; 70 --tileArgNb) { 71 const auto &block{std::get<parser::Block>(loop->t)}; 72 const auto it{block.begin()}; 73 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 74 : nullptr; 75 } 76 77 if (tileArgNb > 0) { 78 messages_.Say(beginLoopDirective.source, 79 "The loop construct with the TILE clause must be followed by %d " 80 "tightly-nested loops"_err_en_US, 81 listTileExpr.size()); 82 } 83 } 84 } 85 } 86 87 // Check constraint on line 1835 in Section 2.9 88 // A tile and collapse clause may not appear on loop that is associated with 89 // do concurrent. 90 template <typename C, typename D> 91 void CheckDoConcurrentClauseRestriction(const C &x) { 92 const auto &doCons{std::get<std::optional<parser::DoConstruct>>(x.t)}; 93 if (!doCons->IsDoConcurrent()) 94 return; 95 const auto &beginLoopDirective = std::get<D>(x.t); 96 const auto &accClauseList = 97 std::get<parser::AccClauseList>(beginLoopDirective.t); 98 for (const auto &clause : accClauseList.v) { 99 if (std::holds_alternative<parser::AccClause::Collapse>(clause.u) || 100 std::holds_alternative<parser::AccClause::Tile>(clause.u)) { 101 messages_.Say(beginLoopDirective.source, 102 "TILE and COLLAPSE clause may not appear on loop construct " 103 "associated with DO CONCURRENT"_err_en_US); 104 } 105 } 106 } 107 108 void RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct &x, 109 parser::Block &block, parser::Block::iterator it) { 110 // Check the sequence of DoConstruct in the same iteration 111 // 112 // Original: 113 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct 114 // ACCBeginLoopDirective 115 // ExecutableConstruct -> DoConstruct 116 // 117 // After rewriting: 118 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct 119 // AccBeginLoopDirective 120 // DoConstruct 121 parser::Block::iterator nextIt; 122 auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 123 auto &dir{std::get<parser::AccLoopDirective>(beginDir.t)}; 124 125 nextIt = it; 126 if (++nextIt != block.end()) { 127 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) { 128 if (doCons->GetLoopControl()) { 129 // move DoConstruct 130 std::get<std::optional<parser::DoConstruct>>(x.t) = 131 std::move(*doCons); 132 nextIt = block.erase(nextIt); 133 } else { 134 messages_.Say(dir.source, 135 "DO loop after the %s directive must have loop control"_err_en_US, 136 parser::ToUpperCaseLetters(dir.source.ToString())); 137 } 138 139 CheckDoConcurrentClauseRestriction<parser::OpenACCLoopConstruct, 140 parser::AccBeginLoopDirective>(x); 141 CheckTileClauseRestriction<parser::OpenACCLoopConstruct, 142 parser::AccBeginLoopDirective>(x); 143 144 return; // found do-loop 145 } 146 } 147 messages_.Say(dir.source, 148 "A DO loop must follow the %s directive"_err_en_US, 149 parser::ToUpperCaseLetters(dir.source.ToString())); 150 } 151 152 void RewriteOpenACCCombinedConstruct(parser::OpenACCCombinedConstruct &x, 153 parser::Block &block, parser::Block::iterator it) { 154 // Check the sequence of DoConstruct in the same iteration 155 // 156 // Original: 157 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct 158 // ACCBeginCombinedDirective 159 // ExecutableConstruct -> DoConstruct 160 // ExecutableConstruct -> AccEndCombinedDirective (if available) 161 // 162 // After rewriting: 163 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct 164 // ACCBeginCombinedDirective 165 // DoConstruct 166 // AccEndCombinedDirective (if available) 167 parser::Block::iterator nextIt; 168 auto &beginDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; 169 auto &dir{std::get<parser::AccCombinedDirective>(beginDir.t)}; 170 171 nextIt = it; 172 if (++nextIt != block.end()) { 173 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) { 174 if (doCons->GetLoopControl()) { 175 // move DoConstruct 176 std::get<std::optional<parser::DoConstruct>>(x.t) = 177 std::move(*doCons); 178 nextIt = block.erase(nextIt); 179 // try to match AccEndCombinedDirective 180 if (nextIt != block.end()) { 181 if (auto *endDir{ 182 parser::Unwrap<parser::AccEndCombinedDirective>(*nextIt)}) { 183 std::get<std::optional<parser::AccEndCombinedDirective>>(x.t) = 184 std::move(*endDir); 185 block.erase(nextIt); 186 } 187 } 188 } else { 189 messages_.Say(dir.source, 190 "DO loop after the %s directive must have loop control"_err_en_US, 191 parser::ToUpperCaseLetters(dir.source.ToString())); 192 } 193 194 CheckDoConcurrentClauseRestriction<parser::OpenACCCombinedConstruct, 195 parser::AccBeginCombinedDirective>(x); 196 CheckTileClauseRestriction<parser::OpenACCCombinedConstruct, 197 parser::AccBeginCombinedDirective>(x); 198 199 return; // found do-loop 200 } 201 } 202 messages_.Say(dir.source, 203 "A DO loop must follow the %s directive"_err_en_US, 204 parser::ToUpperCaseLetters(dir.source.ToString())); 205 } 206 207 parser::Messages &messages_; 208 }; 209 210 bool CanonicalizeAcc(parser::Messages &messages, parser::Program &program) { 211 CanonicalizationOfAcc acc{messages}; 212 Walk(program, acc); 213 return !messages.AnyFatalError(); 214 } 215 } // namespace Fortran::semantics 216