forked from OSchip/llvm-project
[flang] Simple array assignment lowering
This patch handles lowering of simple array assignment. ``` a(:) = 10 ``` or ``` a(1) = 1 ``` This patch is part of the upstreaming effort from fir-dev branch. Reviewed By: PeteSteinfeld, schweitz Differential Revision: https://reviews.llvm.org/D120501 Co-authored-by: Jean Perier <jperier@nvidia.com> Co-authored-by: V Donaldson <vdonaldson@nvidia.com> Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
This commit is contained in:
parent
05d79e3562
commit
f9704f0cfb
|
@ -160,10 +160,17 @@ public:
|
|||
std::optional<Expr<SubscriptInteger>> &&);
|
||||
|
||||
std::optional<Expr<SubscriptInteger>> lower() const;
|
||||
const Expr<SubscriptInteger> *GetLower() const {
|
||||
return lower_.has_value() ? &lower_->value() : nullptr;
|
||||
}
|
||||
Triplet &set_lower(Expr<SubscriptInteger> &&);
|
||||
std::optional<Expr<SubscriptInteger>> upper() const;
|
||||
const Expr<SubscriptInteger> *GetUpper() const {
|
||||
return upper_.has_value() ? &upper_->value() : nullptr;
|
||||
}
|
||||
Triplet &set_upper(Expr<SubscriptInteger> &&);
|
||||
Expr<SubscriptInteger> stride() const; // N.B. result is not optional<>
|
||||
const Expr<SubscriptInteger> &GetStride() const { return stride_.value(); }
|
||||
Triplet &set_stride(Expr<SubscriptInteger> &&);
|
||||
|
||||
bool operator==(const Triplet &) const;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
//===-- ComponentPath.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_COMPONENTPATH_H
|
||||
#define FORTRAN_LOWER_COMPONENTPATH_H
|
||||
|
||||
#include "flang/Lower/IterationSpace.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
namespace fir {
|
||||
class ArrayLoadOp;
|
||||
}
|
||||
namespace Fortran::evaluate {
|
||||
class ArrayRef;
|
||||
}
|
||||
|
||||
namespace Fortran::lower {
|
||||
|
||||
namespace details {
|
||||
class ImplicitSubscripts {};
|
||||
} // namespace details
|
||||
|
||||
using PathComponent =
|
||||
std::variant<const evaluate::ArrayRef *, const evaluate::Component *,
|
||||
const Fortran::evaluate::ComplexPart *,
|
||||
details::ImplicitSubscripts>;
|
||||
|
||||
/// Collection of components.
|
||||
///
|
||||
/// This class is used both to collect front-end post-order functional Expr
|
||||
/// trees and their translations to Values to be used in a pre-order list of
|
||||
/// arguments.
|
||||
class ComponentPath {
|
||||
public:
|
||||
ComponentPath(bool isImplicit) { setPC(isImplicit); }
|
||||
ComponentPath(bool isImplicit, const evaluate::Substring *ss)
|
||||
: substring(ss) {
|
||||
setPC(isImplicit);
|
||||
}
|
||||
ComponentPath() = delete;
|
||||
|
||||
bool isSlice() { return !trips.empty() || hasComponents(); }
|
||||
bool hasComponents() { return !suffixComponents.empty(); }
|
||||
void clear();
|
||||
|
||||
llvm::SmallVector<PathComponent> reversePath;
|
||||
const evaluate::Substring *substring = nullptr;
|
||||
bool applied = false;
|
||||
|
||||
llvm::SmallVector<mlir::Value> prefixComponents;
|
||||
llvm::SmallVector<mlir::Value> trips;
|
||||
llvm::SmallVector<mlir::Value> suffixComponents;
|
||||
std::function<IterationSpace(const IterationSpace &)> pc;
|
||||
|
||||
private:
|
||||
void setPC(bool isImplicit);
|
||||
};
|
||||
|
||||
/// Examine each subscript expression of \p x and return true if and only if any
|
||||
/// of the subscripts is a vector or has a rank greater than 0.
|
||||
bool isRankedArrayAccess(const Fortran::evaluate::ArrayRef &x);
|
||||
|
||||
} // namespace Fortran::lower
|
||||
|
||||
#endif // FORTRAN_LOWER_COMPONENTPATH_H
|
|
@ -36,6 +36,10 @@ namespace Fortran::lower {
|
|||
class AbstractConverter;
|
||||
class StatementContext;
|
||||
class SymMap;
|
||||
class ExplicitIterSpace;
|
||||
class ImplicitIterSpace;
|
||||
class StatementContext;
|
||||
|
||||
using SomeExpr = Fortran::evaluate::Expr<Fortran::evaluate::SomeType>;
|
||||
|
||||
/// Create an extended expression value.
|
||||
|
@ -67,6 +71,44 @@ mlir::Value createSubroutineCall(AbstractConverter &converter,
|
|||
const evaluate::ProcedureRef &call,
|
||||
SymMap &symMap, StatementContext &stmtCtx);
|
||||
|
||||
/// Create the address of the box.
|
||||
/// \p expr must be the designator of an allocatable/pointer entity.
|
||||
fir::MutableBoxValue createMutableBox(mlir::Location loc,
|
||||
AbstractConverter &converter,
|
||||
const SomeExpr &expr, SymMap &symMap);
|
||||
|
||||
/// Lower an array assignment expression.
|
||||
///
|
||||
/// 1. Evaluate the lhs to determine the rank and how to form the ArrayLoad
|
||||
/// (e.g., if there is a slicing op).
|
||||
/// 2. Scan the rhs, creating the ArrayLoads and evaluate the scalar subparts to
|
||||
/// be added to the map.
|
||||
/// 3. Create the loop nest and evaluate the elemental expression, threading the
|
||||
/// results.
|
||||
/// 4. Copy the resulting array back with ArrayMergeStore to the lhs as
|
||||
/// determined per step 1.
|
||||
void createSomeArrayAssignment(AbstractConverter &converter,
|
||||
const SomeExpr &lhs, const SomeExpr &rhs,
|
||||
SymMap &symMap, StatementContext &stmtCtx);
|
||||
|
||||
/// Lower an array assignment expression with pre-evaluated left and right
|
||||
/// hand sides. This implements an array copy taking into account
|
||||
/// non-contiguity and potential overlaps.
|
||||
void createSomeArrayAssignment(AbstractConverter &converter,
|
||||
const fir::ExtendedValue &lhs,
|
||||
const fir::ExtendedValue &rhs, SymMap &symMap,
|
||||
StatementContext &stmtCtx);
|
||||
|
||||
/// Lower an assignment to an allocatable array, allocating the array if
|
||||
/// it is not allocated yet or reallocation it if it does not conform
|
||||
/// with the right hand side.
|
||||
void createAllocatableArrayAssignment(AbstractConverter &converter,
|
||||
const SomeExpr &lhs, const SomeExpr &rhs,
|
||||
ExplicitIterSpace &explicitIterSpace,
|
||||
ImplicitIterSpace &implicitIterSpace,
|
||||
SymMap &symMap,
|
||||
StatementContext &stmtCtx);
|
||||
|
||||
// Attribute for an alloca that is a trivial adaptor for converting a value to
|
||||
// pass-by-ref semantics for a VALUE parameter. The optimizer may be able to
|
||||
// eliminate these.
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
//===-- Lower/DumpEvaluateExpr.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_DUMPEVALUATEEXPR_H
|
||||
#define FORTRAN_LOWER_DUMPEVALUATEEXPR_H
|
||||
|
||||
#include "flang/Evaluate/tools.h"
|
||||
#include "flang/Lower/Support/Utils.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
|
||||
namespace Fortran::lower {
|
||||
|
||||
/// Class to dump Fortran::evaluate::Expr trees out in a user readable way.
|
||||
///
|
||||
/// FIXME: This can be improved to dump more information in some cases.
|
||||
class DumpEvaluateExpr {
|
||||
public:
|
||||
DumpEvaluateExpr() : outs(llvm::errs()) {}
|
||||
DumpEvaluateExpr(llvm::raw_ostream &str) : outs(str) {}
|
||||
|
||||
template <typename A>
|
||||
static void dump(const A &x) {
|
||||
DumpEvaluateExpr{}.show(x);
|
||||
}
|
||||
template <typename A>
|
||||
static void dump(llvm::raw_ostream &stream, const A &x) {
|
||||
DumpEvaluateExpr{stream}.show(x);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename A, bool C>
|
||||
void show(const Fortran::common::Indirection<A, C> &x) {
|
||||
show(x.value());
|
||||
}
|
||||
template <typename A>
|
||||
void show(const Fortran::semantics::SymbolRef x) {
|
||||
show(*x);
|
||||
}
|
||||
template <typename A>
|
||||
void show(const std::unique_ptr<A> &x) {
|
||||
show(x.get());
|
||||
}
|
||||
template <typename A>
|
||||
void show(const std::shared_ptr<A> &x) {
|
||||
show(x.get());
|
||||
}
|
||||
template <typename A>
|
||||
void show(const A *x) {
|
||||
if (x) {
|
||||
show(*x);
|
||||
return;
|
||||
}
|
||||
print("nullptr");
|
||||
}
|
||||
template <typename A>
|
||||
void show(const std::optional<A> &x) {
|
||||
if (x) {
|
||||
show(*x);
|
||||
return;
|
||||
}
|
||||
print("None");
|
||||
}
|
||||
template <typename... A>
|
||||
void show(const std::variant<A...> &u) {
|
||||
std::visit([&](const auto &v) { show(v); }, u);
|
||||
}
|
||||
template <typename A>
|
||||
void show(const std::vector<A> &x) {
|
||||
indent("vector");
|
||||
for (const auto &v : x)
|
||||
show(v);
|
||||
outdent();
|
||||
}
|
||||
void show(const Fortran::evaluate::BOZLiteralConstant &);
|
||||
void show(const Fortran::evaluate::NullPointer &);
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::Constant<T> &x) {
|
||||
if constexpr (T::category == Fortran::common::TypeCategory::Derived) {
|
||||
indent("derived constant");
|
||||
for (const auto &map : x.values())
|
||||
for (const auto &pair : map)
|
||||
show(pair.second.value());
|
||||
outdent();
|
||||
} else {
|
||||
print("constant");
|
||||
}
|
||||
}
|
||||
void show(const Fortran::semantics::Symbol &symbol);
|
||||
void show(const Fortran::evaluate::StaticDataObject &);
|
||||
void show(const Fortran::evaluate::ImpliedDoIndex &);
|
||||
void show(const Fortran::evaluate::BaseObject &x);
|
||||
void show(const Fortran::evaluate::Component &x);
|
||||
void show(const Fortran::evaluate::NamedEntity &x);
|
||||
void show(const Fortran::evaluate::TypeParamInquiry &x);
|
||||
void show(const Fortran::evaluate::Triplet &x);
|
||||
void show(const Fortran::evaluate::Subscript &x);
|
||||
void show(const Fortran::evaluate::ArrayRef &x);
|
||||
void show(const Fortran::evaluate::CoarrayRef &x);
|
||||
void show(const Fortran::evaluate::DataRef &x);
|
||||
void show(const Fortran::evaluate::Substring &x);
|
||||
void show(const Fortran::evaluate::ComplexPart &x);
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::Designator<T> &x) {
|
||||
indent("designator");
|
||||
show(x.u);
|
||||
outdent();
|
||||
}
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::Variable<T> &x) {
|
||||
indent("variable");
|
||||
show(x.u);
|
||||
outdent();
|
||||
}
|
||||
void show(const Fortran::evaluate::DescriptorInquiry &x);
|
||||
void show(const Fortran::evaluate::SpecificIntrinsic &);
|
||||
void show(const Fortran::evaluate::ProcedureDesignator &x);
|
||||
void show(const Fortran::evaluate::ActualArgument &x);
|
||||
void show(const Fortran::evaluate::ProcedureRef &x) {
|
||||
indent("procedure ref");
|
||||
show(x.proc());
|
||||
show(x.arguments());
|
||||
outdent();
|
||||
}
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::FunctionRef<T> &x) {
|
||||
indent("function ref");
|
||||
show(x.proc());
|
||||
show(x.arguments());
|
||||
outdent();
|
||||
}
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::ArrayConstructorValue<T> &x) {
|
||||
show(x.u);
|
||||
}
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::ArrayConstructorValues<T> &x) {
|
||||
indent("array constructor value");
|
||||
for (auto &v : x)
|
||||
show(v);
|
||||
outdent();
|
||||
}
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::ImpliedDo<T> &x) {
|
||||
indent("implied do");
|
||||
show(x.lower());
|
||||
show(x.upper());
|
||||
show(x.stride());
|
||||
show(x.values());
|
||||
outdent();
|
||||
}
|
||||
void show(const Fortran::semantics::ParamValue &x);
|
||||
void
|
||||
show(const Fortran::semantics::DerivedTypeSpec::ParameterMapType::value_type
|
||||
&x);
|
||||
void show(const Fortran::semantics::DerivedTypeSpec &x);
|
||||
void show(const Fortran::evaluate::StructureConstructorValues::value_type &x);
|
||||
void show(const Fortran::evaluate::StructureConstructor &x);
|
||||
template <typename D, typename R, typename O>
|
||||
void show(const Fortran::evaluate::Operation<D, R, O> &op) {
|
||||
indent("unary op");
|
||||
show(op.left());
|
||||
outdent();
|
||||
}
|
||||
template <typename D, typename R, typename LO, typename RO>
|
||||
void show(const Fortran::evaluate::Operation<D, R, LO, RO> &op) {
|
||||
indent("binary op");
|
||||
show(op.left());
|
||||
show(op.right());
|
||||
outdent();
|
||||
}
|
||||
void
|
||||
show(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x);
|
||||
template <typename T>
|
||||
void show(const Fortran::evaluate::Expr<T> &x) {
|
||||
indent("expr T");
|
||||
show(x.u);
|
||||
outdent();
|
||||
}
|
||||
|
||||
const char *getIndentString() const;
|
||||
void print(llvm::Twine s);
|
||||
void indent(llvm::StringRef s);
|
||||
void outdent();
|
||||
|
||||
llvm::raw_ostream &outs;
|
||||
unsigned level = 0;
|
||||
};
|
||||
|
||||
LLVM_DUMP_METHOD void
|
||||
dumpEvExpr(const Fortran::evaluate::Expr<Fortran::evaluate::SomeType> &x);
|
||||
LLVM_DUMP_METHOD void dumpEvExpr(
|
||||
const Fortran::evaluate::Expr<
|
||||
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 4>> &x);
|
||||
LLVM_DUMP_METHOD void dumpEvExpr(
|
||||
const Fortran::evaluate::Expr<
|
||||
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>> &x);
|
||||
LLVM_DUMP_METHOD void dumpEvExpr(const Fortran::evaluate::ArrayRef &x);
|
||||
LLVM_DUMP_METHOD void dumpEvExpr(const Fortran::evaluate::DataRef &x);
|
||||
LLVM_DUMP_METHOD void dumpEvExpr(const Fortran::evaluate::Substring &x);
|
||||
LLVM_DUMP_METHOD void dumpEvExpr(
|
||||
const Fortran::evaluate::Designator<
|
||||
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 4>> &x);
|
||||
|
||||
} // namespace Fortran::lower
|
||||
|
||||
#endif // FORTRAN_LOWER_DUMPEVALUATEEXPR_H
|
|
@ -0,0 +1,587 @@
|
|||
//===-- IterationSpace.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef FORTRAN_LOWER_ITERATIONSPACE_H
|
||||
#define FORTRAN_LOWER_ITERATIONSPACE_H
|
||||
|
||||
#include "flang/Evaluate/tools.h"
|
||||
#include "flang/Lower/StatementContext.h"
|
||||
#include "flang/Lower/SymbolMap.h"
|
||||
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
||||
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
}
|
||||
|
||||
namespace Fortran {
|
||||
namespace evaluate {
|
||||
struct SomeType;
|
||||
template <typename>
|
||||
class Expr;
|
||||
} // namespace evaluate
|
||||
|
||||
namespace lower {
|
||||
|
||||
using FrontEndExpr = const evaluate::Expr<evaluate::SomeType> *;
|
||||
using FrontEndSymbol = const semantics::Symbol *;
|
||||
|
||||
class AbstractConverter;
|
||||
|
||||
unsigned getHashValue(FrontEndExpr x);
|
||||
bool isEqual(FrontEndExpr x, FrontEndExpr y);
|
||||
} // namespace lower
|
||||
} // namespace Fortran
|
||||
|
||||
namespace llvm {
|
||||
template <>
|
||||
struct DenseMapInfo<Fortran::lower::FrontEndExpr> {
|
||||
static inline Fortran::lower::FrontEndExpr getEmptyKey() {
|
||||
return reinterpret_cast<Fortran::lower::FrontEndExpr>(~0);
|
||||
}
|
||||
static inline Fortran::lower::FrontEndExpr getTombstoneKey() {
|
||||
return reinterpret_cast<Fortran::lower::FrontEndExpr>(~0 - 1);
|
||||
}
|
||||
static unsigned getHashValue(Fortran::lower::FrontEndExpr v) {
|
||||
return Fortran::lower::getHashValue(v);
|
||||
}
|
||||
static bool isEqual(Fortran::lower::FrontEndExpr lhs,
|
||||
Fortran::lower::FrontEndExpr rhs) {
|
||||
return Fortran::lower::isEqual(lhs, rhs);
|
||||
}
|
||||
};
|
||||
} // namespace llvm
|
||||
|
||||
namespace Fortran::lower {
|
||||
|
||||
/// Abstraction of the iteration space for building the elemental compute loop
|
||||
/// of an array(-like) statement.
|
||||
class IterationSpace {
|
||||
public:
|
||||
IterationSpace() = default;
|
||||
|
||||
template <typename A>
|
||||
explicit IterationSpace(mlir::Value inArg, mlir::Value outRes,
|
||||
llvm::iterator_range<A> range)
|
||||
: inArg{inArg}, outRes{outRes}, indices{range.begin(), range.end()} {}
|
||||
|
||||
explicit IterationSpace(const IterationSpace &from,
|
||||
llvm::ArrayRef<mlir::Value> idxs)
|
||||
: inArg(from.inArg), outRes(from.outRes), element(from.element),
|
||||
indices(idxs.begin(), idxs.end()) {}
|
||||
|
||||
/// Create a copy of the \p from IterationSpace and prepend the \p prefix
|
||||
/// values and append the \p suffix values, respectively.
|
||||
explicit IterationSpace(const IterationSpace &from,
|
||||
llvm::ArrayRef<mlir::Value> prefix,
|
||||
llvm::ArrayRef<mlir::Value> suffix)
|
||||
: inArg(from.inArg), outRes(from.outRes), element(from.element) {
|
||||
indices.assign(prefix.begin(), prefix.end());
|
||||
indices.append(from.indices.begin(), from.indices.end());
|
||||
indices.append(suffix.begin(), suffix.end());
|
||||
}
|
||||
|
||||
bool empty() const { return indices.empty(); }
|
||||
|
||||
/// This is the output value as it appears as an argument in the innermost
|
||||
/// loop in the nest. The output value is threaded through the loop (and
|
||||
/// conditionals) to maintain proper SSA form.
|
||||
mlir::Value innerArgument() const { return inArg; }
|
||||
|
||||
/// This is the output value as it appears as an output value from the
|
||||
/// outermost loop in the loop nest. The output value is threaded through the
|
||||
/// loop (and conditionals) to maintain proper SSA form.
|
||||
mlir::Value outerResult() const { return outRes; }
|
||||
|
||||
/// Returns a vector for the iteration space. This vector is used to access
|
||||
/// elements of arrays in the compute loop.
|
||||
llvm::SmallVector<mlir::Value> iterVec() const { return indices; }
|
||||
|
||||
mlir::Value iterValue(std::size_t i) const {
|
||||
assert(i < indices.size());
|
||||
return indices[i];
|
||||
}
|
||||
|
||||
/// Set (rewrite) the Value at a given index.
|
||||
void setIndexValue(std::size_t i, mlir::Value v) {
|
||||
assert(i < indices.size());
|
||||
indices[i] = v;
|
||||
}
|
||||
|
||||
void setIndexValues(llvm::ArrayRef<mlir::Value> vals) {
|
||||
indices.assign(vals.begin(), vals.end());
|
||||
}
|
||||
|
||||
void insertIndexValue(std::size_t i, mlir::Value av) {
|
||||
assert(i <= indices.size());
|
||||
indices.insert(indices.begin() + i, av);
|
||||
}
|
||||
|
||||
/// Set the `element` value. This is the SSA value that corresponds to an
|
||||
/// element of the resultant array value.
|
||||
void setElement(fir::ExtendedValue &&ele) {
|
||||
assert(!fir::getBase(element) && "result element already set");
|
||||
element = ele;
|
||||
}
|
||||
|
||||
/// Get the value that will be merged into the resultant array. This is the
|
||||
/// computed value that will be stored to the lhs of the assignment.
|
||||
mlir::Value getElement() const {
|
||||
assert(fir::getBase(element) && "element must be set");
|
||||
return fir::getBase(element);
|
||||
}
|
||||
|
||||
/// Get the element as an extended value.
|
||||
fir::ExtendedValue elementExv() const { return element; }
|
||||
|
||||
void clearIndices() { indices.clear(); }
|
||||
|
||||
private:
|
||||
mlir::Value inArg;
|
||||
mlir::Value outRes;
|
||||
fir::ExtendedValue element;
|
||||
llvm::SmallVector<mlir::Value> indices;
|
||||
};
|
||||
|
||||
using GenerateElementalArrayFunc =
|
||||
std::function<fir::ExtendedValue(const IterationSpace &)>;
|
||||
|
||||
template <typename A>
|
||||
class StackableConstructExpr {
|
||||
public:
|
||||
bool empty() const { return stack.empty(); }
|
||||
|
||||
void growStack() { stack.push_back(A{}); }
|
||||
|
||||
/// Bind a front-end expression to a closure.
|
||||
void bind(FrontEndExpr e, GenerateElementalArrayFunc &&fun) {
|
||||
vmap.insert({e, std::move(fun)});
|
||||
}
|
||||
|
||||
/// Replace the binding of front-end expression `e` with a new closure.
|
||||
void rebind(FrontEndExpr e, GenerateElementalArrayFunc &&fun) {
|
||||
vmap.erase(e);
|
||||
bind(e, std::move(fun));
|
||||
}
|
||||
|
||||
/// Get the closure bound to the front-end expression, `e`.
|
||||
GenerateElementalArrayFunc getBoundClosure(FrontEndExpr e) const {
|
||||
if (!vmap.count(e))
|
||||
llvm::report_fatal_error(
|
||||
"evaluate::Expr is not in the map of lowered mask expressions");
|
||||
return vmap.lookup(e);
|
||||
}
|
||||
|
||||
/// Has the front-end expression, `e`, been lowered and bound?
|
||||
bool isLowered(FrontEndExpr e) const { return vmap.count(e); }
|
||||
|
||||
StatementContext &stmtContext() { return stmtCtx; }
|
||||
|
||||
protected:
|
||||
void shrinkStack() {
|
||||
assert(!empty());
|
||||
stack.pop_back();
|
||||
if (empty()) {
|
||||
stmtCtx.finalize();
|
||||
vmap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// The stack for the construct information.
|
||||
llvm::SmallVector<A> stack;
|
||||
|
||||
// Map each mask expression back to the temporary holding the initial
|
||||
// evaluation results.
|
||||
llvm::DenseMap<FrontEndExpr, GenerateElementalArrayFunc> vmap;
|
||||
|
||||
// Inflate the statement context for the entire construct. We have to cache
|
||||
// the mask expression results, which are always evaluated first, across the
|
||||
// entire construct.
|
||||
StatementContext stmtCtx;
|
||||
};
|
||||
|
||||
class ImplicitIterSpace;
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ImplicitIterSpace &);
|
||||
|
||||
/// All array expressions have an implicit iteration space, which is isomorphic
|
||||
/// to the shape of the base array that facilitates the expression having a
|
||||
/// non-zero rank. This implied iteration space may be conditionalized
|
||||
/// (disjunctively) with an if-elseif-else like structure, specifically
|
||||
/// Fortran's WHERE construct.
|
||||
///
|
||||
/// This class is used in the bridge to collect the expressions from the
|
||||
/// front end (the WHERE construct mask expressions), forward them for lowering
|
||||
/// as array expressions in an "evaluate once" (copy-in, copy-out) semantics.
|
||||
/// See 10.2.3.2p3, 10.2.3.2p13, etc.
|
||||
class ImplicitIterSpace
|
||||
: public StackableConstructExpr<llvm::SmallVector<FrontEndExpr>> {
|
||||
public:
|
||||
using Base = StackableConstructExpr<llvm::SmallVector<FrontEndExpr>>;
|
||||
using FrontEndMaskExpr = FrontEndExpr;
|
||||
|
||||
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
|
||||
const ImplicitIterSpace &);
|
||||
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
void append(FrontEndMaskExpr e) {
|
||||
assert(!empty());
|
||||
getMasks().back().push_back(e);
|
||||
}
|
||||
|
||||
llvm::SmallVector<FrontEndMaskExpr> getExprs() const {
|
||||
llvm::SmallVector<FrontEndMaskExpr> maskList = getMasks()[0];
|
||||
for (size_t i = 1, d = getMasks().size(); i < d; ++i)
|
||||
maskList.append(getMasks()[i].begin(), getMasks()[i].end());
|
||||
return maskList;
|
||||
}
|
||||
|
||||
/// Add a variable binding, `var`, along with its shape for the mask
|
||||
/// expression `exp`.
|
||||
void addMaskVariable(FrontEndExpr exp, mlir::Value var, mlir::Value shape,
|
||||
mlir::Value header) {
|
||||
maskVarMap.try_emplace(exp, std::make_tuple(var, shape, header));
|
||||
}
|
||||
|
||||
/// Lookup the variable corresponding to the temporary buffer that contains
|
||||
/// the mask array expression results.
|
||||
mlir::Value lookupMaskVariable(FrontEndExpr exp) {
|
||||
return std::get<0>(maskVarMap.lookup(exp));
|
||||
}
|
||||
|
||||
/// Lookup the variable containing the shape vector for the mask array
|
||||
/// expression results.
|
||||
mlir::Value lookupMaskShapeBuffer(FrontEndExpr exp) {
|
||||
return std::get<1>(maskVarMap.lookup(exp));
|
||||
}
|
||||
|
||||
mlir::Value lookupMaskHeader(FrontEndExpr exp) {
|
||||
return std::get<2>(maskVarMap.lookup(exp));
|
||||
}
|
||||
|
||||
// Stack of WHERE constructs, each building a list of mask expressions.
|
||||
llvm::SmallVector<llvm::SmallVector<FrontEndMaskExpr>> &getMasks() {
|
||||
return stack;
|
||||
}
|
||||
const llvm::SmallVector<llvm::SmallVector<FrontEndMaskExpr>> &
|
||||
getMasks() const {
|
||||
return stack;
|
||||
}
|
||||
|
||||
// Cleanup at the end of a WHERE statement or construct.
|
||||
void shrinkStack() {
|
||||
Base::shrinkStack();
|
||||
if (stack.empty())
|
||||
maskVarMap.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::DenseMap<FrontEndExpr,
|
||||
std::tuple<mlir::Value, mlir::Value, mlir::Value>>
|
||||
maskVarMap;
|
||||
};
|
||||
|
||||
class ExplicitIterSpace;
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ExplicitIterSpace &);
|
||||
|
||||
/// Create all the array_load ops for the explicit iteration space context. The
|
||||
/// nest of FORALLs must have been analyzed a priori.
|
||||
void createArrayLoads(AbstractConverter &converter, ExplicitIterSpace &esp,
|
||||
SymMap &symMap);
|
||||
|
||||
/// Create the array_merge_store ops after the explicit iteration space context
|
||||
/// is conmpleted.
|
||||
void createArrayMergeStores(AbstractConverter &converter,
|
||||
ExplicitIterSpace &esp);
|
||||
using ExplicitSpaceArrayBases =
|
||||
std::variant<FrontEndSymbol, const evaluate::Component *,
|
||||
const evaluate::ArrayRef *>;
|
||||
|
||||
unsigned getHashValue(const ExplicitSpaceArrayBases &x);
|
||||
bool isEqual(const ExplicitSpaceArrayBases &x,
|
||||
const ExplicitSpaceArrayBases &y);
|
||||
|
||||
} // namespace Fortran::lower
|
||||
|
||||
namespace llvm {
|
||||
template <>
|
||||
struct DenseMapInfo<Fortran::lower::ExplicitSpaceArrayBases> {
|
||||
static inline Fortran::lower::ExplicitSpaceArrayBases getEmptyKey() {
|
||||
return reinterpret_cast<Fortran::lower::FrontEndSymbol>(~0);
|
||||
}
|
||||
static inline Fortran::lower::ExplicitSpaceArrayBases getTombstoneKey() {
|
||||
return reinterpret_cast<Fortran::lower::FrontEndSymbol>(~0 - 1);
|
||||
}
|
||||
static unsigned
|
||||
getHashValue(const Fortran::lower::ExplicitSpaceArrayBases &v) {
|
||||
return Fortran::lower::getHashValue(v);
|
||||
}
|
||||
static bool isEqual(const Fortran::lower::ExplicitSpaceArrayBases &lhs,
|
||||
const Fortran::lower::ExplicitSpaceArrayBases &rhs) {
|
||||
return Fortran::lower::isEqual(lhs, rhs);
|
||||
}
|
||||
};
|
||||
} // namespace llvm
|
||||
|
||||
namespace Fortran::lower {
|
||||
/// Fortran also allows arrays to be evaluated under constructs which allow the
|
||||
/// user to explicitly specify the iteration space using concurrent-control
|
||||
/// expressions. These constructs allow the user to define both an iteration
|
||||
/// space and explicit access vectors on arrays. These need not be isomorphic.
|
||||
/// The explicit iteration spaces may be conditionalized (conjunctively) with an
|
||||
/// "and" structure and may be found in FORALL (and DO CONCURRENT) constructs.
|
||||
///
|
||||
/// This class is used in the bridge to collect a stack of lists of
|
||||
/// concurrent-control expressions to be used to generate the iteration space
|
||||
/// and associated masks (if any) for a set of nested FORALL constructs around
|
||||
/// assignment and WHERE constructs.
|
||||
class ExplicitIterSpace {
|
||||
public:
|
||||
using IterSpaceDim =
|
||||
std::tuple<FrontEndSymbol, FrontEndExpr, FrontEndExpr, FrontEndExpr>;
|
||||
using ConcurrentSpec =
|
||||
std::pair<llvm::SmallVector<IterSpaceDim>, FrontEndExpr>;
|
||||
using ArrayBases = ExplicitSpaceArrayBases;
|
||||
|
||||
friend void createArrayLoads(AbstractConverter &converter,
|
||||
ExplicitIterSpace &esp, SymMap &symMap);
|
||||
friend void createArrayMergeStores(AbstractConverter &converter,
|
||||
ExplicitIterSpace &esp);
|
||||
|
||||
/// Is a FORALL context presently active?
|
||||
/// If we are lowering constructs/statements nested within a FORALL, then a
|
||||
/// FORALL context is active.
|
||||
bool isActive() const { return forallContextOpen != 0; }
|
||||
|
||||
/// Get the statement context.
|
||||
StatementContext &stmtContext() { return stmtCtx; }
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Analysis support
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Open a new construct. The analysis phase starts here.
|
||||
void pushLevel();
|
||||
|
||||
/// Close the construct.
|
||||
void popLevel();
|
||||
|
||||
/// Add new concurrent header control variable symbol.
|
||||
void addSymbol(FrontEndSymbol sym);
|
||||
|
||||
/// Collect array bases from the expression, `x`.
|
||||
void exprBase(FrontEndExpr x, bool lhs);
|
||||
|
||||
/// Called at the end of a assignment statement.
|
||||
void endAssign();
|
||||
|
||||
/// Return all the active control variables on the stack.
|
||||
llvm::SmallVector<FrontEndSymbol> collectAllSymbols();
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Code gen support
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Enter a FORALL context.
|
||||
void enter() { forallContextOpen++; }
|
||||
|
||||
/// Leave a FORALL context.
|
||||
void leave();
|
||||
|
||||
void pushLoopNest(std::function<void()> lambda) {
|
||||
ccLoopNest.push_back(lambda);
|
||||
}
|
||||
|
||||
/// Get the inner arguments that correspond to the output arrays.
|
||||
mlir::ValueRange getInnerArgs() const { return innerArgs; }
|
||||
|
||||
/// Set the inner arguments for the next loop level.
|
||||
void setInnerArgs(llvm::ArrayRef<mlir::BlockArgument> args) {
|
||||
innerArgs.clear();
|
||||
for (auto &arg : args)
|
||||
innerArgs.push_back(arg);
|
||||
}
|
||||
|
||||
/// Reset the outermost `array_load` arguments to the loop nest.
|
||||
void resetInnerArgs() { innerArgs = initialArgs; }
|
||||
|
||||
/// Capture the current outermost loop.
|
||||
void setOuterLoop(fir::DoLoopOp loop) {
|
||||
clearLoops();
|
||||
outerLoop = loop;
|
||||
}
|
||||
|
||||
/// Sets the inner loop argument at position \p offset to \p val.
|
||||
void setInnerArg(size_t offset, mlir::Value val) {
|
||||
assert(offset < innerArgs.size());
|
||||
innerArgs[offset] = val;
|
||||
}
|
||||
|
||||
/// Get the types of the output arrays.
|
||||
llvm::SmallVector<mlir::Type> innerArgTypes() const {
|
||||
llvm::SmallVector<mlir::Type> result;
|
||||
for (auto &arg : innerArgs)
|
||||
result.push_back(arg.getType());
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Create a binding between an Ev::Expr node pointer and a fir::array_load
|
||||
/// op. This bindings will be used when generating the IR.
|
||||
void bindLoad(ArrayBases base, fir::ArrayLoadOp load) {
|
||||
loadBindings.try_emplace(std::move(base), load);
|
||||
}
|
||||
|
||||
fir::ArrayLoadOp findBinding(const ArrayBases &base) {
|
||||
return loadBindings.lookup(base);
|
||||
}
|
||||
|
||||
/// `load` must be a LHS array_load. Returns `llvm::None` on error.
|
||||
llvm::Optional<size_t> findArgPosition(fir::ArrayLoadOp load);
|
||||
|
||||
bool isLHS(fir::ArrayLoadOp load) { return findArgPosition(load).hasValue(); }
|
||||
|
||||
/// `load` must be a LHS array_load. Determine the threaded inner argument
|
||||
/// corresponding to this load.
|
||||
mlir::Value findArgumentOfLoad(fir::ArrayLoadOp load) {
|
||||
if (auto opt = findArgPosition(load))
|
||||
return innerArgs[*opt];
|
||||
llvm_unreachable("array load argument not found");
|
||||
}
|
||||
|
||||
size_t argPosition(mlir::Value arg) {
|
||||
for (auto i : llvm::enumerate(innerArgs))
|
||||
if (arg == i.value())
|
||||
return i.index();
|
||||
llvm_unreachable("inner argument value was not found");
|
||||
}
|
||||
|
||||
llvm::Optional<fir::ArrayLoadOp> getLhsLoad(size_t i) {
|
||||
assert(i < lhsBases.size());
|
||||
if (lhsBases[counter].hasValue())
|
||||
return findBinding(lhsBases[counter].getValue());
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
/// Return the outermost loop in this FORALL nest.
|
||||
fir::DoLoopOp getOuterLoop() {
|
||||
assert(outerLoop.hasValue());
|
||||
return outerLoop.getValue();
|
||||
}
|
||||
|
||||
/// Return the statement context for the entire, outermost FORALL construct.
|
||||
StatementContext &outermostContext() { return outerContext; }
|
||||
|
||||
/// Generate the explicit loop nest.
|
||||
void genLoopNest() {
|
||||
for (auto &lambda : ccLoopNest)
|
||||
lambda();
|
||||
}
|
||||
|
||||
/// Clear the array_load bindings.
|
||||
void resetBindings() { loadBindings.clear(); }
|
||||
|
||||
/// Get the current counter value.
|
||||
std::size_t getCounter() const { return counter; }
|
||||
|
||||
/// Increment the counter value to the next assignment statement.
|
||||
void incrementCounter() { counter++; }
|
||||
|
||||
bool isOutermostForall() const {
|
||||
assert(forallContextOpen);
|
||||
return forallContextOpen == 1;
|
||||
}
|
||||
|
||||
void attachLoopCleanup(std::function<void(fir::FirOpBuilder &builder)> fn) {
|
||||
if (!loopCleanup.hasValue()) {
|
||||
loopCleanup = fn;
|
||||
return;
|
||||
}
|
||||
std::function<void(fir::FirOpBuilder &)> oldFn = loopCleanup.getValue();
|
||||
loopCleanup = [=](fir::FirOpBuilder &builder) {
|
||||
oldFn(builder);
|
||||
fn(builder);
|
||||
};
|
||||
}
|
||||
|
||||
// LLVM standard dump method.
|
||||
LLVM_DUMP_METHOD void dump() const;
|
||||
|
||||
// Pretty-print.
|
||||
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
|
||||
const ExplicitIterSpace &);
|
||||
|
||||
/// Finalize the current body statement context.
|
||||
void finalizeContext() { stmtCtx.finalize(); }
|
||||
|
||||
void appendLoops(const llvm::SmallVector<fir::DoLoopOp> &loops) {
|
||||
loopStack.push_back(loops);
|
||||
}
|
||||
|
||||
void clearLoops() { loopStack.clear(); }
|
||||
|
||||
llvm::SmallVector<llvm::SmallVector<fir::DoLoopOp>> getLoopStack() const {
|
||||
return loopStack;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Cleanup the analysis results.
|
||||
void conditionalCleanup();
|
||||
|
||||
StatementContext outerContext;
|
||||
|
||||
// A stack of lists of front-end symbols.
|
||||
llvm::SmallVector<llvm::SmallVector<FrontEndSymbol>> symbolStack;
|
||||
llvm::SmallVector<llvm::Optional<ArrayBases>> lhsBases;
|
||||
llvm::SmallVector<llvm::SmallVector<ArrayBases>> rhsBases;
|
||||
llvm::DenseMap<ArrayBases, fir::ArrayLoadOp> loadBindings;
|
||||
|
||||
// Stack of lambdas to create the loop nest.
|
||||
llvm::SmallVector<std::function<void()>> ccLoopNest;
|
||||
|
||||
// Assignment statement context (inside the loop nest).
|
||||
StatementContext stmtCtx;
|
||||
llvm::SmallVector<mlir::Value> innerArgs;
|
||||
llvm::SmallVector<mlir::Value> initialArgs;
|
||||
llvm::Optional<fir::DoLoopOp> outerLoop;
|
||||
llvm::SmallVector<llvm::SmallVector<fir::DoLoopOp>> loopStack;
|
||||
llvm::Optional<std::function<void(fir::FirOpBuilder &)>> loopCleanup;
|
||||
std::size_t forallContextOpen = 0;
|
||||
std::size_t counter = 0;
|
||||
};
|
||||
|
||||
/// Is there a Symbol in common between the concurrent header set and the set
|
||||
/// of symbols in the expression?
|
||||
template <typename A>
|
||||
bool symbolSetsIntersect(llvm::ArrayRef<FrontEndSymbol> ctrlSet,
|
||||
const A &exprSyms) {
|
||||
for (const auto &sym : exprSyms)
|
||||
if (std::find(ctrlSet.begin(), ctrlSet.end(), &sym.get()) != ctrlSet.end())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Determine if the subscript expression symbols from an Ev::ArrayRef
|
||||
/// intersects with the set of concurrent control symbols, `ctrlSet`.
|
||||
template <typename A>
|
||||
bool symbolsIntersectSubscripts(llvm::ArrayRef<FrontEndSymbol> ctrlSet,
|
||||
const A &subscripts) {
|
||||
for (auto &sub : subscripts) {
|
||||
if (const auto *expr =
|
||||
std::get_if<evaluate::IndirectSubscriptIntegerExpr>(&sub.u))
|
||||
if (symbolSetsIntersect(ctrlSet, evaluate::CollectSymbols(expr->value())))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Fortran::lower
|
||||
|
||||
#endif // FORTRAN_LOWER_ITERATIONSPACE_H
|
|
@ -286,6 +286,11 @@ public:
|
|||
/// this may create a `fir.shift` op.
|
||||
mlir::Value createShape(mlir::Location loc, const fir::ExtendedValue &exv);
|
||||
|
||||
/// Create a slice op extended value. The value to be sliced, `exv`, must be
|
||||
/// an array.
|
||||
mlir::Value createSlice(mlir::Location loc, const fir::ExtendedValue &exv,
|
||||
mlir::ValueRange triples, mlir::ValueRange path);
|
||||
|
||||
/// Create a boxed value (Fortran descriptor) to be passed to the runtime.
|
||||
/// \p exv is an extended value holding a memory reference to the object that
|
||||
/// must be boxed. This function will crash if provided something that is not
|
||||
|
@ -389,6 +394,13 @@ mlir::Value readCharLen(fir::FirOpBuilder &builder, mlir::Location loc,
|
|||
mlir::Value readExtent(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
const fir::ExtendedValue &box, unsigned dim);
|
||||
|
||||
/// Read or get the lower bound in dimension \p dim of the array described by
|
||||
/// \p box. If the lower bound is left default in the ExtendedValue,
|
||||
/// \p defaultValue will be returned.
|
||||
mlir::Value readLowerBound(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
const fir::ExtendedValue &box, unsigned dim,
|
||||
mlir::Value defaultValue);
|
||||
|
||||
/// Read extents from \p box.
|
||||
llvm::SmallVector<mlir::Value> readExtents(fir::FirOpBuilder &builder,
|
||||
mlir::Location loc,
|
||||
|
@ -447,6 +459,35 @@ mlir::TupleType getRaggedArrayHeaderType(fir::FirOpBuilder &builder);
|
|||
mlir::Value createZeroValue(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
mlir::Type type);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// ExtendedValue helpers
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// Return the extended value for a component of a derived type instance given
|
||||
/// the address of the component.
|
||||
fir::ExtendedValue componentToExtendedValue(fir::FirOpBuilder &builder,
|
||||
mlir::Location loc,
|
||||
mlir::Value component);
|
||||
|
||||
/// Given the address of an array element and the ExtendedValue describing the
|
||||
/// array, returns the ExtendedValue describing the array element. The purpose
|
||||
/// is to propagate the length parameters of the array to the element.
|
||||
/// This can be used for elements of `array` or `array(i:j:k)`. If \p element
|
||||
/// belongs to an array section `array%x` whose base is \p array,
|
||||
/// arraySectionElementToExtendedValue must be used instead.
|
||||
fir::ExtendedValue arrayElementToExtendedValue(fir::FirOpBuilder &builder,
|
||||
mlir::Location loc,
|
||||
const fir::ExtendedValue &array,
|
||||
mlir::Value element);
|
||||
|
||||
/// Build the ExtendedValue for \p element that is an element of an array or
|
||||
/// array section with \p array base (`array` or `array(i:j:k)%x%y`).
|
||||
/// If it is an array section, \p slice must be provided and be a fir::SliceOp
|
||||
/// that describes the section.
|
||||
fir::ExtendedValue arraySectionElementToExtendedValue(
|
||||
fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice);
|
||||
|
||||
} // namespace fir::factory
|
||||
|
||||
#endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H
|
||||
|
|
|
@ -31,6 +31,21 @@ constexpr llvm::StringRef attrFortranArrayOffsets() {
|
|||
return "Fortran.offsets";
|
||||
}
|
||||
|
||||
/// Get extents from fir.shape/fir.shape_shift op. Empty result if
|
||||
/// \p shapeVal is empty or is a fir.shift.
|
||||
inline std::vector<mlir::Value> getExtents(mlir::Value shapeVal) {
|
||||
if (shapeVal)
|
||||
if (auto *shapeOp = shapeVal.getDefiningOp()) {
|
||||
if (auto shOp = mlir::dyn_cast<fir::ShapeOp>(shapeOp)) {
|
||||
auto operands = shOp.getExtents();
|
||||
return {operands.begin(), operands.end()};
|
||||
}
|
||||
if (auto shOp = mlir::dyn_cast<fir::ShapeShiftOp>(shapeOp))
|
||||
return shOp.getExtents();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Get origins from fir.shape_shift/fir.shift op. Empty result if
|
||||
/// \p shapeVal is empty or is a fir.shape.
|
||||
inline std::vector<mlir::Value> getOrigins(mlir::Value shapeVal) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "flang/Lower/ConvertExpr.h"
|
||||
#include "flang/Lower/ConvertType.h"
|
||||
#include "flang/Lower/ConvertVariable.h"
|
||||
#include "flang/Lower/IterationSpace.h"
|
||||
#include "flang/Lower/Mangler.h"
|
||||
#include "flang/Lower/PFTBuilder.h"
|
||||
#include "flang/Lower/Runtime.h"
|
||||
|
@ -517,7 +518,7 @@ private:
|
|||
if (assign.lhs.Rank() > 0) {
|
||||
// Array assignment
|
||||
// See Fortran 2018 10.2.1.3 p5, p6, and p7
|
||||
TODO(toLocation(), "Array assignment");
|
||||
genArrayAssignment(assign, stmtCtx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -835,6 +836,26 @@ private:
|
|||
TODO(toLocation(), "LockStmt lowering");
|
||||
}
|
||||
|
||||
/// Generate an array assignment.
|
||||
/// This is an assignment expression with rank > 0. The assignment may or may
|
||||
/// not be in a WHERE and/or FORALL context.
|
||||
void genArrayAssignment(const Fortran::evaluate::Assignment &assign,
|
||||
Fortran::lower::StatementContext &stmtCtx) {
|
||||
if (isWholeAllocatable(assign.lhs)) {
|
||||
// Assignment to allocatables may require the lhs to be
|
||||
// deallocated/reallocated. See Fortran 2018 10.2.1.3 p3
|
||||
Fortran::lower::createAllocatableArrayAssignment(
|
||||
*this, assign.lhs, assign.rhs, explicitIterSpace, implicitIterSpace,
|
||||
localSymbols, stmtCtx);
|
||||
return;
|
||||
}
|
||||
|
||||
// No masks and the iteration space is implied by the array, so create a
|
||||
// simple array assignment.
|
||||
Fortran::lower::createSomeArrayAssignment(*this, assign.lhs, assign.rhs,
|
||||
localSymbols, stmtCtx);
|
||||
}
|
||||
|
||||
void genFIR(const Fortran::parser::WhereConstruct &c) {
|
||||
TODO(toLocation(), "WhereConstruct lowering");
|
||||
}
|
||||
|
@ -1047,6 +1068,8 @@ private:
|
|||
|
||||
/// Tuple of host assoicated variables.
|
||||
mlir::Value hostAssocTuple;
|
||||
Fortran::lower::ImplicitIterSpace implicitIterSpace;
|
||||
Fortran::lower::ExplicitIterSpace explicitIterSpace;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -9,6 +9,9 @@ add_flang_library(FortranLower
|
|||
ConvertType.cpp
|
||||
ConvertVariable.cpp
|
||||
IntrinsicCall.cpp
|
||||
ComponentPath.cpp
|
||||
DumpEvaluateExpr.cpp
|
||||
IterationSpace.cpp
|
||||
Mangler.cpp
|
||||
OpenACC.cpp
|
||||
OpenMP.cpp
|
||||
|
|
|
@ -601,20 +601,22 @@ private:
|
|||
|
||||
fir::SequenceType::Shape getBounds(const Fortran::evaluate::Shape &shape) {
|
||||
fir::SequenceType::Shape bounds;
|
||||
for (Fortran::evaluate::MaybeExtentExpr extentExpr : shape) {
|
||||
fir::SequenceType::Extent extent = fir::SequenceType::getUnknownExtent();
|
||||
if (std::optional<std::int64_t> constantExtent =
|
||||
toInt64(std::move(extentExpr)))
|
||||
extent = *constantExtent;
|
||||
bounds.push_back(extent);
|
||||
for (const std::optional<Fortran::evaluate::ExtentExpr> &extent : shape) {
|
||||
fir::SequenceType::Extent bound = fir::SequenceType::getUnknownExtent();
|
||||
if (std::optional<std::int64_t> i = toInt64(extent))
|
||||
bound = *i;
|
||||
bounds.emplace_back(bound);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
std::optional<std::int64_t> toInt64(A &&expr) {
|
||||
return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold(
|
||||
getConverter().getFoldingContext(), std::move(expr)));
|
||||
std::optional<std::int64_t>
|
||||
toInt64(std::optional<
|
||||
Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>>
|
||||
expr) {
|
||||
if (expr)
|
||||
return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold(
|
||||
getConverter().getFoldingContext(), toEvExpr(*expr)));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Return a vector with an attribute with the name of the argument if this
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//===-- ComponentPath.cpp -------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "flang/Lower/ComponentPath.h"
|
||||
|
||||
static std::function<
|
||||
Fortran::lower::IterationSpace(const Fortran::lower::IterationSpace &)>
|
||||
getIdentityFunc() {
|
||||
return [](const Fortran::lower::IterationSpace &s) { return s; };
|
||||
}
|
||||
|
||||
static std::function<
|
||||
Fortran::lower::IterationSpace(const Fortran::lower::IterationSpace &)>
|
||||
getNullaryFunc() {
|
||||
return [](const Fortran::lower::IterationSpace &s) {
|
||||
Fortran::lower::IterationSpace newIters(s);
|
||||
newIters.clearIndices();
|
||||
return newIters;
|
||||
};
|
||||
}
|
||||
|
||||
void Fortran::lower::ComponentPath::clear() {
|
||||
reversePath.clear();
|
||||
substring = nullptr;
|
||||
applied = false;
|
||||
prefixComponents.clear();
|
||||
trips.clear();
|
||||
suffixComponents.clear();
|
||||
pc = getIdentityFunc();
|
||||
}
|
||||
|
||||
bool Fortran::lower::isRankedArrayAccess(const Fortran::evaluate::ArrayRef &x) {
|
||||
for (const Fortran::evaluate::Subscript &sub : x.subscript()) {
|
||||
if (std::visit(
|
||||
Fortran::common::visitors{
|
||||
[&](const Fortran::evaluate::Triplet &) { return true; },
|
||||
[&](const Fortran::evaluate::IndirectSubscriptIntegerExpr &e) {
|
||||
return e.value().Rank() > 0;
|
||||
}},
|
||||
sub.u))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Fortran::lower::ComponentPath::setPC(bool isImplicit) {
|
||||
pc = isImplicit ? getIdentityFunc() : getNullaryFunc();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -301,13 +301,13 @@ void Fortran::lower::mapSymbolAttributes(
|
|||
//===--------------------------------------------------------------===//
|
||||
|
||||
[&](const Fortran::lower::details::ScalarStaticChar &x) {
|
||||
TODO(loc, "mapSymbolAttributes ScalarStaticChar");
|
||||
TODO(loc, "ScalarStaticChar variable lowering");
|
||||
},
|
||||
|
||||
//===--------------------------------------------------------------===//
|
||||
|
||||
[&](const Fortran::lower::details::ScalarDynamicChar &x) {
|
||||
TODO(loc, "mapSymbolAttributes ScalarDynamicChar");
|
||||
TODO(loc, "ScalarDynamicChar variable lowering");
|
||||
},
|
||||
|
||||
//===--------------------------------------------------------------===//
|
||||
|
@ -346,31 +346,31 @@ void Fortran::lower::mapSymbolAttributes(
|
|||
//===--------------------------------------------------------------===//
|
||||
|
||||
[&](const Fortran::lower::details::DynamicArray &x) {
|
||||
TODO(loc, "mapSymbolAttributes DynamicArray");
|
||||
TODO(loc, "DynamicArray variable lowering");
|
||||
},
|
||||
|
||||
//===--------------------------------------------------------------===//
|
||||
|
||||
[&](const Fortran::lower::details::StaticArrayStaticChar &x) {
|
||||
TODO(loc, "mapSymbolAttributes StaticArrayStaticChar");
|
||||
TODO(loc, "StaticArrayStaticChar variable lowering");
|
||||
},
|
||||
|
||||
//===--------------------------------------------------------------===//
|
||||
|
||||
[&](const Fortran::lower::details::StaticArrayDynamicChar &x) {
|
||||
TODO(loc, "mapSymbolAttributes StaticArrayDynamicChar");
|
||||
TODO(loc, "StaticArrayDynamicChar variable lowering");
|
||||
},
|
||||
|
||||
//===--------------------------------------------------------------===//
|
||||
|
||||
[&](const Fortran::lower::details::DynamicArrayStaticChar &x) {
|
||||
TODO(loc, "mapSymbolAttributes DynamicArrayStaticChar");
|
||||
TODO(loc, "DynamicArrayStaticChar variable lowering");
|
||||
},
|
||||
|
||||
//===--------------------------------------------------------------===//
|
||||
|
||||
[&](const Fortran::lower::details::DynamicArrayDynamicChar &x) {
|
||||
TODO(loc, "mapSymbolAttributes DynamicArrayDynamicChar");
|
||||
TODO(loc, "DynamicArrayDynamicChar variable lowering");
|
||||
},
|
||||
|
||||
//===--------------------------------------------------------------===//
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
//===-- Lower/DumpEvaluateExpr.cpp ----------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "flang/Lower/DumpEvaluateExpr.h"
|
||||
#include <iostream>
|
||||
|
||||
static constexpr char whiteSpacePadding[] =
|
||||
">> ";
|
||||
static constexpr auto whiteSize = sizeof(whiteSpacePadding) - 1;
|
||||
|
||||
inline const char *Fortran::lower::DumpEvaluateExpr::getIndentString() const {
|
||||
auto count = (level * 2 >= whiteSize) ? whiteSize : level * 2;
|
||||
return whiteSpacePadding + whiteSize - count;
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::CoarrayRef &x) {
|
||||
indent("coarray ref");
|
||||
show(x.base());
|
||||
show(x.subscript());
|
||||
show(x.cosubscript());
|
||||
show(x.stat());
|
||||
show(x.team());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::BOZLiteralConstant &) {
|
||||
print("BOZ literal constant");
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::NullPointer &) {
|
||||
print("null pointer");
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::semantics::Symbol &symbol) {
|
||||
const auto &ultimate{symbol.GetUltimate()};
|
||||
print("symbol: "s + std::string(toStringRef(symbol.name())));
|
||||
if (const auto *assoc =
|
||||
ultimate.detailsIf<Fortran::semantics::AssocEntityDetails>()) {
|
||||
indent("assoc details");
|
||||
show(assoc->expr());
|
||||
outdent();
|
||||
}
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::StaticDataObject &) {
|
||||
print("static data object");
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::ImpliedDoIndex &) {
|
||||
print("implied do index");
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::BaseObject &x) {
|
||||
indent("base object");
|
||||
show(x.u);
|
||||
outdent();
|
||||
}
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::Component &x) {
|
||||
indent("component");
|
||||
show(x.base());
|
||||
show(x.GetLastSymbol());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::NamedEntity &x) {
|
||||
indent("named entity");
|
||||
if (const auto *component = x.UnwrapComponent())
|
||||
show(*component);
|
||||
else
|
||||
show(x.GetFirstSymbol());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::TypeParamInquiry &x) {
|
||||
indent("type inquiry");
|
||||
show(x.base());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::Triplet &x) {
|
||||
indent("triplet");
|
||||
show(x.lower());
|
||||
show(x.upper());
|
||||
show(x.stride());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::Subscript &x) {
|
||||
indent("subscript");
|
||||
show(x.u);
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::ArrayRef &x) {
|
||||
indent("array ref");
|
||||
show(x.base());
|
||||
show(x.subscript());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::DataRef &x) {
|
||||
indent("data ref");
|
||||
show(x.u);
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::Substring &x) {
|
||||
indent("substring");
|
||||
show(x.parent());
|
||||
show(x.lower());
|
||||
show(x.upper());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::semantics::ParamValue &x) {
|
||||
indent("param value");
|
||||
show(x.GetExplicit());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::semantics::DerivedTypeSpec::ParameterMapType::value_type
|
||||
&x) {
|
||||
show(x.second);
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::semantics::DerivedTypeSpec &x) {
|
||||
indent("derived type spec");
|
||||
for (auto &v : x.parameters())
|
||||
show(v);
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::StructureConstructorValues::value_type &x) {
|
||||
show(x.second);
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::StructureConstructor &x) {
|
||||
indent("structure constructor");
|
||||
show(x.derivedTypeSpec());
|
||||
for (auto &v : x)
|
||||
show(v);
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x) {
|
||||
indent("expr some type");
|
||||
show(x.u);
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::ComplexPart &x) {
|
||||
indent("complex part");
|
||||
show(x.complex());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::ActualArgument &x) {
|
||||
indent("actual argument");
|
||||
if (const auto *symbol = x.GetAssumedTypeDummy())
|
||||
show(*symbol);
|
||||
else
|
||||
show(x.UnwrapExpr());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::ProcedureDesignator &x) {
|
||||
indent("procedure designator");
|
||||
if (const auto *component = x.GetComponent())
|
||||
show(*component);
|
||||
else if (const auto *symbol = x.GetSymbol())
|
||||
show(*symbol);
|
||||
else
|
||||
show(DEREF(x.GetSpecificIntrinsic()));
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::SpecificIntrinsic &) {
|
||||
print("specific intrinsic");
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::show(
|
||||
const Fortran::evaluate::DescriptorInquiry &x) {
|
||||
indent("descriptor inquiry");
|
||||
show(x.base());
|
||||
outdent();
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::print(llvm::Twine twine) {
|
||||
outs << getIndentString() << twine << '\n';
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::indent(llvm::StringRef s) {
|
||||
print(s + " {");
|
||||
level++;
|
||||
}
|
||||
|
||||
void Fortran::lower::DumpEvaluateExpr::outdent() {
|
||||
if (level)
|
||||
level--;
|
||||
print("}");
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Boilerplate entry points that the debugger can find.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void Fortran::lower::dumpEvExpr(const Fortran::semantics::SomeExpr &x) {
|
||||
DumpEvaluateExpr::dump(x);
|
||||
}
|
||||
|
||||
void Fortran::lower::dumpEvExpr(
|
||||
const Fortran::evaluate::Expr<
|
||||
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 4>>
|
||||
&x) {
|
||||
DumpEvaluateExpr::dump(x);
|
||||
}
|
||||
|
||||
void Fortran::lower::dumpEvExpr(
|
||||
const Fortran::evaluate::Expr<
|
||||
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>>
|
||||
&x) {
|
||||
DumpEvaluateExpr::dump(x);
|
||||
}
|
||||
|
||||
void Fortran::lower::dumpEvExpr(const Fortran::evaluate::ArrayRef &x) {
|
||||
DumpEvaluateExpr::dump(x);
|
||||
}
|
||||
|
||||
void Fortran::lower::dumpEvExpr(const Fortran::evaluate::DataRef &x) {
|
||||
DumpEvaluateExpr::dump(x);
|
||||
}
|
||||
|
||||
void Fortran::lower::dumpEvExpr(const Fortran::evaluate::Substring &x) {
|
||||
DumpEvaluateExpr::dump(x);
|
||||
}
|
||||
|
||||
void Fortran::lower::dumpEvExpr(
|
||||
const Fortran::evaluate::Designator<
|
||||
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 4>>
|
||||
&x) {
|
||||
DumpEvaluateExpr::dump(x);
|
||||
}
|
|
@ -0,0 +1,940 @@
|
|||
//===-- IterationSpace.cpp ------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "flang/Lower/IterationSpace.h"
|
||||
#include "flang/Evaluate/expression.h"
|
||||
#include "flang/Lower/AbstractConverter.h"
|
||||
#include "flang/Lower/Support/Utils.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
#define DEBUG_TYPE "flang-lower-iteration-space"
|
||||
|
||||
namespace {
|
||||
// Fortran::evaluate::Expr are functional values organized like an AST. A
|
||||
// Fortran::evaluate::Expr is meant to be moved and cloned. Using the front end
|
||||
// tools can often cause copies and extra wrapper classes to be added to any
|
||||
// Fortran::evalute::Expr. These values should not be assumed or relied upon to
|
||||
// have an *object* identity. They are deeply recursive, irregular structures
|
||||
// built from a large number of classes which do not use inheritance and
|
||||
// necessitate a large volume of boilerplate code as a result.
|
||||
//
|
||||
// Contrastingly, LLVM data structures make ubiquitous assumptions about an
|
||||
// object's identity via pointers to the object. An object's location in memory
|
||||
// is thus very often an identifying relation.
|
||||
|
||||
// This class defines a hash computation of a Fortran::evaluate::Expr tree value
|
||||
// so it can be used with llvm::DenseMap. The Fortran::evaluate::Expr need not
|
||||
// have the same address.
|
||||
class HashEvaluateExpr {
|
||||
public:
|
||||
// A Se::Symbol is the only part of an Fortran::evaluate::Expr with an
|
||||
// identity property.
|
||||
static unsigned getHashValue(const Fortran::semantics::Symbol &x) {
|
||||
return static_cast<unsigned>(reinterpret_cast<std::intptr_t>(&x));
|
||||
}
|
||||
template <typename A, bool COPY>
|
||||
static unsigned getHashValue(const Fortran::common::Indirection<A, COPY> &x) {
|
||||
return getHashValue(x.value());
|
||||
}
|
||||
template <typename A>
|
||||
static unsigned getHashValue(const std::optional<A> &x) {
|
||||
if (x.has_value())
|
||||
return getHashValue(x.value());
|
||||
return 0u;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::Subscript &x) {
|
||||
return std::visit([&](const auto &v) { return getHashValue(v); }, x.u);
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::Triplet &x) {
|
||||
return getHashValue(x.lower()) - getHashValue(x.upper()) * 5u -
|
||||
getHashValue(x.stride()) * 11u;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::Component &x) {
|
||||
return getHashValue(x.base()) * 83u - getHashValue(x.GetLastSymbol());
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::ArrayRef &x) {
|
||||
unsigned subs = 1u;
|
||||
for (const Fortran::evaluate::Subscript &v : x.subscript())
|
||||
subs -= getHashValue(v);
|
||||
return getHashValue(x.base()) * 89u - subs;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::CoarrayRef &x) {
|
||||
unsigned subs = 1u;
|
||||
for (const Fortran::evaluate::Subscript &v : x.subscript())
|
||||
subs -= getHashValue(v);
|
||||
unsigned cosubs = 3u;
|
||||
for (const Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger> &v :
|
||||
x.cosubscript())
|
||||
cosubs -= getHashValue(v);
|
||||
unsigned syms = 7u;
|
||||
for (const Fortran::evaluate::SymbolRef &v : x.base())
|
||||
syms += getHashValue(v);
|
||||
return syms * 97u - subs - cosubs + getHashValue(x.stat()) + 257u +
|
||||
getHashValue(x.team());
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::NamedEntity &x) {
|
||||
if (x.IsSymbol())
|
||||
return getHashValue(x.GetFirstSymbol()) * 11u;
|
||||
return getHashValue(x.GetComponent()) * 13u;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::DataRef &x) {
|
||||
return std::visit([&](const auto &v) { return getHashValue(v); }, x.u);
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::ComplexPart &x) {
|
||||
return getHashValue(x.complex()) - static_cast<unsigned>(x.part());
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC1, int KIND,
|
||||
Fortran::common::TypeCategory TC2>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>, TC2>
|
||||
&x) {
|
||||
return getHashValue(x.left()) - (static_cast<unsigned>(TC1) + 2u) -
|
||||
(static_cast<unsigned>(KIND) + 5u);
|
||||
}
|
||||
template <int KIND>
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::ComplexComponent<KIND> &x) {
|
||||
return getHashValue(x.left()) -
|
||||
(static_cast<unsigned>(x.isImaginaryPart) + 1u) * 3u;
|
||||
}
|
||||
template <typename T>
|
||||
static unsigned getHashValue(const Fortran::evaluate::Parentheses<T> &x) {
|
||||
return getHashValue(x.left()) * 17u;
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Negate<Fortran::evaluate::Type<TC, KIND>> &x) {
|
||||
return getHashValue(x.left()) - (static_cast<unsigned>(TC) + 5u) -
|
||||
(static_cast<unsigned>(KIND) + 7u);
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Add<Fortran::evaluate::Type<TC, KIND>> &x) {
|
||||
return (getHashValue(x.left()) + getHashValue(x.right())) * 23u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Subtract<Fortran::evaluate::Type<TC, KIND>> &x) {
|
||||
return (getHashValue(x.left()) - getHashValue(x.right())) * 19u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Multiply<Fortran::evaluate::Type<TC, KIND>> &x) {
|
||||
return (getHashValue(x.left()) + getHashValue(x.right())) * 29u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Divide<Fortran::evaluate::Type<TC, KIND>> &x) {
|
||||
return (getHashValue(x.left()) - getHashValue(x.right())) * 31u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
|
||||
return (getHashValue(x.left()) - getHashValue(x.right())) * 37u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
|
||||
return (getHashValue(x.left()) + getHashValue(x.right())) * 41u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND) +
|
||||
static_cast<unsigned>(x.ordering) * 7u;
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
|
||||
&x) {
|
||||
return (getHashValue(x.left()) - getHashValue(x.right())) * 43u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <int KIND>
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
|
||||
return (getHashValue(x.left()) - getHashValue(x.right())) * 47u +
|
||||
static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <int KIND>
|
||||
static unsigned getHashValue(const Fortran::evaluate::Concat<KIND> &x) {
|
||||
return (getHashValue(x.left()) - getHashValue(x.right())) * 53u +
|
||||
static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <int KIND>
|
||||
static unsigned getHashValue(const Fortran::evaluate::SetLength<KIND> &x) {
|
||||
return (getHashValue(x.left()) - getHashValue(x.right())) * 59u +
|
||||
static_cast<unsigned>(KIND);
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) {
|
||||
return getHashValue(sym.get());
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::Substring &x) {
|
||||
return 61u * std::visit([&](const auto &p) { return getHashValue(p); },
|
||||
x.parent()) -
|
||||
getHashValue(x.lower()) - (getHashValue(x.lower()) + 1u);
|
||||
}
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::StaticDataObject::Pointer &x) {
|
||||
return llvm::hash_value(x->name());
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::SpecificIntrinsic &x) {
|
||||
return llvm::hash_value(x.name);
|
||||
}
|
||||
template <typename A>
|
||||
static unsigned getHashValue(const Fortran::evaluate::Constant<A> &x) {
|
||||
// FIXME: Should hash the content.
|
||||
return 103u;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::ActualArgument &x) {
|
||||
if (const Fortran::evaluate::Symbol *sym = x.GetAssumedTypeDummy())
|
||||
return getHashValue(*sym);
|
||||
return getHashValue(*x.UnwrapExpr());
|
||||
}
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::ProcedureDesignator &x) {
|
||||
return std::visit([&](const auto &v) { return getHashValue(v); }, x.u);
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::ProcedureRef &x) {
|
||||
unsigned args = 13u;
|
||||
for (const std::optional<Fortran::evaluate::ActualArgument> &v :
|
||||
x.arguments())
|
||||
args -= getHashValue(v);
|
||||
return getHashValue(x.proc()) * 101u - args;
|
||||
}
|
||||
template <typename A>
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::ArrayConstructor<A> &x) {
|
||||
// FIXME: hash the contents.
|
||||
return 127u;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::ImpliedDoIndex &x) {
|
||||
return llvm::hash_value(toStringRef(x.name).str()) * 131u;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::TypeParamInquiry &x) {
|
||||
return getHashValue(x.base()) * 137u - getHashValue(x.parameter()) * 3u;
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::DescriptorInquiry &x) {
|
||||
return getHashValue(x.base()) * 139u -
|
||||
static_cast<unsigned>(x.field()) * 13u +
|
||||
static_cast<unsigned>(x.dimension());
|
||||
}
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::StructureConstructor &x) {
|
||||
// FIXME: hash the contents.
|
||||
return 149u;
|
||||
}
|
||||
template <int KIND>
|
||||
static unsigned getHashValue(const Fortran::evaluate::Not<KIND> &x) {
|
||||
return getHashValue(x.left()) * 61u + static_cast<unsigned>(KIND);
|
||||
}
|
||||
template <int KIND>
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::LogicalOperation<KIND> &x) {
|
||||
unsigned result = getHashValue(x.left()) + getHashValue(x.right());
|
||||
return result * 67u + static_cast<unsigned>(x.logicalOperator) * 5u;
|
||||
}
|
||||
template <Fortran::common::TypeCategory TC, int KIND>
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Relational<Fortran::evaluate::Type<TC, KIND>>
|
||||
&x) {
|
||||
return (getHashValue(x.left()) + getHashValue(x.right())) * 71u +
|
||||
static_cast<unsigned>(TC) + static_cast<unsigned>(KIND) +
|
||||
static_cast<unsigned>(x.opr) * 11u;
|
||||
}
|
||||
template <typename A>
|
||||
static unsigned getHashValue(const Fortran::evaluate::Expr<A> &x) {
|
||||
return std::visit([&](const auto &v) { return getHashValue(v); }, x.u);
|
||||
}
|
||||
static unsigned getHashValue(
|
||||
const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x) {
|
||||
return std::visit([&](const auto &v) { return getHashValue(v); }, x.u);
|
||||
}
|
||||
template <typename A>
|
||||
static unsigned getHashValue(const Fortran::evaluate::Designator<A> &x) {
|
||||
return std::visit([&](const auto &v) { return getHashValue(v); }, x.u);
|
||||
}
|
||||
template <int BITS>
|
||||
static unsigned
|
||||
getHashValue(const Fortran::evaluate::value::Integer<BITS> &x) {
|
||||
return static_cast<unsigned>(x.ToSInt());
|
||||
}
|
||||
static unsigned getHashValue(const Fortran::evaluate::NullPointer &x) {
|
||||
return ~179u;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
unsigned Fortran::lower::getHashValue(
|
||||
const Fortran::lower::ExplicitIterSpace::ArrayBases &x) {
|
||||
return std::visit(
|
||||
[&](const auto *p) { return HashEvaluateExpr::getHashValue(*p); }, x);
|
||||
}
|
||||
|
||||
unsigned Fortran::lower::getHashValue(Fortran::lower::FrontEndExpr x) {
|
||||
return HashEvaluateExpr::getHashValue(*x);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Define the is equals test for using Fortran::evaluate::Expr values with
|
||||
// llvm::DenseMap.
|
||||
class IsEqualEvaluateExpr {
|
||||
public:
|
||||
// A Se::Symbol is the only part of an Fortran::evaluate::Expr with an
|
||||
// identity property.
|
||||
static bool isEqual(const Fortran::semantics::Symbol &x,
|
||||
const Fortran::semantics::Symbol &y) {
|
||||
return isEqual(&x, &y);
|
||||
}
|
||||
static bool isEqual(const Fortran::semantics::Symbol *x,
|
||||
const Fortran::semantics::Symbol *y) {
|
||||
return x == y;
|
||||
}
|
||||
template <typename A, bool COPY>
|
||||
static bool isEqual(const Fortran::common::Indirection<A, COPY> &x,
|
||||
const Fortran::common::Indirection<A, COPY> &y) {
|
||||
return isEqual(x.value(), y.value());
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const std::optional<A> &x, const std::optional<A> &y) {
|
||||
if (x.has_value() && y.has_value())
|
||||
return isEqual(x.value(), y.value());
|
||||
return !x.has_value() && !y.has_value();
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const std::vector<A> &x, const std::vector<A> &y) {
|
||||
if (x.size() != y.size())
|
||||
return false;
|
||||
const std::size_t size = x.size();
|
||||
for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i)
|
||||
if (!isEqual(x[i], y[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::Subscript &x,
|
||||
const Fortran::evaluate::Subscript &y) {
|
||||
return std::visit(
|
||||
[&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::Triplet &x,
|
||||
const Fortran::evaluate::Triplet &y) {
|
||||
return isEqual(x.lower(), y.lower()) && isEqual(x.upper(), y.upper()) &&
|
||||
isEqual(x.stride(), y.stride());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::Component &x,
|
||||
const Fortran::evaluate::Component &y) {
|
||||
return isEqual(x.base(), y.base()) &&
|
||||
isEqual(x.GetLastSymbol(), y.GetLastSymbol());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::ArrayRef &x,
|
||||
const Fortran::evaluate::ArrayRef &y) {
|
||||
return isEqual(x.base(), y.base()) && isEqual(x.subscript(), y.subscript());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::CoarrayRef &x,
|
||||
const Fortran::evaluate::CoarrayRef &y) {
|
||||
return isEqual(x.base(), y.base()) &&
|
||||
isEqual(x.subscript(), y.subscript()) &&
|
||||
isEqual(x.cosubscript(), y.cosubscript()) &&
|
||||
isEqual(x.stat(), y.stat()) && isEqual(x.team(), y.team());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::NamedEntity &x,
|
||||
const Fortran::evaluate::NamedEntity &y) {
|
||||
if (x.IsSymbol() && y.IsSymbol())
|
||||
return isEqual(x.GetFirstSymbol(), y.GetFirstSymbol());
|
||||
return !x.IsSymbol() && !y.IsSymbol() &&
|
||||
isEqual(x.GetComponent(), y.GetComponent());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::DataRef &x,
|
||||
const Fortran::evaluate::DataRef &y) {
|
||||
return std::visit(
|
||||
[&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::ComplexPart &x,
|
||||
const Fortran::evaluate::ComplexPart &y) {
|
||||
return isEqual(x.complex(), y.complex()) && x.part() == y.part();
|
||||
}
|
||||
template <typename A, Fortran::common::TypeCategory TC2>
|
||||
static bool isEqual(const Fortran::evaluate::Convert<A, TC2> &x,
|
||||
const Fortran::evaluate::Convert<A, TC2> &y) {
|
||||
return isEqual(x.left(), y.left());
|
||||
}
|
||||
template <int KIND>
|
||||
static bool isEqual(const Fortran::evaluate::ComplexComponent<KIND> &x,
|
||||
const Fortran::evaluate::ComplexComponent<KIND> &y) {
|
||||
return isEqual(x.left(), y.left()) &&
|
||||
x.isImaginaryPart == y.isImaginaryPart;
|
||||
}
|
||||
template <typename T>
|
||||
static bool isEqual(const Fortran::evaluate::Parentheses<T> &x,
|
||||
const Fortran::evaluate::Parentheses<T> &y) {
|
||||
return isEqual(x.left(), y.left());
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Negate<A> &x,
|
||||
const Fortran::evaluate::Negate<A> &y) {
|
||||
return isEqual(x.left(), y.left());
|
||||
}
|
||||
template <typename A>
|
||||
static bool isBinaryEqual(const A &x, const A &y) {
|
||||
return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right());
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Add<A> &x,
|
||||
const Fortran::evaluate::Add<A> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Subtract<A> &x,
|
||||
const Fortran::evaluate::Subtract<A> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Multiply<A> &x,
|
||||
const Fortran::evaluate::Multiply<A> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Divide<A> &x,
|
||||
const Fortran::evaluate::Divide<A> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Power<A> &x,
|
||||
const Fortran::evaluate::Power<A> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Extremum<A> &x,
|
||||
const Fortran::evaluate::Extremum<A> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::RealToIntPower<A> &x,
|
||||
const Fortran::evaluate::RealToIntPower<A> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <int KIND>
|
||||
static bool isEqual(const Fortran::evaluate::ComplexConstructor<KIND> &x,
|
||||
const Fortran::evaluate::ComplexConstructor<KIND> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <int KIND>
|
||||
static bool isEqual(const Fortran::evaluate::Concat<KIND> &x,
|
||||
const Fortran::evaluate::Concat<KIND> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
template <int KIND>
|
||||
static bool isEqual(const Fortran::evaluate::SetLength<KIND> &x,
|
||||
const Fortran::evaluate::SetLength<KIND> &y) {
|
||||
return isBinaryEqual(x, y);
|
||||
}
|
||||
static bool isEqual(const Fortran::semantics::SymbolRef &x,
|
||||
const Fortran::semantics::SymbolRef &y) {
|
||||
return isEqual(x.get(), y.get());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::Substring &x,
|
||||
const Fortran::evaluate::Substring &y) {
|
||||
return std::visit(
|
||||
[&](const auto &p, const auto &q) { return isEqual(p, q); },
|
||||
x.parent(), y.parent()) &&
|
||||
isEqual(x.lower(), y.lower()) && isEqual(x.lower(), y.lower());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::StaticDataObject::Pointer &x,
|
||||
const Fortran::evaluate::StaticDataObject::Pointer &y) {
|
||||
return x->name() == y->name();
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::SpecificIntrinsic &x,
|
||||
const Fortran::evaluate::SpecificIntrinsic &y) {
|
||||
return x.name == y.name;
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Constant<A> &x,
|
||||
const Fortran::evaluate::Constant<A> &y) {
|
||||
return x == y;
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::ActualArgument &x,
|
||||
const Fortran::evaluate::ActualArgument &y) {
|
||||
if (const Fortran::evaluate::Symbol *xs = x.GetAssumedTypeDummy()) {
|
||||
if (const Fortran::evaluate::Symbol *ys = y.GetAssumedTypeDummy())
|
||||
return isEqual(*xs, *ys);
|
||||
return false;
|
||||
}
|
||||
return !y.GetAssumedTypeDummy() &&
|
||||
isEqual(*x.UnwrapExpr(), *y.UnwrapExpr());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::ProcedureDesignator &x,
|
||||
const Fortran::evaluate::ProcedureDesignator &y) {
|
||||
return std::visit(
|
||||
[&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::ProcedureRef &x,
|
||||
const Fortran::evaluate::ProcedureRef &y) {
|
||||
return isEqual(x.proc(), y.proc()) && isEqual(x.arguments(), y.arguments());
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::ArrayConstructor<A> &x,
|
||||
const Fortran::evaluate::ArrayConstructor<A> &y) {
|
||||
llvm::report_fatal_error("not implemented");
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::ImpliedDoIndex &x,
|
||||
const Fortran::evaluate::ImpliedDoIndex &y) {
|
||||
return toStringRef(x.name) == toStringRef(y.name);
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::TypeParamInquiry &x,
|
||||
const Fortran::evaluate::TypeParamInquiry &y) {
|
||||
return isEqual(x.base(), y.base()) && isEqual(x.parameter(), y.parameter());
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::DescriptorInquiry &x,
|
||||
const Fortran::evaluate::DescriptorInquiry &y) {
|
||||
return isEqual(x.base(), y.base()) && x.field() == y.field() &&
|
||||
x.dimension() == y.dimension();
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::StructureConstructor &x,
|
||||
const Fortran::evaluate::StructureConstructor &y) {
|
||||
llvm::report_fatal_error("not implemented");
|
||||
}
|
||||
template <int KIND>
|
||||
static bool isEqual(const Fortran::evaluate::Not<KIND> &x,
|
||||
const Fortran::evaluate::Not<KIND> &y) {
|
||||
return isEqual(x.left(), y.left());
|
||||
}
|
||||
template <int KIND>
|
||||
static bool isEqual(const Fortran::evaluate::LogicalOperation<KIND> &x,
|
||||
const Fortran::evaluate::LogicalOperation<KIND> &y) {
|
||||
return isEqual(x.left(), y.left()) && isEqual(x.right(), x.right());
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Relational<A> &x,
|
||||
const Fortran::evaluate::Relational<A> &y) {
|
||||
return isEqual(x.left(), y.left()) && isEqual(x.right(), y.right());
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Expr<A> &x,
|
||||
const Fortran::evaluate::Expr<A> &y) {
|
||||
return std::visit(
|
||||
[&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
|
||||
}
|
||||
static bool
|
||||
isEqual(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x,
|
||||
const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &y) {
|
||||
return std::visit(
|
||||
[&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
|
||||
}
|
||||
template <typename A>
|
||||
static bool isEqual(const Fortran::evaluate::Designator<A> &x,
|
||||
const Fortran::evaluate::Designator<A> &y) {
|
||||
return std::visit(
|
||||
[&](const auto &v, const auto &w) { return isEqual(v, w); }, x.u, y.u);
|
||||
}
|
||||
template <int BITS>
|
||||
static bool isEqual(const Fortran::evaluate::value::Integer<BITS> &x,
|
||||
const Fortran::evaluate::value::Integer<BITS> &y) {
|
||||
return x == y;
|
||||
}
|
||||
static bool isEqual(const Fortran::evaluate::NullPointer &x,
|
||||
const Fortran::evaluate::NullPointer &y) {
|
||||
return true;
|
||||
}
|
||||
template <typename A, typename B,
|
||||
std::enable_if_t<!std::is_same_v<A, B>, bool> = true>
|
||||
static bool isEqual(const A &, const B &) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool Fortran::lower::isEqual(
|
||||
const Fortran::lower::ExplicitIterSpace::ArrayBases &x,
|
||||
const Fortran::lower::ExplicitIterSpace::ArrayBases &y) {
|
||||
return std::visit(
|
||||
Fortran::common::visitors{
|
||||
// Fortran::semantics::Symbol * are the exception here. These pointers
|
||||
// have identity; if two Symbol * values are the same (different) then
|
||||
// they are the same (different) logical symbol.
|
||||
[&](Fortran::lower::FrontEndSymbol p,
|
||||
Fortran::lower::FrontEndSymbol q) { return p == q; },
|
||||
[&](const auto *p, const auto *q) {
|
||||
if constexpr (std::is_same_v<decltype(p), decltype(q)>) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "is equal: " << p << ' ' << q << ' '
|
||||
<< IsEqualEvaluateExpr::isEqual(*p, *q) << '\n');
|
||||
return IsEqualEvaluateExpr::isEqual(*p, *q);
|
||||
} else {
|
||||
// Different subtree types are never equal.
|
||||
return false;
|
||||
}
|
||||
}},
|
||||
x, y);
|
||||
}
|
||||
|
||||
bool Fortran::lower::isEqual(Fortran::lower::FrontEndExpr x,
|
||||
Fortran::lower::FrontEndExpr y) {
|
||||
auto empty = llvm::DenseMapInfo<Fortran::lower::FrontEndExpr>::getEmptyKey();
|
||||
auto tombstone =
|
||||
llvm::DenseMapInfo<Fortran::lower::FrontEndExpr>::getTombstoneKey();
|
||||
if (x == empty || y == empty || x == tombstone || y == tombstone)
|
||||
return x == y;
|
||||
return x == y || IsEqualEvaluateExpr::isEqual(*x, *y);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// This class can recover the base array in an expression that contains
|
||||
/// explicit iteration space symbols. Most of the class can be ignored as it is
|
||||
/// boilerplate Fortran::evaluate::Expr traversal.
|
||||
class ArrayBaseFinder {
|
||||
public:
|
||||
using RT = bool;
|
||||
|
||||
ArrayBaseFinder(llvm::ArrayRef<Fortran::lower::FrontEndSymbol> syms)
|
||||
: controlVars(syms.begin(), syms.end()) {}
|
||||
|
||||
template <typename T>
|
||||
void operator()(const T &x) {
|
||||
(void)find(x);
|
||||
}
|
||||
|
||||
/// Get the list of bases.
|
||||
llvm::ArrayRef<Fortran::lower::ExplicitIterSpace::ArrayBases>
|
||||
getBases() const {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "number of array bases found: " << bases.size() << '\n');
|
||||
return bases;
|
||||
}
|
||||
|
||||
private:
|
||||
// First, the cases that are of interest.
|
||||
RT find(const Fortran::semantics::Symbol &symbol) {
|
||||
if (symbol.Rank() > 0) {
|
||||
bases.push_back(&symbol);
|
||||
return true;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
RT find(const Fortran::evaluate::Component &x) {
|
||||
auto found = find(x.base());
|
||||
if (!found && x.base().Rank() == 0 && x.Rank() > 0) {
|
||||
bases.push_back(&x);
|
||||
return true;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
RT find(const Fortran::evaluate::ArrayRef &x) {
|
||||
for (const auto &sub : x.subscript())
|
||||
(void)find(sub);
|
||||
if (x.base().IsSymbol()) {
|
||||
if (x.Rank() > 0 || intersection(x.subscript())) {
|
||||
bases.push_back(&x);
|
||||
return true;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
auto found = find(x.base());
|
||||
if (!found && ((x.base().Rank() == 0 && x.Rank() > 0) ||
|
||||
intersection(x.subscript()))) {
|
||||
bases.push_back(&x);
|
||||
return true;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
RT find(const Fortran::evaluate::Triplet &x) {
|
||||
if (const auto *lower = x.GetLower())
|
||||
(void)find(*lower);
|
||||
if (const auto *upper = x.GetUpper())
|
||||
(void)find(*upper);
|
||||
return find(x.GetStride());
|
||||
}
|
||||
RT find(const Fortran::evaluate::IndirectSubscriptIntegerExpr &x) {
|
||||
return find(x.value());
|
||||
}
|
||||
RT find(const Fortran::evaluate::Subscript &x) { return find(x.u); }
|
||||
RT find(const Fortran::evaluate::DataRef &x) { return find(x.u); }
|
||||
RT find(const Fortran::evaluate::CoarrayRef &x) {
|
||||
assert(false && "coarray reference");
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
bool intersection(const A &subscripts) {
|
||||
return Fortran::lower::symbolsIntersectSubscripts(controlVars, subscripts);
|
||||
}
|
||||
|
||||
// The rest is traversal boilerplate and can be ignored.
|
||||
RT find(const Fortran::evaluate::Substring &x) { return find(x.parent()); }
|
||||
template <typename A>
|
||||
RT find(const Fortran::semantics::SymbolRef x) {
|
||||
return find(*x);
|
||||
}
|
||||
RT find(const Fortran::evaluate::NamedEntity &x) {
|
||||
if (x.IsSymbol())
|
||||
return find(x.GetFirstSymbol());
|
||||
return find(x.GetComponent());
|
||||
}
|
||||
|
||||
template <typename A, bool C>
|
||||
RT find(const Fortran::common::Indirection<A, C> &x) {
|
||||
return find(x.value());
|
||||
}
|
||||
template <typename A>
|
||||
RT find(const std::unique_ptr<A> &x) {
|
||||
return find(x.get());
|
||||
}
|
||||
template <typename A>
|
||||
RT find(const std::shared_ptr<A> &x) {
|
||||
return find(x.get());
|
||||
}
|
||||
template <typename A>
|
||||
RT find(const A *x) {
|
||||
if (x)
|
||||
return find(*x);
|
||||
return {};
|
||||
}
|
||||
template <typename A>
|
||||
RT find(const std::optional<A> &x) {
|
||||
if (x)
|
||||
return find(*x);
|
||||
return {};
|
||||
}
|
||||
template <typename... A>
|
||||
RT find(const std::variant<A...> &u) {
|
||||
return std::visit([&](const auto &v) { return find(v); }, u);
|
||||
}
|
||||
template <typename A>
|
||||
RT find(const std::vector<A> &x) {
|
||||
for (auto &v : x)
|
||||
(void)find(v);
|
||||
return {};
|
||||
}
|
||||
RT find(const Fortran::evaluate::BOZLiteralConstant &) { return {}; }
|
||||
RT find(const Fortran::evaluate::NullPointer &) { return {}; }
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::Constant<T> &x) {
|
||||
return {};
|
||||
}
|
||||
RT find(const Fortran::evaluate::StaticDataObject &) { return {}; }
|
||||
RT find(const Fortran::evaluate::ImpliedDoIndex &) { return {}; }
|
||||
RT find(const Fortran::evaluate::BaseObject &x) {
|
||||
(void)find(x.u);
|
||||
return {};
|
||||
}
|
||||
RT find(const Fortran::evaluate::TypeParamInquiry &) { return {}; }
|
||||
RT find(const Fortran::evaluate::ComplexPart &x) { return {}; }
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::Designator<T> &x) {
|
||||
return find(x.u);
|
||||
}
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::Variable<T> &x) {
|
||||
return find(x.u);
|
||||
}
|
||||
RT find(const Fortran::evaluate::DescriptorInquiry &) { return {}; }
|
||||
RT find(const Fortran::evaluate::SpecificIntrinsic &) { return {}; }
|
||||
RT find(const Fortran::evaluate::ProcedureDesignator &x) { return {}; }
|
||||
RT find(const Fortran::evaluate::ProcedureRef &x) {
|
||||
(void)find(x.proc());
|
||||
if (x.IsElemental())
|
||||
(void)find(x.arguments());
|
||||
return {};
|
||||
}
|
||||
RT find(const Fortran::evaluate::ActualArgument &x) {
|
||||
if (const auto *sym = x.GetAssumedTypeDummy())
|
||||
(void)find(*sym);
|
||||
else
|
||||
(void)find(x.UnwrapExpr());
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::FunctionRef<T> &x) {
|
||||
(void)find(static_cast<const Fortran::evaluate::ProcedureRef &>(x));
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::ArrayConstructorValue<T> &) {
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::ArrayConstructorValues<T> &) {
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::ImpliedDo<T> &) {
|
||||
return {};
|
||||
}
|
||||
RT find(const Fortran::semantics::ParamValue &) { return {}; }
|
||||
RT find(const Fortran::semantics::DerivedTypeSpec &) { return {}; }
|
||||
RT find(const Fortran::evaluate::StructureConstructor &) { return {}; }
|
||||
template <typename D, typename R, typename O>
|
||||
RT find(const Fortran::evaluate::Operation<D, R, O> &op) {
|
||||
(void)find(op.left());
|
||||
return false;
|
||||
}
|
||||
template <typename D, typename R, typename LO, typename RO>
|
||||
RT find(const Fortran::evaluate::Operation<D, R, LO, RO> &op) {
|
||||
(void)find(op.left());
|
||||
(void)find(op.right());
|
||||
return false;
|
||||
}
|
||||
RT find(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &x) {
|
||||
(void)find(x.u);
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
RT find(const Fortran::evaluate::Expr<T> &x) {
|
||||
(void)find(x.u);
|
||||
return {};
|
||||
}
|
||||
|
||||
llvm::SmallVector<Fortran::lower::ExplicitIterSpace::ArrayBases> bases;
|
||||
llvm::SmallVector<Fortran::lower::FrontEndSymbol> controlVars;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::leave() {
|
||||
ccLoopNest.pop_back();
|
||||
--forallContextOpen;
|
||||
conditionalCleanup();
|
||||
}
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::addSymbol(
|
||||
Fortran::lower::FrontEndSymbol sym) {
|
||||
assert(!symbolStack.empty());
|
||||
symbolStack.back().push_back(sym);
|
||||
}
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::exprBase(Fortran::lower::FrontEndExpr x,
|
||||
bool lhs) {
|
||||
ArrayBaseFinder finder(collectAllSymbols());
|
||||
finder(*x);
|
||||
llvm::ArrayRef<Fortran::lower::ExplicitIterSpace::ArrayBases> bases =
|
||||
finder.getBases();
|
||||
if (rhsBases.empty())
|
||||
endAssign();
|
||||
if (lhs) {
|
||||
if (bases.empty()) {
|
||||
lhsBases.push_back(llvm::None);
|
||||
return;
|
||||
}
|
||||
assert(bases.size() >= 1 && "must detect an array reference on lhs");
|
||||
if (bases.size() > 1)
|
||||
rhsBases.back().append(bases.begin(), bases.end() - 1);
|
||||
lhsBases.push_back(bases.back());
|
||||
return;
|
||||
}
|
||||
rhsBases.back().append(bases.begin(), bases.end());
|
||||
}
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::endAssign() { rhsBases.emplace_back(); }
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::pushLevel() {
|
||||
symbolStack.push_back(llvm::SmallVector<Fortran::lower::FrontEndSymbol>{});
|
||||
}
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::popLevel() { symbolStack.pop_back(); }
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::conditionalCleanup() {
|
||||
if (forallContextOpen == 0) {
|
||||
// Exiting the outermost FORALL context.
|
||||
// Cleanup any residual mask buffers.
|
||||
outermostContext().finalize();
|
||||
// Clear and reset all the cached information.
|
||||
symbolStack.clear();
|
||||
lhsBases.clear();
|
||||
rhsBases.clear();
|
||||
loadBindings.clear();
|
||||
ccLoopNest.clear();
|
||||
innerArgs.clear();
|
||||
outerLoop = llvm::None;
|
||||
clearLoops();
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Optional<size_t>
|
||||
Fortran::lower::ExplicitIterSpace::findArgPosition(fir::ArrayLoadOp load) {
|
||||
if (lhsBases[counter].hasValue()) {
|
||||
auto ld = loadBindings.find(lhsBases[counter].getValue());
|
||||
llvm::Optional<size_t> optPos;
|
||||
if (ld != loadBindings.end() && ld->second == load)
|
||||
optPos = static_cast<size_t>(0u);
|
||||
assert(optPos.hasValue() && "load does not correspond to lhs");
|
||||
return optPos;
|
||||
}
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
llvm::SmallVector<Fortran::lower::FrontEndSymbol>
|
||||
Fortran::lower::ExplicitIterSpace::collectAllSymbols() {
|
||||
llvm::SmallVector<Fortran::lower::FrontEndSymbol> result;
|
||||
for (llvm::SmallVector<FrontEndSymbol> vec : symbolStack)
|
||||
result.append(vec.begin(), vec.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
llvm::raw_ostream &
|
||||
Fortran::lower::operator<<(llvm::raw_ostream &s,
|
||||
const Fortran::lower::ImplicitIterSpace &e) {
|
||||
for (const llvm::SmallVector<
|
||||
Fortran::lower::ImplicitIterSpace::FrontEndMaskExpr> &xs :
|
||||
e.getMasks()) {
|
||||
s << "{ ";
|
||||
for (const Fortran::lower::ImplicitIterSpace::FrontEndMaskExpr &x : xs)
|
||||
x->AsFortran(s << '(') << "), ";
|
||||
s << "}\n";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
llvm::raw_ostream &
|
||||
Fortran::lower::operator<<(llvm::raw_ostream &s,
|
||||
const Fortran::lower::ExplicitIterSpace &e) {
|
||||
auto dump = [&](const auto &u) {
|
||||
std::visit(Fortran::common::visitors{
|
||||
[&](const Fortran::semantics::Symbol *y) {
|
||||
s << " " << *y << '\n';
|
||||
},
|
||||
[&](const Fortran::evaluate::ArrayRef *y) {
|
||||
s << " ";
|
||||
if (y->base().IsSymbol())
|
||||
s << y->base().GetFirstSymbol();
|
||||
else
|
||||
s << y->base().GetComponent().GetLastSymbol();
|
||||
s << '\n';
|
||||
},
|
||||
[&](const Fortran::evaluate::Component *y) {
|
||||
s << " " << y->GetLastSymbol() << '\n';
|
||||
}},
|
||||
u);
|
||||
};
|
||||
s << "LHS bases:\n";
|
||||
for (const llvm::Optional<Fortran::lower::ExplicitIterSpace::ArrayBases> &u :
|
||||
e.lhsBases)
|
||||
if (u.hasValue())
|
||||
dump(u.getValue());
|
||||
s << "RHS bases:\n";
|
||||
for (const llvm::SmallVector<Fortran::lower::ExplicitIterSpace::ArrayBases>
|
||||
&bases : e.rhsBases) {
|
||||
for (const Fortran::lower::ExplicitIterSpace::ArrayBases &u : bases)
|
||||
dump(u);
|
||||
s << '\n';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void Fortran::lower::ImplicitIterSpace::dump() const {
|
||||
llvm::errs() << *this << '\n';
|
||||
}
|
||||
|
||||
void Fortran::lower::ExplicitIterSpace::dump() const {
|
||||
llvm::errs() << *this << '\n';
|
||||
}
|
|
@ -390,6 +390,57 @@ mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc,
|
|||
[&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); });
|
||||
}
|
||||
|
||||
mlir::Value fir::FirOpBuilder::createSlice(mlir::Location loc,
|
||||
const fir::ExtendedValue &exv,
|
||||
mlir::ValueRange triples,
|
||||
mlir::ValueRange path) {
|
||||
if (triples.empty()) {
|
||||
// If there is no slicing by triple notation, then take the whole array.
|
||||
auto fullShape = [&](const llvm::ArrayRef<mlir::Value> lbounds,
|
||||
llvm::ArrayRef<mlir::Value> extents) -> mlir::Value {
|
||||
llvm::SmallVector<mlir::Value> trips;
|
||||
auto idxTy = getIndexType();
|
||||
auto one = createIntegerConstant(loc, idxTy, 1);
|
||||
if (lbounds.empty()) {
|
||||
for (auto v : extents) {
|
||||
trips.push_back(one);
|
||||
trips.push_back(v);
|
||||
trips.push_back(one);
|
||||
}
|
||||
return create<fir::SliceOp>(loc, trips, path);
|
||||
}
|
||||
for (auto [lbnd, extent] : llvm::zip(lbounds, extents)) {
|
||||
auto lb = createConvert(loc, idxTy, lbnd);
|
||||
auto ext = createConvert(loc, idxTy, extent);
|
||||
auto shift = create<mlir::arith::SubIOp>(loc, lb, one);
|
||||
auto ub = create<mlir::arith::AddIOp>(loc, ext, shift);
|
||||
trips.push_back(lb);
|
||||
trips.push_back(ub);
|
||||
trips.push_back(one);
|
||||
}
|
||||
return create<fir::SliceOp>(loc, trips, path);
|
||||
};
|
||||
return exv.match(
|
||||
[&](const fir::ArrayBoxValue &box) {
|
||||
return fullShape(box.getLBounds(), box.getExtents());
|
||||
},
|
||||
[&](const fir::CharArrayBoxValue &box) {
|
||||
return fullShape(box.getLBounds(), box.getExtents());
|
||||
},
|
||||
[&](const fir::BoxValue &box) {
|
||||
auto extents = fir::factory::readExtents(*this, loc, box);
|
||||
return fullShape(box.getLBounds(), extents);
|
||||
},
|
||||
[&](const fir::MutableBoxValue &) -> mlir::Value {
|
||||
// MutableBoxValue must be read into another category to work with
|
||||
// them outside of allocation/assignment contexts.
|
||||
fir::emitFatalError(loc, "createSlice on MutableBoxValue");
|
||||
},
|
||||
[&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); });
|
||||
}
|
||||
return create<fir::SliceOp>(loc, triples, path);
|
||||
}
|
||||
|
||||
mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc,
|
||||
const fir::ExtendedValue &exv) {
|
||||
mlir::Value itemAddr = fir::getBase(exv);
|
||||
|
@ -518,6 +569,35 @@ mlir::Value fir::factory::readExtent(fir::FirOpBuilder &builder,
|
|||
});
|
||||
}
|
||||
|
||||
mlir::Value fir::factory::readLowerBound(fir::FirOpBuilder &builder,
|
||||
mlir::Location loc,
|
||||
const fir::ExtendedValue &box,
|
||||
unsigned dim,
|
||||
mlir::Value defaultValue) {
|
||||
assert(box.rank() > dim);
|
||||
auto lb = box.match(
|
||||
[&](const fir::ArrayBoxValue &x) -> mlir::Value {
|
||||
return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
|
||||
},
|
||||
[&](const fir::CharArrayBoxValue &x) -> mlir::Value {
|
||||
return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
|
||||
},
|
||||
[&](const fir::BoxValue &x) -> mlir::Value {
|
||||
return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
|
||||
},
|
||||
[&](const fir::MutableBoxValue &x) -> mlir::Value {
|
||||
return readLowerBound(builder, loc,
|
||||
fir::factory::genMutableBoxRead(builder, loc, x),
|
||||
dim, defaultValue);
|
||||
},
|
||||
[&](const auto &) -> mlir::Value {
|
||||
fir::emitFatalError(loc, "lower bound inquiry on scalar");
|
||||
});
|
||||
if (lb)
|
||||
return lb;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
llvm::SmallVector<mlir::Value>
|
||||
fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
const fir::BoxValue &box) {
|
||||
|
@ -653,6 +733,111 @@ fir::factory::createExtents(fir::FirOpBuilder &builder, mlir::Location loc,
|
|||
return extents;
|
||||
}
|
||||
|
||||
// FIXME: This needs some work. To correctly determine the extended value of a
|
||||
// component, one needs the base object, its type, and its type parameters. (An
|
||||
// alternative would be to provide an already computed address of the final
|
||||
// component rather than the base object's address, the point being the result
|
||||
// will require the address of the final component to create the extended
|
||||
// value.) One further needs the full path of components being applied. One
|
||||
// needs to apply type-based expressions to type parameters along this said
|
||||
// path. (See applyPathToType for a type-only derivation.) Finally, one needs to
|
||||
// compose the extended value of the terminal component, including all of its
|
||||
// parameters: array lower bounds expressions, extents, type parameters, etc.
|
||||
// Any of these properties may be deferred until runtime in Fortran. This
|
||||
// operation may therefore generate a sizeable block of IR, including calls to
|
||||
// type-based helper functions, so caching the result of this operation in the
|
||||
// client would be advised as well.
|
||||
fir::ExtendedValue fir::factory::componentToExtendedValue(
|
||||
fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value component) {
|
||||
auto fieldTy = component.getType();
|
||||
if (auto ty = fir::dyn_cast_ptrEleTy(fieldTy))
|
||||
fieldTy = ty;
|
||||
if (fieldTy.isa<fir::BoxType>()) {
|
||||
llvm::SmallVector<mlir::Value> nonDeferredTypeParams;
|
||||
auto eleTy = fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(fieldTy));
|
||||
if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
|
||||
auto lenTy = builder.getCharacterLengthType();
|
||||
if (charTy.hasConstantLen())
|
||||
nonDeferredTypeParams.emplace_back(
|
||||
builder.createIntegerConstant(loc, lenTy, charTy.getLen()));
|
||||
// TODO: Starting, F2003, the dynamic character length might be dependent
|
||||
// on a PDT length parameter. There is no way to make a difference with
|
||||
// deferred length here yet.
|
||||
}
|
||||
if (auto recTy = eleTy.dyn_cast<fir::RecordType>())
|
||||
if (recTy.getNumLenParams() > 0)
|
||||
TODO(loc, "allocatable and pointer components non deferred length "
|
||||
"parameters");
|
||||
|
||||
return fir::MutableBoxValue(component, nonDeferredTypeParams,
|
||||
/*mutableProperties=*/{});
|
||||
}
|
||||
llvm::SmallVector<mlir::Value> extents;
|
||||
if (auto seqTy = fieldTy.dyn_cast<fir::SequenceType>()) {
|
||||
fieldTy = seqTy.getEleTy();
|
||||
auto idxTy = builder.getIndexType();
|
||||
for (auto extent : seqTy.getShape()) {
|
||||
if (extent == fir::SequenceType::getUnknownExtent())
|
||||
TODO(loc, "array component shape depending on length parameters");
|
||||
extents.emplace_back(builder.createIntegerConstant(loc, idxTy, extent));
|
||||
}
|
||||
}
|
||||
if (auto charTy = fieldTy.dyn_cast<fir::CharacterType>()) {
|
||||
auto cstLen = charTy.getLen();
|
||||
if (cstLen == fir::CharacterType::unknownLen())
|
||||
TODO(loc, "get character component length from length type parameters");
|
||||
auto len = builder.createIntegerConstant(
|
||||
loc, builder.getCharacterLengthType(), cstLen);
|
||||
if (!extents.empty())
|
||||
return fir::CharArrayBoxValue{component, len, extents};
|
||||
return fir::CharBoxValue{component, len};
|
||||
}
|
||||
if (auto recordTy = fieldTy.dyn_cast<fir::RecordType>())
|
||||
if (recordTy.getNumLenParams() != 0)
|
||||
TODO(loc,
|
||||
"lower component ref that is a derived type with length parameter");
|
||||
if (!extents.empty())
|
||||
return fir::ArrayBoxValue{component, extents};
|
||||
return component;
|
||||
}
|
||||
|
||||
fir::ExtendedValue fir::factory::arrayElementToExtendedValue(
|
||||
fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
const fir::ExtendedValue &array, mlir::Value element) {
|
||||
return array.match(
|
||||
[&](const fir::CharBoxValue &cb) -> fir::ExtendedValue {
|
||||
return cb.clone(element);
|
||||
},
|
||||
[&](const fir::CharArrayBoxValue &bv) -> fir::ExtendedValue {
|
||||
return bv.cloneElement(element);
|
||||
},
|
||||
[&](const fir::BoxValue &box) -> fir::ExtendedValue {
|
||||
if (box.isCharacter()) {
|
||||
auto len = fir::factory::readCharLen(builder, loc, box);
|
||||
return fir::CharBoxValue{element, len};
|
||||
}
|
||||
if (box.isDerivedWithLengthParameters())
|
||||
TODO(loc, "get length parameters from derived type BoxValue");
|
||||
return element;
|
||||
},
|
||||
[&](const auto &) -> fir::ExtendedValue { return element; });
|
||||
}
|
||||
|
||||
fir::ExtendedValue fir::factory::arraySectionElementToExtendedValue(
|
||||
fir::FirOpBuilder &builder, mlir::Location loc,
|
||||
const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice) {
|
||||
if (!slice)
|
||||
return arrayElementToExtendedValue(builder, loc, array, element);
|
||||
auto sliceOp = mlir::dyn_cast_or_null<fir::SliceOp>(slice.getDefiningOp());
|
||||
assert(sliceOp && "slice must be a sliceOp");
|
||||
if (sliceOp.getFields().empty())
|
||||
return arrayElementToExtendedValue(builder, loc, array, element);
|
||||
// For F95, using componentToExtendedValue will work, but when PDTs are
|
||||
// lowered. It will be required to go down the slice to propagate the length
|
||||
// parameters.
|
||||
return fir::factory::componentToExtendedValue(builder, loc, element);
|
||||
}
|
||||
|
||||
mlir::TupleType
|
||||
fir::factory::getRaggedArrayHeaderType(fir::FirOpBuilder &builder) {
|
||||
mlir::IntegerType i64Ty = builder.getIntegerType(64);
|
||||
|
|
|
@ -298,3 +298,39 @@ end
|
|||
! CHECK: %[[INS0:.*]] = fir.insert_value %[[UNDEF]], %[[C0]], [0 : index] : (!fir.complex<4>, f32) -> !fir.complex<4>
|
||||
! CHECK: %[[INS1:.*]] = fir.insert_value %[[INS0]], %[[C1]], [1 : index] : (!fir.complex<4>, f32) -> !fir.complex<4>
|
||||
! CHECK: fir.store %[[INS1]] to %[[A]] : !fir.ref<!fir.complex<4>>
|
||||
|
||||
subroutine sub1_arr(a)
|
||||
integer :: a(10)
|
||||
a(2) = 10
|
||||
end
|
||||
|
||||
! CHECK-LABEL: func @_QPsub1_arr(
|
||||
! CHECK-SAME: %[[A:.*]]: !fir.ref<!fir.array<10xi32>> {fir.bindc_name = "a"})
|
||||
! CHECK-DAG: %[[C10:.*]] = arith.constant 10 : i32
|
||||
! CHECK-DAG: %[[C2:.*]] = arith.constant 2 : i64
|
||||
! CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i64
|
||||
! CHECK: %[[ZERO_BASED_INDEX:.*]] = arith.subi %[[C2]], %[[C1]] : i64
|
||||
! CHECK: %[[COORD:.*]] = fir.coordinate_of %[[A]], %[[ZERO_BASED_INDEX]] : (!fir.ref<!fir.array<10xi32>>, i64) -> !fir.ref<i32>
|
||||
! CHECK: fir.store %[[C10]] to %[[COORD]] : !fir.ref<i32>
|
||||
! CHECK: return
|
||||
|
||||
subroutine sub2_arr(a)
|
||||
integer :: a(10)
|
||||
a = 10
|
||||
end
|
||||
|
||||
! CHECK-LABEL: func @_QPsub2_arr(
|
||||
! CHECK-SAME: %[[A:.*]]: !fir.ref<!fir.array<10xi32>> {fir.bindc_name = "a"})
|
||||
! CHECK-DAG: %[[C10_0:.*]] = arith.constant 10 : index
|
||||
! CHECK: %[[SHAPE:.*]] = fir.shape %[[C10_0]] : (index) -> !fir.shape<1>
|
||||
! CHECK: %[[LOAD:.*]] = fir.array_load %[[A]](%[[SHAPE]]) : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>) -> !fir.array<10xi32>
|
||||
! CHECK-DAG: %[[C10_1:.*]] = arith.constant 10 : i32
|
||||
! CHECK-DAG: %[[C1:.*]] = arith.constant 1 : index
|
||||
! CHECK-DAG: %[[C0:.*]] = arith.constant 0 : index
|
||||
! CHECK-DAG: %[[UB:.*]] = arith.subi %[[C10_0]], %c1 : index
|
||||
! CHECK: %[[DO_RES:.*]] = fir.do_loop %[[ARG1:.*]] = %[[C0]] to %[[UB]] step %[[C1]] unordered iter_args(%[[ARG2:.*]] = %[[LOAD]]) -> (!fir.array<10xi32>) {
|
||||
! CHECK: %[[RES:.*]] = fir.array_update %[[ARG2]], %[[C10_1]], %[[ARG1]] : (!fir.array<10xi32>, i32, index) -> !fir.array<10xi32>
|
||||
! CHECK: fir.result %[[RES]] : !fir.array<10xi32>
|
||||
! CHECK: }
|
||||
! CHECK: fir.array_merge_store %[[LOAD]], %[[DO_RES]] to %[[A]] : !fir.array<10xi32>, !fir.array<10xi32>, !fir.ref<!fir.array<10xi32>>
|
||||
! CHECK: return
|
||||
|
|
|
@ -62,20 +62,6 @@ end
|
|||
! CHECK-LABEL: func @_QPfct_iarr2() -> !fir.array<10x20xi32>
|
||||
! CHECK: return %{{.*}} : !fir.array<10x20xi32>
|
||||
|
||||
function fct_iarr3()
|
||||
integer, dimension(:, :), allocatable :: fct_iarr3
|
||||
end
|
||||
|
||||
! CHECK-LABEL: func @_QPfct_iarr3() -> !fir.box<!fir.heap<!fir.array<?x?xi32>>>
|
||||
! CHECK: return %{{.*}} : !fir.box<!fir.heap<!fir.array<?x?xi32>>>
|
||||
|
||||
function fct_iarr4()
|
||||
integer, dimension(:), pointer :: fct_iarr4
|
||||
end
|
||||
|
||||
! CHECK-LABEL: func @_QPfct_iarr4() -> !fir.box<!fir.ptr<!fir.array<?xi32>>>
|
||||
! CHECK: return %{{.*}} : !fir.box<!fir.ptr<!fir.array<?xi32>>>
|
||||
|
||||
logical(1) function lfct1()
|
||||
end
|
||||
! CHECK-LABEL: func @_QPlfct1() -> !fir.logical<1>
|
||||
|
|
Loading…
Reference in New Issue