1 //===-- lib/Evaluate/fold-real.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 "fold-implementation.h" 10 #include "fold-reduction.h" 11 12 namespace Fortran::evaluate { 13 14 template <typename T> 15 static Expr<T> FoldTransformationalBessel( 16 FunctionRef<T> &&funcRef, FoldingContext &context) { 17 CHECK(funcRef.arguments().size() == 3); 18 /// Bessel runtime functions use `int` integer arguments. Convert integer 19 /// arguments to Int4, any overflow error will be reported during the 20 /// conversion folding. 21 using Int4 = Type<TypeCategory::Integer, 4>; 22 if (auto args{ 23 GetConstantArguments<Int4, Int4, T>(context, funcRef.arguments())}) { 24 const std::string &name{std::get<SpecificIntrinsic>(funcRef.proc().u).name}; 25 if (auto elementalBessel{GetHostRuntimeWrapper<T, Int4, T>(name)}) { 26 std::vector<Scalar<T>> results; 27 int n1{static_cast<int>( 28 std::get<0>(*args)->GetScalarValue().value().ToInt64())}; 29 int n2{static_cast<int>( 30 std::get<1>(*args)->GetScalarValue().value().ToInt64())}; 31 Scalar<T> x{std::get<2>(*args)->GetScalarValue().value()}; 32 for (int i{n1}; i <= n2; ++i) { 33 results.emplace_back((*elementalBessel)(context, Scalar<Int4>{i}, x)); 34 } 35 return Expr<T>{Constant<T>{ 36 std::move(results), ConstantSubscripts{std::max(n2 - n1 + 1, 0)}}}; 37 } else { 38 context.messages().Say( 39 "%s(integer(kind=4), real(kind=%d)) cannot be folded on host"_warn_en_US, 40 name, T::kind); 41 } 42 } 43 return Expr<T>{std::move(funcRef)}; 44 } 45 46 template <int KIND> 47 Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction( 48 FoldingContext &context, 49 FunctionRef<Type<TypeCategory::Real, KIND>> &&funcRef) { 50 using T = Type<TypeCategory::Real, KIND>; 51 using ComplexT = Type<TypeCategory::Complex, KIND>; 52 ActualArguments &args{funcRef.arguments()}; 53 auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 54 CHECK(intrinsic); 55 std::string name{intrinsic->name}; 56 if (name == "acos" || name == "acosh" || name == "asin" || name == "asinh" || 57 (name == "atan" && args.size() == 1) || name == "atanh" || 58 name == "bessel_j0" || name == "bessel_j1" || name == "bessel_y0" || 59 name == "bessel_y1" || name == "cos" || name == "cosh" || name == "erf" || 60 name == "erfc" || name == "erfc_scaled" || name == "exp" || 61 name == "gamma" || name == "log" || name == "log10" || 62 name == "log_gamma" || name == "sin" || name == "sinh" || name == "tan" || 63 name == "tanh") { 64 CHECK(args.size() == 1); 65 if (auto callable{GetHostRuntimeWrapper<T, T>(name)}) { 66 return FoldElementalIntrinsic<T, T>( 67 context, std::move(funcRef), *callable); 68 } else { 69 context.messages().Say( 70 "%s(real(kind=%d)) cannot be folded on host"_warn_en_US, name, KIND); 71 } 72 } else if (name == "amax0" || name == "amin0" || name == "amin1" || 73 name == "amax1" || name == "dmin1" || name == "dmax1") { 74 return RewriteSpecificMINorMAX(context, std::move(funcRef)); 75 } else if (name == "atan" || name == "atan2") { 76 std::string localName{name == "atan" ? "atan2" : name}; 77 CHECK(args.size() == 2); 78 if (auto callable{GetHostRuntimeWrapper<T, T, T>(localName)}) { 79 return FoldElementalIntrinsic<T, T, T>( 80 context, std::move(funcRef), *callable); 81 } else { 82 context.messages().Say( 83 "%s(real(kind=%d), real(kind%d)) cannot be folded on host"_warn_en_US, 84 name, KIND, KIND); 85 } 86 } else if (name == "bessel_jn" || name == "bessel_yn") { 87 if (args.size() == 2) { // elemental 88 // runtime functions use int arg 89 using Int4 = Type<TypeCategory::Integer, 4>; 90 if (auto callable{GetHostRuntimeWrapper<T, Int4, T>(name)}) { 91 return FoldElementalIntrinsic<T, Int4, T>( 92 context, std::move(funcRef), *callable); 93 } else { 94 context.messages().Say( 95 "%s(integer(kind=4), real(kind=%d)) cannot be folded on host"_warn_en_US, 96 name, KIND); 97 } 98 } else { 99 return FoldTransformationalBessel<T>(std::move(funcRef), context); 100 } 101 } else if (name == "abs") { // incl. zabs & cdabs 102 // Argument can be complex or real 103 if (auto *x{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 104 return FoldElementalIntrinsic<T, T>( 105 context, std::move(funcRef), &Scalar<T>::ABS); 106 } else if (auto *z{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 107 return FoldElementalIntrinsic<T, ComplexT>(context, std::move(funcRef), 108 ScalarFunc<T, ComplexT>([](const Scalar<ComplexT> &z) -> Scalar<T> { 109 return z.ABS().value; 110 })); 111 } else { 112 common::die(" unexpected argument type inside abs"); 113 } 114 } else if (name == "aimag") { 115 if (auto *zExpr{UnwrapExpr<Expr<ComplexT>>(args[0])}) { 116 return Fold(context, Expr<T>{ComplexComponent{true, std::move(*zExpr)}}); 117 } 118 } else if (name == "aint" || name == "anint") { 119 // ANINT rounds ties away from zero, not to even 120 common::RoundingMode mode{name == "aint" 121 ? common::RoundingMode::ToZero 122 : common::RoundingMode::TiesAwayFromZero}; 123 return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 124 ScalarFunc<T, T>([&name, &context, mode]( 125 const Scalar<T> &x) -> Scalar<T> { 126 ValueWithRealFlags<Scalar<T>> y{x.ToWholeNumber(mode)}; 127 if (y.flags.test(RealFlag::Overflow)) { 128 context.messages().Say( 129 "%s intrinsic folding overflow"_warn_en_US, name); 130 } 131 return y.value; 132 })); 133 } else if (name == "dim") { 134 return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 135 ScalarFunc<T, T, T>( 136 [](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> { 137 return x.DIM(y).value; 138 })); 139 } else if (name == "dprod") { 140 if (auto scalars{GetScalarConstantArguments<T, T>(context, args)}) { 141 return Fold(context, 142 Expr<T>{Multiply<T>{ 143 Expr<T>{std::get<0>(*scalars)}, Expr<T>{std::get<1>(*scalars)}}}); 144 } 145 } else if (name == "epsilon") { 146 return Expr<T>{Scalar<T>::EPSILON()}; 147 } else if (name == "huge") { 148 return Expr<T>{Scalar<T>::HUGE()}; 149 } else if (name == "hypot") { 150 CHECK(args.size() == 2); 151 return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 152 ScalarFunc<T, T, T>( 153 [](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> { 154 return x.HYPOT(y).value; 155 })); 156 } else if (name == "max") { 157 return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater); 158 } else if (name == "maxval") { 159 return FoldMaxvalMinval<T>(context, std::move(funcRef), 160 RelationalOperator::GT, T::Scalar::HUGE().Negate()); 161 } else if (name == "merge") { 162 return FoldMerge<T>(context, std::move(funcRef)); 163 } else if (name == "min") { 164 return FoldMINorMAX(context, std::move(funcRef), Ordering::Less); 165 } else if (name == "minval") { 166 return FoldMaxvalMinval<T>( 167 context, std::move(funcRef), RelationalOperator::LT, T::Scalar::HUGE()); 168 } else if (name == "mod") { 169 CHECK(args.size() == 2); 170 return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 171 ScalarFunc<T, T, T>( 172 [&context](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> { 173 auto result{x.MOD(y)}; 174 if (result.flags.test(RealFlag::DivideByZero)) { 175 context.messages().Say( 176 "second argument to MOD must not be zero"_warn_en_US); 177 } 178 return result.value; 179 })); 180 } else if (name == "modulo") { 181 CHECK(args.size() == 2); 182 return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 183 ScalarFunc<T, T, T>( 184 [&context](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> { 185 auto result{x.MODULO(y)}; 186 if (result.flags.test(RealFlag::DivideByZero)) { 187 context.messages().Say( 188 "second argument to MODULO must not be zero"_warn_en_US); 189 } 190 return result.value; 191 })); 192 } else if (name == "nearest") { 193 if (const auto *sExpr{UnwrapExpr<Expr<SomeReal>>(args[1])}) { 194 return common::visit( 195 [&](const auto &sVal) { 196 using TS = ResultType<decltype(sVal)>; 197 return FoldElementalIntrinsic<T, T, TS>(context, std::move(funcRef), 198 ScalarFunc<T, T, TS>([&](const Scalar<T> &x, 199 const Scalar<TS> &s) -> Scalar<T> { 200 if (s.IsZero()) { 201 context.messages().Say( 202 "NEAREST: S argument is zero"_warn_en_US); 203 } 204 auto result{x.NEAREST(!s.IsNegative())}; 205 if (result.flags.test(RealFlag::Overflow)) { 206 context.messages().Say( 207 "NEAREST intrinsic folding overflow"_warn_en_US); 208 } else if (result.flags.test(RealFlag::InvalidArgument)) { 209 context.messages().Say( 210 "NEAREST intrinsic folding: bad argument"_warn_en_US); 211 } 212 return result.value; 213 })); 214 }, 215 sExpr->u); 216 } 217 } else if (name == "product") { 218 auto one{Scalar<T>::FromInteger(value::Integer<8>{1}).value}; 219 return FoldProduct<T>(context, std::move(funcRef), one); 220 } else if (name == "real" || name == "dble") { 221 if (auto *expr{args[0].value().UnwrapExpr()}) { 222 return ToReal<KIND>(context, std::move(*expr)); 223 } 224 } else if (name == "rrspacing") { 225 return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 226 ScalarFunc<T, T>( 227 [](const Scalar<T> &x) -> Scalar<T> { return x.RRSPACING(); })); 228 } else if (name == "scale") { 229 if (const auto *byExpr{UnwrapExpr<Expr<SomeInteger>>(args[1])}) { 230 return common::visit( 231 [&](const auto &byVal) { 232 using TBY = ResultType<decltype(byVal)>; 233 return FoldElementalIntrinsic<T, T, TBY>(context, 234 std::move(funcRef), 235 ScalarFunc<T, T, TBY>( 236 [&](const Scalar<T> &x, const Scalar<TBY> &y) -> Scalar<T> { 237 ValueWithRealFlags<Scalar<T>> result{x. 238 // MSVC chokes on the keyword "template" here in a call to a 239 // member function template. 240 #ifndef _MSC_VER 241 template 242 #endif 243 SCALE(y)}; 244 if (result.flags.test(RealFlag::Overflow)) { 245 context.messages().Say( 246 "SCALE intrinsic folding overflow"_warn_en_US); 247 } 248 return result.value; 249 })); 250 }, 251 byExpr->u); 252 } 253 } else if (name == "sign") { 254 return FoldElementalIntrinsic<T, T, T>( 255 context, std::move(funcRef), &Scalar<T>::SIGN); 256 } else if (name == "spacing") { 257 return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 258 ScalarFunc<T, T>( 259 [](const Scalar<T> &x) -> Scalar<T> { return x.SPACING(); })); 260 } else if (name == "sqrt") { 261 return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 262 ScalarFunc<T, T>( 263 [](const Scalar<T> &x) -> Scalar<T> { return x.SQRT().value; })); 264 } else if (name == "sum") { 265 return FoldSum<T>(context, std::move(funcRef)); 266 } else if (name == "tiny") { 267 return Expr<T>{Scalar<T>::TINY()}; 268 } else if (name == "__builtin_ieee_next_after") { 269 if (const auto *yExpr{UnwrapExpr<Expr<SomeReal>>(args[1])}) { 270 return common::visit( 271 [&](const auto &yVal) { 272 using TY = ResultType<decltype(yVal)>; 273 return FoldElementalIntrinsic<T, T, TY>(context, std::move(funcRef), 274 ScalarFunc<T, T, TY>([&](const Scalar<T> &x, 275 const Scalar<TY> &y) -> Scalar<T> { 276 bool upward{true}; 277 switch (x.Compare(Scalar<T>::Convert(y).value)) { 278 case Relation::Unordered: 279 context.messages().Say( 280 "IEEE_NEXT_AFTER intrinsic folding: bad argument"_warn_en_US); 281 return x; 282 case Relation::Equal: 283 return x; 284 case Relation::Less: 285 upward = true; 286 break; 287 case Relation::Greater: 288 upward = false; 289 break; 290 } 291 auto result{x.NEAREST(upward)}; 292 if (result.flags.test(RealFlag::Overflow)) { 293 context.messages().Say( 294 "IEEE_NEXT_AFTER intrinsic folding overflow"_warn_en_US); 295 } 296 return result.value; 297 })); 298 }, 299 yExpr->u); 300 } 301 } else if (name == "__builtin_ieee_next_up" || 302 name == "__builtin_ieee_next_down") { 303 bool upward{name == "__builtin_ieee_next_up"}; 304 const char *iName{upward ? "IEEE_NEXT_UP" : "IEEE_NEXT_DOWN"}; 305 return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 306 ScalarFunc<T, T>([&](const Scalar<T> &x) -> Scalar<T> { 307 auto result{x.NEAREST(upward)}; 308 if (result.flags.test(RealFlag::Overflow)) { 309 context.messages().Say( 310 "%s intrinsic folding overflow"_warn_en_US, iName); 311 } else if (result.flags.test(RealFlag::InvalidArgument)) { 312 context.messages().Say( 313 "%s intrinsic folding: bad argument"_warn_en_US, iName); 314 } 315 return result.value; 316 })); 317 } 318 // TODO: dot_product, fraction, matmul, norm2, set_exponent 319 return Expr<T>{std::move(funcRef)}; 320 } 321 322 #ifdef _MSC_VER // disable bogus warning about missing definitions 323 #pragma warning(disable : 4661) 324 #endif 325 FOR_EACH_REAL_KIND(template class ExpressionBase, ) 326 template class ExpressionBase<SomeReal>; 327 } // namespace Fortran::evaluate 328