diff --git a/flang/include/flang/Evaluate/fold-designator.h b/flang/include/flang/Evaluate/fold-designator.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Evaluate/fold-designator.h @@ -0,0 +1,183 @@ +//===-- include/flang/Evaluate/fold-designator.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_EVALUATE_FOLD_DESIGNATOR_H_ +#define FORTRAN_EVALUATE_FOLD_DESIGNATOR_H_ + +// Resolves a designator at compilation time to a base symbol, a byte offset +// from that symbol, and a byte size. Also resolves in the reverse direction, +// reconstructing a designator from a symbol, byte offset, and size. +// Used for resolving variables in DATA statements to ranges in their +// initial images. +// Some designators can also be folded into constant pointer descriptors, +// which also have per-dimension extent and stride information suitable +// for initializing a descriptor. +// (The designators that cannot be folded are those with vector-valued +// subscripts; they are allowed as DATA statement objects, but are not valid +// initial pointer targets.) + +#include "common.h" +#include "expression.h" +#include "fold.h" +#include "shape.h" +#include "type.h" +#include "variable.h" +#include +#include + +namespace Fortran::evaluate { + +using common::ConstantSubscript; + +// Identifies a single contiguous interval of bytes at a fixed offset +// from a known symbol. +class OffsetSymbol { +public: + OffsetSymbol(const Symbol &symbol, std::size_t bytes) + : symbol_{symbol}, size_{bytes} {} + DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(OffsetSymbol) + + const Symbol &symbol() const { return *symbol_; } + void set_symbol(const Symbol &symbol) { symbol_ = symbol; }; + ConstantSubscript offset() const { return offset_; } + void Augment(ConstantSubscript n) { offset_ += n; } + std::size_t size() const { return size_; } + void set_size(std::size_t bytes) { size_ = bytes; } + +private: + SymbolRef symbol_; + ConstantSubscript offset_{0}; + std::size_t size_; +}; + +// Folds a Designator into a sequence of OffsetSymbols, if it can +// be so folded. Array sections yield multiple results, each +// corresponding to an element in array element order. +class DesignatorFolder { +public: + explicit DesignatorFolder(FoldingContext &c) : context_{c} {} + + DesignatorFolder &Reset() { + elementNumber_ = 0; + return *this; + } + + template + std::optional FoldDesignator(const Expr &expr) { + return std::visit( + [&](const auto &x) { return FoldDesignator(x, elementNumber_++); }, + expr.u); + } + +private: + std::optional FoldDesignator( + const Symbol &, ConstantSubscript) const; + std::optional FoldDesignator( + const SymbolRef &x, ConstantSubscript which) const { + return FoldDesignator(*x, which); + } + std::optional FoldDesignator( + const ArrayRef &, ConstantSubscript) const; + std::optional FoldDesignator( + const Component &, ConstantSubscript) const; + std::optional FoldDesignator( + const ComplexPart &, ConstantSubscript) const; + std::optional FoldDesignator( + const Substring &, ConstantSubscript) const; + std::optional FoldDesignator( + const DataRef &, ConstantSubscript) const; + std::optional FoldDesignator( + const NamedEntity &, ConstantSubscript) const; + std::optional FoldDesignator( + const CoarrayRef &, ConstantSubscript) const; + std::optional FoldDesignator( + const ProcedureDesignator &, ConstantSubscript) const; + + template + std::optional FoldDesignator( + const Expr &expr, ConstantSubscript which) const { + return std::visit( + [&](const auto &x) { return FoldDesignator(x, which); }, expr.u); + } + + template + std::optional FoldDesignator( + const A &x, ConstantSubscript) const { + DIE("DesignatorFolder::FoldDesignator(): unexpected object in designator"); + } + + template + std::optional FoldDesignator( + const Designator &designator, ConstantSubscript which) const { + return std::visit( + [&](const auto &x) { return FoldDesignator(x, which); }, designator.u); + } + template + std::optional FoldDesignator( + const Designator> &designator, + ConstantSubscript which) const { + return std::visit( + common::visitors{ + [&](const Substring &ss) { + if (const auto *dataRef{ss.GetParentIf()}) { + if (auto result{FoldDesignator(*dataRef, which)}) { + if (auto start{ToInt64(ss.lower())}) { + std::optional end; + if (ss.upper()) { + end = ToInt64(*ss.upper()); + } else if (auto len{dataRef->LEN()}) { + end = ToInt64(*len); + } + if (end) { + result->Augment(KIND * (*start - 1)); + result->set_size( + *end >= *start ? KIND * (*end - *start + 1) : 0); + return result; + } + } + } + } + return std::optional{}; + }, + [&](const auto &x) { return FoldDesignator(x, which); }, + }, + designator.u); + } + + FoldingContext &context_; + ConstantSubscript elementNumber_{0}; // zero-based +}; + +// Reconstructs a Designator<> from a symbol and an offset. +std::optional> OffsetToDesignator( + FoldingContext &, const Symbol &, ConstantSubscript offset, std::size_t); +std::optional> OffsetToDesignator( + FoldingContext &, const OffsetSymbol &); + +// Represents a compile-time constant Descriptor suitable for use +// as a pointer initializer. Lower bounds are always 1. +struct ConstantObjectPointer : public OffsetSymbol { + struct Dimension { + ConstantSubscript byteStride; + ConstantSubscript extent; + }; + using Dimensions = std::vector; + + ConstantObjectPointer( + const Symbol &symbol, std::size_t size, Dimensions &&dims) + : OffsetSymbol{symbol, size}, dimensions{std::move(dims)} {} + + // Folds a designator to a constant pointer. Crashes on failure. + // Use IsInitialDataTarget() to validate the expression beforehand. + static ConstantObjectPointer From(FoldingContext &, const Expr &); + + Dimensions dimensions; +}; + +} // namespace Fortran::evaluate +#endif // FORTRAN_EVALUATE_FOLD_DESIGNATOR_H_ diff --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h --- a/flang/include/flang/Evaluate/shape.h +++ b/flang/include/flang/Evaluate/shape.h @@ -188,11 +188,38 @@ return GetShapeHelper{context}(x); } +template +std::optional> GetConstantShape( + FoldingContext &context, const A &x) { + if (auto shape{GetShape(context, x)}) { + return AsConstantShape(context, *shape); + } else { + return std::nullopt; + } +} + +template +std::optional GetConstantExtents( + FoldingContext &context, const A &x) { + if (auto shape{GetShape(context, x)}) { + return AsConstantExtents(context, *shape); + } else { + return std::nullopt; + } +} + // Compilation-time shape conformance checking, when corresponding extents // are known. bool CheckConformance(parser::ContextualMessages &, const Shape &left, const Shape &right, const char *leftIs = "left operand", const char *rightIs = "right operand"); +// Increments one-based subscripts in element order (first varies fastest) +// and returns true when they remain in range; resets them all to one and +// return false otherwise (including the case where one or more of the +// extents are zero). +bool IncrementSubscripts( + ConstantSubscripts &, const ConstantSubscripts &extents); + } // namespace Fortran::evaluate #endif // FORTRAN_EVALUATE_SHAPE_H_ diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h --- a/flang/include/flang/Evaluate/tools.h +++ b/flang/include/flang/Evaluate/tools.h @@ -700,6 +700,42 @@ VALUE value; }; +// TypedWrapper() wraps a object in an explicitly typed representation +// (e.g., Designator<> or FunctionRef<>) that has been instantiated on +// a dynamically chosen Fortran type. +template typename WRAPPER, + typename WRAPPED> +common::IfNoLvalue>, WRAPPED> WrapperHelper( + int kind, WRAPPED &&x) { + return common::SearchTypes( + TypeKindVisitor{kind, std::move(x)}); +} + +template