1 //===-- lib/Evaluate/constant.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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "flang/Evaluate/constant.h"
10 #include "flang/Evaluate/expression.h"
11 #include "flang/Evaluate/shape.h"
12 #include "flang/Evaluate/type.h"
13 #include <string>
14
15 namespace Fortran::evaluate {
16
TotalElementCount(const ConstantSubscripts & shape)17 std::size_t TotalElementCount(const ConstantSubscripts &shape) {
18 std::size_t size{1};
19 for (auto dim : shape) {
20 CHECK(dim >= 0);
21 size *= dim;
22 }
23 return size;
24 }
25
ConstantBounds(const ConstantSubscripts & shape)26 ConstantBounds::ConstantBounds(const ConstantSubscripts &shape)
27 : shape_(shape), lbounds_(shape_.size(), 1) {}
28
ConstantBounds(ConstantSubscripts && shape)29 ConstantBounds::ConstantBounds(ConstantSubscripts &&shape)
30 : shape_(std::move(shape)), lbounds_(shape_.size(), 1) {}
31
32 ConstantBounds::~ConstantBounds() = default;
33
set_lbounds(ConstantSubscripts && lb)34 void ConstantBounds::set_lbounds(ConstantSubscripts &&lb) {
35 CHECK(lb.size() == shape_.size());
36 lbounds_ = std::move(lb);
37 }
38
SHAPE() const39 Constant<SubscriptInteger> ConstantBounds::SHAPE() const {
40 return AsConstantShape(shape_);
41 }
42
SubscriptsToOffset(const ConstantSubscripts & index) const43 ConstantSubscript ConstantBounds::SubscriptsToOffset(
44 const ConstantSubscripts &index) const {
45 CHECK(GetRank(index) == GetRank(shape_));
46 ConstantSubscript stride{1}, offset{0};
47 int dim{0};
48 for (auto j : index) {
49 auto lb{lbounds_[dim]};
50 auto extent{shape_[dim++]};
51 CHECK(j >= lb && j < lb + extent);
52 offset += stride * (j - lb);
53 stride *= extent;
54 }
55 return offset;
56 }
57
IncrementSubscripts(ConstantSubscripts & indices,const std::vector<int> * dimOrder) const58 bool ConstantBounds::IncrementSubscripts(
59 ConstantSubscripts &indices, const std::vector<int> *dimOrder) const {
60 int rank{GetRank(shape_)};
61 CHECK(GetRank(indices) == rank);
62 CHECK(!dimOrder || static_cast<int>(dimOrder->size()) == rank);
63 for (int j{0}; j < rank; ++j) {
64 ConstantSubscript k{dimOrder ? (*dimOrder)[j] : j};
65 auto lb{lbounds_[k]};
66 CHECK(indices[k] >= lb);
67 if (++indices[k] < lb + shape_[k]) {
68 return true;
69 } else {
70 CHECK(indices[k] == lb + shape_[k]);
71 indices[k] = lb;
72 }
73 }
74 return false; // all done
75 }
76
ValidateDimensionOrder(int rank,const std::vector<int> & order)77 std::optional<std::vector<int>> ValidateDimensionOrder(
78 int rank, const std::vector<int> &order) {
79 std::vector<int> dimOrder(rank);
80 if (static_cast<int>(order.size()) == rank) {
81 std::bitset<common::maxRank> seenDimensions;
82 for (int j{0}; j < rank; ++j) {
83 int dim{order[j]};
84 if (dim < 1 || dim > rank || seenDimensions.test(dim - 1)) {
85 return std::nullopt;
86 }
87 dimOrder[dim - 1] = j;
88 seenDimensions.set(dim - 1);
89 }
90 return dimOrder;
91 } else {
92 return std::nullopt;
93 }
94 }
95
HasNegativeExtent(const ConstantSubscripts & shape)96 bool HasNegativeExtent(const ConstantSubscripts &shape) {
97 for (ConstantSubscript extent : shape) {
98 if (extent < 0) {
99 return true;
100 }
101 }
102 return false;
103 }
104
105 template <typename RESULT, typename ELEMENT>
ConstantBase(std::vector<Element> && x,ConstantSubscripts && sh,Result res)106 ConstantBase<RESULT, ELEMENT>::ConstantBase(
107 std::vector<Element> &&x, ConstantSubscripts &&sh, Result res)
108 : ConstantBounds(std::move(sh)), result_{res}, values_(std::move(x)) {
109 CHECK(size() == TotalElementCount(shape()));
110 }
111
112 template <typename RESULT, typename ELEMENT>
~ConstantBase()113 ConstantBase<RESULT, ELEMENT>::~ConstantBase() {}
114
115 template <typename RESULT, typename ELEMENT>
operator ==(const ConstantBase & that) const116 bool ConstantBase<RESULT, ELEMENT>::operator==(const ConstantBase &that) const {
117 return shape() == that.shape() && values_ == that.values_;
118 }
119
120 template <typename RESULT, typename ELEMENT>
Reshape(const ConstantSubscripts & dims) const121 auto ConstantBase<RESULT, ELEMENT>::Reshape(
122 const ConstantSubscripts &dims) const -> std::vector<Element> {
123 std::size_t n{TotalElementCount(dims)};
124 CHECK(!empty() || n == 0);
125 std::vector<Element> elements;
126 auto iter{values().cbegin()};
127 while (n-- > 0) {
128 elements.push_back(*iter);
129 if (++iter == values().cend()) {
130 iter = values().cbegin();
131 }
132 }
133 return elements;
134 }
135
136 template <typename RESULT, typename ELEMENT>
CopyFrom(const ConstantBase<RESULT,ELEMENT> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)137 std::size_t ConstantBase<RESULT, ELEMENT>::CopyFrom(
138 const ConstantBase<RESULT, ELEMENT> &source, std::size_t count,
139 ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder) {
140 std::size_t copied{0};
141 ConstantSubscripts sourceSubscripts{source.lbounds()};
142 while (copied < count) {
143 values_.at(SubscriptsToOffset(resultSubscripts)) =
144 source.values_.at(source.SubscriptsToOffset(sourceSubscripts));
145 copied++;
146 source.IncrementSubscripts(sourceSubscripts);
147 IncrementSubscripts(resultSubscripts, dimOrder);
148 }
149 return copied;
150 }
151
152 template <typename T>
At(const ConstantSubscripts & index) const153 auto Constant<T>::At(const ConstantSubscripts &index) const -> Element {
154 return Base::values_.at(Base::SubscriptsToOffset(index));
155 }
156
157 template <typename T>
Reshape(ConstantSubscripts && dims) const158 auto Constant<T>::Reshape(ConstantSubscripts &&dims) const -> Constant {
159 return {Base::Reshape(dims), std::move(dims)};
160 }
161
162 template <typename T>
CopyFrom(const Constant<T> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)163 std::size_t Constant<T>::CopyFrom(const Constant<T> &source, std::size_t count,
164 ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder) {
165 return Base::CopyFrom(source, count, resultSubscripts, dimOrder);
166 }
167
168 // Constant<Type<TypeCategory::Character, KIND> specializations
169 template <int KIND>
Constant(const Scalar<Result> & str)170 Constant<Type<TypeCategory::Character, KIND>>::Constant(
171 const Scalar<Result> &str)
172 : values_{str}, length_{static_cast<ConstantSubscript>(values_.size())} {}
173
174 template <int KIND>
Constant(Scalar<Result> && str)175 Constant<Type<TypeCategory::Character, KIND>>::Constant(Scalar<Result> &&str)
176 : values_{std::move(str)}, length_{static_cast<ConstantSubscript>(
177 values_.size())} {}
178
179 template <int KIND>
Constant(ConstantSubscript len,std::vector<Scalar<Result>> && strings,ConstantSubscripts && sh)180 Constant<Type<TypeCategory::Character, KIND>>::Constant(ConstantSubscript len,
181 std::vector<Scalar<Result>> &&strings, ConstantSubscripts &&sh)
182 : ConstantBounds(std::move(sh)), length_{len} {
183 CHECK(strings.size() == TotalElementCount(shape()));
184 values_.assign(strings.size() * length_,
185 static_cast<typename Scalar<Result>::value_type>(' '));
186 ConstantSubscript at{0};
187 for (const auto &str : strings) {
188 auto strLen{static_cast<ConstantSubscript>(str.size())};
189 if (strLen > length_) {
190 values_.replace(at, length_, str.substr(0, length_));
191 } else {
192 values_.replace(at, strLen, str);
193 }
194 at += length_;
195 }
196 CHECK(at == static_cast<ConstantSubscript>(values_.size()));
197 }
198
199 template <int KIND>
~Constant()200 Constant<Type<TypeCategory::Character, KIND>>::~Constant() {}
201
202 template <int KIND>
empty() const203 bool Constant<Type<TypeCategory::Character, KIND>>::empty() const {
204 return size() == 0;
205 }
206
207 template <int KIND>
size() const208 std::size_t Constant<Type<TypeCategory::Character, KIND>>::size() const {
209 if (length_ == 0) {
210 return TotalElementCount(shape());
211 } else {
212 return static_cast<ConstantSubscript>(values_.size()) / length_;
213 }
214 }
215
216 template <int KIND>
At(const ConstantSubscripts & index) const217 auto Constant<Type<TypeCategory::Character, KIND>>::At(
218 const ConstantSubscripts &index) const -> Scalar<Result> {
219 auto offset{SubscriptsToOffset(index)};
220 return values_.substr(offset * length_, length_);
221 }
222
223 template <int KIND>
Reshape(ConstantSubscripts && dims) const224 auto Constant<Type<TypeCategory::Character, KIND>>::Reshape(
225 ConstantSubscripts &&dims) const -> Constant<Result> {
226 std::size_t n{TotalElementCount(dims)};
227 CHECK(!empty() || n == 0);
228 std::vector<Element> elements;
229 ConstantSubscript at{0},
230 limit{static_cast<ConstantSubscript>(values_.size())};
231 while (n-- > 0) {
232 elements.push_back(values_.substr(at, length_));
233 at += length_;
234 if (at == limit) { // subtle: at > limit somehow? substr() will catch it
235 at = 0;
236 }
237 }
238 return {length_, std::move(elements), std::move(dims)};
239 }
240
241 template <int KIND>
CopyFrom(const Constant<Type<TypeCategory::Character,KIND>> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)242 std::size_t Constant<Type<TypeCategory::Character, KIND>>::CopyFrom(
243 const Constant<Type<TypeCategory::Character, KIND>> &source,
244 std::size_t count, ConstantSubscripts &resultSubscripts,
245 const std::vector<int> *dimOrder) {
246 CHECK(length_ == source.length_);
247 if (length_ == 0) {
248 // It's possible that the array of strings consists of all empty strings.
249 // If so, constant folding will result in a string that's completely empty
250 // and the length_ will be zero, and there's nothing to do.
251 return count;
252 } else {
253 std::size_t copied{0};
254 std::size_t elementBytes{length_ * sizeof(decltype(values_[0]))};
255 ConstantSubscripts sourceSubscripts{source.lbounds()};
256 while (copied < count) {
257 auto *dest{&values_.at(SubscriptsToOffset(resultSubscripts) * length_)};
258 const auto *src{&source.values_.at(
259 source.SubscriptsToOffset(sourceSubscripts) * length_)};
260 std::memcpy(dest, src, elementBytes);
261 copied++;
262 source.IncrementSubscripts(sourceSubscripts);
263 IncrementSubscripts(resultSubscripts, dimOrder);
264 }
265 return copied;
266 }
267 }
268
269 // Constant<SomeDerived> specialization
Constant(const StructureConstructor & x)270 Constant<SomeDerived>::Constant(const StructureConstructor &x)
271 : Base{x.values(), Result{x.derivedTypeSpec()}} {}
272
Constant(StructureConstructor && x)273 Constant<SomeDerived>::Constant(StructureConstructor &&x)
274 : Base{std::move(x.values()), Result{x.derivedTypeSpec()}} {}
275
Constant(const semantics::DerivedTypeSpec & spec,std::vector<StructureConstructorValues> && x,ConstantSubscripts && s)276 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
277 std::vector<StructureConstructorValues> &&x, ConstantSubscripts &&s)
278 : Base{std::move(x), std::move(s), Result{spec}} {}
279
AcquireValues(std::vector<StructureConstructor> && x)280 static std::vector<StructureConstructorValues> AcquireValues(
281 std::vector<StructureConstructor> &&x) {
282 std::vector<StructureConstructorValues> result;
283 for (auto &&structure : std::move(x)) {
284 result.emplace_back(std::move(structure.values()));
285 }
286 return result;
287 }
288
Constant(const semantics::DerivedTypeSpec & spec,std::vector<StructureConstructor> && x,ConstantSubscripts && shape)289 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
290 std::vector<StructureConstructor> &&x, ConstantSubscripts &&shape)
291 : Base{AcquireValues(std::move(x)), std::move(shape), Result{spec}} {}
292
293 std::optional<StructureConstructor>
GetScalarValue() const294 Constant<SomeDerived>::GetScalarValue() const {
295 if (Rank() == 0) {
296 return StructureConstructor{result().derivedTypeSpec(), values_.at(0)};
297 } else {
298 return std::nullopt;
299 }
300 }
301
At(const ConstantSubscripts & index) const302 StructureConstructor Constant<SomeDerived>::At(
303 const ConstantSubscripts &index) const {
304 return {result().derivedTypeSpec(), values_.at(SubscriptsToOffset(index))};
305 }
306
Reshape(ConstantSubscripts && dims) const307 auto Constant<SomeDerived>::Reshape(ConstantSubscripts &&dims) const
308 -> Constant {
309 return {result().derivedTypeSpec(), Base::Reshape(dims), std::move(dims)};
310 }
311
CopyFrom(const Constant<SomeDerived> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)312 std::size_t Constant<SomeDerived>::CopyFrom(const Constant<SomeDerived> &source,
313 std::size_t count, ConstantSubscripts &resultSubscripts,
314 const std::vector<int> *dimOrder) {
315 return Base::CopyFrom(source, count, resultSubscripts, dimOrder);
316 }
317
318 INSTANTIATE_CONSTANT_TEMPLATES
319 } // namespace Fortran::evaluate
320