[flang] DATA statement processing (part 2/4): Initial images

Summary:
Defines a representation for the initialized memory image of
a variable.  This image is populated by DATA statement
processing as designator elements are put into correspondence
with values, then converted into an initializer in the symbol
table so that lowering can pass the initial image to the
code generator.

Reviewers: tskeith, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby

Reviewed By: tskeith

Subscribers: mgorny, llvm-commits, flang-commits

Tags: #flang, #llvm

Differential Revision: https://reviews.llvm.org/D82131
This commit is contained in:
peter klausler 2020-06-18 14:37:59 -07:00
parent 0f92096c0a
commit 286d7a2159
3 changed files with 269 additions and 0 deletions

View File

@ -0,0 +1,85 @@
//===-------include/flang/Evaluate/initial-image.h ------------------------===//
//
// 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_INITIAL_IMAGE_H_
#define FORTRAN_EVALUATE_INITIAL_IMAGE_H_
// Represents the initialized storage of an object during DATA statement
// processing, including the conversion of that image to a constant
// initializer for a symbol.
#include "expression.h"
#include <map>
#include <optional>
#include <vector>
namespace Fortran::evaluate {
class InitialImage {
public:
explicit InitialImage(std::size_t bytes) : data_(bytes) {}
std::size_t size() const { return data_.size(); }
template <typename A> bool Add(ConstantSubscript, std::size_t, const A &) {
return false;
}
template <typename T>
bool Add(ConstantSubscript offset, std::size_t bytes, const Constant<T> &x) {
CHECK(offset >= 0 && offset + bytes <= data_.size());
auto elementBytes{x.GetType().MeasureSizeInBytes()};
CHECK(elementBytes && bytes == x.values().size() * *elementBytes);
std::memcpy(&data_.at(offset), &x.values().at(0), bytes);
return true;
}
template <int KIND>
bool Add(ConstantSubscript offset, std::size_t bytes,
const Constant<Type<TypeCategory::Character, KIND>> &x) {
CHECK(offset >= 0 && offset + bytes <= data_.size());
auto elements{TotalElementCount(x.shape())};
auto elementBytes{bytes > 0 ? bytes / elements : 0};
CHECK(elements * elementBytes == bytes);
for (auto at{x.lbounds()}; elements-- > 0; x.IncrementSubscripts(at)) {
auto scalar{x.At(at)}; // this is a std string; size() in chars
// Subtle: an initializer for a substring may have been
// expanded to the length of the entire string.
CHECK(scalar.size() * KIND == elementBytes ||
(elements == 0 && scalar.size() * KIND > elementBytes));
std::memcpy(&data_[offset], scalar.data(), elementBytes);
offset += elementBytes;
}
return true;
}
bool Add(ConstantSubscript, std::size_t, const Constant<SomeDerived> &);
template <typename T>
bool Add(ConstantSubscript offset, std::size_t bytes, const Expr<T> &x) {
return std::visit(
[&](const auto &y) { return Add(offset, bytes, y); }, x.u);
}
void AddPointer(ConstantSubscript, const Expr<SomeType> &);
// Conversions to constant initializers
std::optional<Expr<SomeType>> AsConstant(FoldingContext &,
const DynamicType &, const ConstantSubscripts &,
ConstantSubscript offset = 0) const;
std::optional<Expr<SomeType>> AsConstantDataPointer(
const DynamicType &, ConstantSubscript offset = 0) const;
const ProcedureDesignator &AsConstantProcPointer(
ConstantSubscript offset = 0) const;
friend class AsConstantHelper;
friend class AsConstantDataPointerHelper;
private:
std::vector<char> data_;
std::map<ConstantSubscript, Expr<SomeType>> pointers_;
};
} // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_INITIAL_IMAGE_H_

View File

