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