[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:
Valentin Clement 2022-02-24 21:09:40 +01:00
parent 05d79e3562
commit f9704f0cfb
No known key found for this signature in database
GPG Key ID: 086D54783C928776
18 changed files with 3811 additions and 36 deletions

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");
},
//===--------------------------------------------------------------===//

View File

@ -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);
}

View File

@ -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';
}

View File

@ -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);

View File

@ -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

View File

@ -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>