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