//===-- lib/Evaluate/fold-logical.cpp -------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "fold-implementation.h" #include "fold-reduction.h" #include "flang/Evaluate/check-expression.h" namespace Fortran::evaluate { // for ALL & ANY template static Expr FoldAllAny(FoldingContext &context, FunctionRef &&ref, Scalar (Scalar::*operation)(const Scalar &) const, Scalar identity) { static_assert(T::category == TypeCategory::Logical); using Element = Scalar; std::optional dim; if (std::optional> array{ ProcessReductionArgs(context, ref.arguments(), dim, identity, /*ARRAY(MASK)=*/0, /*DIM=*/1)}) { auto accumulator{[&](Element &element, const ConstantSubscripts &at) { element = (element.*operation)(array->At(at)); }}; return Expr{DoReduction(*array, dim, identity, accumulator)}; } return Expr{std::move(ref)}; } template Expr> FoldIntrinsicFunction( FoldingContext &context, FunctionRef> &&funcRef) { using T = Type; ActualArguments &args{funcRef.arguments()}; auto *intrinsic{std::get_if(&funcRef.proc().u)}; CHECK(intrinsic); std::string name{intrinsic->name}; using SameInt = Type; if (name == "all") { return FoldAllAny( context, std::move(funcRef), &Scalar::AND, Scalar{true}); } else if (name == "any") { return FoldAllAny( context, std::move(funcRef), &Scalar::OR, Scalar{false}); } else if (name == "associated") { bool gotConstant{true}; const Expr *firstArgExpr{args[0]->UnwrapExpr()}; if (!firstArgExpr || !IsNullPointer(*firstArgExpr)) { gotConstant = false; } else if (args[1]) { // There's a second argument const Expr *secondArgExpr{args[1]->UnwrapExpr()}; if (!secondArgExpr || !IsNullPointer(*secondArgExpr)) { gotConstant = false; } } return gotConstant ? Expr{false} : Expr{std::move(funcRef)}; } else if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") { static_assert(std::is_same_v, BOZLiteralConstant>); // Arguments do not have to be of the same integer type. Convert all // arguments to the biggest integer type before comparing them to // simplify. for (int i{0}; i <= 1; ++i) { if (auto *x{UnwrapExpr>(args[i])}) { *args[i] = AsGenericExpr( Fold(context, ConvertToType(std::move(*x)))); } else if (auto *x{UnwrapExpr(args[i])}) { *args[i] = AsGenericExpr(Constant{std::move(*x)}); } } auto fptr{&Scalar::BGE}; if (name == "bge") { // done in fptr declaration } else if (name == "bgt") { fptr = &Scalar::BGT; } else if (name == "ble") { fptr = &Scalar::BLE; } else if (name == "blt") { fptr = &Scalar::BLT; } else { common::die("missing case to fold intrinsic function %s", name.c_str()); } return FoldElementalIntrinsic(context, std::move(funcRef), ScalarFunc( [&fptr](const Scalar &i, const Scalar &j) { return Scalar{std::invoke(fptr, i, j)}; })); } else if (name == "btest") { if (const auto *ix{UnwrapExpr>(args[0])}) { return std::visit( [&](const auto &x) { using IT = ResultType; return FoldElementalIntrinsic(context, std::move(funcRef), ScalarFunc( [&](const Scalar &x, const Scalar &pos) { auto posVal{pos.ToInt64()}; if (posVal < 0 || posVal >= x.bits) { context.messages().Say( "POS=%jd out of range for BTEST"_err_en_US, static_cast(posVal)); } return Scalar{x.BTEST(posVal)}; })); }, ix->u); } } else if (name == "isnan" || name == "__builtin_ieee_is_nan") { // A warning about an invalid argument is discarded from converting // the argument of isnan() / IEEE_IS_NAN(). auto restorer{context.messages().DiscardMessages()}; using DefaultReal = Type; return FoldElementalIntrinsic(context, std::move(funcRef), ScalarFunc([](const Scalar &x) { return Scalar{x.IsNotANumber()}; })); } else if (name == "__builtin_ieee_is_negative") { auto restorer{context.messages().DiscardMessages()}; using DefaultReal = Type; return FoldElementalIntrinsic(context, std::move(funcRef), ScalarFunc([](const Scalar &x) { return Scalar{x.IsNegative()}; })); } else if (name == "__builtin_ieee_is_normal") { auto restorer{context.messages().DiscardMessages()}; using DefaultReal = Type; return FoldElementalIntrinsic(context, std::move(funcRef), ScalarFunc([](const Scalar &x) { return Scalar{x.IsNormal()}; })); } else if (name == "is_contiguous") { if (args.at(0)) { if (auto *expr{args[0]->UnwrapExpr()}) { if (IsSimplyContiguous(*expr, context)) { return Expr{true}; } } } } else if (name == "lge" || name == "lgt" || name == "lle" || name == "llt") { // Rewrite LGE/LGT/LLE/LLT into ASCII character relations auto *cx0{UnwrapExpr>(args[0])}; auto *cx1{UnwrapExpr>(args[1])}; if (cx0 && cx1) { return Fold(context, ConvertToType( PackageRelation(name == "lge" ? RelationalOperator::GE : name == "lgt" ? RelationalOperator::GT : name == "lle" ? RelationalOperator::LE : RelationalOperator::LT, ConvertToType(std::move(*cx0)), ConvertToType(std::move(*cx1))))); } } else if (name == "logical") { if (auto *expr{UnwrapExpr>(args[0])}) { return Fold(context, ConvertToType(std::move(*expr))); } } else if (name == "merge") { return FoldMerge(context, std::move(funcRef)); } else if (name == "__builtin_ieee_support_datatype" || name == "__builtin_ieee_support_denormal" || name == "__builtin_ieee_support_divide" || name == "__builtin_ieee_support_divide" || name == "__builtin_ieee_support_inf" || name == "__builtin_ieee_support_io" || name == "__builtin_ieee_support_nan" || name == "__builtin_ieee_support_sqrt" || name == "__builtin_ieee_support_standard" || name == "__builtin_ieee_support_subnormal" || name == "__builtin_ieee_support_underflow_control") { return Expr{true}; } // TODO: dot_product, is_iostat_end, // is_iostat_eor, logical, matmul, out_of_range, // parity, transfer return Expr{std::move(funcRef)}; } template Expr FoldOperation( FoldingContext &context, Relational &&relation) { if (auto array{ApplyElementwise(context, relation, std::function(Expr &&, Expr &&)>{ [=](Expr &&x, Expr &&y) { return Expr{Relational{ Relational{relation.opr, std::move(x), std::move(y)}}}; }})}) { return *array; } if (auto folded{OperandsAreConstants(relation)}) { bool result{}; if constexpr (T::category == TypeCategory::Integer) { result = Satisfies(relation.opr, folded->first.CompareSigned(folded->second)); } else if constexpr (T::category == TypeCategory::Real) { result = Satisfies(relation.opr, folded->first.Compare(folded->second)); } else if constexpr (T::category == TypeCategory::Complex) { result = (relation.opr == RelationalOperator::EQ) == folded->first.Equals(folded->second); } else if constexpr (T::category == TypeCategory::Character) { result = Satisfies(relation.opr, Compare(folded->first, folded->second)); } else { static_assert(T::category != TypeCategory::Logical); } return Expr{Constant{result}}; } return Expr{Relational{std::move(relation)}}; } Expr FoldOperation( FoldingContext &context, Relational &&relation) { return std::visit( [&](auto &&x) { return Expr{FoldOperation(context, std::move(x))}; }, std::move(relation.u)); } template Expr> FoldOperation( FoldingContext &context, Not &&x) { if (auto array{ApplyElementwise(context, x)}) { return *array; } using Ty = Type; auto &operand{x.left()}; if (auto value{GetScalarConstantValue(operand)}) { return Expr{Constant{!value->IsTrue()}}; } return Expr{x}; } template Expr> FoldOperation( FoldingContext &context, LogicalOperation &&operation) { using LOGICAL = Type; if (auto array{ApplyElementwise(context, operation, std::function(Expr &&, Expr &&)>{ [=](Expr &&x, Expr &&y) { return Expr{LogicalOperation{ operation.logicalOperator, std::move(x), std::move(y)}}; }})}) { return *array; } if (auto folded{OperandsAreConstants(operation)}) { bool xt{folded->first.IsTrue()}, yt{folded->second.IsTrue()}, result{}; switch (operation.logicalOperator) { case LogicalOperator::And: result = xt && yt; break; case LogicalOperator::Or: result = xt || yt; break; case LogicalOperator::Eqv: result = xt == yt; break; case LogicalOperator::Neqv: result = xt != yt; break; case LogicalOperator::Not: DIE("not a binary operator"); } return Expr{Constant{result}}; } return Expr{std::move(operation)}; } #ifdef _MSC_VER // disable bogus warning about missing definitions #pragma warning(disable : 4661) #endif FOR_EACH_LOGICAL_KIND(template class ExpressionBase, ) template class ExpressionBase; } // namespace Fortran::evaluate