blob: 457e86d4fdad9a61bcd3bd8844075e69a2df05b1 [file] [log] [blame]
//===-- 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 <optional>
#include <variant>
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<T> 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} {}
bool isEmpty() const { return isEmpty_; }
bool isOutOfRange() const { return isOutOfRange_; }
template <typename T>
std::optional<OffsetSymbol> FoldDesignator(const Expr<T> &expr) {
return std::visit(
[&](const auto &x) { return FoldDesignator(x, elementNumber_++); },
expr.u);
}
private:
std::optional<OffsetSymbol> FoldDesignator(const Symbol &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const SymbolRef &x, ConstantSubscript which) {
return FoldDesignator(*x, which);
}
std::optional<OffsetSymbol> FoldDesignator(
const ArrayRef &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const Component &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const ComplexPart &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const Substring &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const DataRef &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const NamedEntity &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const CoarrayRef &, ConstantSubscript);
std::optional<OffsetSymbol> FoldDesignator(
const ProcedureDesignator &, ConstantSubscript);
template <typename T>
std::optional<OffsetSymbol> FoldDesignator(
const Expr<T> &expr, ConstantSubscript which) {
return std::visit(
[&](const auto &x) { return FoldDesignator(x, which); }, expr.u);
}
template <typename A>
std::optional<OffsetSymbol> FoldDesignator(const A &x, ConstantSubscript) {
return std::nullopt;
}
template <typename T>
std::optional<OffsetSymbol> FoldDesignator(
const Designator<T> &designator, ConstantSubscript which) {
return std::visit(
[&](const auto &x) { return FoldDesignator(x, which); }, designator.u);
}
template <int KIND>
std::optional<OffsetSymbol> FoldDesignator(
const Designator<Type<TypeCategory::Character, KIND>> &designator,
ConstantSubscript which) {
return std::visit(
common::visitors{
[&](const Substring &ss) {
if (const auto *dataRef{ss.GetParentIf<DataRef>()}) {
if (auto result{FoldDesignator(*dataRef, which)}) {
if (auto start{ToInt64(ss.lower())}) {
std::optional<ConstantSubscript> end;
auto len{dataRef->LEN()};
if (ss.upper()) {
end = ToInt64(*ss.upper());
} else if (len) {
end = ToInt64(*len);
}
if (end) {
if (*start < 1) {
isOutOfRange_ = true;
}
result->Augment(KIND * (*start - 1));
result->set_size(
*end >= *start ? KIND * (*end - *start + 1) : 0);
if (len) {
if (auto lenVal{ToInt64(*len)}) {
if (*end > *lenVal) {
isOutOfRange_ = true;
}
}
}
return result;
}
}
}
}
return std::optional<OffsetSymbol>{};
},
[&](const auto &x) { return FoldDesignator(x, which); },
},
designator.u);
}
FoldingContext &context_;
ConstantSubscript elementNumber_{0}; // zero-based
bool isEmpty_{false};
bool isOutOfRange_{false};
};
// Reconstructs a Designator<> from a symbol and an offset.
std::optional<Expr<SomeType>> OffsetToDesignator(
FoldingContext &, const Symbol &, ConstantSubscript offset, std::size_t);
std::optional<Expr<SomeType>> 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<Dimension>;
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<SomeType> &);
Dimensions dimensions;
};
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_FOLD_DESIGNATOR_H_