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( 56 const C &x, const parser::DoConstruct &outer) { 57 const auto &beginLoopDirective = std::get<D>(x.t); 58 const auto &accClauseList = 59 std::get<parser::AccClauseList>(beginLoopDirective.t); 60 for (const auto &clause : accClauseList.v) { 61 if (const auto *tileClause = 62 std::get_if<parser::AccClause::Tile>(&clause.u)) { 63 const parser::AccTileExprList &tileExprList = tileClause->v; 64 const std::list<parser::AccTileExpr> &listTileExpr = tileExprList.v; 65 std::size_t tileArgNb = listTileExpr.size(); 66 67 if (outer.IsDoConcurrent()) { 68 return; // Tile is not allowed on DO CONCURRENT 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( 93 const C &x, const parser::DoConstruct &doCons) { 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 // Utility to move all parser::CompilerDirective right after it to right 111 // before it. This allows preserving loop directives $DIR that may lie 112 // between an $acc directive and loop and leave lowering decide if it should 113 // ignore them or lower/apply them to the acc loops. 114 void moveCompilerDirectivesBefore( 115 parser::Block &block, parser::Block::iterator it) { 116 parser::Block::iterator nextIt = std::next(it); 117 while (nextIt != block.end() && 118 parser::Unwrap<parser::CompilerDirective>(*nextIt)) { 119 block.emplace(it, std::move(*nextIt)); 120 nextIt = block.erase(nextIt); 121 } 122 } 123 124 void RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct &x, 125 parser::Block &block, parser::Block::iterator it) { 126 parser::Block::iterator nextIt; 127 auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 128 auto &dir{std::get<parser::AccLoopDirective>(beginDir.t)}; 129 auto &nestedDo{std::get<std::optional<parser::DoConstruct>>(x.t)}; 130 131 if (!nestedDo) { 132 moveCompilerDirectivesBefore(block, it); 133 nextIt = it; 134 if (++nextIt != block.end()) { 135 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) { 136 nestedDo = std::move(*doCons); 137 nextIt = block.erase(nextIt); 138 } 139 } 140 } 141 142 if (nestedDo) { 143 if (!nestedDo->GetLoopControl()) { 144 messages_.Say(dir.source, 145 "DO loop after the %s directive must have loop control"_err_en_US, 146 parser::ToUpperCaseLetters(dir.source.ToString())); 147 return; 148 } 149 CheckDoConcurrentClauseRestriction<parser::OpenACCLoopConstruct, 150 parser::AccBeginLoopDirective>(x, *nestedDo); 151 CheckTileClauseRestriction<parser::OpenACCLoopConstruct, 152 parser::AccBeginLoopDirective>(x, *nestedDo); 153 return; 154 } 155 messages_.Say(dir.source, 156 "A DO loop must follow the %s directive"_err_en_US, 157 parser::ToUpperCaseLetters(dir.source.ToString())); 158 } 159 160 void RewriteOpenACCCombinedConstruct(parser::OpenACCCombinedConstruct &x, 161 parser::Block &block, parser::Block::iterator it) { 162 // Check the sequence of DoConstruct in the same iteration. 163 parser::Block::iterator nextIt; 164 auto &beginDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; 165 auto &dir{std::get<parser::AccCombinedDirective>(beginDir.t)}; 166 auto &nestedDo{std::get<std::optional<parser::DoConstruct>>(x.t)}; 167 168 if (!nestedDo) { 169 moveCompilerDirectivesBefore(block, it); 170 nextIt = it; 171 if (++nextIt != block.end()) { 172 if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) { 173 nestedDo = std::move(*doCons); 174 nextIt = block.erase(nextIt); 175 } 176 } 177 } 178 179 if (nestedDo) { 180 CheckDoConcurrentClauseRestriction<parser::OpenACCCombinedConstruct, 181 parser::AccBeginCombinedDirective>(x, *nestedDo); 182 CheckTileClauseRestriction<parser::OpenACCCombinedConstruct, 183 parser::AccBeginCombinedDirective>(x, *nestedDo); 184 if (!nestedDo->GetLoopControl()) { 185 messages_.Say(dir.source, 186 "DO loop after the %s directive must have loop control"_err_en_US, 187 parser::ToUpperCaseLetters(dir.source.ToString())); 188 return; 189 } 190 return; 191 } 192 messages_.Say(dir.source, 193 "A DO loop must follow the %s directive"_err_en_US, 194 parser::ToUpperCaseLetters(dir.source.ToString())); 195 } 196 197 parser::Messages &messages_; 198 }; 199 200 bool CanonicalizeAcc(parser::Messages &messages, parser::Program &program) { 201 CanonicalizationOfAcc acc{messages}; 202 Walk(program, acc); 203 return !messages.AnyFatalError(); 204 } 205 } // namespace Fortran::semantics 206