diff --git a/flang/include/flang/Evaluate/initial-image.h b/flang/include/flang/Evaluate/initial-image.h new file mode 100644 index 000000000000..33c890b02d74 --- /dev/null +++ b/flang/include/flang/Evaluate/initial-image.h @@ -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 +#include +#include + +namespace Fortran::evaluate { + +class InitialImage { +public: + explicit InitialImage(std::size_t bytes) : data_(bytes) {} + + std::size_t size() const { return data_.size(); } + + template bool Add(ConstantSubscript, std::size_t, const A &) { + return false; + } + template + bool Add(ConstantSubscript offset, std::size_t bytes, const Constant &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 + bool Add(ConstantSubscript offset, std::size_t bytes, + const Constant> &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 &); + template + bool Add(ConstantSubscript offset, std::size_t bytes, const Expr &x) { + return std::visit( + [&](const auto &y) { return Add(offset, bytes, y); }, x.u); + } + + void AddPointer(ConstantSubscript, const Expr &); + + // Conversions to constant initializers + std::optional> AsConstant(FoldingContext &, + const DynamicType &, const ConstantSubscripts &, + ConstantSubscript offset = 0) const; + std::optional> AsConstantDataPointer( + const DynamicType &, ConstantSubscript offset = 0) const; + const ProcedureDesignator &AsConstantProcPointer( + ConstantSubscript offset = 0) const; + + friend class AsConstantHelper; + friend class AsConstantDataPointerHelper; + +private: + std::vector data_; + std::map> pointers_; +}; + +} // namespace Fortran::evaluate +#endif // FORTRAN_EVALUATE_INITIAL_IMAGE_H_ diff --git a/flang/lib/Evaluate/CMakeLists.txt b/flang/lib/Evaluate/CMakeLists.txt index 7911b50e13db..ddcdc8018658 100644 --- a/flang/lib/Evaluate/CMakeLists.txt +++ b/flang/lib/Evaluate/CMakeLists.txt @@ -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 diff --git a/flang/lib/Evaluate/initial-image.cpp b/flang/lib/Evaluate/initial-image.cpp new file mode 100644 index 000000000000..a32d359cbb01 --- /dev/null +++ b/flang/lib/Evaluate/initial-image.cpp @@ -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 &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 &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>; + 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 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; + using Scalar = typename Const::Element; + std::size_t elements{TotalElementCount(extents_)}; + std::vector 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() || + component.has()) { + 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(*stride) / T::kind}; + for (std::size_t j{0}; j < elements; ++j) { + using Char = typename Scalar::value_type; + const Char *data{reinterpret_cast( + &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> 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>; + using Types = AllTypes; + AsConstantDataPointerHelper(const DynamicType &type, + const InitialImage &image, ConstantSubscript offset = 0) + : type_{type}, image_{image}, offset_{offset} {} + template 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> 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(&iter->second.u)); +} + +} // namespace Fortran::evaluate