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 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 26 ConstantBounds::ConstantBounds(const ConstantSubscripts &shape) 27 : shape_(shape), lbounds_(shape_.size(), 1) {} 28 29 ConstantBounds::ConstantBounds(ConstantSubscripts &&shape) 30 : shape_(std::move(shape)), lbounds_(shape_.size(), 1) {} 31 32 ConstantBounds::~ConstantBounds() = default; 33 34 void ConstantBounds::set_lbounds(ConstantSubscripts &&lb) { 35 CHECK(lb.size() == shape_.size()); 36 lbounds_ = std::move(lb); 37 } 38 39 Constant<SubscriptInteger> ConstantBounds::SHAPE() const { 40 return AsConstantShape(shape_); 41 } 42 43 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 58 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 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 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> 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> 113 ConstantBase<RESULT, ELEMENT>::~ConstantBase() {} 114 115 template <typename RESULT, typename ELEMENT> 116 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> 121 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> 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> 153 auto Constant<T>::At(const ConstantSubscripts &index) const -> Element { 154 return Base::values_.at(Base::SubscriptsToOffset(index)); 155 } 156 157 template <typename T> 158 auto Constant<T>::Reshape(ConstantSubscripts &&dims) const -> Constant { 159 return {Base::Reshape(dims), std::move(dims)}; 160 } 161 162 template <typename T> 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> 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> 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> 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> 200 Constant<Type<TypeCategory::Character, KIND>>::~Constant() {} 201 202 template <int KIND> 203 bool Constant<Type<TypeCategory::Character, KIND>>::empty() const { 204 return size() == 0; 205 } 206 207 template <int KIND> 208 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> 217 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> 224 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> 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 270 Constant<SomeDerived>::Constant(const StructureConstructor &x) 271 : Base{x.values(), Result{x.derivedTypeSpec()}} {} 272 273 Constant<SomeDerived>::Constant(StructureConstructor &&x) 274 : Base{std::move(x.values()), Result{x.derivedTypeSpec()}} {} 275 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 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 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> 294 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 302 StructureConstructor Constant<SomeDerived>::At( 303 const ConstantSubscripts &index) const { 304 return {result().derivedTypeSpec(), values_.at(SubscriptsToOffset(index))}; 305 } 306 307 auto Constant<SomeDerived>::Reshape(ConstantSubscripts &&dims) const 308 -> Constant { 309 return {result().derivedTypeSpec(), Base::Reshape(dims), std::move(dims)}; 310 } 311 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 bool ComponentCompare::operator()(SymbolRef x, SymbolRef y) const { 319 return semantics::SymbolSourcePositionCompare{}(x, y); 320 } 321 322 INSTANTIATE_CONSTANT_TEMPLATES 323 } // namespace Fortran::evaluate 324