220 lines · cpp
1//===-- lib/Evaluate/initial-image.cpp ------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Evaluate/initial-image.h"10#include "flang/Semantics/scope.h"11#include "flang/Semantics/tools.h"12#include <cstring>13 14namespace Fortran::evaluate {15 16auto InitialImage::Add(ConstantSubscript offset, std::size_t bytes,17 const Constant<SomeDerived> &x, FoldingContext &context) -> Result {18 if (offset < 0 || offset + bytes > data_.size()) {19 return OutOfRange;20 } else {21 auto optElements{TotalElementCount(x.shape())};22 if (!optElements) {23 return TooManyElems;24 }25 auto elements{*optElements};26 auto elementBytes{bytes > 0 ? bytes / elements : 0};27 if (elements * elementBytes != bytes) {28 return SizeMismatch;29 } else {30 auto at{x.lbounds()};31 for (; elements-- > 0; x.IncrementSubscripts(at)) {32 auto scalar{x.At(at)};33 // TODO: length type parameter values?34 for (const auto &[symbolRef, indExpr] : scalar) {35 const Symbol &component{*symbolRef};36 if (component.offset() + component.size() > elementBytes) {37 return SizeMismatch;38 } else if (IsPointer(component)) {39 AddPointer(offset + component.offset(), indExpr.value());40 } else if (IsAllocatable(component) || IsAutomatic(component)) {41 return NotAConstant;42 } else if (auto result{Add(offset + component.offset(),43 component.size(), indExpr.value(), context)};44 result != Ok) {45 return result;46 }47 }48 offset += elementBytes;49 }50 }51 return Ok;52 }53}54 55void InitialImage::AddPointer(56 ConstantSubscript offset, const Expr<SomeType> &pointer) {57 pointers_.emplace(offset, pointer);58}59 60void InitialImage::Incorporate(ConstantSubscript toOffset,61 const InitialImage &from, ConstantSubscript fromOffset,62 ConstantSubscript bytes) {63 CHECK(from.pointers_.empty()); // pointers are not allowed in EQUIVALENCE64 CHECK(fromOffset >= 0 && bytes >= 0 &&65 static_cast<std::size_t>(fromOffset + bytes) <= from.size());66 CHECK(static_cast<std::size_t>(toOffset + bytes) <= size());67 std::memcpy(&data_[toOffset], &from.data_[fromOffset], bytes);68}69 70// Classes used with common::SearchTypes() to (re)construct Constant<> values71// of the right type to initialize each symbol from the values that have72// been placed into its initialization image by DATA statements.73class AsConstantHelper {74public:75 using Result = std::optional<Expr<SomeType>>;76 using Types = AllTypes;77 AsConstantHelper(FoldingContext &context, const DynamicType &type,78 std::optional<std::int64_t> charLength, const ConstantSubscripts &extents,79 const InitialImage &image, bool padWithZero = false,80 ConstantSubscript offset = 0)81 : context_{context}, type_{type}, charLength_{charLength}, image_{image},82 extents_{extents}, padWithZero_{padWithZero}, offset_{offset} {83 CHECK(!type.IsPolymorphic());84 }85 template <typename T> Result Test() {86 if (T::category != type_.category()) {87 return std::nullopt;88 }89 if constexpr (T::category != TypeCategory::Derived) {90 if (T::kind != type_.kind()) {91 return std::nullopt;92 }93 }94 using Const = Constant<T>;95 using Scalar = typename Const::Element;96 std::optional<uint64_t> optElements{TotalElementCount(extents_)};97 CHECK(optElements);98 uint64_t elements{*optElements};99 std::vector<Scalar> typedValue(elements);100 auto elemBytes{ToInt64(type_.MeasureSizeInBytes(101 context_, GetRank(extents_) > 0, charLength_))};102 CHECK(elemBytes && *elemBytes >= 0);103 std::size_t stride{static_cast<std::size_t>(*elemBytes)};104 CHECK(offset_ + elements * stride <= image_.data_.size() || padWithZero_);105 if constexpr (T::category == TypeCategory::Derived) {106 const semantics::DerivedTypeSpec &derived{type_.GetDerivedTypeSpec()};107 for (auto iter : DEREF(derived.scope())) {108 const Symbol &component{*iter.second};109 bool isProcPtr{IsProcedurePointer(component)};110 if (isProcPtr || component.has<semantics::ObjectEntityDetails>()) {111 auto at{offset_ + component.offset()};112 if (isProcPtr) {113 for (std::size_t j{0}; j < elements; ++j, at += stride) {114 if (Result value{image_.AsConstantPointer(at)}) {115 typedValue[j].emplace(component, std::move(*value));116 }117 }118 } else if (IsPointer(component)) {119 for (std::size_t j{0}; j < elements; ++j, at += stride) {120 if (Result value{image_.AsConstantPointer(at)}) {121 typedValue[j].emplace(component, std::move(*value));122 } else {123 typedValue[j].emplace(component, Expr<SomeType>{NullPointer{}});124 }125 }126 } else if (IsAllocatable(component)) {127 // Lowering needs an explicit NULL() for allocatables128 for (std::size_t j{0}; j < elements; ++j, at += stride) {129 typedValue[j].emplace(component, Expr<SomeType>{NullPointer{}});130 }131 } else {132 auto componentType{DynamicType::From(component)};133 CHECK(componentType.has_value());134 auto componentExtents{GetConstantExtents(context_, component)};135 CHECK(componentExtents.has_value());136 for (std::size_t j{0}; j < elements; ++j, at += stride) {137 if (Result value{image_.AsConstant(context_, *componentType,138 std::nullopt, *componentExtents, padWithZero_, at)}) {139 typedValue[j].emplace(component, std::move(*value));140 }141 }142 }143 }144 }145 return AsGenericExpr(146 Const{derived, std::move(typedValue), std::move(extents_)});147 } else if constexpr (T::category == TypeCategory::Character) {148 auto length{static_cast<ConstantSubscript>(stride) / T::kind};149 for (std::size_t j{0}; j < elements; ++j) {150 using Char = typename Scalar::value_type;151 auto at{static_cast<std::size_t>(offset_ + j * stride)};152 auto chunk{length};153 if (at + chunk > image_.data_.size()) {154 CHECK(padWithZero_);155 if (at >= image_.data_.size()) {156 chunk = 0;157 } else {158 chunk = image_.data_.size() - at;159 }160 }161 if (chunk > 0) {162 const Char *data{reinterpret_cast<const Char *>(&image_.data_[at])};163 typedValue[j].assign(data, chunk);164 }165 if (chunk < length && padWithZero_) {166 typedValue[j].append(length - chunk, Char{});167 }168 }169 return AsGenericExpr(170 Const{length, std::move(typedValue), std::move(extents_)});171 } else {172 // Lengthless intrinsic type173 CHECK(sizeof(Scalar) <= stride);174 for (std::size_t j{0}; j < elements; ++j) {175 auto at{static_cast<std::size_t>(offset_ + j * stride)};176 std::size_t chunk{sizeof(Scalar)};177 if (at + chunk > image_.data_.size()) {178 CHECK(padWithZero_);179 if (at >= image_.data_.size()) {180 chunk = 0;181 } else {182 chunk = image_.data_.size() - at;183 }184 }185 // TODO endianness186 if (chunk > 0) {187 std::memcpy(&typedValue[j], &image_.data_[at], chunk);188 }189 }190 return AsGenericExpr(Const{std::move(typedValue), std::move(extents_)});191 }192 }193 194private:195 FoldingContext &context_;196 const DynamicType &type_;197 std::optional<std::int64_t> charLength_;198 const InitialImage &image_;199 ConstantSubscripts extents_; // a copy200 bool padWithZero_;201 ConstantSubscript offset_;202};203 204std::optional<Expr<SomeType>> InitialImage::AsConstant(FoldingContext &context,205 const DynamicType &type, std::optional<std::int64_t> charLength,206 const ConstantSubscripts &extents, bool padWithZero,207 ConstantSubscript offset) const {208 return common::SearchTypes(AsConstantHelper{209 context, type, charLength, extents, *this, padWithZero, offset});210}211 212std::optional<Expr<SomeType>> InitialImage::AsConstantPointer(213 ConstantSubscript offset) const {214 auto iter{pointers_.find(offset)};215 return iter == pointers_.end() ? std::optional<Expr<SomeType>>{}216 : iter->second;217}218 219} // namespace Fortran::evaluate220