From a9446d3d40a89a4bbdf61bad92c1eccc599053d8 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Thu, 23 Aug 2018 10:55:16 -0700 Subject: [PATCH] [flang] work on numeric operations Original-commit: flang-compiler/f18@e0ab91914c6f20d0e242e80795c35cc858860449 Reviewed-on: https://github.com/flang-compiler/f18/pull/183 Tree-same-pre-rewrite: false --- flang/lib/common/fortran.h | 4 ++ flang/lib/evaluate/expression.cc | 2 +- flang/lib/evaluate/expression.h | 12 +++--- flang/lib/evaluate/tools.cc | 80 ++++++++++++++++++++++++++++++++++++--- flang/lib/evaluate/tools.h | 63 ++++++++++++++++++++++++------ flang/lib/semantics/expression.cc | 16 ++------ 6 files changed, 140 insertions(+), 37 deletions(-) diff --git a/flang/lib/common/fortran.h b/flang/lib/common/fortran.h index 695d04b..4b2a8fa 100644 --- a/flang/lib/common/fortran.h +++ b/flang/lib/common/fortran.h @@ -25,6 +25,10 @@ namespace Fortran::common { // Fortran has five kinds of intrinsic data, and the derived types. ENUM_CLASS(TypeCategory, Integer, Real, Complex, Character, Logical, Derived) +static constexpr bool IsNumeric(TypeCategory cat) { + return cat <= TypeCategory::Complex; +} + // Kinds of IMPORT statements. Default means IMPORT or IMPORT :: names. ENUM_CLASS(ImportKind, Default, Only, None, All) diff --git a/flang/lib/evaluate/expression.cc b/flang/lib/evaluate/expression.cc index a4e3261..1fe6544 100644 --- a/flang/lib/evaluate/expression.cc +++ b/flang/lib/evaluate/expression.cc @@ -282,7 +282,7 @@ auto Negate::FoldScalar(FoldingContext &context, const Scalar &c) template auto ComplexComponent::FoldScalar(FoldingContext &context, const Scalar &z) const -> std::optional> { - return {isRealPart ? z.REAL() : z.AIMAG()}; + return {isImaginaryPart ? z.AIMAG() : z.REAL()}; } template diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index aa66ce2..97e4790 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -129,16 +129,16 @@ struct ComplexComponent using typename Base::Result; using Operand = typename Base::template Operand<0>; CLASS_BOILERPLATE(ComplexComponent) - ComplexComponent(bool isReal, const Expr &x) - : Base{x}, isRealPart{isReal} {} - ComplexComponent(bool isReal, Expr &&x) - : Base{std::move(x)}, isRealPart{isReal} {} + ComplexComponent(bool isImaginary, const Expr &x) + : Base{x}, isImaginaryPart{isImaginary} {} + ComplexComponent(bool isImaginary, Expr &&x) + : Base{std::move(x)}, isImaginaryPart{isImaginary} {} std::optional> FoldScalar( FoldingContext &, const Scalar &) const; - const char *suffix() const { return isRealPart ? "%RE)" : "%IM)"; } + const char *suffix() const { return isImaginaryPart ? "%IM)" : "%RE)"; } - bool isRealPart{true}; + bool isImaginaryPart{true}; }; template diff --git a/flang/lib/evaluate/tools.cc b/flang/lib/evaluate/tools.cc index c490289..a3a03a8 100644 --- a/flang/lib/evaluate/tools.cc +++ b/flang/lib/evaluate/tools.cc @@ -13,14 +13,16 @@ // limitations under the License. #include "tools.h" +#include "../common/idioms.h" #include "../parser/message.h" +#include #include using namespace Fortran::parser::literals; namespace Fortran::evaluate { -std::optional, Expr>> ConvertRealOperands( +ConvertRealOperandsResult ConvertRealOperands( parser::ContextualMessages &messages, Expr &&x, Expr &&y) { return std::visit( @@ -51,13 +53,16 @@ std::optional, Expr>> ConvertRealOperands( std::move(x.u), std::move(y.u)); } -std::optional, Expr>> ConvertRealOperands( +ConvertRealOperandsResult ConvertRealOperands( parser::ContextualMessages &messages, std::optional> &&x, std::optional> &&y) { - if (x.has_value() && y.has_value()) { - return ConvertRealOperands(messages, std::move(*x), std::move(*y)); - } - return std::nullopt; + auto partial{[&](Expr &&x, Expr &&y) { + return ConvertRealOperands(messages, std::move(x), std::move(y)); + }}; + using fType = ConvertRealOperandsResult(Expr &&, Expr &&); + std::function f{partial}; + return common::JoinOptionals( + common::MapOptional(f, std::move(x), std::move(y))); } Expr GenericScalarToExpr(const Scalar &x) { @@ -65,4 +70,67 @@ Expr GenericScalarToExpr(const Scalar &x) { [&](const auto &c) { return ToGenericExpr(SomeKindScalarToExpr(c)); }, x.u); } + +template class OPR, TypeCategory CAT> +std::optional> PromoteAndCombine( + Expr> &&x, Expr> &&y) { + return {Expr{std::visit( + [&](auto &&xk, auto &&yk) -> Expr> { + using xt = ResultType; + using yt = ResultType; + using ToType = Type; + return {Expr{OPR{EnsureKind(std::move(xk)), + EnsureKind(std::move(yk))}}}; + }, + std::move(x.u.u), std::move(y.u.u))}}; +} + +template class OPR> +std::optional> NumericOperation( + parser::ContextualMessages &messages, Expr &&x, + Expr &&y) { + return std::visit( + common::visitors{[](Expr &&ix, Expr &&iy) { + return PromoteAndCombine( + std::move(ix), std::move(iy)); + }, + [](Expr &&rx, Expr &&ry) { + return PromoteAndCombine( + std::move(rx), std::move(ry)); + }, + [](Expr &&rx, Expr &&iy) { + return std::optional{Expr{std::visit( + [&](auto &&rxk) -> Expr { + using kindEx = decltype(rxk); + using resultType = ResultType; + return {kindEx{ + OPR{std::move(rxk), kindEx{std::move(iy)}}}}; + }, + std::move(rx.u.u))}}; + }, + [](Expr &&ix, Expr &&ry) { + return std::optional{Expr{std::visit( + [&](auto &&ryk) -> Expr { + using kindEx = decltype(ryk); + using resultType = ResultType; + return {kindEx{ + OPR{kindEx{std::move(ix)}, std::move(ryk)}}}; + }, + std::move(ry.u.u))}}; + }, + [](Expr &&zx, Expr &&zy) { + return PromoteAndCombine( + std::move(zx), std::move(zy)); + }, + // TODO pmk complex; Add/Sub different from Mult/Div + [&](auto &&, auto &&) { + messages.Say("non-numeric operands to numeric operation"_err_en_US); + return std::optional>{std::nullopt}; + }}, + std::move(x.u), std::move(y.u)); +} + +template std::optional> NumericOperation( + parser::ContextualMessages &, Expr &&, Expr &&); + } // namespace Fortran::evaluate diff --git a/flang/lib/evaluate/tools.h b/flang/lib/evaluate/tools.h index 36aba3c..9996a8a 100644 --- a/flang/lib/evaluate/tools.h +++ b/flang/lib/evaluate/tools.h @@ -103,13 +103,8 @@ Expr> ConvertToTypeAndKindOf( to.u.u); } -// Ensure that both operands of an intrinsic REAL operation or CMPLX() -// are INTEGER or REAL, and convert them as necessary to the same REAL type. -using ConvertRealOperandsResult = - std::optional, Expr>>; -ConvertRealOperandsResult ConvertRealOperands( - parser::ContextualMessages &, Expr &&, Expr &&); - +// Given two expressions of the same type category, convert one to the +// kind of the other in place if it has a smaller kind. template void ConvertToSameKind(Expr> &x, Expr> &y) { std::visit( @@ -125,6 +120,16 @@ void ConvertToSameKind(Expr> &x, Expr> &y) { x.u.u, y.u.u); } +// Ensure that both operands of an intrinsic REAL operation (or CMPLX() +// constructor) are INTEGER or REAL, then convert them as necessary to the +// same kind of REAL. +using ConvertRealOperandsResult = + std::optional, Expr>>; +ConvertRealOperandsResult ConvertRealOperands( + parser::ContextualMessages &, Expr &&, Expr &&); +ConvertRealOperandsResult ConvertRealOperands(parser::ContextualMessages &, + std::optional> &&, std::optional> &&); + template Expr> ScalarConstantToExpr(const A &x) { static_assert(std::is_same_v>, std::decay_t> || !"TypeOf<> is broken"); @@ -132,8 +137,8 @@ template Expr> ScalarConstantToExpr(const A &x) { } template -Expr> ToSomeKindExpr(const Expr> &x) { - return {x}; +Expr> ToSomeKindExpr(Expr> &&x) { + return {std::move(x)}; } template @@ -145,9 +150,9 @@ Expr> SomeKindScalarToExpr(const SomeKindScalar &x) { Expr GenericScalarToExpr(const Scalar &); -template -Expr ToGenericExpr(const Expr> &x) { - return Expr{x}; +template +Expr ToGenericExpr(Expr> &&x) { + return Expr{Expr>{std::move(x)}}; } template @@ -155,5 +160,39 @@ Expr ToGenericExpr(Expr> &&x) { return Expr{std::move(x)}; } +// Convert, if necessary, an expression to a specific kind in the same +// category. +template +Expr EnsureKind(Expr> &&x) { + using ToType = TOTYPE; + using FromGenericType = SomeKind; + if (auto *p{std::get_if>(&x.u.u)}) { + return std::move(*p); + } + if constexpr (ToType::category == TypeCategory::Complex) { + return {std::visit( + [](auto &z) -> ComplexConstructor { + using FromType = ResultType; + using FromPart = typename FromType::Part; + using FromGeneric = SomeKind; + using ToPart = typename ToType::Part; + Convert re{Expr{ + Expr{ComplexComponent{false, z}}}}; + Convert im{Expr{ + Expr{ComplexComponent{true, z}}}}; + return {std::move(re), std::move(im)}; + }, + x.u.u)}; + } else { + return {Convert{std::move(x)}}; + } +} + +template class OPR> +std::optional> NumericOperation( + parser::ContextualMessages &, Expr &&, Expr &&); +extern template std::optional> NumericOperation( + parser::ContextualMessages &, Expr &&, Expr &&); + } // namespace Fortran::evaluate #endif // FORTRAN_EVALUATE_TOOLS_H_ diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index 1f97135..ecd26bb 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -431,7 +431,7 @@ MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Divide &x) { template<> MaybeExpr AnalyzeHelper(ExpressionAnalyzer &ea, const parser::Expr::Add &x) { - // TODO + // TODO pmk WIP return std::nullopt; } @@ -567,16 +567,8 @@ ExpressionAnalyzer::KindParam ExpressionAnalyzer::Analyze( std::optional> ExpressionAnalyzer::ConstructComplex( MaybeExpr &&real, MaybeExpr &&imaginary) { - // TODO: pmk abstract further, this will be a common pattern - auto partial{[&](Expr &&x, Expr &&y) { - return evaluate::ConvertRealOperands( - context_.messages, std::move(x), std::move(y)); - }}; - using fType = - evaluate::ConvertRealOperandsResult(Expr &&, Expr &&); - std::function f{partial}; - auto converted{common::MapOptional(f, std::move(real), std::move(imaginary))}; - if (auto joined{common::JoinOptionals(std::move(converted))}) { + if (auto converted{evaluate::ConvertRealOperands( + context_.messages, std::move(real), std::move(imaginary))}) { return {std::visit( [](auto &&rx, auto &&ix) -> Expr { using realType = evaluate::ResultType; @@ -585,7 +577,7 @@ std::optional> ExpressionAnalyzer::ConstructComplex( return {Expr{evaluate::ComplexConstructor{ std::move(rx), std::move(ix)}}}; }, - std::move(joined->first.u.u), std::move(joined->second.u.u))}; + std::move(converted->first.u.u), std::move(converted->second.u.u))}; } return std::nullopt; } -- 2.7.4