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