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 } 29 30 void ConstantBounds::SetLowerBoundsToOne() { 31 for (auto &n : lbounds_) { 32 n = 1; 33 } 34 } 35 36 Constant<SubscriptInteger> ConstantBounds::SHAPE() const { 37 return AsConstantShape(shape_); 38 } 39 40 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 55 std::size_t TotalElementCount(const ConstantSubscripts &shape) { 56 return static_cast<std::size_t>(GetSize(shape)); 57 } 58 59 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 + std::max<ConstantSubscript>(shape_[k], 1)); 72 indices[k] = lb; 73 } 74 } 75 return false; // all done 76 } 77 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[j] = dim - 1; 89 seenDimensions.set(dim - 1); 90 } 91 return dimOrder; 92 } else { 93 return std::nullopt; 94 } 95 } 96 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> 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> 114 ConstantBase<RESULT, ELEMENT>::~ConstantBase() {} 115 116 template <typename RESULT, typename ELEMENT> 117 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> 122 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> 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> 154 auto Constant<T>::At(const ConstantSubscripts &index) const -> Element { 155 return Base::values_.at(Base::SubscriptsToOffset(index)); 156 } 157 158 template <typename T> 159 auto Constant<T>::Reshape(ConstantSubscripts &&dims) const -> Constant { 160 return {Base::Reshape(dims), std::move(dims)}; 161 } 162 163 template <typename T> 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> 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> 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> 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> 201 Constant<Type<TypeCategory::Character, KIND>>::~Constant() {} 202 203 template <int KIND> 204 bool Constant<Type<TypeCategory::Character, KIND>>::empty() const { 205 return size() == 0; 206 } 207 208 template <int KIND> 209 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> 218 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> 225 auto Constant<Type<TypeCategory::Character, KIND>>::Substring( 226 ConstantSubscript lo, ConstantSubscript hi) const 227 -> std::optional<Constant> { 228 std::vector<Element> elements; 229 ConstantSubscript n{GetSize(shape())}; 230 ConstantSubscript newLength{0}; 231 if (lo > hi) { // zero-length results 232 while (n-- > 0) { 233 elements.emplace_back(); // "" 234 } 235 } else if (lo < 1 || hi > length_) { 236 return std::nullopt; 237 } else { 238 newLength = hi - lo + 1; 239 for (ConstantSubscripts at{lbounds()}; n-- > 0; IncrementSubscripts(at)) { 240 elements.emplace_back(At(at).substr(lo - 1, newLength)); 241 } 242 } 243 return Constant{newLength, std::move(elements), ConstantSubscripts{shape()}}; 244 } 245 246 template <int KIND> 247 auto Constant<Type<TypeCategory::Character, KIND>>::Reshape( 248 ConstantSubscripts &&dims) const -> Constant<Result> { 249 std::size_t n{TotalElementCount(dims)}; 250 CHECK(!empty() || n == 0); 251 std::vector<Element> elements; 252 ConstantSubscript at{0}, 253 limit{static_cast<ConstantSubscript>(values_.size())}; 254 while (n-- > 0) { 255 elements.push_back(values_.substr(at, length_)); 256 at += length_; 257 if (at == limit) { // subtle: at > limit somehow? substr() will catch it 258 at = 0; 259 } 260 } 261 return {length_, std::move(elements), std::move(dims)}; 262 } 263 264 template <int KIND> 265 std::size_t Constant<Type<TypeCategory::Character, KIND>>::CopyFrom( 266 const Constant<Type<TypeCategory::Character, KIND>> &source, 267 std::size_t count, ConstantSubscripts &resultSubscripts, 268 const std::vector<int> *dimOrder) { 269 CHECK(length_ == source.length_); 270 if (length_ == 0) { 271 // It's possible that the array of strings consists of all empty strings. 272 // If so, constant folding will result in a string that's completely empty 273 // and the length_ will be zero, and there's nothing to do. 274 return count; 275 } else { 276 std::size_t copied{0}; 277 std::size_t elementBytes{length_ * sizeof(decltype(values_[0]))}; 278 ConstantSubscripts sourceSubscripts{source.lbounds()}; 279 while (copied < count) { 280 auto *dest{&values_.at(SubscriptsToOffset(resultSubscripts) * length_)}; 281 const auto *src{&source.values_.at( 282 source.SubscriptsToOffset(sourceSubscripts) * length_)}; 283 std::memcpy(dest, src, elementBytes); 284 copied++; 285 source.IncrementSubscripts(sourceSubscripts); 286 IncrementSubscripts(resultSubscripts, dimOrder); 287 } 288 return copied; 289 } 290 } 291 292 // Constant<SomeDerived> specialization 293 Constant<SomeDerived>::Constant(const StructureConstructor &x) 294 : Base{x.values(), Result{x.derivedTypeSpec()}} {} 295 296 Constant<SomeDerived>::Constant(StructureConstructor &&x) 297 : Base{std::move(x.values()), Result{x.derivedTypeSpec()}} {} 298 299 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec, 300 std::vector<StructureConstructorValues> &&x, ConstantSubscripts &&s) 301 : Base{std::move(x), std::move(s), Result{spec}} {} 302 303 static std::vector<StructureConstructorValues> AcquireValues( 304 std::vector<StructureConstructor> &&x) { 305 std::vector<StructureConstructorValues> result; 306 for (auto &&structure : std::move(x)) { 307 result.emplace_back(std::move(structure.values())); 308 } 309 return result; 310 } 311 312 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec, 313 std::vector<StructureConstructor> &&x, ConstantSubscripts &&shape) 314 : Base{AcquireValues(std::move(x)), std::move(shape), Result{spec}} {} 315 316 std::optional<StructureConstructor> 317 Constant<SomeDerived>::GetScalarValue() const { 318 if (Rank() == 0) { 319 return StructureConstructor{result().derivedTypeSpec(), values_.at(0)}; 320 } else { 321 return std::nullopt; 322 } 323 } 324 325 StructureConstructor Constant<SomeDerived>::At( 326 const ConstantSubscripts &index) const { 327 return {result().derivedTypeSpec(), values_.at(SubscriptsToOffset(index))}; 328 } 329 330 auto Constant<SomeDerived>::Reshape(ConstantSubscripts &&dims) const 331 -> Constant { 332 return {result().derivedTypeSpec(), Base::Reshape(dims), std::move(dims)}; 333 } 334 335 std::size_t Constant<SomeDerived>::CopyFrom(const Constant<SomeDerived> &source, 336 std::size_t count, ConstantSubscripts &resultSubscripts, 337 const std::vector<int> *dimOrder) { 338 return Base::CopyFrom(source, count, resultSubscripts, dimOrder); 339 } 340 341 bool ComponentCompare::operator()(SymbolRef x, SymbolRef y) const { 342 return semantics::SymbolSourcePositionCompare{}(x, y); 343 } 344 345 #ifdef _MSC_VER // disable bogus warning about missing definitions 346 #pragma warning(disable : 4661) 347 #endif 348 INSTANTIATE_CONSTANT_TEMPLATES 349 } // namespace Fortran::evaluate 350