164ab3302SCarolineConcatto //===-- lib/Evaluate/fold-logical.cpp -------------------------------------===// 264ab3302SCarolineConcatto // 364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information. 564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 664ab3302SCarolineConcatto // 764ab3302SCarolineConcatto //===----------------------------------------------------------------------===// 864ab3302SCarolineConcatto 964ab3302SCarolineConcatto #include "fold-implementation.h" 100fdf9123SPeter Klausler #include "fold-matmul.h" 11503c085eSpeter klausler #include "fold-reduction.h" 1264ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h" 1367f15e76SPeter Klausler #include "flang/Runtime/magic-numbers.h" 1464ab3302SCarolineConcatto 1564ab3302SCarolineConcatto namespace Fortran::evaluate { 1664ab3302SCarolineConcatto 177709f12eSTarun Prabhu template <typename T> 187709f12eSTarun Prabhu static std::optional<Expr<SomeType>> ZeroExtend(const Constant<T> &c) { 197709f12eSTarun Prabhu std::vector<Scalar<LargestInt>> exts; 207709f12eSTarun Prabhu for (const auto &v : c.values()) { 217709f12eSTarun Prabhu exts.push_back(Scalar<LargestInt>::ConvertUnsigned(v).value); 227709f12eSTarun Prabhu } 237709f12eSTarun Prabhu return AsGenericExpr( 247709f12eSTarun Prabhu Constant<LargestInt>(std::move(exts), ConstantSubscripts(c.shape()))); 257709f12eSTarun Prabhu } 267709f12eSTarun Prabhu 272f2fce8eSTarun Prabhu // for ALL, ANY & PARITY 28503c085eSpeter klausler template <typename T> 292f2fce8eSTarun Prabhu static Expr<T> FoldAllAnyParity(FoldingContext &context, FunctionRef<T> &&ref, 30503c085eSpeter klausler Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const, 31503c085eSpeter klausler Scalar<T> identity) { 32503c085eSpeter klausler static_assert(T::category == TypeCategory::Logical); 3382568675SPeter Klausler std::optional<int> dim; 3482e1e412SPeter Klausler if (std::optional<ArrayAndMask<T>> arrayAndMask{ 3582e1e412SPeter Klausler ProcessReductionArgs<T>(context, ref.arguments(), dim, 36503c085eSpeter klausler /*ARRAY(MASK)=*/0, /*DIM=*/1)}) { 3782e1e412SPeter Klausler OperationAccumulator accumulator{arrayAndMask->array, operation}; 3882e1e412SPeter Klausler return Expr<T>{DoReduction<T>( 3982e1e412SPeter Klausler arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)}; 40503c085eSpeter klausler } 41503c085eSpeter klausler return Expr<T>{std::move(ref)}; 42503c085eSpeter klausler } 43503c085eSpeter klausler 441444e5acSPeter Klausler // OUT_OF_RANGE(x,mold[,round]) references are entirely rewritten here into 451444e5acSPeter Klausler // expressions, which are then folded into constants when 'x' and 'round' 461444e5acSPeter Klausler // are constant. It is guaranteed that 'x' is evaluated at most once. 47fc97d2e6SPeter Klausler // TODO: unsigned 481444e5acSPeter Klausler 491444e5acSPeter Klausler template <int X_RKIND, int MOLD_IKIND> 501444e5acSPeter Klausler Expr<SomeReal> RealToIntBoundHelper(bool round, bool negate) { 511444e5acSPeter Klausler using RType = Type<TypeCategory::Real, X_RKIND>; 521444e5acSPeter Klausler using RealType = Scalar<RType>; 531444e5acSPeter Klausler using IntType = Scalar<Type<TypeCategory::Integer, MOLD_IKIND>>; 541444e5acSPeter Klausler RealType result{}; // 0. 551444e5acSPeter Klausler common::RoundingMode roundingMode{round 561444e5acSPeter Klausler ? common::RoundingMode::TiesAwayFromZero 571444e5acSPeter Klausler : common::RoundingMode::ToZero}; 581444e5acSPeter Klausler // Add decreasing powers of two to the result to find the largest magnitude 591444e5acSPeter Klausler // value that can be converted to the integer type without overflow. 601444e5acSPeter Klausler RealType at{RealType::FromInteger(IntType{negate ? -1 : 1}).value}; 611444e5acSPeter Klausler bool decrement{true}; 621444e5acSPeter Klausler while (!at.template ToInteger<IntType>(roundingMode) 631444e5acSPeter Klausler .flags.test(RealFlag::Overflow)) { 641444e5acSPeter Klausler auto tmp{at.SCALE(IntType{1})}; 651444e5acSPeter Klausler if (tmp.flags.test(RealFlag::Overflow)) { 661444e5acSPeter Klausler decrement = false; 671444e5acSPeter Klausler break; 681444e5acSPeter Klausler } 691444e5acSPeter Klausler at = tmp.value; 701444e5acSPeter Klausler } 711444e5acSPeter Klausler while (true) { 721444e5acSPeter Klausler if (decrement) { 731444e5acSPeter Klausler at = at.SCALE(IntType{-1}).value; 741444e5acSPeter Klausler } else { 751444e5acSPeter Klausler decrement = true; 761444e5acSPeter Klausler } 771444e5acSPeter Klausler auto tmp{at.Add(result)}; 781444e5acSPeter Klausler if (tmp.flags.test(RealFlag::Inexact)) { 791444e5acSPeter Klausler break; 801444e5acSPeter Klausler } else if (!tmp.value.template ToInteger<IntType>(roundingMode) 811444e5acSPeter Klausler .flags.test(RealFlag::Overflow)) { 821444e5acSPeter Klausler result = tmp.value; 831444e5acSPeter Klausler } 841444e5acSPeter Klausler } 851444e5acSPeter Klausler return AsCategoryExpr(Constant<RType>{std::move(result)}); 861444e5acSPeter Klausler } 871444e5acSPeter Klausler 881444e5acSPeter Klausler static Expr<SomeReal> RealToIntBound( 891444e5acSPeter Klausler int xRKind, int moldIKind, bool round, bool negate) { 901444e5acSPeter Klausler switch (xRKind) { 911444e5acSPeter Klausler #define ICASES(RK) \ 921444e5acSPeter Klausler switch (moldIKind) { \ 931444e5acSPeter Klausler case 1: \ 941444e5acSPeter Klausler return RealToIntBoundHelper<RK, 1>(round, negate); \ 951444e5acSPeter Klausler break; \ 961444e5acSPeter Klausler case 2: \ 971444e5acSPeter Klausler return RealToIntBoundHelper<RK, 2>(round, negate); \ 981444e5acSPeter Klausler break; \ 991444e5acSPeter Klausler case 4: \ 1001444e5acSPeter Klausler return RealToIntBoundHelper<RK, 4>(round, negate); \ 1011444e5acSPeter Klausler break; \ 1021444e5acSPeter Klausler case 8: \ 1031444e5acSPeter Klausler return RealToIntBoundHelper<RK, 8>(round, negate); \ 1041444e5acSPeter Klausler break; \ 1051444e5acSPeter Klausler case 16: \ 1061444e5acSPeter Klausler return RealToIntBoundHelper<RK, 16>(round, negate); \ 1071444e5acSPeter Klausler break; \ 1081444e5acSPeter Klausler } \ 1091444e5acSPeter Klausler break 1101444e5acSPeter Klausler case 2: 1111444e5acSPeter Klausler ICASES(2); 1121444e5acSPeter Klausler break; 1131444e5acSPeter Klausler case 3: 1141444e5acSPeter Klausler ICASES(3); 1151444e5acSPeter Klausler break; 1161444e5acSPeter Klausler case 4: 1171444e5acSPeter Klausler ICASES(4); 1181444e5acSPeter Klausler break; 1191444e5acSPeter Klausler case 8: 1201444e5acSPeter Klausler ICASES(8); 1211444e5acSPeter Klausler break; 1221444e5acSPeter Klausler case 10: 1231444e5acSPeter Klausler ICASES(10); 1241444e5acSPeter Klausler break; 1251444e5acSPeter Klausler case 16: 1261444e5acSPeter Klausler ICASES(16); 1271444e5acSPeter Klausler break; 1281444e5acSPeter Klausler } 1291444e5acSPeter Klausler DIE("RealToIntBound: no case"); 1301444e5acSPeter Klausler #undef ICASES 1311444e5acSPeter Klausler } 1321444e5acSPeter Klausler 1331444e5acSPeter Klausler class RealToIntLimitHelper { 1341444e5acSPeter Klausler public: 1351444e5acSPeter Klausler using Result = std::optional<Expr<SomeReal>>; 1361444e5acSPeter Klausler using Types = RealTypes; 1371444e5acSPeter Klausler RealToIntLimitHelper( 1381444e5acSPeter Klausler FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo) 1391444e5acSPeter Klausler : context_{context}, hi_{std::move(hi)}, lo_{lo} {} 1401444e5acSPeter Klausler template <typename T> Result Test() { 1411444e5acSPeter Klausler if (UnwrapExpr<Expr<T>>(hi_)) { 1421444e5acSPeter Klausler bool promote{T::kind < 16}; 1431444e5acSPeter Klausler Result constResult; 1441444e5acSPeter Klausler if (auto hiV{GetScalarConstantValue<T>(hi_)}) { 1451444e5acSPeter Klausler auto loV{GetScalarConstantValue<T>(lo_)}; 1461444e5acSPeter Klausler CHECK(loV.has_value()); 1471444e5acSPeter Klausler auto diff{hiV->Subtract(*loV, Rounding{common::RoundingMode::ToZero})}; 1481444e5acSPeter Klausler promote = promote && 1491444e5acSPeter Klausler (diff.flags.test(RealFlag::Overflow) || 1501444e5acSPeter Klausler diff.flags.test(RealFlag::Inexact)); 1511444e5acSPeter Klausler constResult = AsCategoryExpr(Constant<T>{std::move(diff.value)}); 1521444e5acSPeter Klausler } 1531444e5acSPeter Klausler if (promote) { 1541444e5acSPeter Klausler constexpr int nextKind{T::kind < 4 ? 4 : T::kind == 4 ? 8 : 16}; 1551444e5acSPeter Klausler using T2 = Type<TypeCategory::Real, nextKind>; 1561444e5acSPeter Klausler hi_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(hi_)))}; 1571444e5acSPeter Klausler lo_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(lo_)))}; 1581444e5acSPeter Klausler if (constResult) { 1591444e5acSPeter Klausler // Use promoted constants on next iteration of SearchTypes 1601444e5acSPeter Klausler return std::nullopt; 1611444e5acSPeter Klausler } 1621444e5acSPeter Klausler } 1631444e5acSPeter Klausler if (constResult) { 1641444e5acSPeter Klausler return constResult; 1651444e5acSPeter Klausler } else { 1661444e5acSPeter Klausler return AsCategoryExpr(std::move(hi_) - Expr<SomeReal>{lo_}); 1671444e5acSPeter Klausler } 1681444e5acSPeter Klausler } else { 1691444e5acSPeter Klausler return std::nullopt; 1701444e5acSPeter Klausler } 1711444e5acSPeter Klausler } 1721444e5acSPeter Klausler 1731444e5acSPeter Klausler private: 1741444e5acSPeter Klausler FoldingContext &context_; 1751444e5acSPeter Klausler Expr<SomeReal> hi_; 1761444e5acSPeter Klausler Expr<SomeReal> &lo_; 1771444e5acSPeter Klausler }; 1781444e5acSPeter Klausler 1791444e5acSPeter Klausler static std::optional<Expr<SomeReal>> RealToIntLimit( 1801444e5acSPeter Klausler FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo) { 1811444e5acSPeter Klausler return common::SearchTypes(RealToIntLimitHelper{context, std::move(hi), lo}); 1821444e5acSPeter Klausler } 1831444e5acSPeter Klausler 1841444e5acSPeter Klausler // RealToRealBounds() returns a pair (HUGE(x),REAL(HUGE(mold),KIND(x))) 1851444e5acSPeter Klausler // when REAL(HUGE(x),KIND(mold)) overflows, and std::nullopt otherwise. 1861444e5acSPeter Klausler template <int X_RKIND, int MOLD_RKIND> 1871444e5acSPeter Klausler std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>> 1881444e5acSPeter Klausler RealToRealBoundsHelper() { 1891444e5acSPeter Klausler using RType = Type<TypeCategory::Real, X_RKIND>; 1901444e5acSPeter Klausler using RealType = Scalar<RType>; 1911444e5acSPeter Klausler using MoldRealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>; 1921444e5acSPeter Klausler if (!MoldRealType::Convert(RealType::HUGE()).flags.test(RealFlag::Overflow)) { 1931444e5acSPeter Klausler return std::nullopt; 1941444e5acSPeter Klausler } else { 1951444e5acSPeter Klausler return std::make_pair(AsCategoryExpr(Constant<RType>{ 1961444e5acSPeter Klausler RealType::Convert(MoldRealType::HUGE()).value}), 1971444e5acSPeter Klausler AsCategoryExpr(Constant<RType>{RealType::HUGE()})); 1981444e5acSPeter Klausler } 1991444e5acSPeter Klausler } 2001444e5acSPeter Klausler 2011444e5acSPeter Klausler static std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>> 2021444e5acSPeter Klausler RealToRealBounds(int xRKind, int moldRKind) { 2031444e5acSPeter Klausler switch (xRKind) { 2041444e5acSPeter Klausler #define RCASES(RK) \ 2051444e5acSPeter Klausler switch (moldRKind) { \ 2061444e5acSPeter Klausler case 2: \ 2071444e5acSPeter Klausler return RealToRealBoundsHelper<RK, 2>(); \ 2081444e5acSPeter Klausler break; \ 2091444e5acSPeter Klausler case 3: \ 2101444e5acSPeter Klausler return RealToRealBoundsHelper<RK, 3>(); \ 2111444e5acSPeter Klausler break; \ 2121444e5acSPeter Klausler case 4: \ 2131444e5acSPeter Klausler return RealToRealBoundsHelper<RK, 4>(); \ 2141444e5acSPeter Klausler break; \ 2151444e5acSPeter Klausler case 8: \ 2161444e5acSPeter Klausler return RealToRealBoundsHelper<RK, 8>(); \ 2171444e5acSPeter Klausler break; \ 2181444e5acSPeter Klausler case 10: \ 2191444e5acSPeter Klausler return RealToRealBoundsHelper<RK, 10>(); \ 2201444e5acSPeter Klausler break; \ 2211444e5acSPeter Klausler case 16: \ 2221444e5acSPeter Klausler return RealToRealBoundsHelper<RK, 16>(); \ 2231444e5acSPeter Klausler break; \ 2241444e5acSPeter Klausler } \ 2251444e5acSPeter Klausler break 2261444e5acSPeter Klausler case 2: 2271444e5acSPeter Klausler RCASES(2); 2281444e5acSPeter Klausler break; 2291444e5acSPeter Klausler case 3: 2301444e5acSPeter Klausler RCASES(3); 2311444e5acSPeter Klausler break; 2321444e5acSPeter Klausler case 4: 2331444e5acSPeter Klausler RCASES(4); 2341444e5acSPeter Klausler break; 2351444e5acSPeter Klausler case 8: 2361444e5acSPeter Klausler RCASES(8); 2371444e5acSPeter Klausler break; 2381444e5acSPeter Klausler case 10: 2391444e5acSPeter Klausler RCASES(10); 2401444e5acSPeter Klausler break; 2411444e5acSPeter Klausler case 16: 2421444e5acSPeter Klausler RCASES(16); 2431444e5acSPeter Klausler break; 2441444e5acSPeter Klausler } 2451444e5acSPeter Klausler DIE("RealToRealBounds: no case"); 2461444e5acSPeter Klausler #undef RCASES 2471444e5acSPeter Klausler } 2481444e5acSPeter Klausler 2491444e5acSPeter Klausler template <int X_IKIND, int MOLD_RKIND> 2501444e5acSPeter Klausler std::optional<Expr<SomeInteger>> IntToRealBoundHelper(bool negate) { 2511444e5acSPeter Klausler using IType = Type<TypeCategory::Integer, X_IKIND>; 2521444e5acSPeter Klausler using IntType = Scalar<IType>; 2531444e5acSPeter Klausler using RealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>; 2541444e5acSPeter Klausler IntType result{}; // 0 2551444e5acSPeter Klausler while (true) { 2561444e5acSPeter Klausler std::optional<IntType> next; 2571444e5acSPeter Klausler for (int bit{0}; bit < IntType::bits; ++bit) { 2581444e5acSPeter Klausler IntType power{IntType{}.IBSET(bit)}; 2591444e5acSPeter Klausler if (power.IsNegative()) { 2601444e5acSPeter Klausler if (!negate) { 2611444e5acSPeter Klausler break; 2621444e5acSPeter Klausler } 2631444e5acSPeter Klausler } else if (negate) { 2641444e5acSPeter Klausler power = power.Negate().value; 2651444e5acSPeter Klausler } 2661444e5acSPeter Klausler auto tmp{power.AddSigned(result)}; 2671444e5acSPeter Klausler if (tmp.overflow || 2681444e5acSPeter Klausler RealType::FromInteger(tmp.value).flags.test(RealFlag::Overflow)) { 2691444e5acSPeter Klausler break; 2701444e5acSPeter Klausler } 2711444e5acSPeter Klausler next = tmp.value; 2721444e5acSPeter Klausler } 2731444e5acSPeter Klausler if (next) { 2741444e5acSPeter Klausler CHECK(result.CompareSigned(*next) != Ordering::Equal); 2751444e5acSPeter Klausler result = *next; 2761444e5acSPeter Klausler } else { 2771444e5acSPeter Klausler break; 2781444e5acSPeter Klausler } 2791444e5acSPeter Klausler } 2801444e5acSPeter Klausler if (result.CompareSigned(IntType::HUGE()) == Ordering::Equal) { 2811444e5acSPeter Klausler return std::nullopt; 2821444e5acSPeter Klausler } else { 2831444e5acSPeter Klausler return AsCategoryExpr(Constant<IType>{std::move(result)}); 2841444e5acSPeter Klausler } 2851444e5acSPeter Klausler } 2861444e5acSPeter Klausler 2871444e5acSPeter Klausler static std::optional<Expr<SomeInteger>> IntToRealBound( 2881444e5acSPeter Klausler int xIKind, int moldRKind, bool negate) { 2891444e5acSPeter Klausler switch (xIKind) { 2901444e5acSPeter Klausler #define RCASES(IK) \ 2911444e5acSPeter Klausler switch (moldRKind) { \ 2921444e5acSPeter Klausler case 2: \ 2931444e5acSPeter Klausler return IntToRealBoundHelper<IK, 2>(negate); \ 2941444e5acSPeter Klausler break; \ 2951444e5acSPeter Klausler case 3: \ 2961444e5acSPeter Klausler return IntToRealBoundHelper<IK, 3>(negate); \ 2971444e5acSPeter Klausler break; \ 2981444e5acSPeter Klausler case 4: \ 2991444e5acSPeter Klausler return IntToRealBoundHelper<IK, 4>(negate); \ 3001444e5acSPeter Klausler break; \ 3011444e5acSPeter Klausler case 8: \ 3021444e5acSPeter Klausler return IntToRealBoundHelper<IK, 8>(negate); \ 3031444e5acSPeter Klausler break; \ 3041444e5acSPeter Klausler case 10: \ 3051444e5acSPeter Klausler return IntToRealBoundHelper<IK, 10>(negate); \ 3061444e5acSPeter Klausler break; \ 3071444e5acSPeter Klausler case 16: \ 3081444e5acSPeter Klausler return IntToRealBoundHelper<IK, 16>(negate); \ 3091444e5acSPeter Klausler break; \ 3101444e5acSPeter Klausler } \ 3111444e5acSPeter Klausler break 3121444e5acSPeter Klausler case 1: 3131444e5acSPeter Klausler RCASES(1); 3141444e5acSPeter Klausler break; 3151444e5acSPeter Klausler case 2: 3161444e5acSPeter Klausler RCASES(2); 3171444e5acSPeter Klausler break; 3181444e5acSPeter Klausler case 4: 3191444e5acSPeter Klausler RCASES(4); 3201444e5acSPeter Klausler break; 3211444e5acSPeter Klausler case 8: 3221444e5acSPeter Klausler RCASES(8); 3231444e5acSPeter Klausler break; 3241444e5acSPeter Klausler case 16: 3251444e5acSPeter Klausler RCASES(16); 3261444e5acSPeter Klausler break; 3271444e5acSPeter Klausler } 3281444e5acSPeter Klausler DIE("IntToRealBound: no case"); 3291444e5acSPeter Klausler #undef RCASES 3301444e5acSPeter Klausler } 3311444e5acSPeter Klausler 3321444e5acSPeter Klausler template <int X_IKIND, int MOLD_IKIND> 3331444e5acSPeter Klausler std::optional<Expr<SomeInteger>> IntToIntBoundHelper() { 3341444e5acSPeter Klausler if constexpr (X_IKIND <= MOLD_IKIND) { 3351444e5acSPeter Klausler return std::nullopt; 3361444e5acSPeter Klausler } else { 3371444e5acSPeter Klausler using XIType = Type<TypeCategory::Integer, X_IKIND>; 3381444e5acSPeter Klausler using IntegerType = Scalar<XIType>; 3391444e5acSPeter Klausler using MoldIType = Type<TypeCategory::Integer, MOLD_IKIND>; 3401444e5acSPeter Klausler using MoldIntegerType = Scalar<MoldIType>; 3411444e5acSPeter Klausler return AsCategoryExpr(Constant<XIType>{ 3421444e5acSPeter Klausler IntegerType::ConvertSigned(MoldIntegerType::HUGE()).value}); 3431444e5acSPeter Klausler } 3441444e5acSPeter Klausler } 3451444e5acSPeter Klausler 3461444e5acSPeter Klausler static std::optional<Expr<SomeInteger>> IntToIntBound( 3471444e5acSPeter Klausler int xIKind, int moldIKind) { 3481444e5acSPeter Klausler switch (xIKind) { 3491444e5acSPeter Klausler #define ICASES(IK) \ 3501444e5acSPeter Klausler switch (moldIKind) { \ 3511444e5acSPeter Klausler case 1: \ 3521444e5acSPeter Klausler return IntToIntBoundHelper<IK, 1>(); \ 3531444e5acSPeter Klausler break; \ 3541444e5acSPeter Klausler case 2: \ 3551444e5acSPeter Klausler return IntToIntBoundHelper<IK, 2>(); \ 3561444e5acSPeter Klausler break; \ 3571444e5acSPeter Klausler case 4: \ 3581444e5acSPeter Klausler return IntToIntBoundHelper<IK, 4>(); \ 3591444e5acSPeter Klausler break; \ 3601444e5acSPeter Klausler case 8: \ 3611444e5acSPeter Klausler return IntToIntBoundHelper<IK, 8>(); \ 3621444e5acSPeter Klausler break; \ 3631444e5acSPeter Klausler case 16: \ 3641444e5acSPeter Klausler return IntToIntBoundHelper<IK, 16>(); \ 3651444e5acSPeter Klausler break; \ 3661444e5acSPeter Klausler } \ 3671444e5acSPeter Klausler break 3681444e5acSPeter Klausler case 1: 3691444e5acSPeter Klausler ICASES(1); 3701444e5acSPeter Klausler break; 3711444e5acSPeter Klausler case 2: 3721444e5acSPeter Klausler ICASES(2); 3731444e5acSPeter Klausler break; 3741444e5acSPeter Klausler case 4: 3751444e5acSPeter Klausler ICASES(4); 3761444e5acSPeter Klausler break; 3771444e5acSPeter Klausler case 8: 3781444e5acSPeter Klausler ICASES(8); 3791444e5acSPeter Klausler break; 3801444e5acSPeter Klausler case 16: 3811444e5acSPeter Klausler ICASES(16); 3821444e5acSPeter Klausler break; 3831444e5acSPeter Klausler } 3841444e5acSPeter Klausler DIE("IntToIntBound: no case"); 3851444e5acSPeter Klausler #undef ICASES 3861444e5acSPeter Klausler } 3871444e5acSPeter Klausler 3881444e5acSPeter Klausler // ApplyIntrinsic() constructs the typed expression representation 3891444e5acSPeter Klausler // for a specific intrinsic function reference. 3901444e5acSPeter Klausler // TODO: maybe move into tools.h? 3911444e5acSPeter Klausler class IntrinsicCallHelper { 3921444e5acSPeter Klausler public: 3931444e5acSPeter Klausler explicit IntrinsicCallHelper(SpecificCall &&call) : call_{call} { 3941444e5acSPeter Klausler CHECK(proc_.IsFunction()); 3951444e5acSPeter Klausler typeAndShape_ = proc_.functionResult->GetTypeAndShape(); 3961444e5acSPeter Klausler CHECK(typeAndShape_ != nullptr); 3971444e5acSPeter Klausler } 3981444e5acSPeter Klausler using Result = std::optional<Expr<SomeType>>; 3991444e5acSPeter Klausler using Types = LengthlessIntrinsicTypes; 4001444e5acSPeter Klausler template <typename T> Result Test() { 4011444e5acSPeter Klausler if (T::category == typeAndShape_->type().category() && 4021444e5acSPeter Klausler T::kind == typeAndShape_->type().kind()) { 4031444e5acSPeter Klausler return AsGenericExpr(FunctionRef<T>{ 4041444e5acSPeter Klausler ProcedureDesignator{std::move(call_.specificIntrinsic)}, 4051444e5acSPeter Klausler std::move(call_.arguments)}); 4061444e5acSPeter Klausler } else { 4071444e5acSPeter Klausler return std::nullopt; 4081444e5acSPeter Klausler } 4091444e5acSPeter Klausler } 4101444e5acSPeter Klausler 4111444e5acSPeter Klausler private: 4121444e5acSPeter Klausler SpecificCall call_; 4131444e5acSPeter Klausler const characteristics::Procedure &proc_{ 4141444e5acSPeter Klausler call_.specificIntrinsic.characteristics.value()}; 4151444e5acSPeter Klausler const characteristics::TypeAndShape *typeAndShape_{nullptr}; 4161444e5acSPeter Klausler }; 4171444e5acSPeter Klausler 4181444e5acSPeter Klausler static Expr<SomeType> ApplyIntrinsic( 4191444e5acSPeter Klausler FoldingContext &context, const std::string &func, ActualArguments &&args) { 4201444e5acSPeter Klausler auto found{ 4211444e5acSPeter Klausler context.intrinsics().Probe(CallCharacteristics{func}, args, context)}; 4221444e5acSPeter Klausler CHECK(found.has_value()); 4231444e5acSPeter Klausler auto result{common::SearchTypes(IntrinsicCallHelper{std::move(*found)})}; 4241444e5acSPeter Klausler CHECK(result.has_value()); 4251444e5acSPeter Klausler return *result; 4261444e5acSPeter Klausler } 4271444e5acSPeter Klausler 4281444e5acSPeter Klausler static Expr<LogicalResult> CompareUnsigned(FoldingContext &context, 4291444e5acSPeter Klausler const char *intrin, Expr<SomeType> &&x, Expr<SomeType> &&y) { 4301444e5acSPeter Klausler Expr<SomeType> result{ApplyIntrinsic(context, intrin, 4311444e5acSPeter Klausler ActualArguments{ 4321444e5acSPeter Klausler ActualArgument{std::move(x)}, ActualArgument{std::move(y)}})}; 4331444e5acSPeter Klausler return DEREF(UnwrapExpr<Expr<LogicalResult>>(result)); 4341444e5acSPeter Klausler } 4351444e5acSPeter Klausler 4361444e5acSPeter Klausler // Determines the right kind of INTEGER to hold the bits of a REAL type. 4371444e5acSPeter Klausler static Expr<SomeType> IntTransferMold( 4381444e5acSPeter Klausler const TargetCharacteristics &target, DynamicType realType, bool asVector) { 4391444e5acSPeter Klausler CHECK(realType.category() == TypeCategory::Real); 4401444e5acSPeter Klausler int rKind{realType.kind()}; 4411444e5acSPeter Klausler int iKind{std::max<int>(target.GetAlignment(TypeCategory::Real, rKind), 4421444e5acSPeter Klausler target.GetByteSize(TypeCategory::Real, rKind))}; 4431444e5acSPeter Klausler CHECK(target.CanSupportType(TypeCategory::Integer, iKind)); 4441444e5acSPeter Klausler DynamicType iType{TypeCategory::Integer, iKind}; 4451444e5acSPeter Klausler ConstantSubscripts shape; 4461444e5acSPeter Klausler if (asVector) { 4471444e5acSPeter Klausler shape = ConstantSubscripts{1}; 4481444e5acSPeter Klausler } 4491444e5acSPeter Klausler Constant<SubscriptInteger> value{ 4501444e5acSPeter Klausler std::vector<Scalar<SubscriptInteger>>{0}, std::move(shape)}; 4511444e5acSPeter Klausler auto expr{ConvertToType(iType, AsGenericExpr(std::move(value)))}; 4521444e5acSPeter Klausler CHECK(expr.has_value()); 4531444e5acSPeter Klausler return std::move(*expr); 4541444e5acSPeter Klausler } 4551444e5acSPeter Klausler 4561444e5acSPeter Klausler static Expr<SomeType> GetRealBits(FoldingContext &context, Expr<SomeReal> &&x) { 4571444e5acSPeter Klausler auto xType{x.GetType()}; 4581444e5acSPeter Klausler CHECK(xType.has_value()); 4591444e5acSPeter Klausler bool asVector{x.Rank() > 0}; 4601444e5acSPeter Klausler return ApplyIntrinsic(context, "transfer", 4611444e5acSPeter Klausler ActualArguments{ActualArgument{AsGenericExpr(std::move(x))}, 4621444e5acSPeter Klausler ActualArgument{IntTransferMold( 4631444e5acSPeter Klausler context.targetCharacteristics(), *xType, asVector)}}); 4641444e5acSPeter Klausler } 4651444e5acSPeter Klausler 4661444e5acSPeter Klausler template <int KIND> 4671444e5acSPeter Klausler static Expr<Type<TypeCategory::Logical, KIND>> RewriteOutOfRange( 4681444e5acSPeter Klausler FoldingContext &context, 4691444e5acSPeter Klausler FunctionRef<Type<TypeCategory::Logical, KIND>> &&funcRef) { 4701444e5acSPeter Klausler using ResultType = Type<TypeCategory::Logical, KIND>; 4711444e5acSPeter Klausler ActualArguments &args{funcRef.arguments()}; 4721444e5acSPeter Klausler // Fold x= and round= unconditionally 4731444e5acSPeter Klausler if (auto *x{UnwrapExpr<Expr<SomeType>>(args[0])}) { 4741444e5acSPeter Klausler *args[0] = Fold(context, std::move(*x)); 4751444e5acSPeter Klausler } 4761444e5acSPeter Klausler if (args.size() >= 3) { 4771444e5acSPeter Klausler if (auto *round{UnwrapExpr<Expr<SomeType>>(args[2])}) { 4781444e5acSPeter Klausler *args[2] = Fold(context, std::move(*round)); 4791444e5acSPeter Klausler } 4801444e5acSPeter Klausler } 4811444e5acSPeter Klausler if (auto *x{UnwrapExpr<Expr<SomeType>>(args[0])}) { 4821444e5acSPeter Klausler x = UnwrapExpr<Expr<SomeType>>(args[0]); 4831444e5acSPeter Klausler CHECK(x != nullptr); 4841444e5acSPeter Klausler if (const auto *mold{UnwrapExpr<Expr<SomeType>>(args[1])}) { 4851444e5acSPeter Klausler DynamicType xType{x->GetType().value()}; 4861444e5acSPeter Klausler std::optional<Expr<LogicalResult>> result; 4871444e5acSPeter Klausler bool alwaysFalse{false}; 4881444e5acSPeter Klausler if (auto *iXExpr{UnwrapExpr<Expr<SomeInteger>>(*x)}) { 4891444e5acSPeter Klausler int iXKind{iXExpr->GetType().value().kind()}; 4901444e5acSPeter Klausler if (auto *iMoldExpr{UnwrapExpr<Expr<SomeInteger>>(*mold)}) { 4911444e5acSPeter Klausler // INTEGER -> INTEGER 4921444e5acSPeter Klausler int iMoldKind{iMoldExpr->GetType().value().kind()}; 4931444e5acSPeter Klausler if (auto hi{IntToIntBound(iXKind, iMoldKind)}) { 4941444e5acSPeter Klausler // 'hi' is INT(HUGE(mold), KIND(x)) 4951444e5acSPeter Klausler // OUT_OF_RANGE(x,mold) = (x + (hi + 1)) .UGT. (2*hi + 1) 4961444e5acSPeter Klausler auto one{DEREF(UnwrapExpr<Expr<SomeInteger>>(ConvertToType( 4971444e5acSPeter Klausler xType, AsGenericExpr(Constant<SubscriptInteger>{1}))))}; 4981444e5acSPeter Klausler auto lhs{std::move(*iXExpr) + 4991444e5acSPeter Klausler (Expr<SomeInteger>{*hi} + Expr<SomeInteger>{one})}; 5001444e5acSPeter Klausler auto two{DEREF(UnwrapExpr<Expr<SomeInteger>>(ConvertToType( 5011444e5acSPeter Klausler xType, AsGenericExpr(Constant<SubscriptInteger>{2}))))}; 5021444e5acSPeter Klausler auto rhs{std::move(two) * std::move(*hi) + std::move(one)}; 5031444e5acSPeter Klausler result = CompareUnsigned(context, "bgt", 5041444e5acSPeter Klausler Expr<SomeType>{std::move(lhs)}, Expr<SomeType>{std::move(rhs)}); 5051444e5acSPeter Klausler } else { 5061444e5acSPeter Klausler alwaysFalse = true; 5071444e5acSPeter Klausler } 5081444e5acSPeter Klausler } else if (auto *rMoldExpr{UnwrapExpr<Expr<SomeReal>>(*mold)}) { 5091444e5acSPeter Klausler // INTEGER -> REAL 5101444e5acSPeter Klausler int rMoldKind{rMoldExpr->GetType().value().kind()}; 5111444e5acSPeter Klausler if (auto hi{IntToRealBound(iXKind, rMoldKind, /*negate=*/false)}) { 5121444e5acSPeter Klausler // OUT_OF_RANGE(x,mold) = (x - lo) .UGT. (hi - lo) 5131444e5acSPeter Klausler auto lo{IntToRealBound(iXKind, rMoldKind, /*negate=*/true)}; 5141444e5acSPeter Klausler CHECK(lo.has_value()); 5151444e5acSPeter Klausler auto lhs{std::move(*iXExpr) - Expr<SomeInteger>{*lo}}; 5161444e5acSPeter Klausler auto rhs{std::move(*hi) - std::move(*lo)}; 5171444e5acSPeter Klausler result = CompareUnsigned(context, "bgt", 5181444e5acSPeter Klausler Expr<SomeType>{std::move(lhs)}, Expr<SomeType>{std::move(rhs)}); 5191444e5acSPeter Klausler } else { 5201444e5acSPeter Klausler alwaysFalse = true; 5211444e5acSPeter Klausler } 5221444e5acSPeter Klausler } 5231444e5acSPeter Klausler } else if (auto *rXExpr{UnwrapExpr<Expr<SomeReal>>(*x)}) { 5241444e5acSPeter Klausler int rXKind{rXExpr->GetType().value().kind()}; 5251444e5acSPeter Klausler if (auto *iMoldExpr{UnwrapExpr<Expr<SomeInteger>>(*mold)}) { 5261444e5acSPeter Klausler // REAL -> INTEGER 5271444e5acSPeter Klausler int iMoldKind{iMoldExpr->GetType().value().kind()}; 5281444e5acSPeter Klausler auto hi{RealToIntBound(rXKind, iMoldKind, false, false)}; 5291444e5acSPeter Klausler auto lo{RealToIntBound(rXKind, iMoldKind, false, true)}; 5301444e5acSPeter Klausler if (args.size() >= 3) { 5311444e5acSPeter Klausler // Bounds depend on round= value 5321444e5acSPeter Klausler if (auto *round{UnwrapExpr<Expr<SomeType>>(args[2])}) { 5331444e5acSPeter Klausler if (const Symbol * whole{UnwrapWholeSymbolDataRef(*round)}; 534505f6da1SPeter Klausler whole && semantics::IsOptional(whole->GetUltimate()) && 535505f6da1SPeter Klausler context.languageFeatures().ShouldWarn( 536505f6da1SPeter Klausler common::UsageWarning::OptionalMustBePresent)) { 5371444e5acSPeter Klausler if (auto source{args[2]->sourceLocation()}) { 5380f973ac7SPeter Klausler context.messages().Say( 5390f973ac7SPeter Klausler common::UsageWarning::OptionalMustBePresent, *source, 5401444e5acSPeter Klausler "ROUND= argument to OUT_OF_RANGE() is an optional dummy argument that must be present at execution"_warn_en_US); 5411444e5acSPeter Klausler } 5421444e5acSPeter Klausler } 5431444e5acSPeter Klausler auto rlo{RealToIntBound(rXKind, iMoldKind, true, true)}; 5441444e5acSPeter Klausler auto rhi{RealToIntBound(rXKind, iMoldKind, true, false)}; 5451444e5acSPeter Klausler auto mlo{Fold(context, 5461444e5acSPeter Klausler ApplyIntrinsic(context, "merge", 5471444e5acSPeter Klausler ActualArguments{ 5481444e5acSPeter Klausler ActualArgument{Expr<SomeType>{std::move(rlo)}}, 5491444e5acSPeter Klausler ActualArgument{Expr<SomeType>{std::move(lo)}}, 5501444e5acSPeter Klausler ActualArgument{Expr<SomeType>{*round}}}))}; 5511444e5acSPeter Klausler auto mhi{Fold(context, 5521444e5acSPeter Klausler ApplyIntrinsic(context, "merge", 5531444e5acSPeter Klausler ActualArguments{ 5541444e5acSPeter Klausler ActualArgument{Expr<SomeType>{std::move(rhi)}}, 5551444e5acSPeter Klausler ActualArgument{Expr<SomeType>{std::move(hi)}}, 5561444e5acSPeter Klausler ActualArgument{std::move(*round)}}))}; 5571444e5acSPeter Klausler lo = std::move(DEREF(UnwrapExpr<Expr<SomeReal>>(mlo))); 5581444e5acSPeter Klausler hi = std::move(DEREF(UnwrapExpr<Expr<SomeReal>>(mhi))); 5591444e5acSPeter Klausler } 5601444e5acSPeter Klausler } 5611444e5acSPeter Klausler // OUT_OF_RANGE(x,mold[,round]) = 5621444e5acSPeter Klausler // TRANSFER(x - lo, int) .UGT. TRANSFER(hi - lo, int) 5631444e5acSPeter Klausler hi = Fold(context, std::move(hi)); 5641444e5acSPeter Klausler lo = Fold(context, std::move(lo)); 5651444e5acSPeter Klausler if (auto rhs{RealToIntLimit(context, std::move(hi), lo)}) { 5661444e5acSPeter Klausler Expr<SomeReal> lhs{std::move(*rXExpr) - std::move(lo)}; 5671444e5acSPeter Klausler result = CompareUnsigned(context, "bgt", 5681444e5acSPeter Klausler GetRealBits(context, std::move(lhs)), 5691444e5acSPeter Klausler GetRealBits(context, std::move(*rhs))); 5701444e5acSPeter Klausler } 5711444e5acSPeter Klausler } else if (auto *rMoldExpr{UnwrapExpr<Expr<SomeReal>>(*mold)}) { 5721444e5acSPeter Klausler // REAL -> REAL 5731444e5acSPeter Klausler // Only finite arguments with ABS(x) > HUGE(mold) are .TRUE. 5741444e5acSPeter Klausler // OUT_OF_RANGE(x,mold) = 5751444e5acSPeter Klausler // TRANSFER(ABS(x) - HUGE(mold), int) - 1 .ULT. 5761444e5acSPeter Klausler // TRANSFER(HUGE(mold), int) 5771444e5acSPeter Klausler // Note that OUT_OF_RANGE(+/-Inf or NaN,mold) = 5781444e5acSPeter Klausler // TRANSFER(+Inf or Nan, int) - 1 .ULT. TRANSFER(HUGE(mold), int) 5791444e5acSPeter Klausler int rMoldKind{rMoldExpr->GetType().value().kind()}; 5801444e5acSPeter Klausler if (auto bounds{RealToRealBounds(rXKind, rMoldKind)}) { 5811444e5acSPeter Klausler auto &[moldHuge, xHuge]{*bounds}; 5821444e5acSPeter Klausler Expr<SomeType> abs{ApplyIntrinsic(context, "abs", 5831444e5acSPeter Klausler ActualArguments{ 5841444e5acSPeter Klausler ActualArgument{Expr<SomeType>{std::move(*rXExpr)}}})}; 5851444e5acSPeter Klausler auto &absR{DEREF(UnwrapExpr<Expr<SomeReal>>(abs))}; 5861444e5acSPeter Klausler Expr<SomeType> diffBits{ 5871444e5acSPeter Klausler GetRealBits(context, std::move(absR) - std::move(moldHuge))}; 5881444e5acSPeter Klausler auto &diffBitsI{DEREF(UnwrapExpr<Expr<SomeInteger>>(diffBits))}; 5891444e5acSPeter Klausler Expr<SomeType> decr{std::move(diffBitsI) - 5901444e5acSPeter Klausler Expr<SomeInteger>{Expr<SubscriptInteger>{1}}}; 5911444e5acSPeter Klausler result = CompareUnsigned(context, "blt", std::move(decr), 5921444e5acSPeter Klausler GetRealBits(context, std::move(xHuge))); 5931444e5acSPeter Klausler } else { 5941444e5acSPeter Klausler alwaysFalse = true; 5951444e5acSPeter Klausler } 5961444e5acSPeter Klausler } 5971444e5acSPeter Klausler } 5981444e5acSPeter Klausler if (alwaysFalse) { 5991444e5acSPeter Klausler // xType can never overflow moldType, so 6001444e5acSPeter Klausler // OUT_OF_RANGE(x) = (x /= 0) .AND. .FALSE. 6011444e5acSPeter Klausler // which has the same shape as x. 6021444e5acSPeter Klausler Expr<LogicalResult> scalarFalse{ 6031444e5acSPeter Klausler Constant<LogicalResult>{Scalar<LogicalResult>{false}}}; 6041444e5acSPeter Klausler if (x->Rank() > 0) { 6051444e5acSPeter Klausler if (auto nez{Relate(context.messages(), RelationalOperator::NE, 6061444e5acSPeter Klausler std::move(*x), 6071444e5acSPeter Klausler AsGenericExpr(Constant<SubscriptInteger>{0}))}) { 6081444e5acSPeter Klausler result = Expr<LogicalResult>{LogicalOperation<LogicalResult::kind>{ 6091444e5acSPeter Klausler LogicalOperator::And, std::move(*nez), std::move(scalarFalse)}}; 6101444e5acSPeter Klausler } 6111444e5acSPeter Klausler } else { 6121444e5acSPeter Klausler result = std::move(scalarFalse); 6131444e5acSPeter Klausler } 6141444e5acSPeter Klausler } 6151444e5acSPeter Klausler if (result) { 6161444e5acSPeter Klausler auto restorer{context.messages().DiscardMessages()}; 6171444e5acSPeter Klausler return Fold( 6181444e5acSPeter Klausler context, AsExpr(ConvertToType<ResultType>(std::move(*result)))); 6191444e5acSPeter Klausler } 6201444e5acSPeter Klausler } 6211444e5acSPeter Klausler } 6221444e5acSPeter Klausler return AsExpr(std::move(funcRef)); 6231444e5acSPeter Klausler } 6241444e5acSPeter Klausler 6254b57fe65SPeter Klausler static std::optional<common::RoundingMode> GetRoundingMode( 6264b57fe65SPeter Klausler const std::optional<ActualArgument> &arg) { 6274b57fe65SPeter Klausler if (arg) { 6284b57fe65SPeter Klausler if (const auto *cst{UnwrapExpr<Constant<SomeDerived>>(*arg)}) { 6294b57fe65SPeter Klausler if (auto constr{cst->GetScalarValue()}) { 6304b57fe65SPeter Klausler if (StructureConstructorValues & values{constr->values()}; 6314b57fe65SPeter Klausler values.size() == 1) { 6324b57fe65SPeter Klausler const Expr<SomeType> &value{values.begin()->second.value()}; 6334b57fe65SPeter Klausler if (auto code{ToInt64(value)}) { 6344b57fe65SPeter Klausler return static_cast<common::RoundingMode>(*code); 6354b57fe65SPeter Klausler } 6364b57fe65SPeter Klausler } 6374b57fe65SPeter Klausler } 6384b57fe65SPeter Klausler } 6394b57fe65SPeter Klausler } 6404b57fe65SPeter Klausler return std::nullopt; 6414b57fe65SPeter Klausler } 6424b57fe65SPeter Klausler 64364ab3302SCarolineConcatto template <int KIND> 64464ab3302SCarolineConcatto Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction( 64564ab3302SCarolineConcatto FoldingContext &context, 64664ab3302SCarolineConcatto FunctionRef<Type<TypeCategory::Logical, KIND>> &&funcRef) { 64764ab3302SCarolineConcatto using T = Type<TypeCategory::Logical, KIND>; 64864ab3302SCarolineConcatto ActualArguments &args{funcRef.arguments()}; 64964ab3302SCarolineConcatto auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 65064ab3302SCarolineConcatto CHECK(intrinsic); 65164ab3302SCarolineConcatto std::string name{intrinsic->name}; 65264ab3302SCarolineConcatto if (name == "all") { 6532f2fce8eSTarun Prabhu return FoldAllAnyParity( 654503c085eSpeter klausler context, std::move(funcRef), &Scalar<T>::AND, Scalar<T>{true}); 65564ab3302SCarolineConcatto } else if (name == "any") { 6562f2fce8eSTarun Prabhu return FoldAllAnyParity( 657503c085eSpeter klausler context, std::move(funcRef), &Scalar<T>::OR, Scalar<T>{false}); 658c7574188SPeter Steinfeld } else if (name == "associated") { 659c7574188SPeter Steinfeld bool gotConstant{true}; 660c7574188SPeter Steinfeld const Expr<SomeType> *firstArgExpr{args[0]->UnwrapExpr()}; 661c7574188SPeter Steinfeld if (!firstArgExpr || !IsNullPointer(*firstArgExpr)) { 662c7574188SPeter Steinfeld gotConstant = false; 663c7574188SPeter Steinfeld } else if (args[1]) { // There's a second argument 664c7574188SPeter Steinfeld const Expr<SomeType> *secondArgExpr{args[1]->UnwrapExpr()}; 665c7574188SPeter Steinfeld if (!secondArgExpr || !IsNullPointer(*secondArgExpr)) { 666c7574188SPeter Steinfeld gotConstant = false; 667c7574188SPeter Steinfeld } 668c7574188SPeter Steinfeld } 669c7574188SPeter Steinfeld return gotConstant ? Expr<T>{false} : Expr<T>{std::move(funcRef)}; 67064ab3302SCarolineConcatto } else if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") { 67164ab3302SCarolineConcatto static_assert(std::is_same_v<Scalar<LargestInt>, BOZLiteralConstant>); 6727709f12eSTarun Prabhu 6737709f12eSTarun Prabhu // The arguments to these intrinsics can be of different types. In that 6747709f12eSTarun Prabhu // case, the shorter of the two would need to be zero-extended to match 6757709f12eSTarun Prabhu // the size of the other. If at least one of the operands is not a constant, 6767709f12eSTarun Prabhu // the zero-extending will be done during lowering. Otherwise, the folding 6777709f12eSTarun Prabhu // must be done here. 6787709f12eSTarun Prabhu std::optional<Expr<SomeType>> constArgs[2]; 6797709f12eSTarun Prabhu for (int i{0}; i <= 1; i++) { 6807709f12eSTarun Prabhu if (BOZLiteralConstant * x{UnwrapExpr<BOZLiteralConstant>(args[i])}) { 6817709f12eSTarun Prabhu constArgs[i] = AsGenericExpr(Constant<LargestInt>{std::move(*x)}); 6827709f12eSTarun Prabhu } else if (auto *x{UnwrapExpr<Expr<SomeInteger>>(args[i])}) { 6837709f12eSTarun Prabhu common::visit( 6847709f12eSTarun Prabhu [&](const auto &ix) { 6857709f12eSTarun Prabhu using IntT = typename std::decay_t<decltype(ix)>::Result; 6867709f12eSTarun Prabhu if (auto *c{UnwrapConstantValue<IntT>(ix)}) { 6877709f12eSTarun Prabhu constArgs[i] = ZeroExtend(*c); 6887709f12eSTarun Prabhu } 6897709f12eSTarun Prabhu }, 6907709f12eSTarun Prabhu x->u); 69164ab3302SCarolineConcatto } 69264ab3302SCarolineConcatto } 6937709f12eSTarun Prabhu 6947709f12eSTarun Prabhu if (constArgs[0] && constArgs[1]) { 69564ab3302SCarolineConcatto auto fptr{&Scalar<LargestInt>::BGE}; 69664ab3302SCarolineConcatto if (name == "bge") { // done in fptr declaration 69764ab3302SCarolineConcatto } else if (name == "bgt") { 69864ab3302SCarolineConcatto fptr = &Scalar<LargestInt>::BGT; 69964ab3302SCarolineConcatto } else if (name == "ble") { 70064ab3302SCarolineConcatto fptr = &Scalar<LargestInt>::BLE; 70164ab3302SCarolineConcatto } else if (name == "blt") { 70264ab3302SCarolineConcatto fptr = &Scalar<LargestInt>::BLT; 70364ab3302SCarolineConcatto } else { 70464ab3302SCarolineConcatto common::die("missing case to fold intrinsic function %s", name.c_str()); 70564ab3302SCarolineConcatto } 7067709f12eSTarun Prabhu 7077709f12eSTarun Prabhu for (int i{0}; i <= 1; i++) { 7087709f12eSTarun Prabhu *args[i] = std::move(constArgs[i].value()); 7097709f12eSTarun Prabhu } 7107709f12eSTarun Prabhu 71164ab3302SCarolineConcatto return FoldElementalIntrinsic<T, LargestInt, LargestInt>(context, 71264ab3302SCarolineConcatto std::move(funcRef), 71364ab3302SCarolineConcatto ScalarFunc<T, LargestInt, LargestInt>( 7147709f12eSTarun Prabhu [&fptr]( 7157709f12eSTarun Prabhu const Scalar<LargestInt> &i, const Scalar<LargestInt> &j) { 71664ab3302SCarolineConcatto return Scalar<T>{std::invoke(fptr, i, j)}; 71764ab3302SCarolineConcatto })); 7187709f12eSTarun Prabhu } else { 7197709f12eSTarun Prabhu return Expr<T>{std::move(funcRef)}; 7207709f12eSTarun Prabhu } 7212f80b73eSpeter klausler } else if (name == "btest") { 722fc97d2e6SPeter Klausler using SameInt = Type<TypeCategory::Integer, KIND>; 7232f80b73eSpeter klausler if (const auto *ix{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 724cd03e96fSPeter Klausler return common::visit( 7252f80b73eSpeter klausler [&](const auto &x) { 7262f80b73eSpeter klausler using IT = ResultType<decltype(x)>; 7272f80b73eSpeter klausler return FoldElementalIntrinsic<T, IT, SameInt>(context, 7282f80b73eSpeter klausler std::move(funcRef), 7292f80b73eSpeter klausler ScalarFunc<T, IT, SameInt>( 7302f80b73eSpeter klausler [&](const Scalar<IT> &x, const Scalar<SameInt> &pos) { 7312f80b73eSpeter klausler auto posVal{pos.ToInt64()}; 7322f80b73eSpeter klausler if (posVal < 0 || posVal >= x.bits) { 7332f80b73eSpeter klausler context.messages().Say( 7342f80b73eSpeter klausler "POS=%jd out of range for BTEST"_err_en_US, 7352f80b73eSpeter klausler static_cast<std::intmax_t>(posVal)); 7362f80b73eSpeter klausler } 7372f80b73eSpeter klausler return Scalar<T>{x.BTEST(posVal)}; 7382f80b73eSpeter klausler })); 7392f80b73eSpeter klausler }, 7402f80b73eSpeter klausler ix->u); 741fc97d2e6SPeter Klausler } else if (const auto *ux{UnwrapExpr<Expr<SomeUnsigned>>(args[0])}) { 742fc97d2e6SPeter Klausler return common::visit( 743fc97d2e6SPeter Klausler [&](const auto &x) { 744fc97d2e6SPeter Klausler using UT = ResultType<decltype(x)>; 745fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, UT, SameInt>(context, 746fc97d2e6SPeter Klausler std::move(funcRef), 747fc97d2e6SPeter Klausler ScalarFunc<T, UT, SameInt>( 748fc97d2e6SPeter Klausler [&](const Scalar<UT> &x, const Scalar<SameInt> &pos) { 749fc97d2e6SPeter Klausler auto posVal{pos.ToInt64()}; 750fc97d2e6SPeter Klausler if (posVal < 0 || posVal >= x.bits) { 751fc97d2e6SPeter Klausler context.messages().Say( 752fc97d2e6SPeter Klausler "POS=%jd out of range for BTEST"_err_en_US, 753fc97d2e6SPeter Klausler static_cast<std::intmax_t>(posVal)); 754fc97d2e6SPeter Klausler } 755fc97d2e6SPeter Klausler return Scalar<T>{x.BTEST(posVal)}; 756fc97d2e6SPeter Klausler })); 757fc97d2e6SPeter Klausler }, 758fc97d2e6SPeter Klausler ux->u); 7592f80b73eSpeter klausler } 760e723c69bSPeter Klausler } else if (name == "dot_product") { 761e723c69bSPeter Klausler return FoldDotProduct<T>(context, std::move(funcRef)); 762460fc79aSPeter Klausler } else if (name == "extends_type_of") { 763460fc79aSPeter Klausler // Type extension testing with EXTENDS_TYPE_OF() ignores any type 764460fc79aSPeter Klausler // parameters. Returns a constant truth value when the result is known now. 765460fc79aSPeter Klausler if (args[0] && args[1]) { 766460fc79aSPeter Klausler auto t0{args[0]->GetType()}; 767460fc79aSPeter Klausler auto t1{args[1]->GetType()}; 768460fc79aSPeter Klausler if (t0 && t1) { 769460fc79aSPeter Klausler if (auto result{t0->ExtendsTypeOf(*t1)}) { 770460fc79aSPeter Klausler return Expr<T>{*result}; 771460fc79aSPeter Klausler } 772460fc79aSPeter Klausler } 773460fc79aSPeter Klausler } 77471d868cfSpeter klausler } else if (name == "isnan" || name == "__builtin_ieee_is_nan") { 7754ee8aef4SDavid Truby // Only replace the type of the function if we can do the fold 7764ee8aef4SDavid Truby if (args[0] && args[0]->UnwrapExpr() && 7774ee8aef4SDavid Truby IsActuallyConstant(*args[0]->UnwrapExpr())) { 77808bfc002SPeter Klausler auto restorer{context.messages().DiscardMessages()}; 77908bfc002SPeter Klausler using DefaultReal = Type<TypeCategory::Real, 4>; 78029fa4518Speter klausler return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef), 78129fa4518Speter klausler ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) { 78229fa4518Speter klausler return Scalar<T>{x.IsNotANumber()}; 78329fa4518Speter klausler })); 7844ee8aef4SDavid Truby } 785d3f5ef24SYury Gribov } else if (name == "__builtin_ieee_is_negative") { 786d3f5ef24SYury Gribov auto restorer{context.messages().DiscardMessages()}; 787d3f5ef24SYury Gribov using DefaultReal = Type<TypeCategory::Real, 4>; 78809ea692dSV Donaldson if (args[0] && args[0]->UnwrapExpr() && 78909ea692dSV Donaldson IsActuallyConstant(*args[0]->UnwrapExpr())) { 790d3f5ef24SYury Gribov return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef), 791d3f5ef24SYury Gribov ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) { 792d3f5ef24SYury Gribov return Scalar<T>{x.IsNegative()}; 793d3f5ef24SYury Gribov })); 79409ea692dSV Donaldson } 795d3f5ef24SYury Gribov } else if (name == "__builtin_ieee_is_normal") { 796d3f5ef24SYury Gribov auto restorer{context.messages().DiscardMessages()}; 797d3f5ef24SYury Gribov using DefaultReal = Type<TypeCategory::Real, 4>; 79875ddabd5SDavid Truby if (args[0] && args[0]->UnwrapExpr() && 79975ddabd5SDavid Truby IsActuallyConstant(*args[0]->UnwrapExpr())) { 800d3f5ef24SYury Gribov return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef), 801d3f5ef24SYury Gribov ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) { 802d3f5ef24SYury Gribov return Scalar<T>{x.IsNormal()}; 803d3f5ef24SYury Gribov })); 80475ddabd5SDavid Truby } 80564ab3302SCarolineConcatto } else if (name == "is_contiguous") { 80664ab3302SCarolineConcatto if (args.at(0)) { 80764ab3302SCarolineConcatto if (auto *expr{args[0]->UnwrapExpr()}) { 80894896994SPeter Klausler if (auto contiguous{IsContiguous(*expr, context)}) { 80994896994SPeter Klausler return Expr<T>{*contiguous}; 81064ab3302SCarolineConcatto } 8116f5df419SJean Perier } else if (auto *assumedType{args[0]->GetAssumedTypeDummy()}) { 8126f5df419SJean Perier if (auto contiguous{IsContiguous(*assumedType, context)}) { 8136f5df419SJean Perier return Expr<T>{*contiguous}; 8146f5df419SJean Perier } 81564ab3302SCarolineConcatto } 81664ab3302SCarolineConcatto } 81767f15e76SPeter Klausler } else if (name == "is_iostat_end") { 81867f15e76SPeter Klausler if (args[0] && args[0]->UnwrapExpr() && 81967f15e76SPeter Klausler IsActuallyConstant(*args[0]->UnwrapExpr())) { 82067f15e76SPeter Klausler using Int64 = Type<TypeCategory::Integer, 8>; 82167f15e76SPeter Klausler return FoldElementalIntrinsic<T, Int64>(context, std::move(funcRef), 82267f15e76SPeter Klausler ScalarFunc<T, Int64>([](const Scalar<Int64> &x) { 82367f15e76SPeter Klausler return Scalar<T>{x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_END}; 82467f15e76SPeter Klausler })); 82567f15e76SPeter Klausler } 82667f15e76SPeter Klausler } else if (name == "is_iostat_eor") { 82767f15e76SPeter Klausler if (args[0] && args[0]->UnwrapExpr() && 82867f15e76SPeter Klausler IsActuallyConstant(*args[0]->UnwrapExpr())) { 82967f15e76SPeter Klausler using Int64 = Type<TypeCategory::Integer, 8>; 83067f15e76SPeter Klausler return FoldElementalIntrinsic<T, Int64>(context, std::move(funcRef), 83167f15e76SPeter Klausler ScalarFunc<T, Int64>([](const Scalar<Int64> &x) { 83267f15e76SPeter Klausler return Scalar<T>{x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_EOR}; 83367f15e76SPeter Klausler })); 83467f15e76SPeter Klausler } 8356965a776Speter klausler } else if (name == "lge" || name == "lgt" || name == "lle" || name == "llt") { 8366965a776Speter klausler // Rewrite LGE/LGT/LLE/LLT into ASCII character relations 8376965a776Speter klausler auto *cx0{UnwrapExpr<Expr<SomeCharacter>>(args[0])}; 8386965a776Speter klausler auto *cx1{UnwrapExpr<Expr<SomeCharacter>>(args[1])}; 8396965a776Speter klausler if (cx0 && cx1) { 8406965a776Speter klausler return Fold(context, 8416965a776Speter klausler ConvertToType<T>( 8426965a776Speter klausler PackageRelation(name == "lge" ? RelationalOperator::GE 8436965a776Speter klausler : name == "lgt" ? RelationalOperator::GT 8446965a776Speter klausler : name == "lle" ? RelationalOperator::LE 8456965a776Speter klausler : RelationalOperator::LT, 8466965a776Speter klausler ConvertToType<Ascii>(std::move(*cx0)), 8476965a776Speter klausler ConvertToType<Ascii>(std::move(*cx1))))); 8486965a776Speter klausler } 84927899112SJean Perier } else if (name == "logical") { 85027899112SJean Perier if (auto *expr{UnwrapExpr<Expr<SomeLogical>>(args[0])}) { 85127899112SJean Perier return Fold(context, ConvertToType<T>(std::move(*expr))); 85227899112SJean Perier } 8530fdf9123SPeter Klausler } else if (name == "matmul") { 8540fdf9123SPeter Klausler return FoldMatmul(context, std::move(funcRef)); 85554784b18SPeter Klausler } else if (name == "out_of_range") { 8561444e5acSPeter Klausler return RewriteOutOfRange<KIND>(context, std::move(funcRef)); 8572f2fce8eSTarun Prabhu } else if (name == "parity") { 8582f2fce8eSTarun Prabhu return FoldAllAnyParity( 8592f2fce8eSTarun Prabhu context, std::move(funcRef), &Scalar<T>::NEQV, Scalar<T>{false}); 860460fc79aSPeter Klausler } else if (name == "same_type_as") { 861460fc79aSPeter Klausler // Type equality testing with SAME_TYPE_AS() ignores any type parameters. 862460fc79aSPeter Klausler // Returns a constant truth value when the result is known now. 863460fc79aSPeter Klausler if (args[0] && args[1]) { 864460fc79aSPeter Klausler auto t0{args[0]->GetType()}; 865460fc79aSPeter Klausler auto t1{args[1]->GetType()}; 866460fc79aSPeter Klausler if (t0 && t1) { 867460fc79aSPeter Klausler if (auto result{t0->SameTypeAs(*t1)}) { 868460fc79aSPeter Klausler return Expr<T>{*result}; 869460fc79aSPeter Klausler } 870460fc79aSPeter Klausler } 871460fc79aSPeter Klausler } 8724b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_datatype") { 873aa39ddd0Speter klausler return Expr<T>{true}; 8744b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_denormal") { 8754b57fe65SPeter Klausler return Expr<T>{context.targetCharacteristics().ieeeFeatures().test( 8764b57fe65SPeter Klausler IeeeFeature::Denormal)}; 8774b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_divide") { 8784b57fe65SPeter Klausler return Expr<T>{context.targetCharacteristics().ieeeFeatures().test( 8794b57fe65SPeter Klausler IeeeFeature::Divide)}; 8804b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_flag") { 8814b57fe65SPeter Klausler return Expr<T>{context.targetCharacteristics().ieeeFeatures().test( 8824b57fe65SPeter Klausler IeeeFeature::Flags)}; 8834b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_halting") { 884*c28a7c1eSvdonaldson if (!context.targetCharacteristics() 885*c28a7c1eSvdonaldson .haltingSupportIsUnknownAtCompileTime()) { 8864b57fe65SPeter Klausler return Expr<T>{context.targetCharacteristics().ieeeFeatures().test( 8874b57fe65SPeter Klausler IeeeFeature::Halting)}; 888*c28a7c1eSvdonaldson } 8894b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_inf") { 8904b57fe65SPeter Klausler return Expr<T>{ 8914b57fe65SPeter Klausler context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Inf)}; 8924b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_io") { 8934b57fe65SPeter Klausler return Expr<T>{ 8944b57fe65SPeter Klausler context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Io)}; 8954b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_nan") { 8964b57fe65SPeter Klausler return Expr<T>{ 8974b57fe65SPeter Klausler context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::NaN)}; 8984b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_rounding") { 8994b57fe65SPeter Klausler if (context.targetCharacteristics().ieeeFeatures().test( 9004b57fe65SPeter Klausler IeeeFeature::Rounding)) { 9014b57fe65SPeter Klausler if (auto mode{GetRoundingMode(args[0])}) { 9024b57fe65SPeter Klausler return Expr<T>{mode != common::RoundingMode::TiesAwayFromZero}; 9034b57fe65SPeter Klausler } 9044b57fe65SPeter Klausler } 9054b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_sqrt") { 9064b57fe65SPeter Klausler return Expr<T>{ 9074b57fe65SPeter Klausler context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Sqrt)}; 9084b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_standard") { 9094b57fe65SPeter Klausler return Expr<T>{context.targetCharacteristics().ieeeFeatures().test( 9104b57fe65SPeter Klausler IeeeFeature::Standard)}; 9114b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_subnormal") { 9124b57fe65SPeter Klausler return Expr<T>{context.targetCharacteristics().ieeeFeatures().test( 9134b57fe65SPeter Klausler IeeeFeature::Subnormal)}; 9144b57fe65SPeter Klausler } else if (name == "__builtin_ieee_support_underflow_control") { 9156003be7eSvdonaldson // Setting kind=0 checks subnormal flushing control across all type kinds. 9166003be7eSvdonaldson if (args[0]) { 9176003be7eSvdonaldson return Expr<T>{ 9186003be7eSvdonaldson context.targetCharacteristics().hasSubnormalFlushingControl( 9196003be7eSvdonaldson args[0]->GetType().value().kind())}; 9206003be7eSvdonaldson } else { 9216003be7eSvdonaldson return Expr<T>{ 9226003be7eSvdonaldson context.targetCharacteristics().hasSubnormalFlushingControl( 9236003be7eSvdonaldson /*any=*/false)}; 9246003be7eSvdonaldson } 92564ab3302SCarolineConcatto } 92664ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 92764ab3302SCarolineConcatto } 92864ab3302SCarolineConcatto 92964ab3302SCarolineConcatto template <typename T> 93064ab3302SCarolineConcatto Expr<LogicalResult> FoldOperation( 93164ab3302SCarolineConcatto FoldingContext &context, Relational<T> &&relation) { 93264ab3302SCarolineConcatto if (auto array{ApplyElementwise(context, relation, 93364ab3302SCarolineConcatto std::function<Expr<LogicalResult>(Expr<T> &&, Expr<T> &&)>{ 93464ab3302SCarolineConcatto [=](Expr<T> &&x, Expr<T> &&y) { 93564ab3302SCarolineConcatto return Expr<LogicalResult>{Relational<SomeType>{ 93664ab3302SCarolineConcatto Relational<T>{relation.opr, std::move(x), std::move(y)}}}; 93764ab3302SCarolineConcatto }})}) { 93864ab3302SCarolineConcatto return *array; 93964ab3302SCarolineConcatto } 94064ab3302SCarolineConcatto if (auto folded{OperandsAreConstants(relation)}) { 94164ab3302SCarolineConcatto bool result{}; 94264ab3302SCarolineConcatto if constexpr (T::category == TypeCategory::Integer) { 94364ab3302SCarolineConcatto result = 94464ab3302SCarolineConcatto Satisfies(relation.opr, folded->first.CompareSigned(folded->second)); 945fc97d2e6SPeter Klausler } else if constexpr (T::category == TypeCategory::Unsigned) { 946fc97d2e6SPeter Klausler result = Satisfies( 947fc97d2e6SPeter Klausler relation.opr, folded->first.CompareUnsigned(folded->second)); 94864ab3302SCarolineConcatto } else if constexpr (T::category == TypeCategory::Real) { 94964ab3302SCarolineConcatto result = Satisfies(relation.opr, folded->first.Compare(folded->second)); 950df62afd5Speter klausler } else if constexpr (T::category == TypeCategory::Complex) { 951df62afd5Speter klausler result = (relation.opr == RelationalOperator::EQ) == 952df62afd5Speter klausler folded->first.Equals(folded->second); 95364ab3302SCarolineConcatto } else if constexpr (T::category == TypeCategory::Character) { 95464ab3302SCarolineConcatto result = Satisfies(relation.opr, Compare(folded->first, folded->second)); 95564ab3302SCarolineConcatto } else { 956df62afd5Speter klausler static_assert(T::category != TypeCategory::Logical); 95764ab3302SCarolineConcatto } 95864ab3302SCarolineConcatto return Expr<LogicalResult>{Constant<LogicalResult>{result}}; 95964ab3302SCarolineConcatto } 96064ab3302SCarolineConcatto return Expr<LogicalResult>{Relational<SomeType>{std::move(relation)}}; 96164ab3302SCarolineConcatto } 96264ab3302SCarolineConcatto 96364ab3302SCarolineConcatto Expr<LogicalResult> FoldOperation( 96464ab3302SCarolineConcatto FoldingContext &context, Relational<SomeType> &&relation) { 965cd03e96fSPeter Klausler return common::visit( 96664ab3302SCarolineConcatto [&](auto &&x) { 96764ab3302SCarolineConcatto return Expr<LogicalResult>{FoldOperation(context, std::move(x))}; 96864ab3302SCarolineConcatto }, 96964ab3302SCarolineConcatto std::move(relation.u)); 97064ab3302SCarolineConcatto } 97164ab3302SCarolineConcatto 97264ab3302SCarolineConcatto template <int KIND> 97364ab3302SCarolineConcatto Expr<Type<TypeCategory::Logical, KIND>> FoldOperation( 97464ab3302SCarolineConcatto FoldingContext &context, Not<KIND> &&x) { 97564ab3302SCarolineConcatto if (auto array{ApplyElementwise(context, x)}) { 97664ab3302SCarolineConcatto return *array; 97764ab3302SCarolineConcatto } 97864ab3302SCarolineConcatto using Ty = Type<TypeCategory::Logical, KIND>; 97964ab3302SCarolineConcatto auto &operand{x.left()}; 98064ab3302SCarolineConcatto if (auto value{GetScalarConstantValue<Ty>(operand)}) { 98164ab3302SCarolineConcatto return Expr<Ty>{Constant<Ty>{!value->IsTrue()}}; 98264ab3302SCarolineConcatto } 98364ab3302SCarolineConcatto return Expr<Ty>{x}; 98464ab3302SCarolineConcatto } 98564ab3302SCarolineConcatto 98664ab3302SCarolineConcatto template <int KIND> 98764ab3302SCarolineConcatto Expr<Type<TypeCategory::Logical, KIND>> FoldOperation( 98864ab3302SCarolineConcatto FoldingContext &context, LogicalOperation<KIND> &&operation) { 98964ab3302SCarolineConcatto using LOGICAL = Type<TypeCategory::Logical, KIND>; 99064ab3302SCarolineConcatto if (auto array{ApplyElementwise(context, operation, 99164ab3302SCarolineConcatto std::function<Expr<LOGICAL>(Expr<LOGICAL> &&, Expr<LOGICAL> &&)>{ 99264ab3302SCarolineConcatto [=](Expr<LOGICAL> &&x, Expr<LOGICAL> &&y) { 99364ab3302SCarolineConcatto return Expr<LOGICAL>{LogicalOperation<KIND>{ 99464ab3302SCarolineConcatto operation.logicalOperator, std::move(x), std::move(y)}}; 99564ab3302SCarolineConcatto }})}) { 99664ab3302SCarolineConcatto return *array; 99764ab3302SCarolineConcatto } 99864ab3302SCarolineConcatto if (auto folded{OperandsAreConstants(operation)}) { 99964ab3302SCarolineConcatto bool xt{folded->first.IsTrue()}, yt{folded->second.IsTrue()}, result{}; 100064ab3302SCarolineConcatto switch (operation.logicalOperator) { 10011f879005STim Keith case LogicalOperator::And: 10021f879005STim Keith result = xt && yt; 10031f879005STim Keith break; 10041f879005STim Keith case LogicalOperator::Or: 10051f879005STim Keith result = xt || yt; 10061f879005STim Keith break; 10071f879005STim Keith case LogicalOperator::Eqv: 10081f879005STim Keith result = xt == yt; 10091f879005STim Keith break; 10101f879005STim Keith case LogicalOperator::Neqv: 10111f879005STim Keith result = xt != yt; 10121f879005STim Keith break; 10131f879005STim Keith case LogicalOperator::Not: 10141f879005STim Keith DIE("not a binary operator"); 101564ab3302SCarolineConcatto } 101664ab3302SCarolineConcatto return Expr<LOGICAL>{Constant<LOGICAL>{result}}; 101764ab3302SCarolineConcatto } 101864ab3302SCarolineConcatto return Expr<LOGICAL>{std::move(operation)}; 101964ab3302SCarolineConcatto } 102064ab3302SCarolineConcatto 10215c5bde1bSPeter Klausler #ifdef _MSC_VER // disable bogus warning about missing definitions 10225c5bde1bSPeter Klausler #pragma warning(disable : 4661) 10235c5bde1bSPeter Klausler #endif 102464ab3302SCarolineConcatto FOR_EACH_LOGICAL_KIND(template class ExpressionBase, ) 102564ab3302SCarolineConcatto template class ExpressionBase<SomeLogical>; 10261f879005STim Keith } // namespace Fortran::evaluate 1027