mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-22 15:31:00 +00:00
[flang][fir] Upstream the pre-FIR tree changes.
The PFT has been updated to support Fortran 77. clang-tidy cleanup. Authors: Val Donaldson, Jean Perier, Eric Schweitz, et.al. Differential Revision: https://reviews.llvm.org/D98283
This commit is contained in:
parent
201550852b
commit
987ee6e3cc
@ -43,7 +43,7 @@ namespace pft {
|
||||
struct Evaluation;
|
||||
using LabelEvalMap = llvm::DenseMap<Fortran::parser::Label, Evaluation *>;
|
||||
using SymbolRef = Fortran::common::Reference<const Fortran::semantics::Symbol>;
|
||||
using LabelSet = llvm::SmallSet<Fortran::parser::Label, 5>;
|
||||
using LabelSet = llvm::SmallSet<Fortran::parser::Label, 4>;
|
||||
using SymbolLabelMap = llvm::DenseMap<SymbolRef, LabelSet>;
|
||||
} // namespace pft
|
||||
|
||||
|
@ -6,6 +6,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// PFT (Pre-FIR Tree) interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -15,31 +19,20 @@
|
||||
|
||||
#include "flang/Common/reference.h"
|
||||
#include "flang/Common/template.h"
|
||||
#include "flang/Lower/PFTDefs.h"
|
||||
#include "flang/Parser/parse-tree.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "flang/Semantics/attr.h"
|
||||
#include "flang/Semantics/symbol.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace mlir {
|
||||
class Block;
|
||||
}
|
||||
|
||||
namespace Fortran {
|
||||
namespace semantics {
|
||||
class SemanticsContext;
|
||||
class Scope;
|
||||
} // namespace semantics
|
||||
namespace lower {
|
||||
namespace pft {
|
||||
namespace Fortran::lower::pft {
|
||||
|
||||
struct Evaluation;
|
||||
struct Program;
|
||||
struct ModuleLikeUnit;
|
||||
struct FunctionLikeUnit;
|
||||
|
||||
// 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
|
||||
// insert PFT nodes in any order in O(1) time.
|
||||
using EvaluationList = std::list<Evaluation>;
|
||||
using LabelEvalMap = llvm::DenseMap<Fortran::parser::Label, Evaluation *>;
|
||||
|
||||
@ -61,7 +54,11 @@ public:
|
||||
|
||||
template <typename B>
|
||||
constexpr BaseType<B> &get() const {
|
||||
return std::get<Ref<B>> > (u).get();
|
||||
return std::get<Ref<B>>(u).get();
|
||||
}
|
||||
template <typename B>
|
||||
constexpr BaseType<B> &getStatement() const {
|
||||
return std::get<Ref<parser::Statement<B>>>(u).get().statement;
|
||||
}
|
||||
template <typename B>
|
||||
constexpr BaseType<B> *getIf() const {
|
||||
@ -87,10 +84,10 @@ 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
|
||||
/// PftNode 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>;
|
||||
using PftNode = MutableReferenceVariant<Program, ModuleLikeUnit,
|
||||
FunctionLikeUnit, Evaluation>;
|
||||
|
||||
/// Classify the parse-tree nodes from ExecutablePartConstruct
|
||||
|
||||
@ -109,8 +106,8 @@ using ActionStmts = std::tuple<
|
||||
parser::ComputedGotoStmt, parser::ForallStmt, parser::ArithmeticIfStmt,
|
||||
parser::AssignStmt, parser::AssignedGotoStmt, parser::PauseStmt>;
|
||||
|
||||
using OtherStmts = std::tuple<parser::FormatStmt, parser::EntryStmt,
|
||||
parser::DataStmt, parser::NamelistStmt>;
|
||||
using OtherStmts =
|
||||
std::tuple<parser::FormatStmt, parser::EntryStmt, parser::NamelistStmt>;
|
||||
|
||||
using ConstructStmts = std::tuple<
|
||||
parser::AssociateStmt, parser::EndAssociateStmt, parser::BlockStmt,
|
||||
@ -123,6 +120,10 @@ using ConstructStmts = std::tuple<
|
||||
parser::MaskedElsewhereStmt, parser::ElsewhereStmt, parser::EndWhereStmt,
|
||||
parser::ForallConstructStmt, parser::EndForallStmt>;
|
||||
|
||||
using EndStmts =
|
||||
std::tuple<parser::EndProgramStmt, parser::EndFunctionStmt,
|
||||
parser::EndSubroutineStmt, parser::EndMpSubprogramStmt>;
|
||||
|
||||
using Constructs =
|
||||
std::tuple<parser::AssociateConstruct, parser::BlockConstruct,
|
||||
parser::CaseConstruct, parser::ChangeTeamConstruct,
|
||||
@ -144,6 +145,9 @@ static constexpr bool isOtherStmt{common::HasMember<A, OtherStmts>};
|
||||
template <typename A>
|
||||
static constexpr bool isConstructStmt{common::HasMember<A, ConstructStmts>};
|
||||
|
||||
template <typename A>
|
||||
static constexpr bool isEndStmt{common::HasMember<A, EndStmts>};
|
||||
|
||||
template <typename A>
|
||||
static constexpr bool isConstruct{common::HasMember<A, Constructs>};
|
||||
|
||||
@ -168,10 +172,6 @@ static constexpr bool isFunctionLike{common::HasMember<
|
||||
parser::SubroutineSubprogram,
|
||||
parser::SeparateModuleSubprogram>>};
|
||||
|
||||
using LabelSet = llvm::SmallSet<parser::Label, 5>;
|
||||
using SymbolRef = common::Reference<const semantics::Symbol>;
|
||||
using SymbolLabelMap = llvm::DenseMap<SymbolRef, LabelSet>;
|
||||
|
||||
template <typename A>
|
||||
struct MakeReferenceVariantHelper {};
|
||||
template <typename... A>
|
||||
@ -186,8 +186,8 @@ template <typename A>
|
||||
using MakeReferenceVariant = typename MakeReferenceVariantHelper<A>::type;
|
||||
|
||||
using EvaluationTuple =
|
||||
common::CombineTuples<ActionStmts, OtherStmts, ConstructStmts, Constructs,
|
||||
Directives>;
|
||||
common::CombineTuples<ActionStmts, OtherStmts, ConstructStmts, EndStmts,
|
||||
Constructs, 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, ...>).
|
||||
@ -199,16 +199,16 @@ struct Evaluation : EvaluationVariant {
|
||||
|
||||
/// General ctor
|
||||
template <typename A>
|
||||
Evaluation(const A &a, const ParentVariant &parentVariant,
|
||||
Evaluation(const A &a, const PftNode &parent,
|
||||
const parser::CharBlock &position,
|
||||
const std::optional<parser::Label> &label)
|
||||
: EvaluationVariant{a},
|
||||
parentVariant{parentVariant}, position{position}, label{label} {}
|
||||
: EvaluationVariant{a}, parent{parent}, position{position}, label{label} {
|
||||
}
|
||||
|
||||
/// Construct ctor
|
||||
/// Construct and Directive ctor
|
||||
template <typename A>
|
||||
Evaluation(const A &a, const ParentVariant &parentVariant)
|
||||
: EvaluationVariant{a}, parentVariant{parentVariant} {
|
||||
Evaluation(const A &a, const PftNode &parent)
|
||||
: EvaluationVariant{a}, parent{parent} {
|
||||
static_assert(pft::isConstruct<A> || pft::isDirective<A>,
|
||||
"must be a construct or directive");
|
||||
}
|
||||
@ -227,6 +227,10 @@ struct Evaluation : EvaluationVariant {
|
||||
return pft::isConstructStmt<std::decay_t<decltype(r)>>;
|
||||
}});
|
||||
}
|
||||
constexpr bool isEndStmt() const {
|
||||
return visit(common::visitors{
|
||||
[](auto &r) { return pft::isEndStmt<std::decay_t<decltype(r)>>; }});
|
||||
}
|
||||
constexpr bool isConstruct() const {
|
||||
return visit(common::visitors{
|
||||
[](auto &r) { return pft::isConstruct<std::decay_t<decltype(r)>>; }});
|
||||
@ -249,6 +253,8 @@ struct Evaluation : EvaluationVariant {
|
||||
}});
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
/// Return the first non-nop successor of an evaluation, possibly exiting
|
||||
/// from one or more enclosing constructs.
|
||||
Evaluation &nonNopSuccessor() const {
|
||||
@ -287,14 +293,13 @@ struct Evaluation : EvaluationVariant {
|
||||
bool lowerAsStructured() const;
|
||||
bool lowerAsUnstructured() const;
|
||||
|
||||
// FIR generation looks primarily at PFT statement (leaf) nodes. So members
|
||||
// such as lexicalSuccessor and the various block fields are only applicable
|
||||
// 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.
|
||||
// FIR generation looks primarily at PFT ActionStmt and ConstructStmt leaf
|
||||
// nodes. Members such as lexicalSuccessor and block are applicable only
|
||||
// to these nodes. The controlSuccessor member is used for nonlexical
|
||||
// successors, such as linking to a GOTO target. For multiway branches,
|
||||
// it is set to the first target. Successor and exit links always target
|
||||
// statements. An internal Construct node has a constructExit link that
|
||||
// applies to exits from anywhere within the construct.
|
||||
//
|
||||
// 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
|
||||
@ -303,25 +308,21 @@ struct Evaluation : EvaluationVariant {
|
||||
// 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 block member is set for statements that begin a new block. This
|
||||
// block is the target of any branch to the statement. Statements may have
|
||||
// additional (unstructured) "local" blocks, but such blocks cannot be the
|
||||
// target of any explicit branch. The primary example of an (unstructured)
|
||||
// statement that may have multiple associated blocks is NonLabelDoStmt,
|
||||
// which may have a loop preheader block for loop initialization code (the
|
||||
// block member), and always has a "local" header block that is the target
|
||||
// of the loop back edge. If the NonLabelDoStmt is a concurrent loop, it
|
||||
// may be associated with an arbitrary number of nested preheader, header,
|
||||
// and mask blocks.
|
||||
//
|
||||
// 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.
|
||||
// (and debugging) and does not affect FIR generation.
|
||||
|
||||
ParentVariant parentVariant;
|
||||
PftNode parent;
|
||||
parser::CharBlock position{};
|
||||
std::optional<parser::Label> label{};
|
||||
std::unique_ptr<EvaluationList> evaluationList; // nested evaluations
|
||||
@ -331,9 +332,8 @@ struct Evaluation : EvaluationVariant {
|
||||
Evaluation *constructExit{nullptr}; // set for constructs
|
||||
bool isNewBlock{false}; // evaluation begins a new basic block
|
||||
bool isUnstructured{false}; // evaluation has unstructured control flow
|
||||
bool skip{false}; // evaluation has been processed in advance
|
||||
mlir::Block *block{nullptr}; // isNewBlock block
|
||||
llvm::SmallVector<mlir::Block *, 1> localBlocks{}; // construct local blocks
|
||||
bool negateCondition{false}; // If[Then]Stmt condition must be negated
|
||||
mlir::Block *block{nullptr}; // isNewBlock block (ActionStmt, ConstructStmt)
|
||||
int printIndex{0}; // (ActionStmt, ConstructStmt) evaluation index for dumps
|
||||
};
|
||||
|
||||
@ -341,49 +341,201 @@ using ProgramVariant =
|
||||
ReferenceVariant<parser::MainProgram, parser::FunctionSubprogram,
|
||||
parser::SubroutineSubprogram, parser::Module,
|
||||
parser::Submodule, parser::SeparateModuleSubprogram,
|
||||
parser::BlockData>;
|
||||
parser::BlockData, parser::CompilerDirective>;
|
||||
/// A program is a list of program units.
|
||||
/// These units can be function like, module like, or block data.
|
||||
struct ProgramUnit : ProgramVariant {
|
||||
template <typename A>
|
||||
ProgramUnit(const A &p, const ParentVariant &parentVariant)
|
||||
: ProgramVariant{p}, parentVariant{parentVariant} {}
|
||||
ProgramUnit(const A &p, const PftNode &parent)
|
||||
: ProgramVariant{p}, parent{parent} {}
|
||||
ProgramUnit(ProgramUnit &&) = default;
|
||||
ProgramUnit(const ProgramUnit &) = delete;
|
||||
|
||||
ParentVariant parentVariant;
|
||||
PftNode parent;
|
||||
};
|
||||
|
||||
/// A variable captures an object to be created per the declaration part of a
|
||||
/// function like unit.
|
||||
///
|
||||
/// Fortran EQUIVALENCE statements are a mechanism that introduces aliasing
|
||||
/// between named variables. The set of overlapping aliases will materialize a
|
||||
/// generic store object with a designated offset and size. Participant
|
||||
/// symbols will simply be pointers into the aggregate store.
|
||||
///
|
||||
/// EQUIVALENCE can also interact with COMMON and other global variables to
|
||||
/// imply aliasing between (subparts of) a global and other local variable
|
||||
/// names.
|
||||
///
|
||||
/// 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 {
|
||||
/// Most variables are nominal and require the allocation of local/global
|
||||
/// storage space. A nominal variable may also be an alias for some other
|
||||
/// (subpart) of storage.
|
||||
struct Nominal {
|
||||
Nominal(const semantics::Symbol *symbol, int depth, bool global)
|
||||
: symbol{symbol}, depth{depth}, global{global} {}
|
||||
const semantics::Symbol *symbol{};
|
||||
|
||||
bool isGlobal() const { return global; }
|
||||
bool isDeclaration() const {
|
||||
return !symbol || symbol != &symbol->GetUltimate();
|
||||
}
|
||||
|
||||
int depth{};
|
||||
bool global{};
|
||||
bool heapAlloc{}; // variable needs deallocation on exit
|
||||
bool pointer{};
|
||||
bool target{};
|
||||
bool aliaser{}; // participates in EQUIVALENCE union
|
||||
std::size_t aliasOffset{};
|
||||
};
|
||||
|
||||
using Interval = std::tuple<std::size_t, std::size_t>;
|
||||
|
||||
/// An interval of storage is a contiguous block of memory to be allocated or
|
||||
/// mapped onto another variable. Aliasing variables will be pointers into
|
||||
/// interval stores and may overlap each other.
|
||||
struct AggregateStore {
|
||||
AggregateStore(Interval &&interval, const Fortran::semantics::Scope &scope,
|
||||
bool isDeclaration = false)
|
||||
: interval{std::move(interval)}, scope{&scope}, isDecl{isDeclaration} {}
|
||||
AggregateStore(Interval &&interval, const Fortran::semantics::Scope &scope,
|
||||
const llvm::SmallVector<const semantics::Symbol *, 8> &vars,
|
||||
bool isDeclaration = false)
|
||||
: interval{std::move(interval)}, scope{&scope}, vars{vars},
|
||||
isDecl{isDeclaration} {}
|
||||
|
||||
bool isGlobal() const { return vars.size() > 0; }
|
||||
bool isDeclaration() const { return isDecl; }
|
||||
/// Get offset of the aggregate inside its scope.
|
||||
std::size_t getOffset() const { return std::get<0>(interval); }
|
||||
|
||||
Interval interval{};
|
||||
/// scope in which the interval is.
|
||||
const Fortran::semantics::Scope *scope;
|
||||
llvm::SmallVector<const semantics::Symbol *, 8> vars{};
|
||||
/// Is this a declaration of a storage defined in another scope ?
|
||||
bool isDecl;
|
||||
};
|
||||
|
||||
explicit Variable(const Fortran::semantics::Symbol &sym, bool global = false,
|
||||
int depth = 0)
|
||||
: sym{&sym}, depth{depth}, global{global} {}
|
||||
: var{Nominal(&sym, depth, global)} {}
|
||||
explicit Variable(AggregateStore &&istore) : var{std::move(istore)} {}
|
||||
|
||||
const Fortran::semantics::Symbol &getSymbol() const { return *sym; }
|
||||
/// Return the front-end symbol for a nominal variable.
|
||||
const Fortran::semantics::Symbol &getSymbol() const {
|
||||
assert(hasSymbol() && "variable is not nominal");
|
||||
return *std::get<Nominal>(var).symbol;
|
||||
}
|
||||
|
||||
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; }
|
||||
/// Return the aggregate store.
|
||||
const AggregateStore &getAggregateStore() const {
|
||||
assert(isAggregateStore());
|
||||
return std::get<AggregateStore>(var);
|
||||
}
|
||||
|
||||
void setHeapAlloc(bool to = true) { heapAlloc = to; }
|
||||
void setPointer(bool to = true) { pointer = to; }
|
||||
void setTarget(bool to = true) { target = to; }
|
||||
/// Return the interval range of an aggregate store.
|
||||
const Interval &getInterval() const {
|
||||
assert(isAggregateStore());
|
||||
return std::get<AggregateStore>(var).interval;
|
||||
}
|
||||
|
||||
/// Only nominal variable have front-end symbols.
|
||||
bool hasSymbol() const { return std::holds_alternative<Nominal>(var); }
|
||||
|
||||
/// Is this an aggregate store?
|
||||
bool isAggregateStore() const {
|
||||
return std::holds_alternative<AggregateStore>(var);
|
||||
}
|
||||
|
||||
/// Is this variable a global?
|
||||
bool isGlobal() const {
|
||||
return std::visit([](const auto &x) { return x.isGlobal(); }, var);
|
||||
}
|
||||
|
||||
/// Is this a declaration of a variable owned by another scope ?
|
||||
bool isDeclaration() const {
|
||||
return std::visit([](const auto &x) { return x.isDeclaration(); }, var);
|
||||
}
|
||||
|
||||
const Fortran::semantics::Scope *getOwningScope() const {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
[](const Nominal &x) { return &x.symbol->GetUltimate().owner(); },
|
||||
[](const AggregateStore &agg) { return agg.scope; }},
|
||||
var);
|
||||
}
|
||||
|
||||
bool isHeapAlloc() const {
|
||||
if (const auto *s = std::get_if<Nominal>(&var))
|
||||
return s->heapAlloc;
|
||||
return false;
|
||||
}
|
||||
bool isPointer() const {
|
||||
if (const auto *s = std::get_if<Nominal>(&var))
|
||||
return s->pointer;
|
||||
return false;
|
||||
}
|
||||
bool isTarget() const {
|
||||
if (const auto *s = std::get_if<Nominal>(&var))
|
||||
return s->target;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// An alias(er) is a variable that is part of a EQUIVALENCE that is allocated
|
||||
/// locally on the stack.
|
||||
bool isAlias() const {
|
||||
if (const auto *s = std::get_if<Nominal>(&var))
|
||||
return s->aliaser;
|
||||
return false;
|
||||
}
|
||||
std::size_t getAlias() const {
|
||||
if (auto *s = std::get_if<Nominal>(&var))
|
||||
return s->aliasOffset;
|
||||
return 0;
|
||||
}
|
||||
void setAlias(std::size_t offset) {
|
||||
if (auto *s = std::get_if<Nominal>(&var)) {
|
||||
s->aliaser = true;
|
||||
s->aliasOffset = offset;
|
||||
} else {
|
||||
llvm_unreachable("not a nominal var");
|
||||
}
|
||||
}
|
||||
|
||||
void setHeapAlloc(bool to = true) {
|
||||
if (auto *s = std::get_if<Nominal>(&var))
|
||||
s->heapAlloc = to;
|
||||
else
|
||||
llvm_unreachable("not a nominal var");
|
||||
}
|
||||
void setPointer(bool to = true) {
|
||||
if (auto *s = std::get_if<Nominal>(&var))
|
||||
s->pointer = to;
|
||||
else
|
||||
llvm_unreachable("not a nominal var");
|
||||
}
|
||||
void setTarget(bool to = true) {
|
||||
if (auto *s = std::get_if<Nominal>(&var))
|
||||
s->target = to;
|
||||
else
|
||||
llvm_unreachable("not a nominal var");
|
||||
}
|
||||
|
||||
/// The depth is recorded for nominal variables as a debugging aid.
|
||||
int getDepth() const {
|
||||
if (const auto *s = std::get_if<Nominal>(&var))
|
||||
return s->depth;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
private:
|
||||
const Fortran::semantics::Symbol *sym;
|
||||
int depth;
|
||||
bool global;
|
||||
bool heapAlloc{false}; // variable needs deallocation on exit
|
||||
bool pointer{false};
|
||||
bool target{false};
|
||||
std::variant<Nominal, AggregateStore> var;
|
||||
};
|
||||
|
||||
/// Function-like units may contain evaluations (executable statements) and
|
||||
@ -401,22 +553,30 @@ struct FunctionLikeUnit : public ProgramUnit {
|
||||
parser::Statement<parser::EndMpSubprogramStmt>>;
|
||||
|
||||
FunctionLikeUnit(
|
||||
const parser::MainProgram &f, const ParentVariant &parentVariant,
|
||||
const parser::MainProgram &f, const PftNode &parent,
|
||||
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||
FunctionLikeUnit(
|
||||
const parser::FunctionSubprogram &f, const ParentVariant &parentVariant,
|
||||
const parser::FunctionSubprogram &f, const PftNode &parent,
|
||||
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||
FunctionLikeUnit(
|
||||
const parser::SubroutineSubprogram &f, const ParentVariant &parentVariant,
|
||||
const parser::SubroutineSubprogram &f, const PftNode &parent,
|
||||
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||
FunctionLikeUnit(
|
||||
const parser::SeparateModuleSubprogram &f,
|
||||
const ParentVariant &parentVariant,
|
||||
const parser::SeparateModuleSubprogram &f, const PftNode &parent,
|
||||
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||
FunctionLikeUnit(FunctionLikeUnit &&) = default;
|
||||
FunctionLikeUnit(const FunctionLikeUnit &) = delete;
|
||||
|
||||
void processSymbolTable(const Fortran::semantics::Scope &);
|
||||
/// Return true iff this function like unit is Fortran recursive (actually
|
||||
/// meaning it's reentrant).
|
||||
bool isRecursive() const {
|
||||
if (isMainProgram())
|
||||
return false;
|
||||
const auto &sym = getSubprogramSymbol();
|
||||
return sym.attrs().test(semantics::Attr::RECURSIVE) ||
|
||||
(!sym.attrs().test(semantics::Attr::NON_RECURSIVE) &&
|
||||
defaultRecursiveFunctionSetting());
|
||||
}
|
||||
|
||||
std::vector<Variable> getOrderedSymbolTable() { return varList[0]; }
|
||||
|
||||
@ -433,18 +593,36 @@ struct FunctionLikeUnit : public ProgramUnit {
|
||||
return stmtSourceLoc(endStmt);
|
||||
}
|
||||
|
||||
/// Returns reference to the subprogram symbol of this FunctionLikeUnit.
|
||||
/// Dies if the FunctionLikeUnit is not a subprogram.
|
||||
void setActiveEntry(int entryIndex) {
|
||||
assert(entryIndex >= 0 && entryIndex < (int)entryPointList.size() &&
|
||||
"invalid entry point index");
|
||||
activeEntry = entryIndex;
|
||||
}
|
||||
|
||||
/// Return a reference to the subprogram symbol of this FunctionLikeUnit.
|
||||
/// This should not be called if the FunctionLikeUnit is the main program
|
||||
/// since anonymous main programs do not have a symbol.
|
||||
const semantics::Symbol &getSubprogramSymbol() const {
|
||||
assert(symbol && "not inside a procedure");
|
||||
const auto *symbol = entryPointList[activeEntry].first;
|
||||
if (!symbol)
|
||||
llvm::report_fatal_error(
|
||||
"not inside a procedure; do not call on main program.");
|
||||
return *symbol;
|
||||
}
|
||||
|
||||
/// Return a pointer to the current entry point Evaluation.
|
||||
/// This is null for a primary entry point.
|
||||
Evaluation *getEntryEval() const {
|
||||
return entryPointList[activeEntry].second;
|
||||
}
|
||||
|
||||
/// 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; }});
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
/// Anonymous programs do not have a begin statement
|
||||
std::optional<FunctionStatement> beginStmt;
|
||||
FunctionStatement endStmt;
|
||||
@ -452,11 +630,20 @@ struct FunctionLikeUnit : public ProgramUnit {
|
||||
LabelEvalMap labelEvaluationMap;
|
||||
SymbolLabelMap assignSymbolLabelMap;
|
||||
std::list<FunctionLikeUnit> nestedFunctions;
|
||||
/// Symbol associated to this FunctionLikeUnit.
|
||||
/// Null if the FunctionLikeUnit is an anonymous program.
|
||||
/// The symbol has MainProgramDetails for named programs, otherwise it has
|
||||
/// SubprogramDetails.
|
||||
const semantics::Symbol *symbol{nullptr};
|
||||
/// <Symbol, Evaluation> pairs for each entry point. The pair at index 0
|
||||
/// is the primary entry point; remaining pairs are alternate entry points.
|
||||
/// The primary entry point symbol is Null for an anonymous program.
|
||||
/// A named program symbol has MainProgramDetails. Other symbols have
|
||||
/// SubprogramDetails. Evaluations are filled in for alternate entries.
|
||||
llvm::SmallVector<std::pair<const semantics::Symbol *, Evaluation *>, 1>
|
||||
entryPointList{std::pair{nullptr, nullptr}};
|
||||
/// Current index into entryPointList. Index 0 is the primary entry point.
|
||||
int activeEntry = 0;
|
||||
/// Dummy arguments that are not universal across entry points.
|
||||
llvm::SmallVector<const semantics::Symbol *, 3> nonUniversalDummyArguments;
|
||||
/// Primary result for function subprograms with alternate entries. This
|
||||
/// is one of the largest result values, not necessarily the first one.
|
||||
const semantics::Symbol *primaryResult{nullptr};
|
||||
/// Terminal basic block (if any)
|
||||
mlir::Block *finalBlock{};
|
||||
std::vector<std::vector<Variable>> varList;
|
||||
@ -471,44 +658,66 @@ struct ModuleLikeUnit : public ProgramUnit {
|
||||
parser::Statement<parser::SubmoduleStmt>,
|
||||
parser::Statement<parser::EndSubmoduleStmt>>;
|
||||
|
||||
ModuleLikeUnit(const parser::Module &m, const ParentVariant &parentVariant);
|
||||
ModuleLikeUnit(const parser::Submodule &m,
|
||||
const ParentVariant &parentVariant);
|
||||
ModuleLikeUnit(const parser::Module &m, const PftNode &parent);
|
||||
ModuleLikeUnit(const parser::Submodule &m, const PftNode &parent);
|
||||
~ModuleLikeUnit() = default;
|
||||
ModuleLikeUnit(ModuleLikeUnit &&) = default;
|
||||
ModuleLikeUnit(const ModuleLikeUnit &) = delete;
|
||||
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
std::vector<Variable> getOrderedSymbolTable() { return varList[0]; }
|
||||
|
||||
ModuleStatement beginStmt;
|
||||
ModuleStatement endStmt;
|
||||
std::list<FunctionLikeUnit> nestedFunctions;
|
||||
std::vector<std::vector<Variable>> varList;
|
||||
};
|
||||
|
||||
/// Block data units contain the variables and data initializers for common
|
||||
/// blocks, etc.
|
||||
struct BlockDataUnit : public ProgramUnit {
|
||||
BlockDataUnit(const parser::BlockData &bd,
|
||||
const ParentVariant &parentVariant);
|
||||
BlockDataUnit(const parser::BlockData &bd, const PftNode &parent,
|
||||
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||
BlockDataUnit(BlockDataUnit &&) = default;
|
||||
BlockDataUnit(const BlockDataUnit &) = delete;
|
||||
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
const Fortran::semantics::Scope &symTab; // symbol table
|
||||
};
|
||||
|
||||
// Top level compiler directives
|
||||
struct CompilerDirectiveUnit : public ProgramUnit {
|
||||
CompilerDirectiveUnit(const parser::CompilerDirective &directive,
|
||||
const PftNode &parent)
|
||||
: ProgramUnit{directive, parent} {};
|
||||
CompilerDirectiveUnit(CompilerDirectiveUnit &&) = default;
|
||||
CompilerDirectiveUnit(const CompilerDirectiveUnit &) = delete;
|
||||
};
|
||||
|
||||
/// A Program is the top-level root of the PFT.
|
||||
struct Program {
|
||||
using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit>;
|
||||
using Units = std::variant<FunctionLikeUnit, ModuleLikeUnit, BlockDataUnit,
|
||||
CompilerDirectiveUnit>;
|
||||
|
||||
Program() = default;
|
||||
Program(Program &&) = default;
|
||||
Program(const Program &) = delete;
|
||||
|
||||
const std::list<Units> &getUnits() const { return units; }
|
||||
std::list<Units> &getUnits() { return units; }
|
||||
|
||||
/// LLVM dump method on a Program.
|
||||
void dump();
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
private:
|
||||
std::list<Units> units;
|
||||
};
|
||||
|
||||
} // namespace pft
|
||||
} // namespace Fortran::lower::pft
|
||||
|
||||
namespace Fortran::lower {
|
||||
/// Create a PFT (Pre-FIR Tree) from the parse tree.
|
||||
///
|
||||
/// A PFT is a light weight tree over the parse tree that is used to create FIR.
|
||||
@ -522,9 +731,8 @@ createPFT(const parser::Program &root,
|
||||
const Fortran::semantics::SemanticsContext &semanticsContext);
|
||||
|
||||
/// Dumper for displaying a PFT.
|
||||
void dumpPFT(llvm::raw_ostream &outputStream, pft::Program &pft);
|
||||
void dumpPFT(llvm::raw_ostream &outputStream, const pft::Program &pft);
|
||||
|
||||
} // namespace lower
|
||||
} // namespace Fortran
|
||||
} // namespace Fortran::lower
|
||||
|
||||
#endif // FORTRAN_LOWER_PFTBUILDER_H
|
||||
|
62
flang/include/flang/Lower/PFTDefs.h
Normal file
62
flang/include/flang/Lower/PFTDefs.h
Normal file
@ -0,0 +1,62 @@
|
||||
//===-- Lower/PFTDefs.h -- shared PFT info ----------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef FORTRAN_LOWER_PFTDEFS_H
|
||||
#define FORTRAN_LOWER_PFTDEFS_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace mlir {
|
||||
class Block;
|
||||
}
|
||||
|
||||
namespace Fortran {
|
||||
namespace semantics {
|
||||
class Symbol;
|
||||
class SemanticsContext;
|
||||
class Scope;
|
||||
} // namespace semantics
|
||||
|
||||
namespace evaluate {
|
||||
template <typename A>
|
||||
class Expr;
|
||||
struct SomeType;
|
||||
} // namespace evaluate
|
||||
|
||||
namespace common {
|
||||
template <typename A>
|
||||
class Reference;
|
||||
}
|
||||
|
||||
namespace lower {
|
||||
|
||||
bool definedInCommonBlock(const semantics::Symbol &sym);
|
||||
bool defaultRecursiveFunctionSetting();
|
||||
|
||||
namespace pft {
|
||||
|
||||
struct Evaluation;
|
||||
|
||||
using SomeExpr = Fortran::evaluate::Expr<Fortran::evaluate::SomeType>;
|
||||
using SymbolRef = Fortran::common::Reference<const Fortran::semantics::Symbol>;
|
||||
using Label = std::uint64_t;
|
||||
using LabelSet = llvm::SmallSet<Label, 4>;
|
||||
using SymbolLabelMap = llvm::DenseMap<SymbolRef, LabelSet>;
|
||||
using LabelEvalMap = llvm::DenseMap<Label, Evaluation *>;
|
||||
|
||||
} // namespace pft
|
||||
} // namespace lower
|
||||
} // namespace Fortran
|
||||
|
||||
#endif // FORTRAN_LOWER_PFTDEFS_H
|
49
flang/include/flang/Lower/Support/Utils.h
Normal file
49
flang/include/flang/Lower/Support/Utils.h
Normal file
@ -0,0 +1,49 @@
|
||||
//===-- Lower/Support/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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef FORTRAN_LOWER_SUPPORT_UTILS_H
|
||||
#define FORTRAN_LOWER_SUPPORT_UTILS_H
|
||||
|
||||
#include "flang/Common/indirection.h"
|
||||
#include "flang/Parser/char-block.h"
|
||||
#include "mlir/Dialect/StandardOps/IR/Ops.h"
|
||||
#include "mlir/IR/BuiltinAttributes.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cstdint>
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Small inline helper functions to deal with repetitive, clumsy conversions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Convert an F18 CharBlock to an LLVM StringRef.
|
||||
inline llvm::StringRef toStringRef(const Fortran::parser::CharBlock &cb) {
|
||||
return {cb.begin(), cb.size()};
|
||||
}
|
||||
|
||||
namespace fir {
|
||||
/// Return the integer value of a ConstantOp.
|
||||
inline std::int64_t toInt(mlir::ConstantOp cop) {
|
||||
return cop.getValue().cast<mlir::IntegerAttr>().getValue().getSExtValue();
|
||||
}
|
||||
} // namespace fir
|
||||
|
||||
/// 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_SUPPORT_UTILS_H
|
109
flang/lib/Lower/IntervalSet.h
Normal file
109
flang/lib/Lower/IntervalSet.h
Normal file
@ -0,0 +1,109 @@
|
||||
//===-- IntervalSet.h -------------------------------------------*- 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_INTERVALSET_H
|
||||
#define FORTRAN_LOWER_INTERVALSET_H
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
|
||||
namespace Fortran::lower {
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Interval set
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Interval set to keep track of intervals, merging them when they overlap one
|
||||
/// another. Used to refine the pseudo-offset ranges of the front-end symbols
|
||||
/// into groups of aliasing variables.
|
||||
struct IntervalSet {
|
||||
using MAP = std::map<std::size_t, std::size_t>;
|
||||
using Iterator = MAP::const_iterator;
|
||||
|
||||
// Handles the merging of overlapping intervals correctly, efficiently.
|
||||
void merge(std::size_t lo, std::size_t up) {
|
||||
assert(lo <= up);
|
||||
if (empty()) {
|
||||
m.insert({lo, up});
|
||||
return;
|
||||
}
|
||||
auto i = m.lower_bound(lo);
|
||||
// i->first >= lo
|
||||
if (i == begin()) {
|
||||
if (up < i->first) {
|
||||
// [lo..up] < i->first
|
||||
m.insert({lo, up});
|
||||
return;
|
||||
}
|
||||
// up >= i->first
|
||||
if (i->second > up)
|
||||
up = i->second;
|
||||
fuse(lo, up, i);
|
||||
return;
|
||||
}
|
||||
auto i1 = i;
|
||||
if (i == end() || i->first > lo)
|
||||
i = std::prev(i);
|
||||
// i->first <= lo
|
||||
if (i->second >= up) {
|
||||
// i->first <= lo && up <= i->second, keep i
|
||||
return;
|
||||
}
|
||||
// i->second < up
|
||||
if (i->second < lo) {
|
||||
if (i1 == end() || i1->first > up) {
|
||||
// i < [lo..up] < i1
|
||||
m.insert({lo, up});
|
||||
return;
|
||||
}
|
||||
// i < [lo..up], i1->first <= up --> [lo..up] union [i1..?]
|
||||
i = i1;
|
||||
} else {
|
||||
// i->first <= lo, lo <= i->second --> [i->first..up] union [i..?]
|
||||
lo = i->first;
|
||||
}
|
||||
fuse(lo, up, i);
|
||||
}
|
||||
|
||||
Iterator find(std::size_t pt) const {
|
||||
auto i = m.lower_bound(pt);
|
||||
if (i != end() && i->first == pt)
|
||||
return i;
|
||||
if (i == begin())
|
||||
return end();
|
||||
i = std::prev(i);
|
||||
if (i->second < pt)
|
||||
return end();
|
||||
return i;
|
||||
}
|
||||
|
||||
Iterator begin() const { return m.begin(); }
|
||||
Iterator end() const { return m.end(); }
|
||||
bool empty() const { return m.empty(); }
|
||||
std::size_t size() const { return m.size(); }
|
||||
|
||||
private:
|
||||
// Find and fuse overlapping sets.
|
||||
void fuse(std::size_t lo, std::size_t up, Iterator i) {
|
||||
auto j = m.upper_bound(up);
|
||||
// up < j->first
|
||||
auto cu = std::prev(j)->second;
|
||||
// cu < j->first
|
||||
if (cu > up)
|
||||
up = cu;
|
||||
m.erase(i, j);
|
||||
// merge [i .. j) with [i->first, max(up, cu)]
|
||||
m.insert({lo, up});
|
||||
}
|
||||
|
||||
MAP m{};
|
||||
};
|
||||
|
||||
} // namespace Fortran::lower
|
||||
|
||||
#endif // FORTRAN_LOWER_INTERVALSET_H
|
File diff suppressed because it is too large
Load Diff
@ -20,8 +20,9 @@ subroutine foo()
|
||||
! CHECK: EndDoStmt
|
||||
end do
|
||||
! CHECK: <<End DoConstruct>>
|
||||
! CHECK: EndSubroutineStmt
|
||||
end subroutine
|
||||
! CHECK: EndSubroutine foo
|
||||
! CHECK: End Subroutine foo
|
||||
|
||||
! CHECK: BlockData
|
||||
block data
|
||||
@ -29,7 +30,7 @@ block data
|
||||
integer, dimension(n) :: a, b, c
|
||||
common /arrays/ a, b, c
|
||||
end
|
||||
! CHECK: EndBlockData
|
||||
! CHECK: End BlockData
|
||||
|
||||
! CHECK: ModuleLike
|
||||
module test_mod
|
||||
@ -44,49 +45,57 @@ end interface
|
||||
contains
|
||||
! CHECK: Subroutine foo
|
||||
subroutine foo()
|
||||
! CHECK: EndSubroutineStmt
|
||||
contains
|
||||
! CHECK: Subroutine subfoo
|
||||
subroutine subfoo()
|
||||
end subroutine
|
||||
! CHECK: EndSubroutine subfoo
|
||||
! CHECK: EndSubroutineStmt
|
||||
9 end subroutine
|
||||
! CHECK: End Subroutine subfoo
|
||||
! CHECK: Function subfoo2
|
||||
function subfoo2()
|
||||
end function
|
||||
! CHECK: EndFunction subfoo2
|
||||
! CHECK: EndFunctionStmt
|
||||
9 end function
|
||||
! CHECK: End Function subfoo2
|
||||
end subroutine
|
||||
! CHECK: EndSubroutine foo
|
||||
! CHECK: End Subroutine foo
|
||||
|
||||
! CHECK: Function foo2
|
||||
function foo2(i, j)
|
||||
integer i, j, foo2
|
||||
! CHECK: AssignmentStmt
|
||||
foo2 = i + j
|
||||
! CHECK: EndFunctionStmt
|
||||
contains
|
||||
! CHECK: Subroutine subfoo
|
||||
subroutine subfoo()
|
||||
! CHECK: EndSubroutineStmt
|
||||
end subroutine
|
||||
! CHECK: EndSubroutine subfoo
|
||||
! CHECK: End Subroutine subfoo
|
||||
end function
|
||||
! CHECK: EndFunction foo2
|
||||
! CHECK: End Function foo2
|
||||
end module
|
||||
! CHECK: EndModuleLike
|
||||
! CHECK: End ModuleLike
|
||||
|
||||
! CHECK: ModuleLike
|
||||
submodule (test_mod) test_mod_impl
|
||||
contains
|
||||
! CHECK: Subroutine foo
|
||||
subroutine foo()
|
||||
! CHECK: EndSubroutineStmt
|
||||
contains
|
||||
! CHECK: Subroutine subfoo
|
||||
subroutine subfoo()
|
||||
! CHECK: EndSubroutineStmt
|
||||
end subroutine
|
||||
! CHECK: EndSubroutine subfoo
|
||||
! CHECK: End Subroutine subfoo
|
||||
! CHECK: Function subfoo2
|
||||
function subfoo2()
|
||||
! CHECK: EndFunctionStmt
|
||||
end function
|
||||
! CHECK: EndFunction subfoo2
|
||||
! CHECK: End Function subfoo2
|
||||
end subroutine
|
||||
! CHECK: EndSubroutine foo
|
||||
! CHECK: End Subroutine foo
|
||||
! CHECK: MpSubprogram dump
|
||||
module procedure dump
|
||||
! CHECK: FormatStmt
|
||||
@ -105,19 +114,34 @@ contains
|
||||
! CHECK: <<End IfConstruct>>
|
||||
end procedure
|
||||
end submodule
|
||||
! CHECK: EndModuleLike
|
||||
! CHECK: End ModuleLike
|
||||
|
||||
! CHECK: BlockData
|
||||
block data named_block
|
||||
integer i, j, k
|
||||
common /indexes/ i, j, k
|
||||
end
|
||||
! CHECK: EndBlockData
|
||||
! CHECK: End BlockData
|
||||
|
||||
! CHECK: Function bar
|
||||
function bar()
|
||||
! CHECK: EndFunctionStmt
|
||||
end function
|
||||
! CHECK: EndFunction bar
|
||||
! CHECK: End Function bar
|
||||
|
||||
! Test top level directives
|
||||
!DIR$ INTEGER=64
|
||||
! CHECK: CompilerDirective:
|
||||
! CHECK: End CompilerDirective
|
||||
|
||||
! Test nested directive
|
||||
! CHECK: Subroutine test_directive
|
||||
subroutine test_directive()
|
||||
!DIR$ INTEGER=64
|
||||
! CHECK: <<CompilerDirective>>
|
||||
! CHECK: <<End CompilerDirective>>
|
||||
end subroutine
|
||||
! CHECK: EndSubroutine
|
||||
|
||||
! CHECK: Program <anonymous>
|
||||
! check specification parts are not part of the PFT.
|
||||
@ -127,4 +151,4 @@ end function
|
||||
! CHECK: AllocateStmt
|
||||
allocate(x(foo2(10, 30)))
|
||||
end
|
||||
! CHECK: EndProgram
|
||||
! CHECK: End Program
|
||||
|
@ -146,12 +146,15 @@ end
|
||||
|
||||
! CHECK: ModuleLike
|
||||
module test
|
||||
type :: a_type
|
||||
integer :: x
|
||||
end type
|
||||
type, extends(a_type) :: b_type
|
||||
integer :: y
|
||||
end type
|
||||
!! When derived type processing is implemented, remove all instances of:
|
||||
!! - !![disable]
|
||||
!! - COM:
|
||||
!![disable]type :: a_type
|
||||
!![disable] integer :: x
|
||||
!![disable]end type
|
||||
!![disable]type, extends(a_type) :: b_type
|
||||
!![disable] integer :: y
|
||||
!![disable]end type
|
||||
contains
|
||||
! CHECK: Function foo
|
||||
function foo(x)
|
||||
@ -191,12 +194,12 @@ contains
|
||||
type is (integer)
|
||||
! CHECK: AssignmentStmt
|
||||
bar = 0
|
||||
! CHECK: TypeGuardStmt
|
||||
class is (a_type)
|
||||
! CHECK: AssignmentStmt
|
||||
bar = 1
|
||||
! CHECK: ReturnStmt
|
||||
return
|
||||
!![disable]! COM: CHECK: TypeGuardStmt
|
||||
!![disable]class is (a_type)
|
||||
!![disable] ! COM: CHECK: AssignmentStmt
|
||||
!![disable] bar = 1
|
||||
!![disable] ! COM: CHECK: ReturnStmt
|
||||
!![disable] return
|
||||
! CHECK: TypeGuardStmt
|
||||
class default
|
||||
! CHECK: AssignmentStmt
|
||||
@ -329,6 +332,5 @@ end subroutine
|
||||
subroutine sub4()
|
||||
integer :: i
|
||||
print*, "test"
|
||||
! CHECK: DataStmt
|
||||
data i /1/
|
||||
end subroutine
|
||||
|
@ -1,4 +1,4 @@
|
||||
! RUN: %f18 -fdebug-pre-fir-tree -fsyntax-only %s | FileCheck %s
|
||||
! RUN: %flang_fc1 -fsyntax-only -fdebug-pre-fir-tree %s | FileCheck %s
|
||||
|
||||
! Test Pre-FIR Tree captures all the coarray related statements
|
||||
|
||||
|
@ -27,9 +27,9 @@ subroutine foo()
|
||||
!$acc end parallel
|
||||
! CHECK-NEXT: <<End OpenACCConstruct>>
|
||||
! CHECK-NEXT: <<End OpenACCConstruct>>
|
||||
! CHECK-NEXT: ContinueStmt
|
||||
! CHECK-NEXT: EndSubroutineStmt
|
||||
end subroutine
|
||||
! CHECK-NEXT: EndSubroutine foo
|
||||
! CHECK-NEXT: End Subroutine foo
|
||||
|
||||
! CHECK: Subroutine foo
|
||||
subroutine foo2()
|
||||
@ -43,7 +43,7 @@ subroutine foo2()
|
||||
end do
|
||||
!$acc end parallel loop
|
||||
! CHECK-NEXT: <<End OpenACCConstruct>>
|
||||
! CHECK-NEXT: ContinueStmt
|
||||
! CHECK-NEXT: EndSubroutineStmt
|
||||
end subroutine
|
||||
! CHECK-NEXT: EndSubroutine foo2
|
||||
! CHECK-NEXT: End Subroutine foo2
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user