llvm-project/flang/lib/Evaluate/fold-logical.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

227 lines
8.5 KiB
C++
Raw Normal View History

//===-- 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 "flang/Evaluate/check-expression.h"
namespace Fortran::evaluate {
template <int KIND>
Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
FoldingContext &context,
FunctionRef<Type<TypeCategory::Logical, KIND>> &&funcRef) {
using T = Type<TypeCategory::Logical, KIND>;
ActualArguments &args{funcRef.arguments()};
auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
CHECK(intrinsic);
std::string name{intrinsic->name};
if (name == "all") {
if (!args[1]) { // TODO: ALL(x,DIM=d)
if (const auto *constant{UnwrapConstantValue<T>(args[0])}) {
bool result{true};
for (const auto &element : constant->values()) {
if (!element.IsTrue()) {
result = false;
break;
}
}
return Expr<T>{result};
}
}
} else if (name == "any") {
if (!args[1]) { // TODO: ANY(x,DIM=d)
if (const auto *constant{UnwrapConstantValue<T>(args[0])}) {
bool result{false};
for (const auto &element : constant->values()) {
if (element.IsTrue()) {
result = true;
break;
}
}
return Expr<T>{result};
}
}
} else if (name == "associated") {
bool gotConstant{true};
const Expr<SomeType> *firstArgExpr{args[0]->UnwrapExpr()};
if (!firstArgExpr || !IsNullPointer(*firstArgExpr)) {
gotConstant = false;
} else if (args[1]) { // There's a second argument
const Expr<SomeType> *secondArgExpr{args[1]->UnwrapExpr()};
if (!secondArgExpr || !IsNullPointer(*secondArgExpr)) {
gotConstant = false;
}
}
return gotConstant ? Expr<T>{false} : Expr<T>{std::move(funcRef)};
} else if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") {
using LargestInt = Type<TypeCategory::Integer, 16>;
static_assert(std::is_same_v<Scalar<LargestInt>, 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<Expr<SomeInteger>>(args[i])}) {
*args[i] = AsGenericExpr(
Fold(context, ConvertToType<LargestInt>(std::move(*x))));
} else if (auto *x{UnwrapExpr<BOZLiteralConstant>(args[i])}) {
*args[i] = AsGenericExpr(Constant<LargestInt>{std::move(*x)});
}
}
auto fptr{&Scalar<LargestInt>::BGE};
if (name == "bge") { // done in fptr declaration
} else if (name == "bgt") {
fptr = &Scalar<LargestInt>::BGT;
} else if (name == "ble") {
fptr = &Scalar<LargestInt>::BLE;
} else if (name == "blt") {
fptr = &Scalar<LargestInt>::BLT;
} else {
common::die("missing case to fold intrinsic function %s", name.c_str());
}
return FoldElementalIntrinsic<T, LargestInt, LargestInt>(context,
std::move(funcRef),
ScalarFunc<T, LargestInt, LargestInt>(
[&fptr](const Scalar<LargestInt> &i, const Scalar<LargestInt> &j) {
return Scalar<T>{std::invoke(fptr, i, j)};
}));
} else if (name == "isnan") {
// A warning about an invalid argument is discarded from converting
// the argument of isnan().
auto restorer{context.messages().DiscardMessages()};
using DefaultReal = Type<TypeCategory::Real, 4>;
return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef),
ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) {
return Scalar<T>{x.IsNotANumber()};
}));
} else if (name == "is_contiguous") {
if (args.at(0)) {
if (auto *expr{args[0]->UnwrapExpr()}) {
[flang] Improve initializer semantics, esp. for component default values This patch plugs many holes in static initializer semantics, improves error messages for default initial values and other component properties in parameterized derived type instantiations, and cleans up several small issues noticed during development. We now do proper scalar expansion, folding, and type, rank, and shape conformance checking for component default initializers in derived types and PDT instantiations. The initial values of named constants are now guaranteed to have been folded when installed in the symbol table, and are no longer folded or scalar-expanded at each use in expression folding. Semantics documentation was extended with information about the various kinds of initializations in Fortran and when each of them are processed in the compiler. Some necessary concomitant changes have bulked this patch out a bit: * contextual messages attachments, which are now produced for parameterized derived type instantiations so that the user can figure out which instance caused a problem with a component, have been added as part of ContextualMessages, and their implementation was debugged * several APIs in evaluate::characteristics was changed so that a FoldingContext is passed as an argument rather than just its intrinsic procedure table; this affected client call sites in many files * new tools in Evaluate/check-expression.cpp to determine when an Expr actually is a single constant value and to validate a non-pointer variable initializer or object component default value * shape conformance checking has additional arguments that control whether scalar expansion is allowed * several now-unused functions and data members noticed and removed * several crashes and bogus errors exposed by testing this new code were fixed * a -fdebug-stack-trace option to enable LLVM's stack tracing on a crash, which might be useful in the future TL;DR: Initialization processing does more and takes place at the right times for all of the various kinds of things that can be initialized. Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
if (IsSimplyContiguous(*expr, context)) {
return Expr<T>{true};
}
}
}
} else if (name == "logical") {
if (auto *expr{UnwrapExpr<Expr<SomeLogical>>(args[0])}) {
return Fold(context, ConvertToType<T>(std::move(*expr)));
}
} else if (name == "merge") {
return FoldMerge<T>(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<T>{true};
}
// TODO: btest, cshift, dot_product, eoshift, is_iostat_end,
// is_iostat_eor, lge, lgt, lle, llt, logical, matmul, out_of_range,
// pack, parity, reduce, spread, transfer, transpose, unpack,
// extends_type_of, same_type_as
return Expr<T>{std::move(funcRef)};
}
template <typename T>
Expr<LogicalResult> FoldOperation(
FoldingContext &context, Relational<T> &&relation) {
if (auto array{ApplyElementwise(context, relation,
std::function<Expr<LogicalResult>(Expr<T> &&, Expr<T> &&)>{
[=](Expr<T> &&x, Expr<T> &&y) {
return Expr<LogicalResult>{Relational<SomeType>{
Relational<T>{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<LogicalResult>{Constant<LogicalResult>{result}};
}
return Expr<LogicalResult>{Relational<SomeType>{std::move(relation)}};
}
Expr<LogicalResult> FoldOperation(
FoldingContext &context, Relational<SomeType> &&relation) {
return std::visit(
[&](auto &&x) {
return Expr<LogicalResult>{FoldOperation(context, std::move(x))};
},
std::move(relation.u));
}
template <int KIND>
Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(
FoldingContext &context, Not<KIND> &&x) {
if (auto array{ApplyElementwise(context, x)}) {
return *array;
}
using Ty = Type<TypeCategory::Logical, KIND>;
auto &operand{x.left()};
if (auto value{GetScalarConstantValue<Ty>(operand)}) {
return Expr<Ty>{Constant<Ty>{!value->IsTrue()}};
}
return Expr<Ty>{x};
}
template <int KIND>
Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(
FoldingContext &context, LogicalOperation<KIND> &&operation) {
using LOGICAL = Type<TypeCategory::Logical, KIND>;
if (auto array{ApplyElementwise(context, operation,
std::function<Expr<LOGICAL>(Expr<LOGICAL> &&, Expr<LOGICAL> &&)>{
[=](Expr<LOGICAL> &&x, Expr<LOGICAL> &&y) {
return Expr<LOGICAL>{LogicalOperation<KIND>{
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<LOGICAL>{Constant<LOGICAL>{result}};
}
return Expr<LOGICAL>{std::move(operation)};
}
FOR_EACH_LOGICAL_KIND(template class ExpressionBase, )
template class ExpressionBase<SomeLogical>;
} // namespace Fortran::evaluate