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