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