@ -16,6 +16,7 @@ add_flang_library(FortranEvaluate
fold-real.cpp
formatting.cpp
host.cpp
initial-image.cpp
integer.cpp
intrinsics.cpp
intrinsics-library.cpp

View File

@ -0,0 +1,183 @@
//===-- lib/Evaluate/initial-image.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/Evaluate/initial-image.h"
#include "flang/Semantics/scope.h"
#include "flang/Semantics/tools.h"
namespace Fortran::evaluate {
bool InitialImage::Add(ConstantSubscript offset, std::size_t bytes,
const Constant<SomeDerived> &x) {
CHECK(offset >= 0 && offset + bytes <= data_.size());
auto elements{TotalElementCount(x.shape())};
auto elementBytes{bytes > 0 ? bytes / elements : 0};
CHECK(elements * elementBytes == bytes);
auto at{x.lbounds()};
for (auto elements{TotalElementCount(x.shape())}; elements-- > 0;
x.IncrementSubscripts(at)) {
auto scalar{x.At(at)};
// TODO: length type parameter values?
for (const auto &[symbolRef, indExpr] : scalar) {
const Symbol &component{*symbolRef};
CHECK(component.offset() + component.size() <= elementBytes);
if (IsPointer(component)) {
AddPointer(offset + component.offset(), indExpr.value());
} else if (!Add(offset + component.offset(), component.size(),
indExpr.value())) {
return false;
}
}
offset += elementBytes;
}
return true;
}
void InitialImage::AddPointer(
ConstantSubscript offset, const Expr<SomeType> &pointer) {
pointers_.emplace(offset, pointer);
}
// Classes used with common::SearchTypes() to (re)construct Constant<> values
// of the right type to initialize each symbol from the values that have
// been placed into its initialization image by DATA statements.
class AsConstantHelper {
public:
using Result = std::optional<Expr<SomeType>>;
using Types = AllTypes;
AsConstantHelper(FoldingContext &context, const DynamicType &type,
const ConstantSubscripts &extents, const InitialImage &image,
ConstantSubscript offset = 0)
: context_{context}, type_{type}, image_{image}, extents_{extents},
offset_{offset} {
CHECK(!type.IsPolymorphic());
}
template <typename T> Result Test() {
if (T::category != type_.category()) {
return std::nullopt;
}
if constexpr (T::category != TypeCategory::Derived) {
if (T::kind != type_.kind()) {
return std::nullopt;
}
}
using Const = Constant<T>;
using Scalar = typename Const::Element;
std::size_t elements{TotalElementCount(extents_)};
std::vector<Scalar> typedValue(elements);
auto stride{type_.MeasureSizeInBytes()};
CHECK(stride > 0);
CHECK(offset_ + elements * *stride <= image_.data_.size());
if constexpr (T::category == TypeCategory::Derived) {
const semantics::DerivedTypeSpec &derived{type_.GetDerivedTypeSpec()};
for (auto iter : DEREF(derived.scope())) {
const Symbol &component{*iter.second};
bool isPointer{IsPointer(component)};
if (component.has<semantics::ObjectEntityDetails>() ||
component.has<semantics::ProcEntityDetails>()) {
auto componentType{DynamicType::From(component)};
CHECK(componentType);
auto at{offset_ + component.offset()};
if (isPointer) {
for (std::size_t j{0}; j < elements; ++j, at += *stride) {
Result value{image_.AsConstantDataPointer(*componentType, at)};
CHECK(value);
typedValue[j].emplace(component, std::move(*value));
}
} else {
auto componentExtents{GetConstantExtents(context_, component)};
CHECK(componentExtents);
for (std::size_t j{0}; j < elements; ++j, at += *stride) {
Result value{image_.AsConstant(
context_, *componentType, *componentExtents, at)};
CHECK(value);
typedValue[j].emplace(component, std::move(*value));
}
}
}
}
return AsGenericExpr(
Const{derived, std::move(typedValue), std::move(extents_)});
} else if constexpr (T::category == TypeCategory::Character) {
auto length{static_cast<ConstantSubscript>(*stride) / T::kind};
for (std::size_t j{0}; j < elements; ++j) {
using Char = typename Scalar::value_type;
const Char *data{reinterpret_cast<const Char *>(
&image_.data_[offset_ + j * *stride])};
typedValue[j].assign(data, length);
}
return AsGenericExpr(
Const{length, std::move(typedValue), std::move(extents_)});
} else {
// Lengthless intrinsic type
CHECK(sizeof(Scalar) <= *stride);
for (std::size_t j{0}; j < elements; ++j) {
std::memcpy(&typedValue[j], &image_.data_[offset_ + j * *stride],
sizeof(Scalar));
}
return AsGenericExpr(Const{std::move(typedValue), std::move(extents_)});
}
}
private:
FoldingContext &context_;
const DynamicType &type_;
const InitialImage &image_;
ConstantSubscripts extents_; // a copy
ConstantSubscript offset_;
};
std::optional<Expr<SomeType>> InitialImage::AsConstant(FoldingContext &context,
const DynamicType &type, const ConstantSubscripts &extents,
ConstantSubscript offset) const {
return common::SearchTypes(
AsConstantHelper{context, type, extents, *this, offset});
}
class AsConstantDataPointerHelper {
public:
using Result = std::optional<Expr<SomeType>>;
using Types = AllTypes;
AsConstantDataPointerHelper(const DynamicType &type,
const InitialImage &image, ConstantSubscript offset = 0)
: type_{type}, image_{image}, offset_{offset} {}
template <typename T> Result Test() {
if (T::category != type_.category()) {
return std::nullopt;
}
if constexpr (T::category != TypeCategory::Derived) {
if (T::kind != type_.kind()) {
return std::nullopt;
}
}
auto iter{image_.pointers_.find(offset_)};
if (iter == image_.pointers_.end()) {
return AsGenericExpr(NullPointer{});
}
return iter->second;
}
private:
const DynamicType &type_;
const InitialImage &image_;
ConstantSubscript offset_;
};
std::optional<Expr<SomeType>> InitialImage::AsConstantDataPointer(
const DynamicType &type, ConstantSubscript offset) const {
return common::SearchTypes(AsConstantDataPointerHelper{type, *this, offset});
}
const ProcedureDesignator &InitialImage::AsConstantProcPointer(
ConstantSubscript offset) const {
auto iter{pointers_.find(0)};
CHECK(iter != pointers_.end());
return DEREF(std::get_if<ProcedureDesignator>(&iter->second.u));
}
} // namespace Fortran::evaluate