206 lines · cpp
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-exception6//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 OpenACC14// Constructs more structured which provide explicit scopes for later15// structural checks and semantic analysis.16// 1. move structured DoConstruct into17// OpenACCLoopConstruct. Compilation will not proceed in case of errors18// after this pass.19// 2. move structured DoConstruct into OpenACCCombinedConstruct. Move20// AccEndCombinedConstruct into OpenACCCombinedConstruct if present.21// Compilation will not proceed in case of errors after this pass.22namespace Fortran::semantics {23 24using namespace parser::literals;25 26class CanonicalizationOfAcc {27public: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 AccEndCombinedDirective42 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 list48 }49 50private:51 // Check constraint in 2.9.752 // If there are n tile sizes in the list, the loop construct must be53 // 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 CONCURRENT69 }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.989 // A tile and collapse clause may not appear on loop that is associated with90 // 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 right111 // before it. This allows preserving loop directives $DIR that may lie112 // between an $acc directive and loop and leave lowering decide if it should113 // 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 200bool CanonicalizeAcc(parser::Messages &messages, parser::Program &program) {201 CanonicalizationOfAcc acc{messages};202 Walk(program, acc);203 return !messages.AnyFatalError();204}205} // namespace Fortran::semantics206