From bd146ed3e8127f65b0e0ea86625df70d8c2c8130 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Wed, 25 Jul 2018 13:46:13 -0700 Subject: [PATCH] [flang] fold real**int Original-commit: flang-compiler/f18@2dc2c2a6a52d506d74354664015ae37645f11fa2 Reviewed-on: https://github.com/flang-compiler/f18/pull/162 Tree-same-pre-rewrite: false --- flang/lib/evaluate/expression.cc | 16 +++++++++-- flang/lib/evaluate/expression.h | 1 + flang/lib/evaluate/int-power.h | 60 ++++++++++++++++++++++++++++++++++++++++ flang/lib/evaluate/integer.h | 2 +- flang/lib/evaluate/real.cc | 20 +++++++------- flang/lib/evaluate/real.h | 34 +++++++++++------------ 6 files changed, 102 insertions(+), 31 deletions(-) create mode 100644 flang/lib/evaluate/int-power.h diff --git a/flang/lib/evaluate/expression.cc b/flang/lib/evaluate/expression.cc index cfdfee7..75929f7 100644 --- a/flang/lib/evaluate/expression.cc +++ b/flang/lib/evaluate/expression.cc @@ -13,6 +13,7 @@ // limitations under the License. #include "expression.h" +#include "int-power.h" #include "variable.h" #include "../common/idioms.h" #include "../parser/characters.h" @@ -471,7 +472,13 @@ template auto RealExpr::IntPower::FoldScalar(FoldingContext &context, const Scalar &a, const ScalarConstant &b) -> std::optional { - return {}; // TODO + return std::visit( + [&](const auto &pow) -> std::optional { + auto power{evaluate::IntPower(a, pow)}; + RealFlagWarnings(context, power.flags, "raising to integer power"); + return {std::move(power.value)}; + }, + b.u); } template @@ -504,7 +511,6 @@ auto RealExpr::AIMAG::FoldScalar( return {z.AIMAG()}; } -// TODO: generalize over Expr rather than instantiating same for each template auto RealExpr::Fold(FoldingContext &context) -> std::optional { return std::visit( @@ -516,7 +522,11 @@ auto RealExpr::Fold(FoldingContext &context) -> std::optional { if constexpr (evaluate::FoldableTrait) { auto c{x.Fold(context)}; if (c.has_value()) { - u_ = *c; + if (context.flushDenormalsToZero && c->IsDenormal()) { + u_ = Scalar{}; + } else { + u_ = *c; + } return c; } } diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index 4bcf5c0..1f81d48 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -37,6 +37,7 @@ CLASS_TRAIT(FoldableTrait); struct FoldingContext { parser::ContextualMessages &messages; Rounding rounding{Rounding::TiesToEven}; + bool flushDenormalsToZero{false}; }; // Helper base classes for packaging subexpressions. diff --git a/flang/lib/evaluate/int-power.h b/flang/lib/evaluate/int-power.h new file mode 100644 index 0000000..147b339 --- /dev/null +++ b/flang/lib/evaluate/int-power.h @@ -0,0 +1,60 @@ +// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_EVALUATE_INT_POWER_H_ +#define FORTRAN_EVALUATE_INT_POWER_H_ + +// Computes an integer power of a real or complex value. + +#include "common.h" + +namespace Fortran::evaluate { + +template +ValueWithRealFlags IntPower(const REAL &base, const INT &power, + Rounding rounding = Rounding::TiesToEven) { + REAL one{REAL::FromInteger(INT{1}).value}; + ValueWithRealFlags result; + result.value = one; + if (base.IsNotANumber()) { + result.value = REAL::NaN(); + if (base.IsSignalingNaN()) { + result.flags.set(RealFlag::InvalidArgument); + } + } else if (power.IsZero()) { + if (base.IsZero() || base.IsInfinite()) { + result.flags.set(RealFlag::InvalidArgument); + } + } else { + bool negativePower{power.IsNegative()}; + INT absPower{power.ABS().value}; + REAL shifted{base}; + int nbits{INT::bits - absPower.LEADZ()}; + for (int j{0}; j + 1 < nbits; ++j) { + if (absPower.BTEST(j)) { + result.value = + result.value.Multiply(shifted).AccumulateFlags(result.flags); + } + shifted = shifted.Add(shifted).AccumulateFlags(result.flags); + } + result.value = result.value.Multiply(shifted).AccumulateFlags(result.flags); + if (negativePower) { + result.value = one.Divide(result.value).AccumulateFlags(result.flags); + } + } + return result; +} + +} // namespace Fortran::evaluate +#endif // FORTRAN_EVALUATE_INT_POWER_H_ diff --git a/flang/lib/evaluate/integer.h b/flang/lib/evaluate/integer.h index 0bd09b2..24bb0cb 100644 --- a/flang/lib/evaluate/integer.h +++ b/flang/lib/evaluate/integer.h @@ -102,7 +102,7 @@ public: struct PowerWithErrors { Integer power; - bool divisionByZero, overflow, zeroToZero; + bool divisionByZero{false}, overflow{false}, zeroToZero{false}; }; // Constructors and value-generating static functions diff --git a/flang/lib/evaluate/real.cc b/flang/lib/evaluate/real.cc index 2daae14..5a5a643 100644 --- a/flang/lib/evaluate/real.cc +++ b/flang/lib/evaluate/real.cc @@ -60,7 +60,7 @@ ValueWithRealFlags> Real::Add( const Real &y, Rounding rounding) const { ValueWithRealFlags result; if (IsNotANumber() || y.IsNotANumber()) { - result.value.word_ = NaNWord(); // NaN + x -> NaN + result.value = NaN(); // NaN + x -> NaN if (IsSignalingNaN() || y.IsSignalingNaN()) { result.flags.set(RealFlag::InvalidArgument); } @@ -73,7 +73,7 @@ ValueWithRealFlags> Real::Add( if (isNegative == yIsNegative) { result.value = *this; // +/-Inf + +/-Inf -> +/-Inf } else { - result.value.word_ = NaNWord(); // +/-Inf + -/+Inf -> NaN + result.value = NaN(); // +/-Inf + -/+Inf -> NaN result.flags.set(RealFlag::InvalidArgument); } } else { @@ -140,7 +140,7 @@ ValueWithRealFlags> Real::Multiply( const Real &y, Rounding rounding) const { ValueWithRealFlags result; if (IsNotANumber() || y.IsNotANumber()) { - result.value.word_ = NaNWord(); // NaN * x -> NaN + result.value = NaN(); // NaN * x -> NaN if (IsSignalingNaN() || y.IsSignalingNaN()) { result.flags.set(RealFlag::InvalidArgument); } @@ -148,10 +148,10 @@ ValueWithRealFlags> Real::Multiply( bool isNegative{IsNegative() != y.IsNegative()}; if (IsInfinite() || y.IsInfinite()) { if (IsZero() || y.IsZero()) { - result.value.word_ = NaNWord(); // 0 * Inf -> NaN + result.value = NaN(); // 0 * Inf -> NaN result.flags.set(RealFlag::InvalidArgument); } else { - result.value.word_ = InfinityWord(isNegative); + result.value = Infinity(isNegative); } } else { auto product{GetFraction().MultiplyUnsigned(y.GetFraction())}; @@ -200,7 +200,7 @@ ValueWithRealFlags> Real::Divide( const Real &y, Rounding rounding) const { ValueWithRealFlags result; if (IsNotANumber() || y.IsNotANumber()) { - result.value.word_ = NaNWord(); // NaN / x -> NaN, x / NaN -> NaN + result.value = NaN(); // NaN / x -> NaN, x / NaN -> NaN if (IsSignalingNaN() || y.IsSignalingNaN()) { result.flags.set(RealFlag::InvalidArgument); } @@ -208,17 +208,17 @@ ValueWithRealFlags> Real::Divide( bool isNegative{IsNegative() != y.IsNegative()}; if (IsInfinite()) { if (y.IsInfinite()) { - result.value.word_ = NaNWord(); // Inf/Inf -> NaN + result.value = NaN(); // Inf/Inf -> NaN result.flags.set(RealFlag::InvalidArgument); } else { // Inf/x -> Inf, Inf/0 -> Inf - result.value.word_ = InfinityWord(isNegative); + result.value = Infinity(isNegative); } } else if (y.IsZero()) { if (IsZero()) { // 0/0 -> NaN - result.value.word_ = NaNWord(); + result.value = NaN(); result.flags.set(RealFlag::InvalidArgument); } else { // x/0 -> Inf, Inf/0 -> Inf - result.value.word_ = InfinityWord(isNegative); + result.value = Infinity(isNegative); result.flags.set(RealFlag::DivideByZero); } } else if (IsZero() || y.IsInfinite()) { // 0/x, x/Inf -> 0 diff --git a/flang/lib/evaluate/real.h b/flang/lib/evaluate/real.h index 6d1c2cf..e92a5a4 100644 --- a/flang/lib/evaluate/real.h +++ b/flang/lib/evaluate/real.h @@ -117,6 +117,23 @@ public: return epsilon; } + // TODO: Configurable NaN representations + static constexpr Real NaN() { + return {Word{maxExponent} + .SHIFTL(significandBits) + .IBSET(significandBits - 1) + .IBSET(significandBits - 2)}; + } + + static constexpr Real Infinity(bool negative) { + Word infinity{maxExponent}; + infinity = infinity.SHIFTL(significandBits); + if (negative) { + infinity = infinity.IBSET(infinity.bits - 1); + } + return {infinity}; + } + template static ValueWithRealFlags FromInteger( const INT &n, Rounding rounding = Rounding::TiesToEven) { @@ -285,23 +302,6 @@ private: return greaterOrEqual; } - // TODO: Configurable NaN representations - static constexpr Word NaNWord() { - return Word{maxExponent} - .SHIFTL(significandBits) - .IBSET(significandBits - 1) - .IBSET(significandBits - 2); - } - - static constexpr Word InfinityWord(bool negative) { - Word infinity{maxExponent}; - infinity = infinity.SHIFTL(significandBits); - if (negative) { - infinity = infinity.IBSET(infinity.bits - 1); - } - return infinity; - } - // Normalizes and marshals the fields of a floating-point number in place. // The value is not a NaN, and a zero fraction means a zero value (i.e., // a maximal exponent and zero fraction doesn't signify infinity, although -- 2.7.4