| //===-- lib/Parser/parse-tree.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 "flang/Parser/parse-tree.h" | |
| #include "flang/Common/idioms.h" | |
| #include "flang/Common/indirection.h" | |
| #include "flang/Parser/openmp-utils.h" | |
| #include "flang/Parser/tools.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/Frontend/OpenMP/OMP.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| namespace Fortran::parser { | |
| // R867 | |
| ImportStmt::ImportStmt(common::ImportKind &&k, std::list<Name> &&n) | |
| : t(k, std::move(n)) { | |
| const auto &[kind, names]{t}; | |
| CHECK(kind == common::ImportKind::Default || | |
| kind == common::ImportKind::Only || names.empty()); | |
| } | |
| // R873 | |
| CommonStmt::CommonStmt(std::optional<Name> &&name, | |
| std::list<CommonBlockObject> &&objects, std::list<Block> &&others) { | |
| v.emplace_front(std::move(name), std::move(objects)); | |
| v.splice(v.end(), std::move(others)); | |
| } | |
| // R901 designator | |
| bool Designator::EndsInBareName() const { | |
| return common::visit( | |
| common::visitors{ | |
| [](const DataRef &dr) { | |
| return std::holds_alternative<Name>(dr.u) || | |
| std::holds_alternative<common::Indirection<StructureComponent>>( | |
| dr.u); | |
| }, | |
| [](const Substring &) { return false; }, | |
| }, | |
| u); | |
| } | |
| // R911 data-ref -> part-ref [% part-ref]... | |
| DataRef::DataRef(std::list<PartRef> &&prl) | |
| : u{std::move(std::get<Name>(prl.front().t))} { | |
| for (bool first{true}; !prl.empty(); first = false, prl.pop_front()) { | |
| auto &&[name, subscripts, imageSelector]{prl.front().t}; | |
| if (!first) { | |
| u = common::Indirection<StructureComponent>::Make( | |
| std::move(*this), std::move(name)); | |
| } | |
| if (!subscripts.empty()) { | |
| u = common::Indirection<ArrayElement>::Make( | |
| std::move(*this), std::move(subscripts)); | |
| } | |
| if (imageSelector) { | |
| u = common::Indirection<CoindexedNamedObject>::Make( | |
| std::move(*this), std::move(*imageSelector)); | |
| } | |
| } | |
| } | |
| // R1001 - R1022 expression | |
| Expr::Expr(Designator &&x) | |
| : u{common::Indirection<Designator>::Make(std::move(x))} {} | |
| Expr::Expr(FunctionReference &&x) | |
| : u{common::Indirection<FunctionReference>::Make(std::move(x))} {} | |
| const std::optional<LoopControl> &DoConstruct::GetLoopControl() const { | |
| const NonLabelDoStmt &doStmt{ | |
| std::get<Statement<NonLabelDoStmt>>(t).statement}; | |
| const std::optional<LoopControl> &control{ | |
| std::get<std::optional<LoopControl>>(doStmt.t)}; | |
| return control; | |
| } | |
| bool DoConstruct::IsDoNormal() const { | |
| const std::optional<LoopControl> &control{GetLoopControl()}; | |
| return control && std::holds_alternative<LoopControl::Bounds>(control->u); | |
| } | |
| bool DoConstruct::IsDoWhile() const { | |
| const std::optional<LoopControl> &control{GetLoopControl()}; | |
| return control && std::holds_alternative<ScalarLogicalExpr>(control->u); | |
| } | |
| bool DoConstruct::IsDoConcurrent() const { | |
| const std::optional<LoopControl> &control{GetLoopControl()}; | |
| return control && std::holds_alternative<LoopControl::Concurrent>(control->u); | |
| } | |
| static Designator MakeArrayElementRef( | |
| const Name &name, std::list<Expr> &&subscripts) { | |
| ArrayElement arrayElement{DataRef{Name{name}}, std::list<SectionSubscript>{}}; | |
| for (Expr &expr : subscripts) { | |
| std::get<std::list<SectionSubscript>>(arrayElement.t) | |
| .push_back( | |
| SectionSubscript{Integer{common::Indirection{std::move(expr)}}}); | |
| } | |
| return Designator{DataRef{common::Indirection{std::move(arrayElement)}}}; | |
| } | |
| static Designator MakeArrayElementRef( | |
| StructureComponent &&sc, std::list<Expr> &&subscripts) { | |
| ArrayElement arrayElement{DataRef{common::Indirection{std::move(sc)}}, | |
| std::list<SectionSubscript>{}}; | |
| for (Expr &expr : subscripts) { | |
| std::get<std::list<SectionSubscript>>(arrayElement.t) | |
| .push_back( | |
| SectionSubscript{Integer{common::Indirection{std::move(expr)}}}); | |
| } | |
| return Designator{DataRef{common::Indirection{std::move(arrayElement)}}}; | |
| } | |
| // Set source in any type of node that has it. | |
| template <typename T> T WithSource(CharBlock source, T &&x) { | |
| x.source = source; | |
| return std::move(x); | |
| } | |
| static Expr ActualArgToExpr(ActualArgSpec &arg) { | |
| return common::visit( | |
| common::visitors{ | |
| [&](common::Indirection<Expr> &y) { return std::move(y.value()); }, | |
| [&](common::Indirection<Variable> &y) { | |
| return common::visit( | |
| common::visitors{ | |
| [&](common::Indirection<Designator> &z) { | |
| return WithSource( | |
| z.value().source, Expr{std::move(z.value())}); | |
| }, | |
| [&](common::Indirection<FunctionReference> &z) { | |
| return WithSource( | |
| z.value().source, Expr{std::move(z.value())}); | |
| }, | |
| }, | |
| y.value().u); | |
| }, | |
| [&](auto &) -> Expr { common::die("unexpected type"); }, | |
| }, | |
| std::get<ActualArg>(arg.t).u); | |
| } | |
| Designator FunctionReference::ConvertToArrayElementRef() { | |
| std::list<Expr> args; | |
| for (auto &arg : std::get<std::list<ActualArgSpec>>(v.t)) { | |
| args.emplace_back(ActualArgToExpr(arg)); | |
| } | |
| return common::visit( | |
| common::visitors{ | |
| [&](const Name &name) { | |
| return WithSource( | |
| source, MakeArrayElementRef(name, std::move(args))); | |
| }, | |
| [&](ProcComponentRef &pcr) { | |
| return WithSource(source, | |
| MakeArrayElementRef(std::move(pcr.v.thing), std::move(args))); | |
| }, | |
| }, | |
| std::get<ProcedureDesignator>(v.t).u); | |
| } | |
| StructureConstructor FunctionReference::ConvertToStructureConstructor( | |
| const semantics::DerivedTypeSpec &derived) { | |
| Name name{std::get<parser::Name>(std::get<ProcedureDesignator>(v.t).u)}; | |
| std::list<ComponentSpec> components; | |
| for (auto &arg : std::get<std::list<ActualArgSpec>>(v.t)) { | |
| std::optional<Keyword> keyword; | |
| if (auto &kw{std::get<std::optional<Keyword>>(arg.t)}) { | |
| keyword.emplace(Keyword{Name{kw->v}}); | |
| } | |
| components.emplace_back( | |
| std::move(keyword), ComponentDataSource{ActualArgToExpr(arg)}); | |
| } | |
| DerivedTypeSpec spec{std::move(name), std::list<TypeParamSpec>{}}; | |
| spec.derivedTypeSpec = &derived; | |
| return StructureConstructor{std::move(spec), std::move(components)}; | |
| } | |
| StructureConstructor ArrayElement::ConvertToStructureConstructor( | |
| const semantics::DerivedTypeSpec &derived) { | |
| auto &[base, subscripts]{t}; | |
| Name name{std::get<parser::Name>(base.u)}; | |
| std::list<ComponentSpec> components; | |
| for (auto &subscript : subscripts) { | |
| components.emplace_back(std::optional<Keyword>{}, | |
| ComponentDataSource{std::move(UnwrapRef<Expr>(subscript))}); | |
| } | |
| DerivedTypeSpec spec{std::move(name), std::list<TypeParamSpec>{}}; | |
| spec.derivedTypeSpec = &derived; | |
| return StructureConstructor{std::move(spec), std::move(components)}; | |
| } | |
| Substring ArrayElement::ConvertToSubstring() { | |
| auto &[base, subscripts]{t}; | |
| auto iter{subscripts.begin()}; | |
| CHECK(iter != subscripts.end()); | |
| auto &triplet{std::get<SubscriptTriplet>(iter->u)}; | |
| CHECK(!std::get<2>(triplet.t)); | |
| CHECK(++iter == subscripts.end()); | |
| return Substring{std::move(base), | |
| SubstringRange{std::get<0>(std::move(triplet.t)), | |
| std::get<1>(std::move(triplet.t))}}; | |
| } | |
| // R1544 stmt-function-stmt | |
| // Convert this stmt-function-stmt to an assignment to the result of a | |
| // pointer-valued function call -- which itself will be converted to a | |
| // much more likely array element assignment statement if it needs | |
| // to be. | |
| Statement<ActionStmt> StmtFunctionStmt::ConvertToAssignment() { | |
| auto &funcName{std::get<Name>(t)}; | |
| auto &funcArgs{std::get<std::list<Name>>(t)}; | |
| auto &funcExpr{std::get<Scalar<Expr>>(t).thing}; | |
| CharBlock source{funcName.source}; | |
| // Extend source to include closing parenthesis | |
| if (funcArgs.empty()) { | |
| CHECK(*source.end() == '('); | |
| source = CharBlock{source.begin(), source.end() + 1}; | |
| } | |
| std::list<ActualArgSpec> actuals; | |
| for (const Name &arg : funcArgs) { | |
| actuals.emplace_back(std::optional<Keyword>{}, | |
| ActualArg{Expr{WithSource( | |
| arg.source, Designator{DataRef{Name{arg.source, arg.symbol}}})}}); | |
| source.ExtendToCover(arg.source); | |
| } | |
| CHECK(*source.end() == ')'); | |
| source = CharBlock{source.begin(), source.end() + 1}; | |
| FunctionReference funcRef{ | |
| Call{ProcedureDesignator{Name{funcName.source, funcName.symbol}}, | |
| std::move(actuals)}}; | |
| funcRef.source = source; | |
| auto variable{Variable{common::Indirection{std::move(funcRef)}}}; | |
| return Statement{std::nullopt, | |
| ActionStmt{common::Indirection{ | |
| AssignmentStmt{std::move(variable), std::move(funcExpr)}}}}; | |
| } | |
| CharBlock Variable::GetSource() const { | |
| return common::visit( | |
| common::visitors{ | |
| [&](const common::Indirection<Designator> &des) { | |
| return des.value().source; | |
| }, | |
| [&](const common::Indirection<parser::FunctionReference> &call) { | |
| return call.value().source; | |
| }, | |
| }, | |
| u); | |
| } | |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Name &x) { | |
| return os << x.ToString(); | |
| } | |
| OmpDirectiveName::OmpDirectiveName(const Verbatim &name) { | |
| std::string_view nameView{name.source.begin(), name.source.size()}; | |
| std::string nameLower{ToLowerCaseLetters(nameView)}; | |
| // The function getOpenMPDirectiveKind will return OMPD_unknown in two cases: | |
| // (1) if the given string doesn't match any actual directive, or | |
| // (2) if the given string was "unknown". | |
| // The Verbatim(<token>) parser will succeed as long as the given token | |
| // matches the source. | |
| // Since using "construct<OmpDirectiveName>(verbatim(...))" will succeed | |
| // if the verbatim parser succeeds, in order to get OMPD_unknown the | |
| // token given to Verbatim must be invalid. Because it's an internal issue | |
| // asserting is ok. | |
| v = llvm::omp::getOpenMPDirectiveKind(nameLower); | |
| assert(v != llvm::omp::Directive::OMPD_unknown && "Invalid directive name"); | |
| source = name.source; | |
| } | |
| OmpDependenceType::Value OmpDoacross::GetDepType() const { | |
| auto &modifiers{std::get<std::optional<std::list<Modifier>>>(t)}; | |
| if (modifiers && !modifiers->empty()) { | |
| return common::visit([](auto &&s) { return s.v; }, modifiers->front().u); | |
| } else { | |
| llvm_unreachable("expecting modifiers on OmpDoacross"); | |
| } | |
| } | |
| OmpTaskDependenceType::Value OmpDependClause::TaskDep::GetTaskDepType() const { | |
| using Modifier = OmpDependClause::TaskDep::Modifier; | |
| auto &modifiers{std::get<std::optional<std::list<Modifier>>>(t)}; | |
| if (modifiers) { | |
| for (auto &m : *modifiers) { | |
| if (auto *dep{std::get_if<OmpTaskDependenceType>(&m.u)}) { | |
| return dep->v; | |
| } | |
| } | |
| llvm_unreachable("expecting OmpTaskDependenceType in TaskDep"); | |
| } else { | |
| llvm_unreachable("expecting modifiers on OmpDependClause::TaskDep"); | |
| } | |
| } | |
| std::string OmpTraitSelectorName::ToString() const { | |
| return common::visit( // | |
| common::visitors{ | |
| [&](Value v) { // | |
| return std::string(EnumToString(v)); | |
| }, | |
| [&](llvm::omp::Directive d) { | |
| return llvm::omp::getOpenMPDirectiveName( | |
| d, llvm::omp::FallbackVersion) | |
| .str(); | |
| }, | |
| [&](const std::string &s) { // | |
| return s; | |
| }, | |
| }, | |
| u); | |
| } | |
| std::string OmpTraitSetSelectorName::ToString() const { | |
| return std::string(EnumToString(v)); | |
| } | |
| llvm::omp::Clause OpenMPAtomicConstruct::GetKind() const { | |
| const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)}; | |
| for (auto &clause : dirSpec.Clauses().v) { | |
| switch (clause.Id()) { | |
| case llvm::omp::Clause::OMPC_read: | |
| case llvm::omp::Clause::OMPC_write: | |
| case llvm::omp::Clause::OMPC_update: | |
| return clause.Id(); | |
| default: | |
| break; | |
| } | |
| } | |
| return llvm::omp::Clause::OMPC_update; | |
| } | |
| bool OpenMPAtomicConstruct::IsCapture() const { | |
| const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)}; | |
| return omp::FindClause(dirSpec, llvm::omp::Clause::OMPC_capture); | |
| } | |
| bool OpenMPAtomicConstruct::IsCompare() const { | |
| const OmpDirectiveSpecification &dirSpec{std::get<OmpBeginDirective>(t)}; | |
| return omp::FindClause(dirSpec, llvm::omp::Clause::OMPC_compare); | |
| } | |
| } // namespace Fortran::parser | |
| template <typename C> static llvm::omp::Clause getClauseIdForClass(C &&) { | |
| using namespace Fortran; | |
| using A = llvm::remove_cvref_t<C>; // A is referenced in OMP.inc | |
| // The code included below contains a sequence of checks like the following | |
| // for each OpenMP clause | |
| // if constexpr (std::is_same_v<A, parser::OmpClause::AcqRel>) | |
| // return llvm::omp::Clause::OMPC_acq_rel; | |
| // [...] | |
| #define GEN_FLANG_CLAUSE_PARSER_KIND_MAP | |
| #include "llvm/Frontend/OpenMP/OMP.inc" | |
| } | |
| namespace Fortran::parser { | |
| llvm::omp::Clause OmpClause::Id() const { | |
| return std::visit([](auto &&s) { return getClauseIdForClass(s); }, u); | |
| } | |
| bool OmpDirectiveName::IsExecutionPart() const { | |
| // Can the directive appear in the execution part of the program. | |
| llvm::omp::Directive id{v}; | |
| switch (llvm::omp::getDirectiveCategory(id)) { | |
| case llvm::omp::Category::Executable: | |
| return true; | |
| case llvm::omp::Category::Declarative: | |
| switch (id) { | |
| case llvm::omp::Directive::OMPD_allocate: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| break; | |
| case llvm::omp::Category::Informational: | |
| switch (id) { | |
| case llvm::omp::Directive::OMPD_assume: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| break; | |
| case llvm::omp::Category::Meta: | |
| return true; | |
| case llvm::omp::Category::Subsidiary: | |
| switch (id) { | |
| // TODO: case llvm::omp::Directive::OMPD_task_iteration: | |
| case llvm::omp::Directive::OMPD_section: | |
| case llvm::omp::Directive::OMPD_scan: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| break; | |
| case llvm::omp::Category::Utility: | |
| switch (id) { | |
| case llvm::omp::Directive::OMPD_error: | |
| case llvm::omp::Directive::OMPD_nothing: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| break; | |
| } | |
| return false; | |
| } | |
| const OmpArgumentList &OmpDirectiveSpecification::Arguments() const { | |
| static OmpArgumentList empty{decltype(OmpArgumentList::v){}}; | |
| if (auto &arguments = std::get<std::optional<OmpArgumentList>>(t)) { | |
| return *arguments; | |
| } | |
| return empty; | |
| } | |
| const OmpClauseList &OmpDirectiveSpecification::Clauses() const { | |
| static OmpClauseList empty{decltype(OmpClauseList::v){}}; | |
| if (auto &clauses = std::get<std::optional<OmpClauseList>>(t)) { | |
| return *clauses; | |
| } | |
| return empty; | |
| } | |
| const DoConstruct *OpenMPLoopConstruct::GetNestedLoop() const { | |
| auto getFromBlock{[](const Block &body, auto self) -> const DoConstruct * { | |
| for (auto &stmt : body) { | |
| if (auto *block{Unwrap<BlockConstruct>(&stmt)}) { | |
| return self(std::get<Block>(block->t), self); | |
| } | |
| if (auto *loop{Unwrap<DoConstruct>(&stmt)}) { | |
| return loop; | |
| } | |
| } | |
| return nullptr; | |
| }}; | |
| return getFromBlock(std::get<Block>(t), getFromBlock); | |
| } | |
| const OpenMPLoopConstruct *OpenMPLoopConstruct::GetNestedConstruct() const { | |
| auto getFromBlock{ | |
| [](const Block &body, auto self) -> const OpenMPLoopConstruct * { | |
| for (auto &stmt : body) { | |
| if (auto *block{Unwrap<BlockConstruct>(&stmt)}) { | |
| return self(std::get<Block>(block->t), self); | |
| } | |
| if (auto *omp{Unwrap<OpenMPLoopConstruct>(&stmt)}) { | |
| return omp; | |
| } | |
| } | |
| return nullptr; | |
| }}; | |
| return getFromBlock(std::get<Block>(t), getFromBlock); | |
| } | |
| static bool InitCharBlocksFromStrings(llvm::MutableArrayRef<CharBlock> blocks, | |
| llvm::ArrayRef<std::string> strings) { | |
| for (auto [i, n] : llvm::enumerate(strings)) { | |
| blocks[i] = CharBlock(n); | |
| } | |
| return true; | |
| } | |
| // The names should have static storage duration. Keep these names | |
| // in a sigle place. | |
| llvm::ArrayRef<CharBlock> OmpCombinerExpression::Variables() { | |
| static std::string names[]{"omp_in", "omp_out"}; | |
| static CharBlock vars[std::size(names)]; | |
| [[maybe_unused]] static bool init = InitCharBlocksFromStrings(vars, names); | |
| return vars; | |
| } | |
| llvm::ArrayRef<CharBlock> OmpInitializerExpression::Variables() { | |
| static std::string names[]{"omp_orig", "omp_priv"}; | |
| static CharBlock vars[std::size(names)]; | |
| [[maybe_unused]] static bool init = InitCharBlocksFromStrings(vars, names); | |
| return vars; | |
| } | |
| } // namespace Fortran::parser |