diff --git a/flang/lib/evaluate/int-power.h b/flang/lib/evaluate/int-power.h index 87ee395f45ea..b7d6366fcb06 100644 --- a/flang/lib/evaluate/int-power.h +++ b/flang/lib/evaluate/int-power.h @@ -22,16 +22,12 @@ namespace Fortran::evaluate { template -ValueWithRealFlags IntPower( - const REAL &base, const INT &power, Rounding rounding = defaultRounding) { - REAL one{REAL::FromInteger(INT{1}).value}; - ValueWithRealFlags result; - result.value = one; +ValueWithRealFlags TimesIntPowerOf(const REAL &factor, const REAL &base, + const INT &power, Rounding rounding = defaultRounding) { + ValueWithRealFlags result{factor}; if (base.IsNotANumber()) { result.value = REAL::NotANumber(); - if (base.IsSignalingNaN()) { - result.flags.set(RealFlag::InvalidArgument); - } + result.flags.set(RealFlag::InvalidArgument); } else if (power.IsZero()) { if (base.IsZero() || base.IsInfinite()) { result.flags.set(RealFlag::InvalidArgument); @@ -39,21 +35,29 @@ ValueWithRealFlags IntPower( } else { bool negativePower{power.IsNegative()}; INT absPower{power.ABS().value}; - REAL shifted{base}; + REAL squares{base}; int nbits{INT::bits - absPower.LEADZ()}; - for (int j{0}; j + 1 < nbits; ++j) { + for (int j{0}; j < nbits; ++j) { if (absPower.BTEST(j)) { - result.value = - result.value.Multiply(shifted).AccumulateFlags(result.flags); + if (negativePower) { + result.value = + result.value.Divide(squares).AccumulateFlags(result.flags); + } else { + result.value = + result.value.Multiply(squares).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); + squares = squares.Multiply(squares).AccumulateFlags(result.flags); } } return result; } + +template +ValueWithRealFlags IntPower( + const REAL &base, const INT &power, Rounding rounding = defaultRounding) { + REAL one{REAL::FromInteger(INT{1}).value}; + return TimesIntPowerOf(one, base, power, rounding); +} } #endif // FORTRAN_EVALUATE_INT_POWER_H_ diff --git a/flang/lib/evaluate/real.cc b/flang/lib/evaluate/real.cc index 88f84f2d5fba..98cb66231373 100644 --- a/flang/lib/evaluate/real.cc +++ b/flang/lib/evaluate/real.cc @@ -383,31 +383,58 @@ template ValueWithRealFlags> Real::Read( const char *&p, Rounding rounding) { ValueWithRealFlags result; - Real ten{FromInteger(Integer<32>{10}).value}; - for (; parser::IsDecimalDigit(*p); ++p) { - result.value = - result.value.Multiply(ten, rounding).AccumulateFlags(result.flags); - result.value = - result.value.Add(FromInteger(Integer<32>{*p - '0'}).value, rounding) - .AccumulateFlags(result.flags); + Real ten{FromInteger(Integer<8>{10}).value}; + while (*p == ' ') { + ++p; } - std::int64_t exponent{0}; - if (*p == '.') { - for (++p; parser::IsDecimalDigit(*p); ++p) { - --exponent; - result.value = - result.value.Multiply(ten, rounding).AccumulateFlags(result.flags); - result.value = - result.value.Add(FromInteger(Integer<32>{*p - '0'}).value, rounding) - .AccumulateFlags(result.flags); + bool isNegative{*p == '-'}; + if (*p == '-' || *p == '+') { + ++p; + } + Word integer{0}; + int decimalExponent{0}; + bool full{false}; + bool inFraction{false}; + for (;; ++p) { + if (*p == '.') { + if (inFraction) { + break; + } + inFraction = true; + } else if (!parser::IsDecimalDigit(*p)) { + break; + } else if (full) { + if (!inFraction) { + ++decimalExponent; + } + } else { + auto times10{integer.MultiplyUnsigned(Word{10})}; + if (!times10.upper.IsZero()) { + full = true; + if (!inFraction) { + ++decimalExponent; + } + } else { + auto augmented{times10.lower.AddUnsigned(Word{*p - '0'})}; + if (augmented.carry) { + full = true; + if (!inFraction) { + ++decimalExponent; + } + } else { + integer = augmented.value; + if (inFraction) { + --decimalExponent; + } + } + } } } + if (parser::IsLetter(*p)) { - bool negExpo{false}; - if (*++p == '-') { - negExpo = true; - ++p; - } else if (*p == '+') { + ++p; + bool negExpo{*p == '-'}; + if (*p == '+' || *p == '-') { ++p; } auto expo{Integer<32>::ReadUnsigned(p)}; @@ -417,20 +444,64 @@ ValueWithRealFlags> Real::Read( } else if (negExpo) { expoVal *= -1; } - exponent += expoVal; + decimalExponent += expoVal; } - if (exponent == 0) { - return result; - } - Real tenPower{IntPower(ten, Integer<64>{std::abs(exponent)}, rounding) - .AccumulateFlags(result.flags)}; - if (exponent > 0) { - result.value = - result.value.Multiply(tenPower, rounding).AccumulateFlags(result.flags); + + int binaryExponent{exponentBias + bits - 1}; + if (integer.IsZero()) { + decimalExponent = 0; } else { - result.value = - result.value.Divide(tenPower, rounding).AccumulateFlags(result.flags); + int leadz{integer.LEADZ()}; + binaryExponent -= leadz; + integer = integer.SHIFTL(leadz); } + for (; decimalExponent > 0; --decimalExponent) { + auto times5{integer.MultiplyUnsigned(Word{5})}; + ++binaryExponent; + integer = times5.lower; + for (; !times5.upper.IsZero(); times5.upper = times5.upper.SHIFTR(1)) { + ++binaryExponent; + integer = integer.SHIFTR(1); + if (times5.upper.BTEST(0)) { + integer = integer.IBSET(bits - 1); + } + } + } + for (; decimalExponent < 0; ++decimalExponent) { + auto div5{integer.DivideUnsigned(Word{5})}; + --binaryExponent; + integer = div5.quotient; + std::uint8_t lost = div5.remainder.ToUInt64() * 0x33; + while (!integer.BTEST(bits - 1)) { + integer = integer.SHIFTL(1); + if (lost & 0x80) { + integer = integer.IBSET(0); + } + lost <<= 1; + --binaryExponent; + } + } + + RoundingBits roundingBits; + for (int j{0}; bits - j > precision; ++j) { + roundingBits.ShiftRight(integer.BTEST(0)); + integer = integer.SHIFTR(1); + } + + Fraction fraction{Fraction::ConvertUnsigned(integer).value}; + while (binaryExponent < 1) { + if (fraction.IsZero()) { + binaryExponent = 0; + break; + } else { + ++binaryExponent; + roundingBits.ShiftRight(fraction.BTEST(0)); + fraction = fraction.SHIFTR(1); + } + } + + NormalizeAndRound( + result, isNegative, binaryExponent, fraction, rounding, roundingBits); return result; } @@ -580,30 +651,31 @@ auto Real::AsScaledDecimal(Rounding rounding) const } } else { // Divide asInt by 2**(-twoPower). - unsigned lower3{0}; + std::uint32_t lower{0}; for (; twoPower < 0; ++twoPower) { auto times5{asInt.MultiplyUnsigned(five)}; if (!times5.upper.IsZero()) { // asInt is too big to need scaling, just shift it down. - lower3 >>= 1; + lower >>= 1; if (asInt.BTEST(0)) { - lower3 |= 4; + lower |= 1 << 31; } asInt = asInt.SHIFTR(1); } else { - // asInt is small enough to be scaled; do so. - unsigned times5lower3{lower3 * 5}; - unsigned round{times5lower3 >> 3}; + std::uint64_t lowerTimes5{lower * static_cast(5)}; + std::uint32_t round = lowerTimes5 >> 32; auto rounded{times5.lower.AddUnsigned(Word{round})}; if (rounded.carry) { - lower3 >>= 1; + // asInt is still too big to need scaling (rounding would overflow) + lower >>= 1; if (asInt.BTEST(0)) { - lower3 |= 4; + lower |= 1 << 31; } asInt = asInt.SHIFTR(1); } else { + // asInt is small enough to be scaled; do so. --result.value.decimalExponent; - lower3 = times5lower3 & 7; + lower = lowerTimes5; asInt = rounded.value; } } diff --git a/flang/lib/evaluate/variable.cc b/flang/lib/evaluate/variable.cc index 38a7d279f9a4..9f1161653428 100644 --- a/flang/lib/evaluate/variable.cc +++ b/flang/lib/evaluate/variable.cc @@ -91,27 +91,27 @@ CoarrayRef &CoarrayRef::set_team(Expr &&v, bool isTeamNumber) { return *this; } -void Substring::SetBounds(std::optional> &first, - std::optional> &last) { - if (first.has_value()) { - first_ = IndirectSubscriptIntegerExpr::Make(std::move(*first)); +void Substring::SetBounds(std::optional> &lower, + std::optional> &upper) { + if (lower.has_value()) { + lower_ = IndirectSubscriptIntegerExpr::Make(std::move(*lower)); } - if (last.has_value()) { - last_ = IndirectSubscriptIntegerExpr::Make(std::move(*last)); + if (upper.has_value()) { + upper_ = IndirectSubscriptIntegerExpr::Make(std::move(*upper)); } } -Expr Substring::first() const { - if (first_.has_value()) { - return **first_; +Expr Substring::lower() const { + if (lower_.has_value()) { + return **lower_; } else { return AsExpr(Constant{1}); } } -Expr Substring::last() const { - if (last_.has_value()) { - return **last_; +Expr Substring::upper() const { + if (upper_.has_value()) { + return **upper_; } else { return std::visit( common::visitors{[](const DataRef &dataRef) { return dataRef.LEN(); }, @@ -123,23 +123,23 @@ Expr Substring::last() const { } std::optional> Substring::Fold(FoldingContext &context) { - if (!first_.has_value()) { - first_ = AsExpr(Constant{1}); + if (!lower_.has_value()) { + lower_ = AsExpr(Constant{1}); } - *first_ = evaluate::Fold(context, std::move(**first_)); - std::optional lbi{ToInt64(**first_)}; + *lower_ = evaluate::Fold(context, std::move(**lower_)); + std::optional lbi{ToInt64(**lower_)}; if (lbi.has_value() && *lbi < 1) { context.messages.Say( "lower bound (%jd) on substring is less than one"_en_US, static_cast(*lbi)); *lbi = 1; - first_ = AsExpr(Constant{1}); + lower_ = AsExpr(Constant{1}); } - if (!last_.has_value()) { - last_ = last(); + if (!upper_.has_value()) { + upper_ = upper(); } - *last_ = evaluate::Fold(context, std::move(**last_)); - if (std::optional ubi{ToInt64(**last_)}) { + *upper_ = evaluate::Fold(context, std::move(**upper_)); + if (std::optional ubi{ToInt64(**upper_)}) { auto *literal{std::get_if(&parent_)}; std::optional length; if (literal != nullptr) { @@ -150,8 +150,8 @@ std::optional> Substring::Fold(FoldingContext &context) { if (*ubi < 1 || (lbi.has_value() && *ubi < *lbi)) { // Zero-length string: canonicalize *lbi = 1, *ubi = 0; - first_ = AsExpr(Constant{*lbi}); - last_ = AsExpr(Constant{*ubi}); + lower_ = AsExpr(Constant{*lbi}); + upper_ = AsExpr(Constant{*ubi}); } else if (length.has_value() && *ubi > *length) { context.messages.Say("upper bound (&jd) on substring is greater " "than character length (%jd)"_en_US, @@ -170,9 +170,9 @@ std::optional> Substring::Fold(FoldingContext &context) { newStaticData->data().push_back(from[j]); } parent_ = newStaticData; - first_ = AsExpr(Constant{1}); + lower_ = AsExpr(Constant{1}); std::int64_t length = newStaticData->data().size(); - last_ = AsExpr(Constant{length}); + upper_ = AsExpr(Constant{length}); switch (width) { case 1: return { @@ -324,8 +324,8 @@ std::ostream &DataRef::AsFortran(std::ostream &o) const { return Emit(o, u); } std::ostream &Substring::AsFortran(std::ostream &o) const { Emit(o, parent_) << '('; - Emit(o, first_) << ':'; - return Emit(o, last_); + Emit(o, lower_) << ':'; + return Emit(o, upper_); } std::ostream &ComplexPart::AsFortran(std::ostream &o) const { @@ -380,7 +380,7 @@ Expr DataRef::LEN() const { Expr Substring::LEN() const { return AsExpr( Extremum{AsExpr(Constant{0}), - last() - first() + AsExpr(Constant{1})}); + upper() - lower() + AsExpr(Constant{1})}); } template Expr Designator::LEN() const { if constexpr (Result::category == TypeCategory::Character) { diff --git a/flang/lib/evaluate/variable.h b/flang/lib/evaluate/variable.h index 5331d3feb9d0..a761c5593861 100644 --- a/flang/lib/evaluate/variable.h +++ b/flang/lib/evaluate/variable.h @@ -193,14 +193,14 @@ public: SetBounds(first, last); } Substring(StaticDataObject::Pointer &&parent, - std::optional> &&first, - std::optional> &&last) + std::optional> &&lower, + std::optional> &&upper) : parent_{std::move(parent)} { - SetBounds(first, last); + SetBounds(lower, upper); } - Expr first() const; // TODO pmk: lower/upper - Expr last() const; + Expr lower() const; + Expr upper() const; int Rank() const; BaseObject GetBaseObject() const; const Symbol *GetLastSymbol() const; @@ -213,7 +213,7 @@ private: void SetBounds(std::optional> &, std::optional> &); std::variant parent_; - std::optional first_, last_; + std::optional lower_, upper_; }; // R915 complex-part-designator diff --git a/flang/lib/semantics/type.cc b/flang/lib/semantics/type.cc index edaa7def064f..cfb14fb5ddbe 100644 --- a/flang/lib/semantics/type.cc +++ b/flang/lib/semantics/type.cc @@ -270,7 +270,9 @@ void ExprResolver::Resolve(Symbol &symbol) { if (auto *type{symbol.GetType()}) { if (type->category() == DeclTypeSpec::TypeDerived) { DerivedTypeSpec &dts{type->derivedTypeSpec()}; - for (auto &[name, value] : dts.paramValues()) { + for (auto &nameAndValue : dts.paramValues()) { + // &[name, value] elicits "unused variable" warnings + auto &value{nameAndValue.second}; if (value.isExplicit()) { value.ResolveExplicit(context_); } diff --git a/flang/test/evaluate/real.cc b/flang/test/evaluate/real.cc index 5a82618736aa..8aa6313d3cd3 100644 --- a/flang/test/evaluate/real.cc +++ b/flang/test/evaluate/real.cc @@ -367,9 +367,11 @@ void subsetTests(int pass, Rounding rounding, std::uint32_t opds) { auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; actualFlags &= ~Inexact; // x86 std::trunc can set Inexact; AINT ain't u.f = fcheck; +#ifndef __clang__ if (IsNaN(u.ui)) { - actualFlags |= InvalidArgument; // x86 std::trunc(NaN) WAR + actualFlags |= InvalidArgument; // x86 std::trunc(NaN) workaround } +#endif UINT rcheck{NormalizeNaN(u.ui)}; UINT check = aint.value.RawBits().ToUInt64(); MATCH(rcheck, check) @@ -384,10 +386,6 @@ void subsetTests(int pass, Rounding rounding, std::uint32_t opds) { MATCH(IsInfinite(rj), x.IsInfinite()) ("%d IsInfinite(0x%llx)", pass, static_cast(rj)); - // Rounding mode doesn't affect the conversion of binary floating-point - // data to scaled decimal, but it does affect the check in which the - // result is converted back to binary floating-point, and can cause - // spurious failures. if (rounding == Rounding::TiesToEven) { auto scaled{x.AsScaledDecimal()}; if (IsNaN(rj)) { @@ -401,17 +399,13 @@ void subsetTests(int pass, Rounding rounding, std::uint32_t opds) { MATCH(x.IsNegative(), scaled.value.negative) ("%d IsNegative(0x%llx)", pass, static_cast(rj)); char buffer[128]; + const char *p = buffer; snprintf(buffer, sizeof buffer, "%c%llu.0E%d", "+-"[scaled.value.negative], static_cast(integer), scaled.value.decimalExponent); - if constexpr (std::is_same_v) { - char *p; - u.f = std::strtof(buffer, &p); - } else { - u.f = std::atof(buffer); - } - MATCH(rj, u.ui) + auto readBack{REAL::Read(p, rounding)}; + MATCH(rj, readBack.value.RawBits().ToUInt64()) ("%d scaled decimal 0x%llx %s", pass, static_cast(rj), buffer); }