| //===-- lib/Semantics/canonicalize-directives.cpp -------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "canonicalize-directives.h" | |
| #include "flang/Parser/parse-tree-visitor.h" | |
| #include "flang/Semantics/tools.h" | |
| namespace Fortran::semantics { | |
| using namespace parser::literals; | |
| // Check that directives are associated with the correct constructs. | |
| // Directives that need to be associated with other constructs in the execution | |
| // part are moved to the execution part so they can be checked there. | |
| class CanonicalizationOfDirectives { | |
| public: | |
| CanonicalizationOfDirectives(parser::Messages &messages) | |
| : messages_{messages} {} | |
| template <typename T> bool Pre(T &) { return true; } | |
| template <typename T> void Post(T &) {} | |
| // Move directives that must appear in the Execution part out of the | |
| // Specification part. | |
| void Post(parser::SpecificationPart &spec); | |
| bool Pre(parser::ExecutionPart &x); | |
| // Ensure that directives associated with constructs appear accompanying the | |
| // construct. | |
| void Post(parser::Block &block); | |
| private: | |
| // Ensure that loop directives appear immediately before a loop. | |
| void CheckLoopDirective(parser::CompilerDirective &dir, parser::Block &block, | |
| std::list<parser::ExecutionPartConstruct>::iterator it); | |
| parser::Messages &messages_; | |
| // Directives to be moved to the Execution part from the Specification part. | |
| std::list<common::Indirection<parser::CompilerDirective>> | |
| directivesToConvert_; | |
| }; | |
| bool CanonicalizeDirectives( | |
| parser::Messages &messages, parser::Program &program) { | |
| CanonicalizationOfDirectives dirs{messages}; | |
| Walk(program, dirs); | |
| return !messages.AnyFatalError(); | |
| } | |
| static bool IsExecutionDirective(const parser::CompilerDirective &dir) { | |
| return std::holds_alternative<parser::CompilerDirective::VectorAlways>( | |
| dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::VectorLength>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::Unroll>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::UnrollAndJam>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::NoVector>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::NoUnroll>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::NoUnrollAndJam>( | |
| dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::ForceInline>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::Inline>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::NoInline>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::IVDep>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::InlineAlways>(dir.u) || | |
| std::holds_alternative<parser::CompilerDirective::Simd>(dir.u); | |
| } | |
| void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) { | |
| auto &list{ | |
| std::get<std::list<common::Indirection<parser::CompilerDirective>>>( | |
| spec.t)}; | |
| for (auto it{list.begin()}; it != list.end();) { | |
| if (IsExecutionDirective(it->value())) { | |
| directivesToConvert_.emplace_back(std::move(*it)); | |
| it = list.erase(it); | |
| } else { | |
| ++it; | |
| } | |
| } | |
| } | |
| bool CanonicalizationOfDirectives::Pre(parser::ExecutionPart &x) { | |
| auto origFirst{x.v.begin()}; | |
| for (auto &dir : directivesToConvert_) { | |
| x.v.insert(origFirst, | |
| parser::ExecutionPartConstruct{ | |
| parser::ExecutableConstruct{std::move(dir)}}); | |
| } | |
| directivesToConvert_.clear(); | |
| return true; | |
| } | |
| void CanonicalizationOfDirectives::CheckLoopDirective( | |
| parser::CompilerDirective &dir, parser::Block &block, | |
| std::list<parser::ExecutionPartConstruct>::iterator it) { | |
| // Skip over this and other compiler directives | |
| while (it != block.end() && parser::Unwrap<parser::CompilerDirective>(*it)) { | |
| ++it; | |
| } | |
| if (it == block.end() || | |
| (!parser::Unwrap<parser::DoConstruct>(*it) && | |
| !parser::Unwrap<parser::OpenACCLoopConstruct>(*it) && | |
| !parser::Unwrap<parser::OpenACCCombinedConstruct>(*it))) { | |
| std::string s{parser::ToUpperCaseLetters(dir.source.ToString())}; | |
| s.pop_back(); // Remove trailing newline from source string | |
| messages_.Say( | |
| dir.source, "A DO loop must follow the %s directive"_warn_en_US, s); | |
| } | |
| } | |
| void CanonicalizationOfDirectives::Post(parser::Block &block) { | |
| for (auto it{block.begin()}; it != block.end(); ++it) { | |
| if (auto *dir{parser::Unwrap<parser::CompilerDirective>(*it)}) { | |
| std::visit( | |
| common::visitors{[&](parser::CompilerDirective::VectorAlways &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::VectorLength &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::Unroll &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::UnrollAndJam &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::NoVector &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::NoUnroll &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::NoUnrollAndJam &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::IVDep &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](parser::CompilerDirective::Simd &) { | |
| CheckLoopDirective(*dir, block, it); | |
| }, | |
| [&](auto &) {}}, | |
| dir->u); | |
| } | |
| } | |
| } | |
| } // namespace Fortran::semantics |