/* This file is part of the dynarmic project. * Copyright (c) 2018 MerryMage * SPDX-License-Identifier: 0BSD */ #include "common/fp/fpsr.h" #include "common/fp/info.h" #include "common/fp/mantissa_util.h" #include "common/fp/process_exception.h" #include "common/fp/rounding_mode.h" #include "common/fp/unpacked.h" #include "common/safe_ops.h" namespace Dynarmic::FP { template std::tuple FPUnpackBase(FPT op, FPCR fpcr, [[maybe_unused]] FPSR& fpsr) { constexpr size_t sign_bit = FPInfo::exponent_width + FPInfo::explicit_mantissa_width; constexpr size_t exponent_high_bit = FPInfo::exponent_width + FPInfo::explicit_mantissa_width - 1; constexpr size_t exponent_low_bit = FPInfo::explicit_mantissa_width; constexpr size_t mantissa_high_bit = FPInfo::explicit_mantissa_width - 1; constexpr size_t mantissa_low_bit = 0; constexpr int denormal_exponent = FPInfo::exponent_min - int(FPInfo::explicit_mantissa_width); constexpr bool is_half_precision = std::is_same_v; const bool sign = Common::Bit(op); const FPT exp_raw = Common::Bits(op); const FPT frac_raw = Common::Bits(op); if (exp_raw == 0) { if constexpr (is_half_precision) { if (frac_raw == 0 || fpcr.FZ16()) { return {FPType::Zero, sign, {sign, 0, 0}}; } return {FPType::Nonzero, sign, ToNormalized(sign, denormal_exponent, frac_raw)}; } else { if (frac_raw == 0 || fpcr.FZ()) { if (frac_raw != 0) { FPProcessException(FPExc::InputDenorm, fpcr, fpsr); } return {FPType::Zero, sign, {sign, 0, 0}}; } return {FPType::Nonzero, sign, ToNormalized(sign, denormal_exponent, frac_raw)}; } } const bool exp_all_ones = exp_raw == Common::Ones(FPInfo::exponent_width); const bool ahp_disabled = is_half_precision && !fpcr.AHP(); if ((exp_all_ones && !is_half_precision) || (exp_all_ones && ahp_disabled)) { if (frac_raw == 0) { return {FPType::Infinity, sign, ToNormalized(sign, 1000000, 1)}; } const bool is_quiet = Common::Bit(frac_raw); return {is_quiet ? FPType::QNaN : FPType::SNaN, sign, {sign, 0, 0}}; } const int exp = static_cast(exp_raw) - FPInfo::exponent_bias; const u64 frac = static_cast(frac_raw | FPInfo::implicit_leading_bit) << (normalized_point_position - FPInfo::explicit_mantissa_width); return {FPType::Nonzero, sign, {sign, exp, frac}}; } template std::tuple FPUnpackBase(u16 op, FPCR fpcr, FPSR& fpsr); template std::tuple FPUnpackBase(u32 op, FPCR fpcr, FPSR& fpsr); template std::tuple FPUnpackBase(u64 op, FPCR fpcr, FPSR& fpsr); template std::tuple Normalize(FPUnpacked op, int extra_right_shift = 0) { const int highest_set_bit = Common::HighestSetBit(op.mantissa); const int shift_amount = highest_set_bit - static_cast(F) + extra_right_shift; const u64 mantissa = Safe::LogicalShiftRight(op.mantissa, shift_amount); const ResidualError error = ResidualErrorOnRightShift(op.mantissa, shift_amount); const int exponent = op.exponent + highest_set_bit - normalized_point_position; return std::make_tuple(op.sign, exponent, mantissa, error); } template FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) { ASSERT(op.mantissa != 0); ASSERT(rounding != RoundingMode::ToNearest_TieAwayFromZero); constexpr int minimum_exp = FPInfo::exponent_min; constexpr size_t E = FPInfo::exponent_width; constexpr size_t F = FPInfo::explicit_mantissa_width; constexpr bool isFP16 = FPInfo::total_width == 16; auto [sign, exponent, mantissa, error] = Normalize(op); if (((!isFP16 && fpcr.FZ()) || (isFP16 && fpcr.FZ16())) && exponent < minimum_exp) { fpsr.UFC(true); return FPInfo::Zero(sign); } int biased_exp = std::max(exponent - minimum_exp + 1, 0); if (biased_exp == 0) { std::tie(sign, exponent, mantissa, error) = Normalize(op, minimum_exp - exponent); } if (biased_exp == 0 && (error != ResidualError::Zero || fpcr.UFE())) { FPProcessException(FPExc::Underflow, fpcr, fpsr); } bool round_up = false, overflow_to_inf = false; switch (rounding) { case RoundingMode::ToNearest_TieEven: { round_up = (error > ResidualError::Half) || (error == ResidualError::Half && Common::Bit<0>(mantissa)); overflow_to_inf = true; break; } case RoundingMode::TowardsPlusInfinity: round_up = error != ResidualError::Zero && !sign; overflow_to_inf = !sign; break; case RoundingMode::TowardsMinusInfinity: round_up = error != ResidualError::Zero && sign; overflow_to_inf = sign; break; default: break; } if (round_up) { if ((mantissa & FPInfo::mantissa_mask) == FPInfo::mantissa_mask) { // Overflow on rounding up is going to happen if (mantissa == FPInfo::mantissa_mask) { // Rounding up from denormal to normal mantissa++; biased_exp++; } else { // Rounding up to next exponent mantissa = (mantissa + 1) / 2; biased_exp++; } } else { mantissa++; } } if (error != ResidualError::Zero && rounding == RoundingMode::ToOdd) { mantissa = Common::ModifyBit<0>(mantissa, true); } FPT result = 0; #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4127) // C4127: conditional expression is constant #endif if (!isFP16 || !fpcr.AHP()) { #ifdef _MSC_VER #pragma warning(pop) #endif constexpr int max_biased_exp = (1 << E) - 1; if (biased_exp >= max_biased_exp) { result = overflow_to_inf ? FPInfo::Infinity(sign) : FPInfo::MaxNormal(sign); FPProcessException(FPExc::Overflow, fpcr, fpsr); FPProcessException(FPExc::Inexact, fpcr, fpsr); } else { result = sign ? 1 : 0; result <<= E; result += FPT(biased_exp); result <<= F; result |= static_cast(mantissa) & FPInfo::mantissa_mask; if (error != ResidualError::Zero) { FPProcessException(FPExc::Inexact, fpcr, fpsr); } } } else { constexpr int max_biased_exp = (1 << E); if (biased_exp >= max_biased_exp) { result = sign ? 0xFFFF : 0x7FFF; FPProcessException(FPExc::InvalidOp, fpcr, fpsr); } else { result = sign ? 1 : 0; result <<= E; result += FPT(biased_exp); result <<= F; result |= static_cast(mantissa) & FPInfo::mantissa_mask; if (error != ResidualError::Zero) { FPProcessException(FPExc::Inexact, fpcr, fpsr); } } } return result; } template u16 FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr); template u32 FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr); template u64 FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr); } // namespace Dynarmic::FP