[flang] Batch together the changes to the PFT intermediate data
structure for upstreaming to llvm-project. These files have had many changes since they were originally upstreamed. Some of the changes are cosmetic. Most of the functional changes were done to support the lowering of control-flow syntax from the front-end parse trees to the FIR dialect. This patch is meant to be a reviewable size. The functionality it provides will be used by code yet to be upstreamed in lowering. review comments: [review D80449][NFC] make PFT ParentVariant a ReferenceVariant ReferenceVariant had to be slightly updated to also support non constant references (which is required for ParentType). [review D80449] extend Variable implementation beyond a comment
This commit is contained in:
parent
e4d2037a5c
commit
fbac9ce226
|
@ -1,35 +1,35 @@
|
||||||
//===-- include/flang/Lower/PFTBuilder.h ------------------------*- C++ -*-===//
|
//===-- Lower/PFTBuilder.h -- PFT builder -----------------------*- C++ -*-===//
|
||||||
//
|
//
|
||||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
// See https://llvm.org/LICENSE.txt for license information.
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// PFT (Pre-FIR Tree) interface.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#ifndef FORTRAN_LOWER_PFT_BUILDER_H_
|
#ifndef FORTRAN_LOWER_PFTBUILDER_H
|
||||||
#define FORTRAN_LOWER_PFT_BUILDER_H_
|
#define FORTRAN_LOWER_PFTBUILDER_H
|
||||||
|
|
||||||
|
#include "flang/Common/reference.h"
|
||||||
#include "flang/Common/template.h"
|
#include "flang/Common/template.h"
|
||||||
#include "flang/Parser/parse-tree.h"
|
#include "flang/Parser/parse-tree.h"
|
||||||
#include <memory>
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
#include "llvm/ADT/SmallSet.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
/// Build a light-weight tree over the parse-tree to help with lowering to FIR.
|
namespace mlir {
|
||||||
/// It is named Pre-FIR Tree (PFT) to underline it has no other usage than
|
class Block;
|
||||||
/// helping lowering to FIR.
|
|
||||||
/// The PFT will capture pointers back into the parse tree, so the parse tree
|
|
||||||
/// data structure may <em>not</em> be changed between the construction of the
|
|
||||||
/// PFT and all of its uses.
|
|
||||||
///
|
|
||||||
/// The PFT captures a structured view of the program. The program is a list of
|
|
||||||
/// units. Function like units will contain lists of evaluations. Evaluations
|
|
||||||
/// are either statements or constructs, where a construct contains a list of
|
|
||||||
/// evaluations. The resulting PFT structure can then be used to create FIR.
|
|
||||||
|
|
||||||
namespace llvm {
|
|
||||||
class raw_ostream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Fortran::lower {
|
namespace Fortran {
|
||||||
|
namespace semantics {
|
||||||
|
class SemanticsContext;
|
||||||
|
class Scope;
|
||||||
|
} // namespace semantics
|
||||||
|
namespace lower {
|
||||||
namespace pft {
|
namespace pft {
|
||||||
|
|
||||||
struct Evaluation;
|
struct Evaluation;
|
||||||
|
@ -40,40 +40,56 @@ struct FunctionLikeUnit;
|
||||||
// TODO: A collection of Evaluations can obviously be any of the container
|
// TODO: A collection of Evaluations can obviously be any of the container
|
||||||
// types; leaving this as a std::list _for now_ because we reserve the right to
|
// types; leaving this as a std::list _for now_ because we reserve the right to
|
||||||
// insert PFT nodes in any order in O(1) time.
|
// insert PFT nodes in any order in O(1) time.
|
||||||
using EvaluationCollection = std::list<Evaluation>;
|
using EvaluationList = std::list<Evaluation>;
|
||||||
|
using LabelEvalMap = llvm::DenseMap<Fortran::parser::Label, Evaluation *>;
|
||||||
|
|
||||||
struct ParentType {
|
/// Provide a variant like container that can hold references. It can hold
|
||||||
template <typename A>
|
/// constant or mutable references. It is used in the other classes to provide
|
||||||
ParentType(A &parent) : p{&parent} {}
|
/// union of const references to parse-tree nodes.
|
||||||
const std::variant<Program *, ModuleLikeUnit *, FunctionLikeUnit *,
|
template <bool isConst, typename... A>
|
||||||
Evaluation *>
|
class ReferenceVariantBase {
|
||||||
p;
|
public:
|
||||||
};
|
template <typename B>
|
||||||
|
using BaseType = std::conditional_t<isConst, const B, B>;
|
||||||
|
template <typename B>
|
||||||
|
using Ref = common::Reference<BaseType<B>>;
|
||||||
|
|
||||||
/// Flags to describe the impact of parse-trees nodes on the program
|
ReferenceVariantBase() = delete;
|
||||||
/// control flow. These annotations to parse-tree nodes are later used to
|
template <typename B>
|
||||||
/// build the control flow graph when lowering to FIR.
|
ReferenceVariantBase(B &b) : u{Ref<B>{b}} {}
|
||||||
enum class CFGAnnotation {
|
|
||||||
None, // Node does not impact control flow.
|
|
||||||
Goto, // Node acts like a goto on the control flow.
|
|
||||||
CondGoto, // Node acts like a conditional goto on the control flow.
|
|
||||||
IndGoto, // Node acts like an indirect goto on the control flow.
|
|
||||||
IoSwitch, // Node is an IO statement with ERR, END, or EOR specifier.
|
|
||||||
Switch, // Node acts like a switch on the control flow.
|
|
||||||
Iterative, // Node creates iterations in the control flow.
|
|
||||||
FirStructuredOp, // Node is a structured loop.
|
|
||||||
Return, // Node triggers a return from the current procedure.
|
|
||||||
Terminate // Node terminates the program.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Compiler-generated jump
|
template <typename B>
|
||||||
///
|
constexpr BaseType<B> &get() const {
|
||||||
/// This is used to convert implicit control-flow edges to explicit form in the
|
return std::get<Ref<B>> > (u).get();
|
||||||
/// decorated PFT
|
}
|
||||||
struct CGJump {
|
template <typename B>
|
||||||
CGJump(Evaluation &to) : target{to} {}
|
constexpr BaseType<B> *getIf() const {
|
||||||
Evaluation ⌖
|
auto *ptr = std::get_if<Ref<B>>(&u);
|
||||||
|
return ptr ? &ptr->get() : nullptr;
|
||||||
|
}
|
||||||
|
template <typename B>
|
||||||
|
constexpr bool isA() const {
|
||||||
|
return std::holds_alternative<Ref<B>>(u);
|
||||||
|
}
|
||||||
|
template <typename VISITOR>
|
||||||
|
constexpr auto visit(VISITOR &&visitor) const {
|
||||||
|
return std::visit(
|
||||||
|
common::visitors{[&visitor](auto ref) { return visitor(ref.get()); }},
|
||||||
|
u);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::variant<Ref<A>...> u;
|
||||||
};
|
};
|
||||||
|
template <typename... A>
|
||||||
|
using ReferenceVariant = ReferenceVariantBase<true, A...>;
|
||||||
|
template <typename... A>
|
||||||
|
using MutableReferenceVariant = ReferenceVariantBase<false, A...>;
|
||||||
|
|
||||||
|
/// ParentVariant is used to provide a reference to the unit a parse-tree node
|
||||||
|
/// belongs to. It is a variant of non-nullable pointers.
|
||||||
|
using ParentVariant = MutableReferenceVariant<Program, ModuleLikeUnit,
|
||||||
|
FunctionLikeUnit, Evaluation>;
|
||||||
|
|
||||||
/// Classify the parse-tree nodes from ExecutablePartConstruct
|
/// Classify the parse-tree nodes from ExecutablePartConstruct
|
||||||
|
|
||||||
|
@ -95,15 +111,6 @@ using ActionStmts = std::tuple<
|
||||||
using OtherStmts = std::tuple<parser::FormatStmt, parser::EntryStmt,
|
using OtherStmts = std::tuple<parser::FormatStmt, parser::EntryStmt,
|
||||||
parser::DataStmt, parser::NamelistStmt>;
|
parser::DataStmt, parser::NamelistStmt>;
|
||||||
|
|
||||||
using Constructs =
|
|
||||||
std::tuple<parser::AssociateConstruct, parser::BlockConstruct,
|
|
||||||
parser::CaseConstruct, parser::ChangeTeamConstruct,
|
|
||||||
parser::CriticalConstruct, parser::DoConstruct,
|
|
||||||
parser::IfConstruct, parser::SelectRankConstruct,
|
|
||||||
parser::SelectTypeConstruct, parser::WhereConstruct,
|
|
||||||
parser::ForallConstruct, parser::CompilerDirective,
|
|
||||||
parser::OpenMPConstruct, parser::OmpEndLoopDirective>;
|
|
||||||
|
|
||||||
using ConstructStmts = std::tuple<
|
using ConstructStmts = std::tuple<
|
||||||
parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt,
|
parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt,
|
||||||
parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt,
|
parser::EndBlockStmt, parser::SelectCaseStmt, parser::CaseStmt,
|
||||||
|
@ -115,257 +122,342 @@ using ConstructStmts = std::tuple<
|
||||||
parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt,
|
parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt,
|
||||||
parser::ForallConstructStmt, parser::EndForallStmt>;
|
parser::ForallConstructStmt, parser::EndForallStmt>;
|
||||||
|
|
||||||
template <typename A>
|
using Constructs =
|
||||||
constexpr static bool isActionStmt{common::HasMember<A, ActionStmts>};
|
std::tuple<parser::AssociateConstruct, parser::BlockConstruct,
|
||||||
|
parser::CaseConstruct, parser::ChangeTeamConstruct,
|
||||||
|
parser::CriticalConstruct, parser::DoConstruct,
|
||||||
|
parser::IfConstruct, parser::SelectRankConstruct,
|
||||||
|
parser::SelectTypeConstruct, parser::WhereConstruct,
|
||||||
|
parser::ForallConstruct>;
|
||||||
|
|
||||||
|
using Directives =
|
||||||
|
std::tuple<parser::CompilerDirective, parser::OpenMPConstruct,
|
||||||
|
parser::OmpEndLoopDirective>;
|
||||||
|
|
||||||
template <typename A>
|
template <typename A>
|
||||||
constexpr static bool isConstruct{common::HasMember<A, Constructs>};
|
static constexpr bool isActionStmt{common::HasMember<A, ActionStmts>};
|
||||||
|
|
||||||
template <typename A>
|
template <typename A>
|
||||||
constexpr static bool isConstructStmt{common::HasMember<A, ConstructStmts>};
|
static constexpr bool isOtherStmt{common::HasMember<A, OtherStmts>};
|
||||||
|
|
||||||
template <typename A>
|
template <typename A>
|
||||||
constexpr static bool isOtherStmt{common::HasMember<A, OtherStmts>};
|
static constexpr bool isConstructStmt{common::HasMember<A, ConstructStmts>};
|
||||||
|
|
||||||
template <typename A>
|
template <typename A>
|
||||||
constexpr static bool isGenerated{std::is_same_v<A, CGJump>};
|
static constexpr bool isConstruct{common::HasMember<A, Constructs>};
|
||||||
|
|
||||||
template <typename A>
|
template <typename A>
|
||||||
constexpr static bool isFunctionLike{common::HasMember<
|
static constexpr bool isDirective{common::HasMember<A, Directives>};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
static constexpr bool isIntermediateConstructStmt{common::HasMember<
|
||||||
|
A, std::tuple<parser::CaseStmt, parser::ElseIfStmt, parser::ElseStmt,
|
||||||
|
parser::SelectRankCaseStmt, parser::TypeGuardStmt>>};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
static constexpr bool isNopConstructStmt{common::HasMember<
|
||||||
|
A, std::tuple<parser::EndAssociateStmt, parser::CaseStmt,
|
||||||
|
parser::EndSelectStmt, parser::ElseIfStmt, parser::ElseStmt,
|
||||||
|
parser::EndIfStmt, parser::SelectRankCaseStmt,
|
||||||
|
parser::TypeGuardStmt>>};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
static constexpr bool isFunctionLike{common::HasMember<
|
||||||
A, std::tuple<parser::MainProgram, parser::FunctionSubprogram,
|
A, std::tuple<parser::MainProgram, parser::FunctionSubprogram,
|
||||||
parser::SubroutineSubprogram,
|
parser::SubroutineSubprogram,
|
||||||
parser::SeparateModuleSubprogram>>};
|
parser::SeparateModuleSubprogram>>};
|
||||||
|
|
||||||
/// Function-like units can contains lists of evaluations. These can be
|
using LabelSet = llvm::SmallSet<parser::Label, 5>;
|
||||||
/// (simple) statements or constructs, where a construct contains its own
|
using SymbolRef = common::Reference<const semantics::Symbol>;
|
||||||
/// evaluations.
|
using SymbolLabelMap = llvm::DenseMap<SymbolRef, LabelSet>;
|
||||||
struct Evaluation {
|
|
||||||
using EvalTuple = common::CombineTuples<ActionStmts, OtherStmts, Constructs,
|
|
||||||
ConstructStmts>;
|
|
||||||
|
|
||||||
/// Hide non-nullable pointers to the parse-tree node.
|
template <typename A>
|
||||||
template <typename A>
|
struct MakeReferenceVariantHelper {};
|
||||||
using MakeRefType = const A *const;
|
template <typename... A>
|
||||||
using EvalVariant =
|
struct MakeReferenceVariantHelper<std::variant<A...>> {
|
||||||
common::CombineVariants<common::MapTemplate<MakeRefType, EvalTuple>,
|
using type = ReferenceVariant<A...>;
|
||||||
std::variant<CGJump>>;
|
};
|
||||||
template <typename A>
|
template <typename... A>
|
||||||
constexpr auto visit(A visitor) const {
|
struct MakeReferenceVariantHelper<std::tuple<A...>> {
|
||||||
return std::visit(common::visitors{
|
using type = ReferenceVariant<A...>;
|
||||||
[&](const auto *p) { return visitor(*p); },
|
};
|
||||||
[&](auto &r) { return visitor(r); },
|
template <typename A>
|
||||||
},
|
using MakeReferenceVariant = typename MakeReferenceVariantHelper<A>::type;
|
||||||
u);
|
|
||||||
}
|
|
||||||
template <typename A>
|
|
||||||
constexpr const A *getIf() const {
|
|
||||||
if constexpr (!std::is_same_v<A, CGJump>) {
|
|
||||||
if (auto *ptr{std::get_if<MakeRefType<A>>(&u)}) {
|
|
||||||
return *ptr;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return std::get_if<CGJump>(&u);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
template <typename A>
|
|
||||||
constexpr bool isA() const {
|
|
||||||
if constexpr (!std::is_same_v<A, CGJump>) {
|
|
||||||
return std::holds_alternative<MakeRefType<A>>(u);
|
|
||||||
}
|
|
||||||
return std::holds_alternative<CGJump>(u);
|
|
||||||
}
|
|
||||||
|
|
||||||
Evaluation() = delete;
|
using EvaluationTuple =
|
||||||
Evaluation(const Evaluation &) = delete;
|
common::CombineTuples<ActionStmts, OtherStmts, ConstructStmts, Constructs,
|
||||||
Evaluation(Evaluation &&) = default;
|
Directives>;
|
||||||
|
/// Hide non-nullable pointers to the parse-tree node.
|
||||||
|
/// Build type std::variant<const A* const, const B* const, ...>
|
||||||
|
/// from EvaluationTuple type (std::tuple<A, B, ...>).
|
||||||
|
using EvaluationVariant = MakeReferenceVariant<EvaluationTuple>;
|
||||||
|
|
||||||
|
/// Function-like units contain lists of evaluations. These can be simple
|
||||||
|
/// statements or constructs, where a construct contains its own evaluations.
|
||||||
|
struct Evaluation : EvaluationVariant {
|
||||||
|
|
||||||
/// General ctor
|
/// General ctor
|
||||||
template <typename A>
|
template <typename A>
|
||||||
Evaluation(const A &a, const ParentType &p, const parser::CharBlock &pos,
|
Evaluation(const A &a, const ParentVariant &parentVariant,
|
||||||
const std::optional<parser::Label> &lab)
|
const parser::CharBlock &position,
|
||||||
: u{&a}, parent{p}, pos{pos}, lab{lab} {}
|
const std::optional<parser::Label> &label)
|
||||||
|
: EvaluationVariant{a},
|
||||||
/// Compiler-generated jump
|
parentVariant{parentVariant}, position{position}, label{label} {}
|
||||||
Evaluation(const CGJump &jump, const ParentType &p)
|
|
||||||
: u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {}
|
|
||||||
|
|
||||||
/// Construct ctor
|
/// Construct ctor
|
||||||
template <typename A>
|
template <typename A>
|
||||||
Evaluation(const A &a, const ParentType &parent) : u{&a}, parent{parent} {
|
Evaluation(const A &a, const ParentVariant &parentVariant)
|
||||||
static_assert(pft::isConstruct<A>, "must be a construct");
|
: EvaluationVariant{a}, parentVariant{parentVariant} {
|
||||||
|
static_assert(pft::isConstruct<A> || pft::isDirective<A>,
|
||||||
|
"must be a construct or directive");
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool isActionOrGenerated() const {
|
/// Evaluation classification predicates.
|
||||||
|
constexpr bool isActionStmt() const {
|
||||||
return visit(common::visitors{
|
return visit(common::visitors{
|
||||||
[](auto &r) {
|
[](auto &r) { return pft::isActionStmt<std::decay_t<decltype(r)>>; }});
|
||||||
using T = std::decay_t<decltype(r)>;
|
|
||||||
return isActionStmt<T> || isGenerated<T>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
constexpr bool isOtherStmt() const {
|
||||||
constexpr bool isStmt() const {
|
|
||||||
return visit(common::visitors{
|
return visit(common::visitors{
|
||||||
[](auto &r) {
|
[](auto &r) { return pft::isOtherStmt<std::decay_t<decltype(r)>>; }});
|
||||||
using T = std::decay_t<decltype(r)>;
|
|
||||||
static constexpr bool isStmt{isActionStmt<T> || isOtherStmt<T> ||
|
|
||||||
isConstructStmt<T>};
|
|
||||||
static_assert(!(isStmt && pft::isConstruct<T>),
|
|
||||||
"statement classification is inconsistent");
|
|
||||||
return isStmt;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
constexpr bool isConstruct() const { return !isStmt(); }
|
constexpr bool isConstructStmt() const {
|
||||||
|
return visit(common::visitors{[](auto &r) {
|
||||||
/// Set the type of originating control flow type for this evaluation.
|
return pft::isConstructStmt<std::decay_t<decltype(r)>>;
|
||||||
void setCFG(CFGAnnotation a, Evaluation *cstr) {
|
}});
|
||||||
cfg = a;
|
}
|
||||||
setBranches(cstr);
|
constexpr bool isConstruct() const {
|
||||||
|
return visit(common::visitors{
|
||||||
|
[](auto &r) { return pft::isConstruct<std::decay_t<decltype(r)>>; }});
|
||||||
|
}
|
||||||
|
constexpr bool isDirective() const {
|
||||||
|
return visit(common::visitors{
|
||||||
|
[](auto &r) { return pft::isDirective<std::decay_t<decltype(r)>>; }});
|
||||||
|
}
|
||||||
|
/// Return the predicate: "This is a non-initial, non-terminal construct
|
||||||
|
/// statement." For an IfConstruct, this is ElseIfStmt and ElseStmt.
|
||||||
|
constexpr bool isIntermediateConstructStmt() const {
|
||||||
|
return visit(common::visitors{[](auto &r) {
|
||||||
|
return pft::isIntermediateConstructStmt<std::decay_t<decltype(r)>>;
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
constexpr bool isNopConstructStmt() const {
|
||||||
|
return visit(common::visitors{[](auto &r) {
|
||||||
|
return pft::isNopConstructStmt<std::decay_t<decltype(r)>>;
|
||||||
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this evaluation a control-flow origin? (The PFT must be annotated)
|
/// Return FunctionLikeUnit to which this evaluation
|
||||||
bool isControlOrigin() const { return cfg != CFGAnnotation::None; }
|
/// belongs. Nullptr if it does not belong to such unit.
|
||||||
|
FunctionLikeUnit *getOwningProcedure() const;
|
||||||
|
|
||||||
/// Is this evaluation a control-flow target? (The PFT must be annotated)
|
bool lowerAsStructured() const;
|
||||||
bool isControlTarget() const { return isTarget; }
|
bool lowerAsUnstructured() const;
|
||||||
|
|
||||||
/// Set the containsBranches flag iff this evaluation (a construct) contains
|
// FIR generation looks primarily at PFT statement (leaf) nodes. So members
|
||||||
/// control flow
|
// such as lexicalSuccessor and the various block fields are only applicable
|
||||||
void setBranches() { containsBranches = true; }
|
// to statement nodes. One exception is that an internal construct node is
|
||||||
|
// a convenient place for a constructExit link that applies to exits from any
|
||||||
|
// statement within the construct. The controlSuccessor member is used for
|
||||||
|
// nonlexical successors, such as linking to a GOTO target. For multiway
|
||||||
|
// branches, controlSuccessor is set to one of the targets (might as well be
|
||||||
|
// the first target). Successor and exit links always target statements.
|
||||||
|
//
|
||||||
|
// An unstructured construct is one that contains some form of goto. This
|
||||||
|
// is indicated by the isUnstructured member flag, which may be set on a
|
||||||
|
// statement and propagated to enclosing constructs. This distinction allows
|
||||||
|
// a structured IF or DO statement to be materialized with custom structured
|
||||||
|
// FIR operations. An unstructured statement is materialized as mlir
|
||||||
|
// operation sequences that include explicit branches.
|
||||||
|
//
|
||||||
|
// There are two mlir::Block members. The block member is set for statements
|
||||||
|
// that begin a new block. If a statement may have more than one associated
|
||||||
|
// block, this member must be the block that would be the target of a branch
|
||||||
|
// to the statement. The prime example of a statement that may have multiple
|
||||||
|
// associated blocks is NonLabelDoStmt, which may have a loop preheader block
|
||||||
|
// for loop initialization code, and always has a header block that is the
|
||||||
|
// target of the loop back edge. If the NonLabelDoStmt is a concurrent loop,
|
||||||
|
// there may be an arbitrary number of nested preheader, header, and mask
|
||||||
|
// blocks. Any such additional blocks in the localBlocks member are local
|
||||||
|
// to a construct and cannot be the target of an unstructured branch. For
|
||||||
|
// NonLabelDoStmt, the block member designates the preheader block, which may
|
||||||
|
// be absent if loop initialization code may be appended to a predecessor
|
||||||
|
// block. The primary loop header block is localBlocks[0], with additional
|
||||||
|
// DO CONCURRENT blocks at localBlocks[1], etc.
|
||||||
|
//
|
||||||
|
// The printIndex member is only set for statements. It is used for dumps
|
||||||
|
// and does not affect FIR generation. It may also be helpful for debugging.
|
||||||
|
|
||||||
EvaluationCollection *getConstructEvals() {
|
ParentVariant parentVariant;
|
||||||
auto *evals{subs.get()};
|
parser::CharBlock position{};
|
||||||
if (isStmt() && !evals) {
|
std::optional<parser::Label> label{};
|
||||||
return nullptr;
|
std::unique_ptr<EvaluationList> evaluationList; // nested evaluations
|
||||||
}
|
Evaluation *parentConstruct{nullptr}; // set for nodes below the top level
|
||||||
if (isConstruct() && evals) {
|
Evaluation *lexicalSuccessor{nullptr}; // set for ActionStmt, ConstructStmt
|
||||||
return evals;
|
Evaluation *controlSuccessor{nullptr}; // set for some statements
|
||||||
}
|
Evaluation *constructExit{nullptr}; // set for constructs
|
||||||
llvm_unreachable("evaluation subs is inconsistent");
|
bool isNewBlock{false}; // evaluation begins a new basic block
|
||||||
return nullptr;
|
bool isUnstructured{false}; // evaluation has unstructured control flow
|
||||||
}
|
bool skip{false}; // evaluation has been processed in advance
|
||||||
|
class mlir::Block *block{nullptr}; // isNewBlock block
|
||||||
/// Set that the construct `cstr` (if not a nullptr) has branches.
|
llvm::SmallVector<mlir::Block *, 1> localBlocks{}; // construct local blocks
|
||||||
static void setBranches(Evaluation *cstr) {
|
int printIndex{0}; // (ActionStmt, ConstructStmt) evaluation index for dumps
|
||||||
if (cstr)
|
|
||||||
cstr->setBranches();
|
|
||||||
}
|
|
||||||
|
|
||||||
EvalVariant u;
|
|
||||||
ParentType parent;
|
|
||||||
parser::CharBlock pos;
|
|
||||||
std::optional<parser::Label> lab;
|
|
||||||
std::unique_ptr<EvaluationCollection> subs; // construct sub-statements
|
|
||||||
CFGAnnotation cfg{CFGAnnotation::None};
|
|
||||||
bool isTarget{false}; // this evaluation is a control target
|
|
||||||
bool containsBranches{false}; // construct contains branches
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ProgramVariant =
|
||||||
|
ReferenceVariant<parser::MainProgram, parser::FunctionSubprogram,
|
||||||
|
parser::SubroutineSubprogram, parser::Module,
|
||||||
|
parser::Submodule, parser::SeparateModuleSubprogram,
|
||||||
|
parser::BlockData>;
|
||||||
/// A program is a list of program units.
|
/// A program is a list of program units.
|
||||||
/// These units can be function like, module like, or block data
|
/// These units can be function like, module like, or block data.
|
||||||
struct ProgramUnit {
|
struct ProgramUnit : ProgramVariant {
|
||||||
template <typename A>
|
template <typename A>
|
||||||
ProgramUnit(const A &ptr, const ParentType &parent)
|
ProgramUnit(const A &p, const ParentVariant &parentVariant)
|
||||||
: p{&ptr}, parent{parent} {}
|
: ProgramVariant{p}, parentVariant{parentVariant} {}
|
||||||
ProgramUnit(ProgramUnit &&) = default;
|
ProgramUnit(ProgramUnit &&) = default;
|
||||||
ProgramUnit(const ProgramUnit &) = delete;
|
ProgramUnit(const ProgramUnit &) = delete;
|
||||||
|
|
||||||
const std::variant<
|
ParentVariant parentVariant;
|
||||||
const parser::MainProgram *, const parser::FunctionSubprogram *,
|
|
||||||
const parser::SubroutineSubprogram *, const parser::Module *,
|
|
||||||
const parser::Submodule *, const parser::SeparateModuleSubprogram *,
|
|
||||||
const parser::BlockData *>
|
|
||||||
p;
|
|
||||||
ParentType parent;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Function-like units have similar structure. They all can contain executable
|
/// A variable captures an object to be created per the declaration part of a
|
||||||
/// statements as well as other function-like units (internal procedures and
|
/// function like unit.
|
||||||
/// function statements).
|
///
|
||||||
|
/// Properties can be applied by lowering. For example, a local array that is
|
||||||
|
/// known to be very large may be transformed into a heap allocated entity by
|
||||||
|
/// lowering. That decision would be tracked in its Variable instance.
|
||||||
|
struct Variable {
|
||||||
|
explicit Variable(const Fortran::semantics::Symbol &sym, bool global = false,
|
||||||
|
int depth = 0)
|
||||||
|
: sym{&sym}, depth{depth}, global{global} {}
|
||||||
|
|
||||||
|
const Fortran::semantics::Symbol &getSymbol() const { return *sym; }
|
||||||
|
|
||||||
|
bool isGlobal() const { return global; }
|
||||||
|
bool isHeapAlloc() const { return heapAlloc; }
|
||||||
|
bool isPointer() const { return pointer; }
|
||||||
|
bool isTarget() const { return target; }
|
||||||
|
int getDepth() const { return depth; }
|
||||||
|
|
||||||
|
void setHeapAlloc(bool to = true) { heapAlloc = to; }
|
||||||
|
void setPointer(bool to = true) { pointer = to; }
|
||||||
|
void setTarget(bool to = true) { target = to; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Fortran::semantics::Symbol *sym;
|
||||||
|
int depth;
|
||||||
|
bool global;
|
||||||
|
bool heapAlloc{false}; // variable needs deallocation on exit
|
||||||
|
bool pointer{false};
|
||||||
|
bool target{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Function-like units may contain evaluations (executable statements) and
|
||||||
|
/// nested function-like units (internal procedures and function statements).
|
||||||
struct FunctionLikeUnit : public ProgramUnit {
|
struct FunctionLikeUnit : public ProgramUnit {
|
||||||
// wrapper statements for function-like syntactic structures
|
// wrapper statements for function-like syntactic structures
|
||||||
using FunctionStatement =
|
using FunctionStatement =
|
||||||
std::variant<const parser::Statement<parser::ProgramStmt> *,
|
ReferenceVariant<parser::Statement<parser::ProgramStmt>,
|
||||||
const parser::Statement<parser::EndProgramStmt> *,
|
parser::Statement<parser::EndProgramStmt>,
|
||||||
const parser::Statement<parser::FunctionStmt> *,
|
parser::Statement<parser::FunctionStmt>,
|
||||||
const parser::Statement<parser::EndFunctionStmt> *,
|
parser::Statement<parser::EndFunctionStmt>,
|
||||||
const parser::Statement<parser::SubroutineStmt> *,
|
parser::Statement<parser::SubroutineStmt>,
|
||||||
const parser::Statement<parser::EndSubroutineStmt> *,
|
parser::Statement<parser::EndSubroutineStmt>,
|
||||||
const parser::Statement<parser::MpSubprogramStmt> *,
|
parser::Statement<parser::MpSubprogramStmt>,
|
||||||
const parser::Statement<parser::EndMpSubprogramStmt> *>;
|
parser::Statement<parser::EndMpSubprogramStmt>>;
|
||||||
|
|
||||||
FunctionLikeUnit(const parser::MainProgram &f, const ParentType &parent);
|
FunctionLikeUnit(
|
||||||
FunctionLikeUnit(const parser::FunctionSubprogram &f,
|
const parser::MainProgram &f, const ParentVariant &parentVariant,
|
||||||
const ParentType &parent);
|
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||||
FunctionLikeUnit(const parser::SubroutineSubprogram &f,
|
FunctionLikeUnit(
|
||||||
const ParentType &parent);
|
const parser::FunctionSubprogram &f, const ParentVariant &parentVariant,
|
||||||
FunctionLikeUnit(const parser::SeparateModuleSubprogram &f,
|
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||||
const ParentType &parent);
|
FunctionLikeUnit(
|
||||||
|
const parser::SubroutineSubprogram &f, const ParentVariant &parentVariant,
|
||||||
|
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||||
|
FunctionLikeUnit(
|
||||||
|
const parser::SeparateModuleSubprogram &f,
|
||||||
|
const ParentVariant &parentVariant,
|
||||||
|
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||||
FunctionLikeUnit(FunctionLikeUnit &&) = default;
|
FunctionLikeUnit(FunctionLikeUnit &&) = default;
|
||||||
FunctionLikeUnit(const FunctionLikeUnit &) = delete;
|
FunctionLikeUnit(const FunctionLikeUnit &) = delete;
|
||||||
|
|
||||||
bool isMainProgram() {
|
void processSymbolTable(const Fortran::semantics::Scope &);
|
||||||
return std::holds_alternative<
|
|
||||||
const parser::Statement<parser::EndProgramStmt> *>(endStmt);
|
std::vector<Variable> getOrderedSymbolTable() { return varList[0]; }
|
||||||
|
|
||||||
|
bool isMainProgram() const {
|
||||||
|
return endStmt.isA<parser::Statement<parser::EndProgramStmt>>();
|
||||||
}
|
}
|
||||||
const parser::FunctionStmt *getFunction() {
|
|
||||||
return getA<parser::FunctionStmt>();
|
/// Get the starting source location for this function like unit
|
||||||
|
parser::CharBlock getStartingSourceLoc() {
|
||||||
|
if (beginStmt)
|
||||||
|
return stmtSourceLoc(*beginStmt);
|
||||||
|
if (!evaluationList.empty())
|
||||||
|
return evaluationList.front().position;
|
||||||
|
return stmtSourceLoc(endStmt);
|
||||||
}
|
}
|
||||||
const parser::SubroutineStmt *getSubroutine() {
|
|
||||||
return getA<parser::SubroutineStmt>();
|
/// Returns reference to the subprogram symbol of this FunctionLikeUnit.
|
||||||
|
/// Dies if the FunctionLikeUnit is not a subprogram.
|
||||||
|
const semantics::Symbol &getSubprogramSymbol() const {
|
||||||
|
assert(symbol && "not inside a procedure");
|
||||||
|
return *symbol;
|
||||||
}
|
}
|
||||||
const parser::MpSubprogramStmt *getMPSubp() {
|
|
||||||
return getA<parser::MpSubprogramStmt>();
|
/// Helper to get location from FunctionLikeUnit begin/end statements.
|
||||||
|
static parser::CharBlock stmtSourceLoc(const FunctionStatement &stmt) {
|
||||||
|
return stmt.visit(common::visitors{[](const auto &x) { return x.source; }});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Anonymous programs do not have a begin statement
|
/// Anonymous programs do not have a begin statement
|
||||||
std::optional<FunctionStatement> beginStmt;
|
std::optional<FunctionStatement> beginStmt;
|
||||||
FunctionStatement endStmt;
|
FunctionStatement endStmt;
|
||||||
EvaluationCollection evals; // statements
|
EvaluationList evaluationList;
|
||||||
std::list<FunctionLikeUnit> funcs; // internal procedures
|
LabelEvalMap labelEvaluationMap;
|
||||||
|
SymbolLabelMap assignSymbolLabelMap;
|
||||||
private:
|
std::list<FunctionLikeUnit> nestedFunctions;
|
||||||
template <typename A>
|
/// Symbol associated to this FunctionLikeUnit.
|
||||||
const A *getA() {
|
/// Null if the FunctionLikeUnit is an anonymous program.
|
||||||
if (beginStmt) {
|
/// The symbol has MainProgramDetails for named programs, otherwise it has
|
||||||
if (auto p =
|
/// SubprogramDetails.
|
||||||
std::get_if<const parser::Statement<A> *>(&beginStmt.value()))
|
const semantics::Symbol *symbol{nullptr};
|
||||||
return &(*p)->statement;
|
/// Terminal basic block (if any)
|
||||||
}
|
mlir::Block *finalBlock{};
|
||||||
return nullptr;
|
std::vector<std::vector<Variable>> varList;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Module-like units have similar structure. They all can contain a list of
|
/// Module-like units contain a list of function-like units.
|
||||||
/// function-like units.
|
|
||||||
struct ModuleLikeUnit : public ProgramUnit {
|
struct ModuleLikeUnit : public ProgramUnit {
|
||||||
// wrapper statements for module-like syntactic structures
|
// wrapper statements for module-like syntactic structures
|
||||||
using ModuleStatement =
|
using ModuleStatement =
|
||||||
std::variant<const parser::Statement<parser::ModuleStmt> *,
|
ReferenceVariant<parser::Statement<parser::ModuleStmt>,
|
||||||
const parser::Statement<parser::EndModuleStmt> *,
|
parser::Statement<parser::EndModuleStmt>,
|
||||||
const parser::Statement<parser::SubmoduleStmt> *,
|
parser::Statement<parser::SubmoduleStmt>,
|
||||||
const parser::Statement<parser::EndSubmoduleStmt> *>;
|
parser::Statement<parser::EndSubmoduleStmt>>;
|
||||||
|
|
||||||
ModuleLikeUnit(const parser::Module &m, const ParentType &parent);
|
ModuleLikeUnit(const parser::Module &m, const ParentVariant &parentVariant);
|
||||||
ModuleLikeUnit(const parser::Submodule &m, const ParentType &parent);
|
ModuleLikeUnit(const parser::Submodule &m,
|
||||||
|
const ParentVariant &parentVariant);
|
||||||
~ModuleLikeUnit() = default;
|
~ModuleLikeUnit() = default;
|
||||||
ModuleLikeUnit(ModuleLikeUnit &&) = default;
|
ModuleLikeUnit(ModuleLikeUnit &&) = default;
|
||||||
ModuleLikeUnit(const ModuleLikeUnit &) = delete;
|
ModuleLikeUnit(const ModuleLikeUnit &) = delete;
|
||||||
|
|
||||||
ModuleStatement beginStmt;
|
ModuleStatement beginStmt;
|
||||||
ModuleStatement endStmt;
|
ModuleStatement endStmt;
|
||||||
std::list<FunctionLikeUnit> funcs;
|
std::list<FunctionLikeUnit> nestedFunctions;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlockDataUnit : public ProgramUnit {
|
struct BlockDataUnit : public ProgramUnit {
|
||||||
BlockDataUnit(const parser::BlockData &bd, const ParentType &parent);
|
BlockDataUnit(const parser::BlockData &bd,
|
||||||
|
const ParentVariant &parentVariant);
|
||||||
BlockDataUnit(BlockDataUnit &&) = default;
|
BlockDataUnit(BlockDataUnit &&) = default;
|
||||||
BlockDataUnit(const BlockDataUnit &) = delete;
|
BlockDataUnit(const BlockDataUnit &) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A Program is the top-level PFT
|
/// A Program is the top-level root of the PFT.
|
||||||
struct Program {
|
struct Program {
|
||||||
using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit>;
|
using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit>;
|
||||||
|
|
||||||
|
@ -375,23 +467,31 @@ struct Program {
|
||||||
|
|
||||||
std::list<Units> &getUnits() { return units; }
|
std::list<Units> &getUnits() { return units; }
|
||||||
|
|
||||||
|
/// LLVM dump method on a Program.
|
||||||
|
void dump();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<Units> units;
|
std::list<Units> units;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pft
|
} // namespace pft
|
||||||
|
|
||||||
/// Create an PFT from the parse tree
|
/// Create a PFT (Pre-FIR Tree) from the parse tree.
|
||||||
std::unique_ptr<pft::Program> createPFT(const parser::Program &root);
|
|
||||||
|
|
||||||
/// Decorate the PFT with control flow annotations
|
|
||||||
///
|
///
|
||||||
/// The PFT must be decorated with control-flow annotations to prepare it for
|
/// A PFT is a light weight tree over the parse tree that is used to create FIR.
|
||||||
/// use in generating a CFG-like structure.
|
/// The PFT captures pointers back into the parse tree, so the parse tree must
|
||||||
void annotateControl(pft::Program &);
|
/// not be changed between the construction of the PFT and its last use. The
|
||||||
|
/// PFT captures a structured view of a program. A program is a list of units.
|
||||||
|
/// A function like unit contains a list of evaluations. An evaluation is
|
||||||
|
/// either a statement, or a construct with a nested list of evaluations.
|
||||||
|
std::unique_ptr<pft::Program>
|
||||||
|
createPFT(const parser::Program &root,
|
||||||
|
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||||
|
|
||||||
void dumpPFT(llvm::raw_ostream &o, pft::Program &);
|
/// Dumper for displaying a PFT.
|
||||||
|
void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft);
|
||||||
|
|
||||||
} // namespace Fortran::lower
|
} // namespace lower
|
||||||
|
} // namespace Fortran
|
||||||
|
|
||||||
#endif // FORTRAN_LOWER_PFT_BUILDER_H_
|
#endif // FORTRAN_LOWER_PFTBUILDER_H
|
||||||
|
|
31
flang/include/flang/Lower/Utils.h
Normal file
31
flang/include/flang/Lower/Utils.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//===-- Lower/Utils.h -- utilities ------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef FORTRAN_LOWER_UTILS_H
|
||||||
|
#define FORTRAN_LOWER_UTILS_H
|
||||||
|
|
||||||
|
#include "flang/Common/indirection.h"
|
||||||
|
#include "flang/Parser/char-block.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
|
||||||
|
/// Convert an F18 CharBlock to an LLVM StringRef
|
||||||
|
inline llvm::StringRef toStringRef(const Fortran::parser::CharBlock &cb) {
|
||||||
|
return {cb.begin(), cb.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Template helper to remove Fortran::common::Indirection wrappers.
|
||||||
|
template <typename A>
|
||||||
|
const A &removeIndirection(const A &a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
template <typename A>
|
||||||
|
const A &removeIndirection(const Fortran::common::Indirection<A> &a) {
|
||||||
|
return a.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FORTRAN_LOWER_UTILS_H
|
|
@ -13,6 +13,7 @@
|
||||||
#include "flang/Common/Fortran.h"
|
#include "flang/Common/Fortran.h"
|
||||||
#include "flang/Common/enum-set.h"
|
#include "flang/Common/enum-set.h"
|
||||||
#include "flang/Common/reference.h"
|
#include "flang/Common/reference.h"
|
||||||
|
#include "llvm/ADT/DenseMapInfo.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -760,4 +761,30 @@ inline bool operator<(MutableSymbolRef x, MutableSymbolRef y) {
|
||||||
using SymbolSet = std::set<SymbolRef>;
|
using SymbolSet = std::set<SymbolRef>;
|
||||||
|
|
||||||
} // namespace Fortran::semantics
|
} // namespace Fortran::semantics
|
||||||
|
|
||||||
|
// Define required info so that SymbolRef can be used inside llvm::DenseMap.
|
||||||
|
namespace llvm {
|
||||||
|
template <> struct DenseMapInfo<Fortran::semantics::SymbolRef> {
|
||||||
|
static inline Fortran::semantics::SymbolRef getEmptyKey() {
|
||||||
|
auto ptr = DenseMapInfo<const Fortran::semantics::Symbol *>::getEmptyKey();
|
||||||
|
return *reinterpret_cast<Fortran::semantics::SymbolRef *>(&ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Fortran::semantics::SymbolRef getTombstoneKey() {
|
||||||
|
auto ptr =
|
||||||
|
DenseMapInfo<const Fortran::semantics::Symbol *>::getTombstoneKey();
|
||||||
|
return *reinterpret_cast<Fortran::semantics::SymbolRef *>(&ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) {
|
||||||
|
return DenseMapInfo<const Fortran::semantics::Symbol *>::getHashValue(
|
||||||
|
&sym.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isEqual(const Fortran::semantics::SymbolRef &LHS,
|
||||||
|
const Fortran::semantics::SymbolRef &RHS) {
|
||||||
|
return LHS == RHS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace llvm
|
||||||
#endif // FORTRAN_SEMANTICS_SYMBOL_H_
|
#endif // FORTRAN_SEMANTICS_SYMBOL_H_
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,10 +16,10 @@ subroutine foo()
|
||||||
print *, "hello", i, j
|
print *, "hello", i, j
|
||||||
! CHECK: EndDoStmt
|
! CHECK: EndDoStmt
|
||||||
end do
|
end do
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct>>
|
||||||
! CHECK: EndDoStmt
|
! CHECK: EndDoStmt
|
||||||
end do
|
end do
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct>>
|
||||||
end subroutine
|
end subroutine
|
||||||
! CHECK: EndSubroutine foo
|
! CHECK: EndSubroutine foo
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ contains
|
||||||
write (*, 11) "test: ", xdim, pressure
|
write (*, 11) "test: ", xdim, pressure
|
||||||
! CHECK: EndIfStmt
|
! CHECK: EndIfStmt
|
||||||
end if
|
end if
|
||||||
! CHECK: <<EndIfConstruct>>
|
! CHECK: <<End IfConstruct>>
|
||||||
end procedure
|
end procedure
|
||||||
end submodule
|
end submodule
|
||||||
! CHECK: EndModuleLike
|
! CHECK: EndModuleLike
|
||||||
|
|
|
@ -27,10 +27,10 @@ program test_prog
|
||||||
print *, "hello", i, j
|
print *, "hello", i, j
|
||||||
! CHECK: EndDoStmt
|
! CHECK: EndDoStmt
|
||||||
end do
|
end do
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct>>
|
||||||
! CHECK: EndDoStmt
|
! CHECK: EndDoStmt
|
||||||
end do
|
end do
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct>>
|
||||||
|
|
||||||
! CHECK: <<AssociateConstruct>>
|
! CHECK: <<AssociateConstruct>>
|
||||||
! CHECK: AssociateStmt
|
! CHECK: AssociateStmt
|
||||||
|
@ -39,9 +39,9 @@ program test_prog
|
||||||
allocate(x(k))
|
allocate(x(k))
|
||||||
! CHECK: EndAssociateStmt
|
! CHECK: EndAssociateStmt
|
||||||
end associate
|
end associate
|
||||||
! CHECK: <<EndAssociateConstruct>>
|
! CHECK: <<End AssociateConstruct>>
|
||||||
|
|
||||||
! CHECK: <<BlockConstruct>>
|
! CHECK: <<BlockConstruct!>>
|
||||||
! CHECK: BlockStmt
|
! CHECK: BlockStmt
|
||||||
block
|
block
|
||||||
integer :: k, l
|
integer :: k, l
|
||||||
|
@ -52,7 +52,7 @@ program test_prog
|
||||||
k = size(p)
|
k = size(p)
|
||||||
! CHECK: AssignmentStmt
|
! CHECK: AssignmentStmt
|
||||||
l = 1
|
l = 1
|
||||||
! CHECK: <<CaseConstruct>>
|
! CHECK: <<CaseConstruct!>>
|
||||||
! CHECK: SelectCaseStmt
|
! CHECK: SelectCaseStmt
|
||||||
select case (k)
|
select case (k)
|
||||||
! CHECK: CaseStmt
|
! CHECK: CaseStmt
|
||||||
|
@ -76,13 +76,13 @@ program test_prog
|
||||||
print *, "-"
|
print *, "-"
|
||||||
! CHECK: EndIfStmt
|
! CHECK: EndIfStmt
|
||||||
end if
|
end if
|
||||||
! CHECK: <<EndIfConstruct>>
|
! CHECK: <<End IfConstruct>>
|
||||||
! CHECK: CaseStmt
|
! CHECK: CaseStmt
|
||||||
case (2:10)
|
case (2:10)
|
||||||
! CHECK: CaseStmt
|
! CHECK: CaseStmt
|
||||||
case default
|
case default
|
||||||
! Note: label-do-loop are canonicalized into do constructs
|
! Note: label-do-loop are canonicalized into do constructs
|
||||||
! CHECK: <<DoConstruct>>
|
! CHECK: <<DoConstruct!>>
|
||||||
! CHECK: NonLabelDoStmt
|
! CHECK: NonLabelDoStmt
|
||||||
do 22 while(l<=k)
|
do 22 while(l<=k)
|
||||||
! CHECK: IfStmt
|
! CHECK: IfStmt
|
||||||
|
@ -90,15 +90,15 @@ program test_prog
|
||||||
! CHECK: CallStmt
|
! CHECK: CallStmt
|
||||||
22 call incr(l)
|
22 call incr(l)
|
||||||
! CHECK: EndDoStmt
|
! CHECK: EndDoStmt
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct!>>
|
||||||
! CHECK: CaseStmt
|
! CHECK: CaseStmt
|
||||||
case (100:)
|
case (100:)
|
||||||
! CHECK: EndSelectStmt
|
! CHECK: EndSelectStmt
|
||||||
end select
|
end select
|
||||||
! CHECK: <<EndCaseConstruct>>
|
! CHECK: <<End CaseConstruct!>>
|
||||||
! CHECK: EndBlockStmt
|
! CHECK: EndBlockStmt
|
||||||
end block
|
end block
|
||||||
! CHECK: <<EndBlockConstruct>>
|
! CHECK: <<End BlockConstruct!>>
|
||||||
|
|
||||||
! CHECK-NOT: WhereConstruct
|
! CHECK-NOT: WhereConstruct
|
||||||
! CHECK: WhereStmt
|
! CHECK: WhereStmt
|
||||||
|
@ -118,14 +118,14 @@ program test_prog
|
||||||
! CHECK: AssignmentStmt
|
! CHECK: AssignmentStmt
|
||||||
y = y/2.
|
y = y/2.
|
||||||
end where
|
end where
|
||||||
! CHECK: <<EndWhereConstruct>>
|
! CHECK: <<End WhereConstruct>>
|
||||||
! CHECK: ElsewhereStmt
|
! CHECK: ElsewhereStmt
|
||||||
elsewhere
|
elsewhere
|
||||||
! CHECK: AssignmentStmt
|
! CHECK: AssignmentStmt
|
||||||
x = x + 1.
|
x = x + 1.
|
||||||
! CHECK: EndWhereStmt
|
! CHECK: EndWhereStmt
|
||||||
end where
|
end where
|
||||||
! CHECK: <<EndWhereConstruct>>
|
! CHECK: <<End WhereConstruct>>
|
||||||
|
|
||||||
! CHECK-NOT: ForAllConstruct
|
! CHECK-NOT: ForAllConstruct
|
||||||
! CHECK: ForallStmt
|
! CHECK: ForallStmt
|
||||||
|
@ -138,7 +138,7 @@ program test_prog
|
||||||
x(i) = x(i) + y(10*i)
|
x(i) = x(i) + y(10*i)
|
||||||
! CHECK: EndForallStmt
|
! CHECK: EndForallStmt
|
||||||
end forall
|
end forall
|
||||||
! CHECK: <<EndForallConstruct>>
|
! CHECK: <<End ForallConstruct>>
|
||||||
|
|
||||||
! CHECK: DeallocateStmt
|
! CHECK: DeallocateStmt
|
||||||
deallocate(x)
|
deallocate(x)
|
||||||
|
@ -157,7 +157,7 @@ contains
|
||||||
function foo(x)
|
function foo(x)
|
||||||
real x(..)
|
real x(..)
|
||||||
integer :: foo
|
integer :: foo
|
||||||
! CHECK: <<SelectRankConstruct>>
|
! CHECK: <<SelectRankConstruct!>>
|
||||||
! CHECK: SelectRankStmt
|
! CHECK: SelectRankStmt
|
||||||
select rank(x)
|
select rank(x)
|
||||||
! CHECK: SelectRankCaseStmt
|
! CHECK: SelectRankCaseStmt
|
||||||
|
@ -178,13 +178,13 @@ contains
|
||||||
foo = 2
|
foo = 2
|
||||||
! CHECK: EndSelectStmt
|
! CHECK: EndSelectStmt
|
||||||
end select
|
end select
|
||||||
! CHECK: <<EndSelectRankConstruct>>
|
! CHECK: <<End SelectRankConstruct!>>
|
||||||
end function
|
end function
|
||||||
|
|
||||||
! CHECK: Function bar
|
! CHECK: Function bar
|
||||||
function bar(x)
|
function bar(x)
|
||||||
class(*) :: x
|
class(*) :: x
|
||||||
! CHECK: <<SelectTypeConstruct>>
|
! CHECK: <<SelectTypeConstruct!>>
|
||||||
! CHECK: SelectTypeStmt
|
! CHECK: SelectTypeStmt
|
||||||
select type(x)
|
select type(x)
|
||||||
! CHECK: TypeGuardStmt
|
! CHECK: TypeGuardStmt
|
||||||
|
@ -203,7 +203,7 @@ contains
|
||||||
bar = -1
|
bar = -1
|
||||||
! CHECK: EndSelectStmt
|
! CHECK: EndSelectStmt
|
||||||
end select
|
end select
|
||||||
! CHECK: <<EndSelectTypeConstruct>>
|
! CHECK: <<End SelectTypeConstruct!>>
|
||||||
end function
|
end function
|
||||||
|
|
||||||
! CHECK: Subroutine sub
|
! CHECK: Subroutine sub
|
||||||
|
@ -219,7 +219,7 @@ end module
|
||||||
|
|
||||||
! CHECK: Subroutine altreturn
|
! CHECK: Subroutine altreturn
|
||||||
subroutine altreturn(i, j, *, *)
|
subroutine altreturn(i, j, *, *)
|
||||||
! CHECK: <<IfConstruct>>
|
! CHECK: <<IfConstruct!>>
|
||||||
if (i>j) then
|
if (i>j) then
|
||||||
! CHECK: ReturnStmt
|
! CHECK: ReturnStmt
|
||||||
return 1
|
return 1
|
||||||
|
@ -227,7 +227,7 @@ subroutine altreturn(i, j, *, *)
|
||||||
! CHECK: ReturnStmt
|
! CHECK: ReturnStmt
|
||||||
return 2
|
return 2
|
||||||
end if
|
end if
|
||||||
! CHECK: <<EndIfConstruct>>
|
! CHECK: <<End IfConstruct!>>
|
||||||
end subroutine
|
end subroutine
|
||||||
|
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ subroutine iostmts(filename, a, b, c)
|
||||||
! CHECK: OpenStmt
|
! CHECK: OpenStmt
|
||||||
open(10, FILE=filename)
|
open(10, FILE=filename)
|
||||||
end if
|
end if
|
||||||
! CHECK: <<EndIfConstruct>>
|
! CHECK: <<End IfConstruct>>
|
||||||
! CHECK: ReadStmt
|
! CHECK: ReadStmt
|
||||||
read(10, *) length
|
read(10, *) length
|
||||||
! CHECK: RewindStmt
|
! CHECK: RewindStmt
|
||||||
|
@ -297,18 +297,18 @@ subroutine sub2()
|
||||||
5 j = j + 1
|
5 j = j + 1
|
||||||
6 i = i + j/2
|
6 i = i + j/2
|
||||||
|
|
||||||
! CHECK: <<DoConstruct>>
|
! CHECK: <<DoConstruct!>>
|
||||||
do1: do k=1,10
|
do1: do k=1,10
|
||||||
! CHECK: <<DoConstruct>>
|
! CHECK: <<DoConstruct!>>
|
||||||
do2: do l=5,20
|
do2: do l=5,20
|
||||||
! CHECK: CycleStmt
|
! CHECK: CycleStmt
|
||||||
cycle do1
|
cycle do1
|
||||||
! CHECK: ExitStmt
|
! CHECK: ExitStmt
|
||||||
exit do2
|
exit do2
|
||||||
end do do2
|
end do do2
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct!>>
|
||||||
end do do1
|
end do do1
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct!>>
|
||||||
|
|
||||||
! CHECK: PauseStmt
|
! CHECK: PauseStmt
|
||||||
pause 7
|
pause 7
|
||||||
|
|
|
@ -20,10 +20,10 @@ program test_omp
|
||||||
print *, "in omp do"
|
print *, "in omp do"
|
||||||
! CHECK: EndDoStmt
|
! CHECK: EndDoStmt
|
||||||
end do
|
end do
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct>>
|
||||||
! CHECK: OmpEndLoopDirective
|
! CHECK: OmpEndLoopDirective
|
||||||
!$omp end do
|
!$omp end do
|
||||||
! CHECK: <<EndOpenMPConstruct>>
|
! CHECK: <<End OpenMPConstruct>>
|
||||||
|
|
||||||
! CHECK: PrintStmt
|
! CHECK: PrintStmt
|
||||||
print *, "not in omp do"
|
print *, "not in omp do"
|
||||||
|
@ -37,13 +37,13 @@ program test_omp
|
||||||
print *, "in omp do"
|
print *, "in omp do"
|
||||||
! CHECK: EndDoStmt
|
! CHECK: EndDoStmt
|
||||||
end do
|
end do
|
||||||
! CHECK: <<EndDoConstruct>>
|
! CHECK: <<End DoConstruct>>
|
||||||
! CHECK: <<EndOpenMPConstruct>>
|
! CHECK: <<End OpenMPConstruct>>
|
||||||
! CHECK-NOT: OmpEndLoopDirective
|
! CHECK-NOT: OmpEndLoopDirective
|
||||||
! CHECK: PrintStmt
|
! CHECK: PrintStmt
|
||||||
print *, "no in omp do"
|
print *, "no in omp do"
|
||||||
!$omp end parallel
|
!$omp end parallel
|
||||||
! CHECK: <<EndOpenMPConstruct>>
|
! CHECK: <<End OpenMPConstruct>>
|
||||||
|
|
||||||
! CHECK: PrintStmt
|
! CHECK: PrintStmt
|
||||||
print *, "sequential again"
|
print *, "sequential again"
|
||||||
|
@ -53,7 +53,7 @@ program test_omp
|
||||||
! CHECK: PrintStmt
|
! CHECK: PrintStmt
|
||||||
print *, "in task"
|
print *, "in task"
|
||||||
!$omp end task
|
!$omp end task
|
||||||
! CHECK: <<EndOpenMPConstruct>>
|
! CHECK: <<End OpenMPConstruct>>
|
||||||
|
|
||||||
! CHECK: PrintStmt
|
! CHECK: PrintStmt
|
||||||
print *, "sequential again"
|
print *, "sequential again"
|
||||||
|
|
|
@ -16,7 +16,7 @@ Subroutine test_coarray
|
||||||
! CHECK: AssignmentStmt
|
! CHECK: AssignmentStmt
|
||||||
x = x[4, 1]
|
x = x[4, 1]
|
||||||
end team
|
end team
|
||||||
! CHECK: <<EndChangeTeamConstruct>>
|
! CHECK: <<End ChangeTeamConstruct>>
|
||||||
! CHECK: FormTeamStmt
|
! CHECK: FormTeamStmt
|
||||||
form team(1, t)
|
form team(1, t)
|
||||||
|
|
||||||
|
@ -28,14 +28,14 @@ Subroutine test_coarray
|
||||||
! CHECK: EventWaitStmt
|
! CHECK: EventWaitStmt
|
||||||
event wait (done)
|
event wait (done)
|
||||||
end if
|
end if
|
||||||
! CHECK: <<EndIfConstruct>>
|
! CHECK: <<End IfConstruct>>
|
||||||
|
|
||||||
! CHECK: <<CriticalConstruct>>
|
! CHECK: <<CriticalConstruct>>
|
||||||
critical
|
critical
|
||||||
! CHECK: AssignmentStmt
|
! CHECK: AssignmentStmt
|
||||||
counter[1] = counter[1] + 1
|
counter[1] = counter[1] + 1
|
||||||
end critical
|
end critical
|
||||||
! CHECK: <<EndCriticalConstruct>>
|
! CHECK: <<End CriticalConstruct>>
|
||||||
|
|
||||||
! CHECK: LockStmt
|
! CHECK: LockStmt
|
||||||
lock(alock)
|
lock(alock)
|
||||||
|
@ -59,12 +59,12 @@ Subroutine test_coarray
|
||||||
! CHECK: SyncImagesStmt
|
! CHECK: SyncImagesStmt
|
||||||
sync images(1)
|
sync images(1)
|
||||||
end if
|
end if
|
||||||
! CHECK: <<EndIfConstruct>>
|
! CHECK: <<End IfConstruct>>
|
||||||
|
|
||||||
! CHECK: <<IfConstruct>>
|
! CHECK: <<IfConstruct>>
|
||||||
if (y<0.) then
|
if (y<0.) then
|
||||||
! CHECK: FailImageStmt
|
! CHECK: FailImageStmt
|
||||||
fail image
|
fail image
|
||||||
end if
|
end if
|
||||||
! CHECK: <<EndIfConstruct>>
|
! CHECK: <<End IfConstruct>>
|
||||||
end
|
end
|
||||||
|
|
|
@ -315,8 +315,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (driver.dumpPreFirTree) {
|
if (driver.dumpPreFirTree) {
|
||||||
if (auto ast{Fortran::lower::createPFT(parseTree)}) {
|
if (auto ast{Fortran::lower::createPFT(parseTree, semanticsContext)}) {
|
||||||
Fortran::lower::annotateControl(*ast);
|
|
||||||
Fortran::lower::dumpPFT(llvm::outs(), *ast);
|
Fortran::lower::dumpPFT(llvm::outs(), *ast);
|
||||||
} else {
|
} else {
|
||||||
llvm::errs() << "Pre FIR Tree is NULL.\n";
|
llvm::errs() << "Pre FIR Tree is NULL.\n";
|
||||||
|
|
Loading…
Reference in a new issue