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