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 
ConstantBounds(const ConstantSubscripts & shape)17 ConstantBounds::ConstantBounds(const ConstantSubscripts &shape)
18     : shape_(shape), lbounds_(shape_.size(), 1) {}
19 
ConstantBounds(ConstantSubscripts && shape)20 ConstantBounds::ConstantBounds(ConstantSubscripts &&shape)
21     : shape_(std::move(shape)), lbounds_(shape_.size(), 1) {}
22 
23 ConstantBounds::~ConstantBounds() = default;
24 
set_lbounds(ConstantSubscripts && lb)25 void ConstantBounds::set_lbounds(ConstantSubscripts &&lb) {
26   CHECK(lb.size() == shape_.size());
27   lbounds_ = std::move(lb);
28   for (std::size_t j{0}; j < shape_.size(); ++j) {
29     if (shape_[j] == 0) {
30       lbounds_[j] = 1;
31     }
32   }
33 }
34 
ComputeUbounds(std::optional<int> dim) const35 ConstantSubscripts ConstantBounds::ComputeUbounds(
36     std::optional<int> dim) const {
37   if (dim) {
38     CHECK(*dim < Rank());
39     return {lbounds_[*dim] + shape_[*dim] - 1};
40   } else {
41     ConstantSubscripts ubounds(Rank());
42     for (int i{0}; i < Rank(); ++i) {
43       ubounds[i] = lbounds_[i] + shape_[i] - 1;
44     }
45     return ubounds;
46   }
47 }
48 
SetLowerBoundsToOne()49 void ConstantBounds::SetLowerBoundsToOne() {
50   for (auto &n : lbounds_) {
51     n = 1;
52   }
53 }
54 
SHAPE() const55 Constant<SubscriptInteger> ConstantBounds::SHAPE() const {
56   return AsConstantShape(shape_);
57 }
58 
SubscriptsToOffset(const ConstantSubscripts & index) const59 ConstantSubscript ConstantBounds::SubscriptsToOffset(
60     const ConstantSubscripts &index) const {
61   CHECK(GetRank(index) == GetRank(shape_));
62   ConstantSubscript stride{1}, offset{0};
63   int dim{0};
64   for (auto j : index) {
65     auto lb{lbounds_[dim]};
66     auto extent{shape_[dim++]};
67     CHECK(j >= lb && j < lb + extent);
68     offset += stride * (j - lb);
69     stride *= extent;
70   }
71   return offset;
72 }
73 
TotalElementCount(const ConstantSubscripts & shape)74 std::size_t TotalElementCount(const ConstantSubscripts &shape) {
75   return static_cast<std::size_t>(GetSize(shape));
76 }
77 
IncrementSubscripts(ConstantSubscripts & indices,const std::vector<int> * dimOrder) const78 bool ConstantBounds::IncrementSubscripts(
79     ConstantSubscripts &indices, const std::vector<int> *dimOrder) const {
80   int rank{GetRank(shape_)};
81   CHECK(GetRank(indices) == rank);
82   CHECK(!dimOrder || static_cast<int>(dimOrder->size()) == rank);
83   for (int j{0}; j < rank; ++j) {
84     ConstantSubscript k{dimOrder ? (*dimOrder)[j] : j};
85     auto lb{lbounds_[k]};
86     CHECK(indices[k] >= lb);
87     if (++indices[k] < lb + shape_[k]) {
88       return true;
89     } else {
90       CHECK(indices[k] == lb + std::max<ConstantSubscript>(shape_[k], 1));
91       indices[k] = lb;
92     }
93   }
94   return false; // all done
95 }
96 
ValidateDimensionOrder(int rank,const std::vector<int> & order)97 std::optional<std::vector<int>> ValidateDimensionOrder(
98     int rank, const std::vector<int> &order) {
99   std::vector<int> dimOrder(rank);
100   if (static_cast<int>(order.size()) == rank) {
101     std::bitset<common::maxRank> seenDimensions;
102     for (int j{0}; j < rank; ++j) {
103       int dim{order[j]};
104       if (dim < 1 || dim > rank || seenDimensions.test(dim - 1)) {
105         return std::nullopt;
106       }
107       dimOrder[j] = dim - 1;
108       seenDimensions.set(dim - 1);
109     }
110     return dimOrder;
111   } else {
112     return std::nullopt;
113   }
114 }
115 
HasNegativeExtent(const ConstantSubscripts & shape)116 bool HasNegativeExtent(const ConstantSubscripts &shape) {
117   for (ConstantSubscript extent : shape) {
118     if (extent < 0) {
119       return true;
120     }
121   }
122   return false;
123 }
124 
125 template <typename RESULT, typename ELEMENT>
ConstantBase(std::vector<Element> && x,ConstantSubscripts && sh,Result res)126 ConstantBase<RESULT, ELEMENT>::ConstantBase(
127     std::vector<Element> &&x, ConstantSubscripts &&sh, Result res)
128     : ConstantBounds(std::move(sh)), result_{res}, values_(std::move(x)) {
129   CHECK(size() == TotalElementCount(shape()));
130 }
131 
132 template <typename RESULT, typename ELEMENT>
~ConstantBase()133 ConstantBase<RESULT, ELEMENT>::~ConstantBase() {}
134 
135 template <typename RESULT, typename ELEMENT>
operator ==(const ConstantBase & that) const136 bool ConstantBase<RESULT, ELEMENT>::operator==(const ConstantBase &that) const {
137   return shape() == that.shape() && values_ == that.values_;
138 }
139 
140 template <typename RESULT, typename ELEMENT>
Reshape(const ConstantSubscripts & dims) const141 auto ConstantBase<RESULT, ELEMENT>::Reshape(
142     const ConstantSubscripts &dims) const -> std::vector<Element> {
143   std::size_t n{TotalElementCount(dims)};
144   CHECK(!empty() || n == 0);
145   std::vector<Element> elements;
146   auto iter{values().cbegin()};
147   while (n-- > 0) {
148     elements.push_back(*iter);
149     if (++iter == values().cend()) {
150       iter = values().cbegin();
151     }
152   }
153   return elements;
154 }
155 
156 template <typename RESULT, typename ELEMENT>
CopyFrom(const ConstantBase<RESULT,ELEMENT> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)157 std::size_t ConstantBase<RESULT, ELEMENT>::CopyFrom(
158     const ConstantBase<RESULT, ELEMENT> &source, std::size_t count,
159     ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder) {
160   std::size_t copied{0};
161   ConstantSubscripts sourceSubscripts{source.lbounds()};
162   while (copied < count) {
163     values_.at(SubscriptsToOffset(resultSubscripts)) =
164         source.values_.at(source.SubscriptsToOffset(sourceSubscripts));
165     copied++;
166     source.IncrementSubscripts(sourceSubscripts);
167     IncrementSubscripts(resultSubscripts, dimOrder);
168   }
169   return copied;
170 }
171 
172 template <typename T>
At(const ConstantSubscripts & index) const173 auto Constant<T>::At(const ConstantSubscripts &index) const -> Element {
174   return Base::values_.at(Base::SubscriptsToOffset(index));
175 }
176 
177 template <typename T>
Reshape(ConstantSubscripts && dims) const178 auto Constant<T>::Reshape(ConstantSubscripts &&dims) const -> Constant {
179   return {Base::Reshape(dims), std::move(dims)};
180 }
181 
182 template <typename T>
CopyFrom(const Constant<T> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)183 std::size_t Constant<T>::CopyFrom(const Constant<T> &source, std::size_t count,
184     ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder) {
185   return Base::CopyFrom(source, count, resultSubscripts, dimOrder);
186 }
187 
188 // Constant<Type<TypeCategory::Character, KIND> specializations
189 template <int KIND>
Constant(const Scalar<Result> & str)190 Constant<Type<TypeCategory::Character, KIND>>::Constant(
191     const Scalar<Result> &str)
192     : values_{str}, length_{static_cast<ConstantSubscript>(values_.size())} {}
193 
194 template <int KIND>
Constant(Scalar<Result> && str)195 Constant<Type<TypeCategory::Character, KIND>>::Constant(Scalar<Result> &&str)
196     : values_{std::move(str)}, length_{static_cast<ConstantSubscript>(
197                                    values_.size())} {}
198 
199 template <int KIND>
Constant(ConstantSubscript len,std::vector<Scalar<Result>> && strings,ConstantSubscripts && sh)200 Constant<Type<TypeCategory::Character, KIND>>::Constant(ConstantSubscript len,
201     std::vector<Scalar<Result>> &&strings, ConstantSubscripts &&sh)
202     : ConstantBounds(std::move(sh)), length_{len} {
203   CHECK(strings.size() == TotalElementCount(shape()));
204   values_.assign(strings.size() * length_,
205       static_cast<typename Scalar<Result>::value_type>(' '));
206   ConstantSubscript at{0};
207   for (const auto &str : strings) {
208     auto strLen{static_cast<ConstantSubscript>(str.size())};
209     if (strLen > length_) {
210       values_.replace(at, length_, str.substr(0, length_));
211     } else {
212       values_.replace(at, strLen, str);
213     }
214     at += length_;
215   }
216   CHECK(at == static_cast<ConstantSubscript>(values_.size()));
217 }
218 
219 template <int KIND>
~Constant()220 Constant<Type<TypeCategory::Character, KIND>>::~Constant() {}
221 
222 template <int KIND>
empty() const223 bool Constant<Type<TypeCategory::Character, KIND>>::empty() const {
224   return size() == 0;
225 }
226 
227 template <int KIND>
size() const228 std::size_t Constant<Type<TypeCategory::Character, KIND>>::size() const {
229   if (length_ == 0) {
230     return TotalElementCount(shape());
231   } else {
232     return static_cast<ConstantSubscript>(values_.size()) / length_;
233   }
234 }
235 
236 template <int KIND>
At(const ConstantSubscripts & index) const237 auto Constant<Type<TypeCategory::Character, KIND>>::At(
238     const ConstantSubscripts &index) const -> Scalar<Result> {
239   auto offset{SubscriptsToOffset(index)};
240   return values_.substr(offset * length_, length_);
241 }
242 
243 template <int KIND>
Substring(ConstantSubscript lo,ConstantSubscript hi) const244 auto Constant<Type<TypeCategory::Character, KIND>>::Substring(
245     ConstantSubscript lo, ConstantSubscript hi) const
246     -> std::optional<Constant> {
247   std::vector<Element> elements;
248   ConstantSubscript n{GetSize(shape())};
249   ConstantSubscript newLength{0};
250   if (lo > hi) { // zero-length results
251     while (n-- > 0) {
252       elements.emplace_back(); // ""
253     }
254   } else if (lo < 1 || hi > length_) {
255     return std::nullopt;
256   } else {
257     newLength = hi - lo + 1;
258     for (ConstantSubscripts at{lbounds()}; n-- > 0; IncrementSubscripts(at)) {
259       elements.emplace_back(At(at).substr(lo - 1, newLength));
260     }
261   }
262   return Constant{newLength, std::move(elements), ConstantSubscripts{shape()}};
263 }
264 
265 template <int KIND>
Reshape(ConstantSubscripts && dims) const266 auto Constant<Type<TypeCategory::Character, KIND>>::Reshape(
267     ConstantSubscripts &&dims) const -> Constant<Result> {
268   std::size_t n{TotalElementCount(dims)};
269   CHECK(!empty() || n == 0);
270   std::vector<Element> elements;
271   ConstantSubscript at{0},
272       limit{static_cast<ConstantSubscript>(values_.size())};
273   while (n-- > 0) {
274     elements.push_back(values_.substr(at, length_));
275     at += length_;
276     if (at == limit) { // subtle: at > limit somehow? substr() will catch it
277       at = 0;
278     }
279   }
280   return {length_, std::move(elements), std::move(dims)};
281 }
282 
283 template <int KIND>
CopyFrom(const Constant<Type<TypeCategory::Character,KIND>> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)284 std::size_t Constant<Type<TypeCategory::Character, KIND>>::CopyFrom(
285     const Constant<Type<TypeCategory::Character, KIND>> &source,
286     std::size_t count, ConstantSubscripts &resultSubscripts,
287     const std::vector<int> *dimOrder) {
288   CHECK(length_ == source.length_);
289   if (length_ == 0) {
290     // It's possible that the array of strings consists of all empty strings.
291     // If so, constant folding will result in a string that's completely empty
292     // and the length_ will be zero, and there's nothing to do.
293     return count;
294   } else {
295     std::size_t copied{0};
296     std::size_t elementBytes{length_ * sizeof(decltype(values_[0]))};
297     ConstantSubscripts sourceSubscripts{source.lbounds()};
298     while (copied < count) {
299       auto *dest{&values_.at(SubscriptsToOffset(resultSubscripts) * length_)};
300       const auto *src{&source.values_.at(
301           source.SubscriptsToOffset(sourceSubscripts) * length_)};
302       std::memcpy(dest, src, elementBytes);
303       copied++;
304       source.IncrementSubscripts(sourceSubscripts);
305       IncrementSubscripts(resultSubscripts, dimOrder);
306     }
307     return copied;
308   }
309 }
310 
311 // Constant<SomeDerived> specialization
Constant(const StructureConstructor & x)312 Constant<SomeDerived>::Constant(const StructureConstructor &x)
313     : Base{x.values(), Result{x.derivedTypeSpec()}} {}
314 
Constant(StructureConstructor && x)315 Constant<SomeDerived>::Constant(StructureConstructor &&x)
316     : Base{std::move(x.values()), Result{x.derivedTypeSpec()}} {}
317 
Constant(const semantics::DerivedTypeSpec & spec,std::vector<StructureConstructorValues> && x,ConstantSubscripts && s)318 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
319     std::vector<StructureConstructorValues> &&x, ConstantSubscripts &&s)
320     : Base{std::move(x), std::move(s), Result{spec}} {}
321 
AcquireValues(std::vector<StructureConstructor> && x)322 static std::vector<StructureConstructorValues> AcquireValues(
323     std::vector<StructureConstructor> &&x) {
324   std::vector<StructureConstructorValues> result;
325   for (auto &&structure : std::move(x)) {
326     result.emplace_back(std::move(structure.values()));
327   }
328   return result;
329 }
330 
Constant(const semantics::DerivedTypeSpec & spec,std::vector<StructureConstructor> && x,ConstantSubscripts && shape)331 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
332     std::vector<StructureConstructor> &&x, ConstantSubscripts &&shape)
333     : Base{AcquireValues(std::move(x)), std::move(shape), Result{spec}} {}
334 
335 std::optional<StructureConstructor>
GetScalarValue() const336 Constant<SomeDerived>::GetScalarValue() const {
337   if (Rank() == 0) {
338     return StructureConstructor{result().derivedTypeSpec(), values_.at(0)};
339   } else {
340     return std::nullopt;
341   }
342 }
343 
At(const ConstantSubscripts & index) const344 StructureConstructor Constant<SomeDerived>::At(
345     const ConstantSubscripts &index) const {
346   return {result().derivedTypeSpec(), values_.at(SubscriptsToOffset(index))};
347 }
348 
Reshape(ConstantSubscripts && dims) const349 auto Constant<SomeDerived>::Reshape(ConstantSubscripts &&dims) const
350     -> Constant {
351   return {result().derivedTypeSpec(), Base::Reshape(dims), std::move(dims)};
352 }
353 
CopyFrom(const Constant<SomeDerived> & source,std::size_t count,ConstantSubscripts & resultSubscripts,const std::vector<int> * dimOrder)354 std::size_t Constant<SomeDerived>::CopyFrom(const Constant<SomeDerived> &source,
355     std::size_t count, ConstantSubscripts &resultSubscripts,
356     const std::vector<int> *dimOrder) {
357   return Base::CopyFrom(source, count, resultSubscripts, dimOrder);
358 }
359 
operator ()(SymbolRef x,SymbolRef y) const360 bool ComponentCompare::operator()(SymbolRef x, SymbolRef y) const {
361   return semantics::SymbolSourcePositionCompare{}(x, y);
362 }
363 
364 #ifdef _MSC_VER // disable bogus warning about missing definitions
365 #pragma warning(disable : 4661)
366 #endif
367 INSTANTIATE_CONSTANT_TEMPLATES
368 } // namespace Fortran::evaluate
369