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