early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

207
externals/dynarmic/src/common/fp/fpcr.h vendored Executable file
View File

@@ -0,0 +1,207 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <optional>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/fp/rounding_mode.h"
namespace Dynarmic::FP {
/**
* Representation of the Floating-Point Control Register.
*/
class FPCR final {
public:
FPCR() = default;
FPCR(const FPCR&) = default;
FPCR(FPCR&&) = default;
explicit FPCR(u32 data) : value{data & mask} {}
FPCR& operator=(const FPCR&) = default;
FPCR& operator=(FPCR&&) = default;
FPCR& operator=(u32 data) {
value = data & mask;
return *this;
}
/// Get alternate half-precision control flag.
bool AHP() const {
return Common::Bit<26>(value);
}
/// Set alternate half-precision control flag.
void AHP(bool ahp) {
value = Common::ModifyBit<26>(value, ahp);
}
/// Get default NaN mode control bit.
bool DN() const {
return Common::Bit<25>(value);
}
/// Set default NaN mode control bit.
void DN(bool dn) {
value = Common::ModifyBit<25>(value, dn);
}
/// Get flush-to-zero mode control bit.
bool FZ() const {
return Common::Bit<24>(value);
}
/// Set flush-to-zero mode control bit.
void FZ(bool fz) {
value = Common::ModifyBit<24>(value, fz);
}
/// Get rounding mode control field.
FP::RoundingMode RMode() const {
return static_cast<FP::RoundingMode>(Common::Bits<22, 23>(value));
}
/// Set rounding mode control field.
void RMode(FP::RoundingMode rounding_mode) {
ASSERT_MSG(static_cast<u32>(rounding_mode) <= 0b11, "FPCR: Invalid rounding mode");
value = Common::ModifyBits<22, 23>(value, static_cast<u32>(rounding_mode));
}
/// Get the stride of a vector when executing AArch32 VFP instructions.
/// This field has no function in AArch64 state.
std::optional<size_t> Stride() const {
switch (Common::Bits<20, 21>(value)) {
case 0b00:
return 1;
case 0b11:
return 2;
default:
return std::nullopt;
}
}
/// Set the stride of a vector when executing AArch32 VFP instructions.
/// This field has no function in AArch64 state.
void Stride(size_t stride) {
ASSERT_MSG(stride >= 1 && stride <= 2, "FPCR: Invalid stride");
value = Common::ModifyBits<20, 21>(value, stride == 1 ? 0b00u : 0b11u);
}
/// Get flush-to-zero (half-precision specific) mode control bit.
bool FZ16() const {
return Common::Bit<19>(value);
}
/// Set flush-to-zero (half-precision specific) mode control bit.
void FZ16(bool fz16) {
value = Common::ModifyBit<19>(value, fz16);
}
/// Gets the length of a vector when executing AArch32 VFP instructions.
/// This field has no function in AArch64 state.
size_t Len() const {
return Common::Bits<16, 18>(value) + 1;
}
/// Sets the length of a vector when executing AArch32 VFP instructions.
/// This field has no function in AArch64 state.
void Len(size_t len) {
ASSERT_MSG(len >= 1 && len <= 8, "FPCR: Invalid len");
value = Common::ModifyBits<16, 18>(value, static_cast<u32>(len - 1));
}
/// Get input denormal exception trap enable flag.
bool IDE() const {
return Common::Bit<15>(value);
}
/// Set input denormal exception trap enable flag.
void IDE(bool ide) {
value = Common::ModifyBit<15>(value, ide);
}
/// Get inexact exception trap enable flag.
bool IXE() const {
return Common::Bit<12>(value);
}
/// Set inexact exception trap enable flag.
void IXE(bool ixe) {
value = Common::ModifyBit<12>(value, ixe);
}
/// Get underflow exception trap enable flag.
bool UFE() const {
return Common::Bit<11>(value);
}
/// Set underflow exception trap enable flag.
void UFE(bool ufe) {
value = Common::ModifyBit<11>(value, ufe);
}
/// Get overflow exception trap enable flag.
bool OFE() const {
return Common::Bit<10>(value);
}
/// Set overflow exception trap enable flag.
void OFE(bool ofe) {
value = Common::ModifyBit<10>(value, ofe);
}
/// Get division by zero exception trap enable flag.
bool DZE() const {
return Common::Bit<9>(value);
}
/// Set division by zero exception trap enable flag.
void DZE(bool dze) {
value = Common::ModifyBit<9>(value, dze);
}
/// Get invalid operation exception trap enable flag.
bool IOE() const {
return Common::Bit<8>(value);
}
/// Set invalid operation exception trap enable flag.
void IOE(bool ioe) {
value = Common::ModifyBit<8>(value, ioe);
}
/// Gets the underlying raw value within the FPCR.
u32 Value() const {
return value;
}
/// Gets the StandardFPSCRValue (A32 ASIMD).
FPCR ASIMDStandardValue() const {
FPCR stdvalue;
stdvalue.AHP(AHP());
stdvalue.FZ16(FZ16());
stdvalue.FZ(true);
stdvalue.DN(true);
return stdvalue;
}
private:
// Bits 0-7, 13-14, and 27-31 are reserved.
static constexpr u32 mask = 0x07FF9F00;
u32 value = 0;
};
inline bool operator==(FPCR lhs, FPCR rhs) {
return lhs.Value() == rhs.Value();
}
inline bool operator!=(FPCR lhs, FPCR rhs) {
return !operator==(lhs, rhs);
}
} // namespace Dynarmic::FP

159
externals/dynarmic/src/common/fp/fpsr.h vendored Executable file
View File

@@ -0,0 +1,159 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Dynarmic::FP {
/**
* Representation of the Floating-Point Status Register.
*/
class FPSR final {
public:
FPSR() = default;
FPSR(const FPSR&) = default;
FPSR(FPSR&&) = default;
explicit FPSR(u32 data) : value{data & mask} {}
FPSR& operator=(const FPSR&) = default;
FPSR& operator=(FPSR&&) = default;
FPSR& operator=(u32 data) {
value = data & mask;
return *this;
}
/// Get negative condition flag
bool N() const {
return Common::Bit<31>(value);
}
/// Set negative condition flag
void N(bool N_) {
value = Common::ModifyBit<31>(value, N_);
}
/// Get zero condition flag
bool Z() const {
return Common::Bit<30>(value);
}
/// Set zero condition flag
void Z(bool Z_) {
value = Common::ModifyBit<30>(value, Z_);
}
/// Get carry condition flag
bool C() const {
return Common::Bit<29>(value);
}
/// Set carry condition flag
void C(bool C_) {
value = Common::ModifyBit<29>(value, C_);
}
/// Get overflow condition flag
bool V() const {
return Common::Bit<28>(value);
}
/// Set overflow condition flag
void V(bool V_) {
value = Common::ModifyBit<28>(value, V_);
}
/// Get cumulative saturation bit
bool QC() const {
return Common::Bit<27>(value);
}
/// Set cumulative saturation bit
void QC(bool QC_) {
value = Common::ModifyBit<27>(value, QC_);
}
/// Get input denormal floating-point exception bit
bool IDC() const {
return Common::Bit<7>(value);
}
/// Set input denormal floating-point exception bit
void IDC(bool IDC_) {
value = Common::ModifyBit<7>(value, IDC_);
}
/// Get inexact cumulative floating-point exception bit
bool IXC() const {
return Common::Bit<4>(value);
}
/// Set inexact cumulative floating-point exception bit
void IXC(bool IXC_) {
value = Common::ModifyBit<4>(value, IXC_);
}
/// Get underflow cumulative floating-point exception bit
bool UFC() const {
return Common::Bit<3>(value);
}
/// Set underflow cumulative floating-point exception bit
void UFC(bool UFC_) {
value = Common::ModifyBit<3>(value, UFC_);
}
/// Get overflow cumulative floating-point exception bit
bool OFC() const {
return Common::Bit<2>(value);
}
/// Set overflow cumulative floating-point exception bit
void OFC(bool OFC_) {
value = Common::ModifyBit<2>(value, OFC_);
}
/// Get divide by zero cumulative floating-point exception bit
bool DZC() const {
return Common::Bit<1>(value);
}
/// Set divide by zero cumulative floating-point exception bit
void DZC(bool DZC_) {
value = Common::ModifyBit<1>(value, DZC_);
}
/// Get invalid operation cumulative floating-point exception bit
bool IOC() const {
return Common::Bit<0>(value);
}
/// Set invalid operation cumulative floating-point exception bit
void IOC(bool IOC_) {
value = Common::ModifyBit<0>(value, IOC_);
}
/// Gets the underlying raw value within the FPSR.
u32 Value() const {
return value;
}
private:
// Bits 5-6 and 8-26 are reserved.
static constexpr u32 mask = 0xF800009F;
u32 value = 0;
};
inline bool operator==(FPSR lhs, FPSR rhs) {
return lhs.Value() == rhs.Value();
}
inline bool operator!=(FPSR lhs, FPSR rhs) {
return !operator==(lhs, rhs);
}
} // namespace Dynarmic::FP

89
externals/dynarmic/src/common/fp/fused.cpp vendored Executable file
View File

@@ -0,0 +1,89 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/fused.h"
#include "common/fp/mantissa_util.h"
#include "common/fp/unpacked.h"
#include "common/u128.h"
namespace Dynarmic::FP {
constexpr size_t product_point_position = normalized_point_position * 2;
static FPUnpacked ReduceMantissa(bool sign, int exponent, const u128& mantissa) {
constexpr int point_position_correction = normalized_point_position - (product_point_position - 64);
// We round-to-odd here when reducing the bitwidth of the mantissa so that subsequent roundings are accurate.
return {sign, exponent + point_position_correction, mantissa.upper | static_cast<u64>(mantissa.lower != 0)};
}
FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2) {
const bool product_sign = op1.sign != op2.sign;
const auto [product_exponent, product_value] = [op1, op2]{
int exponent = op1.exponent + op2.exponent;
u128 value = Multiply64To128(op1.mantissa, op2.mantissa);
if (value.Bit<product_point_position + 1>()) {
value = value >> 1;
exponent++;
}
return std::make_tuple(exponent, value);
}();
if (product_value == 0) {
return addend;
}
if (addend.mantissa == 0) {
return ReduceMantissa(product_sign, product_exponent, product_value);
}
const int exp_diff = product_exponent - addend.exponent;
if (product_sign == addend.sign) {
// Addition
if (exp_diff <= 0) {
// addend > product
const u64 result = addend.mantissa + StickyLogicalShiftRight(product_value, normalized_point_position - exp_diff).lower;
return FPUnpacked{addend.sign, addend.exponent, result};
}
// addend < product
const u128 result = product_value + StickyLogicalShiftRight(addend.mantissa, exp_diff - normalized_point_position);
return ReduceMantissa(product_sign, product_exponent, result);
}
// Subtraction
const u128 addend_long = u128(addend.mantissa) << normalized_point_position;
bool result_sign;
u128 result;
int result_exponent;
if (exp_diff == 0 && product_value > addend_long) {
result_sign = product_sign;
result_exponent = product_exponent;
result = product_value - addend_long;
} else if (exp_diff <= 0) {
result_sign = !product_sign;
result_exponent = addend.exponent;
result = addend_long - StickyLogicalShiftRight(product_value, -exp_diff);
} else {
result_sign = product_sign;
result_exponent = product_exponent;
result = product_value - StickyLogicalShiftRight(addend_long, exp_diff);
}
if (result.upper == 0) {
return FPUnpacked{result_sign, result_exponent, result.lower};
}
const int required_shift = normalized_point_position - Common::HighestSetBit(result.upper);
result = result << required_shift;
result_exponent -= required_shift;
return ReduceMantissa(result_sign, result_exponent, result);
}
} // namespace Dynarmic::FP

15
externals/dynarmic/src/common/fp/fused.h vendored Executable file
View File

@@ -0,0 +1,15 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
struct FPUnpacked;
/// This function assumes all arguments have been normalized.
FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2);
} // namespace Dynarmic::FP

138
externals/dynarmic/src/common/fp/info.h vendored Executable file
View File

@@ -0,0 +1,138 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Dynarmic::FP {
template<typename FPT>
struct FPInfo {};
template<>
struct FPInfo<u16> {
static constexpr size_t total_width = 16;
static constexpr size_t exponent_width = 5;
static constexpr size_t explicit_mantissa_width = 10;
static constexpr size_t mantissa_width = explicit_mantissa_width + 1;
static constexpr u32 implicit_leading_bit = u32(1) << explicit_mantissa_width;
static constexpr u32 sign_mask = 0x8000;
static constexpr u32 exponent_mask = 0x7C00;
static constexpr u32 mantissa_mask = 0x3FF;
static constexpr u32 mantissa_msb = 0x200;
static constexpr int exponent_min = -14;
static constexpr int exponent_max = 15;
static constexpr int exponent_bias = 15;
static constexpr u16 Zero(bool sign) {
return sign ? static_cast<u16>(sign_mask) : u16{0};
}
static constexpr u16 Infinity(bool sign) {
return static_cast<u16>(exponent_mask | Zero(sign));
}
static constexpr u16 MaxNormal(bool sign) {
return static_cast<u16>((exponent_mask - 1) | Zero(sign));
}
static constexpr u16 DefaultNaN() {
return static_cast<u16>(exponent_mask | (u32(1) << (explicit_mantissa_width - 1)));
}
};
template<>
struct FPInfo<u32> {
static constexpr size_t total_width = 32;
static constexpr size_t exponent_width = 8;
static constexpr size_t explicit_mantissa_width = 23;
static constexpr size_t mantissa_width = explicit_mantissa_width + 1;
static constexpr u32 implicit_leading_bit = u32(1) << explicit_mantissa_width;
static constexpr u32 sign_mask = 0x80000000;
static constexpr u32 exponent_mask = 0x7F800000;
static constexpr u32 mantissa_mask = 0x007FFFFF;
static constexpr u32 mantissa_msb = 0x00400000;
static constexpr int exponent_min = -126;
static constexpr int exponent_max = 127;
static constexpr int exponent_bias = 127;
static constexpr u32 Zero(bool sign) {
return sign ? sign_mask : 0;
}
static constexpr u32 Infinity(bool sign) {
return exponent_mask | Zero(sign);
}
static constexpr u32 MaxNormal(bool sign) {
return (exponent_mask - 1) | Zero(sign);
}
static constexpr u32 DefaultNaN() {
return exponent_mask | (u32(1) << (explicit_mantissa_width - 1));
}
};
template<>
struct FPInfo<u64> {
static constexpr size_t total_width = 64;
static constexpr size_t exponent_width = 11;
static constexpr size_t explicit_mantissa_width = 52;
static constexpr size_t mantissa_width = explicit_mantissa_width + 1;
static constexpr u64 implicit_leading_bit = u64(1) << explicit_mantissa_width;
static constexpr u64 sign_mask = 0x8000'0000'0000'0000;
static constexpr u64 exponent_mask = 0x7FF0'0000'0000'0000;
static constexpr u64 mantissa_mask = 0x000F'FFFF'FFFF'FFFF;
static constexpr u64 mantissa_msb = 0x0008'0000'0000'0000;
static constexpr int exponent_min = -1022;
static constexpr int exponent_max = 1023;
static constexpr int exponent_bias = 1023;
static constexpr u64 Zero(bool sign) {
return sign ? sign_mask : 0;
}
static constexpr u64 Infinity(bool sign) {
return exponent_mask | Zero(sign);
}
static constexpr u64 MaxNormal(bool sign) {
return (exponent_mask - 1) | Zero(sign);
}
static constexpr u64 DefaultNaN() {
return exponent_mask | (u64(1) << (explicit_mantissa_width - 1));
}
};
/// value = (sign ? -1 : +1) * 2^exponent * value
/// @note We do not handle denormals. Denormals will static_assert.
template<typename FPT, bool sign, int exponent, FPT value>
constexpr FPT FPValue() {
if constexpr (value == 0) {
return FPInfo<FPT>::Zero(sign);
}
constexpr int point_position = static_cast<int>(FPInfo<FPT>::explicit_mantissa_width);
constexpr int highest_bit = Common::HighestSetBit(value);
constexpr int offset = point_position - highest_bit;
constexpr int normalized_exponent = exponent - offset + point_position;
static_assert(offset >= 0);
static_assert(normalized_exponent >= FPInfo<FPT>::exponent_min && normalized_exponent <= FPInfo<FPT>::exponent_max);
constexpr FPT mantissa = (value << offset) & FPInfo<FPT>::mantissa_mask;
constexpr FPT biased_exponent = static_cast<FPT>(normalized_exponent + FPInfo<FPT>::exponent_bias);
return FPT(FPInfo<FPT>::Zero(sign) | mantissa | (biased_exponent << FPInfo<FPT>::explicit_mantissa_width));
}
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,46 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Dynarmic::FP {
enum class ResidualError {
Zero,
LessThanHalf,
Half,
GreaterThanHalf,
};
inline ResidualError ResidualErrorOnRightShift(u64 mantissa, int shift_amount) {
if (shift_amount <= 0 || mantissa == 0) {
return ResidualError::Zero;
}
if (shift_amount > static_cast<int>(Common::BitSize<u64>())) {
return Common::MostSignificantBit(mantissa) ? ResidualError::GreaterThanHalf : ResidualError::LessThanHalf;
}
const size_t half_bit_position = static_cast<size_t>(shift_amount - 1);
const u64 half = static_cast<u64>(1) << half_bit_position;
const u64 error_mask = Common::Ones<u64>(static_cast<size_t>(shift_amount));
const u64 error = mantissa & error_mask;
if (error == 0) {
return ResidualError::Zero;
}
if (error < half) {
return ResidualError::LessThanHalf;
}
if (error == half) {
return ResidualError::Half;
}
return ResidualError::GreaterThanHalf;
}
} // namespace Dynarmic::FP

17
externals/dynarmic/src/common/fp/op.h vendored Executable file
View File

@@ -0,0 +1,17 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/fp/op/FPCompare.h"
#include "common/fp/op/FPConvert.h"
#include "common/fp/op/FPMulAdd.h"
#include "common/fp/op/FPRecipEstimate.h"
#include "common/fp/op/FPRecipExponent.h"
#include "common/fp/op/FPRecipStepFused.h"
#include "common/fp/op/FPRoundInt.h"
#include "common/fp/op/FPRSqrtEstimate.h"
#include "common/fp/op/FPRSqrtStepFused.h"
#include "common/fp/op/FPToFixed.h"

View File

@@ -0,0 +1,40 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/op/FPCompare.h"
#include "common/fp/process_exception.h"
#include "common/fp/unpacked.h"
namespace Dynarmic::FP {
template <typename FPT>
bool FPCompareEQ(FPT lhs, FPT rhs, FPCR fpcr, FPSR& fpsr) {
const auto unpacked1 = FPUnpack(lhs, fpcr, fpsr);
const auto unpacked2 = FPUnpack(rhs, fpcr, fpsr);
const auto type1 = std::get<FPType>(unpacked1);
const auto type2 = std::get<FPType>(unpacked2);
const auto& value1 = std::get<FPUnpacked>(unpacked1);
const auto& value2 = std::get<FPUnpacked>(unpacked2);
if (type1 == FPType::QNaN || type1 == FPType::SNaN ||
type2 == FPType::QNaN || type2 == FPType::SNaN) {
if (type1 == FPType::SNaN || type2 == FPType::SNaN) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
}
// Comparisons against NaN are never equal.
return false;
}
return value1 == value2 || (type1 == FPType::Zero && type2 == FPType::Zero);
}
template bool FPCompareEQ<u16>(u16 lhs, u16 rhs, FPCR fpcr, FPSR& fpsr);
template bool FPCompareEQ<u32>(u32 lhs, u32 rhs, FPCR fpcr, FPSR& fpsr);
template bool FPCompareEQ<u64>(u64 lhs, u64 rhs, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,16 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
template <typename FPT>
bool FPCompareEQ(FPT lhs, FPT rhs, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,89 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/info.h"
#include "common/fp/op/FPConvert.h"
#include "common/fp/process_exception.h"
#include "common/fp/unpacked.h"
namespace Dynarmic::FP {
namespace {
template <typename FPT_TO, typename FPT_FROM>
FPT_TO FPConvertNaN(FPT_FROM op) {
const bool sign = Common::Bit<Common::BitSize<FPT_FROM>() - 1>(op);
const u64 frac = [op] {
if constexpr (sizeof(FPT_FROM) == sizeof(u64)) {
return Common::Bits<0, 50>(op);
} else if constexpr (sizeof(FPT_FROM) == sizeof(u32)) {
return u64{Common::Bits<0, 21>(op)} << 29;
} else {
return u64{Common::Bits<0, 8>(op)} << 42;
}
}();
const size_t dest_bit_size = Common::BitSize<FPT_TO>();
const u64 shifted_sign = u64{sign} << (dest_bit_size - 1);
const u64 exponent = Common::Ones<u64>(dest_bit_size - FPInfo<FPT_TO>::explicit_mantissa_width);
if constexpr (sizeof(FPT_TO) == sizeof(u64)) {
return FPT_TO(shifted_sign | exponent << 51 | frac);
} else if constexpr (sizeof(FPT_TO) == sizeof(u32)) {
return FPT_TO(shifted_sign | exponent << 22 | Common::Bits<29, 50>(frac));
} else {
return FPT_TO(shifted_sign | exponent << 9 | Common::Bits<42, 50>(frac));
}
}
} // Anonymous namespace
template <typename FPT_TO, typename FPT_FROM>
FPT_TO FPConvert(FPT_FROM op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr) {
const auto [type, sign, value] = FPUnpackCV<FPT_FROM>(op, fpcr, fpsr);
const bool is_althp = Common::BitSize<FPT_TO>() == 16 && fpcr.AHP();
if (type == FPType::SNaN || type == FPType::QNaN) {
std::uintmax_t result{};
if (is_althp) {
result = FPInfo<FPT_TO>::Zero(sign);
} else if (fpcr.DN()) {
result = FPInfo<FPT_TO>::DefaultNaN();
} else {
result = FPConvertNaN<FPT_TO>(op);
}
if (type == FPType::SNaN || is_althp) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
}
return FPT_TO(result);
}
if (type == FPType::Infinity) {
if (is_althp) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
return FPT_TO(u32{sign} << 15 | 0b111111111111111);
}
return FPInfo<FPT_TO>::Infinity(sign);
}
if (type == FPType::Zero) {
return FPInfo<FPT_TO>::Zero(sign);
}
return FPRoundCV<FPT_TO>(value, fpcr, rounding_mode, fpsr);
}
template u16 FPConvert<u16, u32>(u32 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr);
template u16 FPConvert<u16, u64>(u64 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr);
template u32 FPConvert<u32, u16>(u16 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr);
template u32 FPConvert<u32, u64>(u64 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr);
template u64 FPConvert<u64, u16>(u16 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr);
template u64 FPConvert<u64, u32>(u32 op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,17 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
enum class RoundingMode;
template <typename FPT_TO, typename FPT_FROM>
FPT_TO FPConvert(FPT_FROM op, FPCR fpcr, RoundingMode rounding_mode, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,79 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/info.h"
#include "common/fp/fused.h"
#include "common/fp/op/FPMulAdd.h"
#include "common/fp/process_exception.h"
#include "common/fp/process_nan.h"
#include "common/fp/unpacked.h"
namespace Dynarmic::FP {
template<typename FPT>
FPT FPMulAdd(FPT addend, FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr) {
const RoundingMode rounding = fpcr.RMode();
const auto [typeA, signA, valueA] = FPUnpack(addend, fpcr, fpsr);
const auto [type1, sign1, value1] = FPUnpack(op1, fpcr, fpsr);
const auto [type2, sign2, value2] = FPUnpack(op2, fpcr, fpsr);
const bool infA = typeA == FPType::Infinity;
const bool inf1 = type1 == FPType::Infinity;
const bool inf2 = type2 == FPType::Infinity;
const bool zeroA = typeA == FPType::Zero;
const bool zero1 = type1 == FPType::Zero;
const bool zero2 = type2 == FPType::Zero;
const auto maybe_nan = FPProcessNaNs3<FPT>(typeA, type1, type2, addend, op1, op2, fpcr, fpsr);
if (typeA == FPType::QNaN && ((inf1 && zero2) || (zero1 && inf2))) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
return FPInfo<FPT>::DefaultNaN();
}
if (maybe_nan) {
return *maybe_nan;
}
// Calculate properties of product (op1 * op2).
const bool signP = sign1 != sign2;
const bool infP = inf1 || inf2;
const bool zeroP = zero1 || zero2;
// Raise NaN on (inf * inf) of opposite signs or (inf * zero).
if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
return FPInfo<FPT>::DefaultNaN();
}
// Handle infinities
if ((infA && !signA) || (infP && !signP)) {
return FPInfo<FPT>::Infinity(false);
}
if ((infA && signA) || (infP && signP)) {
return FPInfo<FPT>::Infinity(true);
}
// Result is exactly zero
if (zeroA && zeroP && signA == signP) {
return FPInfo<FPT>::Zero(signA);
}
const FPUnpacked result_value = FusedMulAdd(valueA, value1, value2);
if (result_value.mantissa == 0) {
return FPInfo<FPT>::Zero(rounding == RoundingMode::TowardsMinusInfinity);
}
return FPRound<FPT>(result_value, fpcr, fpsr);
}
template u16 FPMulAdd<u16>(u16 addend, u16 op1, u16 op2, FPCR fpcr, FPSR& fpsr);
template u32 FPMulAdd<u32>(u32 addend, u32 op1, u32 op2, FPCR fpcr, FPSR& fpsr);
template u64 FPMulAdd<u64>(u64 addend, u64 op1, u64 op2, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,16 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
template<typename FPT>
FPT FPMulAdd(FPT addend, FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

17
externals/dynarmic/src/common/fp/op/FPNeg.h vendored Executable file
View File

@@ -0,0 +1,17 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/fp/info.h"
namespace Dynarmic::FP {
template<typename FPT>
constexpr FPT FPNeg(FPT op) {
return op ^ FPInfo<FPT>::sign_mask;
}
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,56 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/info.h"
#include "common/fp/op/FPRSqrtEstimate.h"
#include "common/fp/process_exception.h"
#include "common/fp/process_nan.h"
#include "common/fp/unpacked.h"
#include "common/math_util.h"
#include "common/safe_ops.h"
namespace Dynarmic::FP {
template<typename FPT>
FPT FPRSqrtEstimate(FPT op, FPCR fpcr, FPSR& fpsr) {
const auto [type, sign, value] = FPUnpack<FPT>(op, fpcr, fpsr);
if (type == FPType::SNaN || type == FPType::QNaN) {
return FPProcessNaN(type, op, fpcr, fpsr);
}
if (type == FPType::Zero) {
FPProcessException(FPExc::DivideByZero, fpcr, fpsr);
return FPInfo<FPT>::Infinity(sign);
}
if (sign) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
return FPInfo<FPT>::DefaultNaN();
}
if (type == FPType::Infinity) {
return FPInfo<FPT>::Zero(false);
}
const int result_exponent = (-(value.exponent + 1)) >> 1;
const bool was_exponent_odd = (value.exponent) % 2 == 0;
const u64 scaled = Safe::LogicalShiftRight(value.mantissa, normalized_point_position - (was_exponent_odd ? 7 : 8));
const u64 estimate = Common::RecipSqrtEstimate(scaled);
const FPT bits_exponent = static_cast<FPT>(result_exponent + FPInfo<FPT>::exponent_bias);
const FPT bits_mantissa = static_cast<FPT>(estimate << (FPInfo<FPT>::explicit_mantissa_width - 8));
return (bits_exponent << FPInfo<FPT>::explicit_mantissa_width) | (bits_mantissa & FPInfo<FPT>::mantissa_mask);
}
template u16 FPRSqrtEstimate<u16>(u16 op, FPCR fpcr, FPSR& fpsr);
template u32 FPRSqrtEstimate<u32>(u32 op, FPCR fpcr, FPSR& fpsr);
template u64 FPRSqrtEstimate<u64>(u64 op, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,16 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
template<typename FPT>
FPT FPRSqrtEstimate(FPT op, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,56 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/fused.h"
#include "common/fp/info.h"
#include "common/fp/op/FPNeg.h"
#include "common/fp/op/FPRSqrtStepFused.h"
#include "common/fp/process_nan.h"
#include "common/fp/unpacked.h"
namespace Dynarmic::FP {
template<typename FPT>
FPT FPRSqrtStepFused(FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr) {
op1 = FPNeg(op1);
const auto [type1, sign1, value1] = FPUnpack(op1, fpcr, fpsr);
const auto [type2, sign2, value2] = FPUnpack(op2, fpcr, fpsr);
if (const auto maybe_nan = FPProcessNaNs(type1, type2, op1, op2, fpcr, fpsr)) {
return *maybe_nan;
}
const bool inf1 = type1 == FPType::Infinity;
const bool inf2 = type2 == FPType::Infinity;
const bool zero1 = type1 == FPType::Zero;
const bool zero2 = type2 == FPType::Zero;
if ((inf1 && zero2) || (zero1 && inf2)) {
// return +1.5
return FPValue<FPT, false, -1, 3>();
}
if (inf1 || inf2) {
return FPInfo<FPT>::Infinity(sign1 != sign2);
}
// result_value = (3.0 + (value1 * value2)) / 2.0
FPUnpacked result_value = FusedMulAdd(ToNormalized(false, 0, 3), value1, value2);
result_value.exponent--;
if (result_value.mantissa == 0) {
return FPInfo<FPT>::Zero(fpcr.RMode() == RoundingMode::TowardsMinusInfinity);
}
return FPRound<FPT>(result_value, fpcr, fpsr);
}
template u16 FPRSqrtStepFused<u16>(u16 op1, u16 op2, FPCR fpcr, FPSR& fpsr);
template u32 FPRSqrtStepFused<u32>(u32 op1, u32 op2, FPCR fpcr, FPSR& fpsr);
template u64 FPRSqrtStepFused<u64>(u64 op1, u64 op2, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,16 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
template<typename FPT>
FPT FPRSqrtStepFused(FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,98 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <tuple>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/info.h"
#include "common/fp/op/FPRecipEstimate.h"
#include "common/fp/process_exception.h"
#include "common/fp/process_nan.h"
#include "common/fp/unpacked.h"
#include "common/math_util.h"
namespace Dynarmic::FP {
template<typename FPT>
FPT FPRecipEstimate(FPT op, FPCR fpcr, FPSR& fpsr) {
FPType type;
bool sign;
FPUnpacked value;
std::tie(type, sign, value) = FPUnpack<FPT>(op, fpcr, fpsr);
if (type == FPType::SNaN || type == FPType::QNaN) {
return FPProcessNaN(type, op, fpcr, fpsr);
}
if (type == FPType::Infinity) {
return FPInfo<FPT>::Zero(sign);
}
if (type == FPType::Zero) {
FPProcessException(FPExc::DivideByZero, fpcr, fpsr);
return FPInfo<FPT>::Infinity(sign);
}
if (value.exponent < FPInfo<FPT>::exponent_min - 2) {
const bool overflow_to_inf = [&]{
switch (fpcr.RMode()) {
case RoundingMode::ToNearest_TieEven:
return true;
case RoundingMode::TowardsPlusInfinity:
return !sign;
case RoundingMode::TowardsMinusInfinity:
return sign;
case RoundingMode::TowardsZero:
return false;
default:
UNREACHABLE();
}
}();
FPProcessException(FPExc::Overflow, fpcr, fpsr);
FPProcessException(FPExc::Inexact, fpcr, fpsr);
return overflow_to_inf ? FPInfo<FPT>::Infinity(sign) : FPInfo<FPT>::MaxNormal(sign);
}
if ((fpcr.FZ() && !std::is_same_v<FPT, u16>) || (fpcr.FZ16() && std::is_same_v<FPT, u16>)) {
if (value.exponent >= -FPInfo<FPT>::exponent_min) {
fpsr.UFC(true);
return FPInfo<FPT>::Zero(sign);
}
}
const u64 scaled = value.mantissa >> (normalized_point_position - 8);
u64 estimate = static_cast<u64>(Common::RecipEstimate(scaled)) << (FPInfo<FPT>::explicit_mantissa_width - 8);
int result_exponent = -(value.exponent + 1);
if (result_exponent < FPInfo<FPT>::exponent_min) {
switch (result_exponent) {
case (FPInfo<FPT>::exponent_min - 1):
estimate |= FPInfo<FPT>::implicit_leading_bit;
estimate >>= 1;
break;
case (FPInfo<FPT>::exponent_min - 2):
estimate |= FPInfo<FPT>::implicit_leading_bit;
estimate >>= 2;
result_exponent++;
break;
default:
UNREACHABLE();
}
}
const FPT bits_sign = FPInfo<FPT>::Zero(sign);
const FPT bits_exponent = static_cast<FPT>(result_exponent + FPInfo<FPT>::exponent_bias);
const FPT bits_mantissa = static_cast<FPT>(estimate);
return FPT((bits_exponent << FPInfo<FPT>::explicit_mantissa_width) | (bits_mantissa & FPInfo<FPT>::mantissa_mask) | bits_sign);
}
template u16 FPRecipEstimate<u16>(u16 op, FPCR fpcr, FPSR& fpsr);
template u32 FPRecipEstimate<u32>(u32 op, FPCR fpcr, FPSR& fpsr);
template u64 FPRecipEstimate<u64>(u64 op, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,16 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
template<typename FPT>
FPT FPRecipEstimate(FPT op, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,57 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/common_types.h"
#include "common/bit_util.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/info.h"
#include "common/fp/op/FPRecipExponent.h"
#include "common/fp/process_nan.h"
#include "common/fp/unpacked.h"
namespace Dynarmic::FP {
namespace {
template <typename FPT>
FPT DetermineExponentValue(size_t value) {
if constexpr (sizeof(FPT) == sizeof(u32)) {
return static_cast<FPT>(Common::Bits<23, 30>(value));
} else if constexpr (sizeof(FPT) == sizeof(u64)) {
return static_cast<FPT>(Common::Bits<52, 62>(value));
} else {
return static_cast<FPT>(Common::Bits<10, 14>(value));
}
}
} // Anonymous namespace
template <typename FPT>
FPT FPRecipExponent(FPT op, FPCR fpcr, FPSR& fpsr) {
const auto [type, sign, value] = FPUnpack<FPT>(op, fpcr, fpsr);
(void)value;
if (type == FPType::SNaN || type == FPType::QNaN) {
return FPProcessNaN(type, op, fpcr, fpsr);
}
const FPT sign_bits = FPInfo<FPT>::Zero(sign);
const FPT exponent = DetermineExponentValue<FPT>(op);
// Zero and denormals
if (exponent == 0) {
const FPT max_exponent = Common::Ones<FPT>(FPInfo<FPT>::exponent_width) - 1;
return FPT(sign_bits | (max_exponent << FPInfo<FPT>::explicit_mantissa_width));
}
// Infinities and normals
const FPT negated_exponent = FPT(~exponent);
const FPT adjusted_exponent = FPT(negated_exponent << FPInfo<FPT>::explicit_mantissa_width) & FPInfo<FPT>::exponent_mask;
return FPT(sign_bits | adjusted_exponent);
}
template u16 FPRecipExponent<u16>(u16 op, FPCR fpcr, FPSR& fpsr);
template u32 FPRecipExponent<u32>(u32 op, FPCR fpcr, FPSR& fpsr);
template u64 FPRecipExponent<u64>(u64 op, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,16 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
template <typename FPT>
FPT FPRecipExponent(FPT op, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,55 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/fused.h"
#include "common/fp/info.h"
#include "common/fp/op/FPNeg.h"
#include "common/fp/op/FPRecipStepFused.h"
#include "common/fp/process_nan.h"
#include "common/fp/unpacked.h"
namespace Dynarmic::FP {
template<typename FPT>
FPT FPRecipStepFused(FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr) {
op1 = FPNeg(op1);
const auto [type1, sign1, value1] = FPUnpack<FPT>(op1, fpcr, fpsr);
const auto [type2, sign2, value2] = FPUnpack<FPT>(op2, fpcr, fpsr);
if (const auto maybe_nan = FPProcessNaNs(type1, type2, op1, op2, fpcr, fpsr)) {
return *maybe_nan;
}
const bool inf1 = type1 == FPType::Infinity;
const bool inf2 = type2 == FPType::Infinity;
const bool zero1 = type1 == FPType::Zero;
const bool zero2 = type2 == FPType::Zero;
if ((inf1 && zero2) || (zero1 && inf2)) {
// return +2.0
return FPValue<FPT, false, 0, 2>();
}
if (inf1 || inf2) {
return FPInfo<FPT>::Infinity(sign1 != sign2);
}
// result_value = 2.0 + (value1 * value2)
const FPUnpacked result_value = FusedMulAdd(ToNormalized(false, 0, 2), value1, value2);
if (result_value.mantissa == 0) {
return FPInfo<FPT>::Zero(fpcr.RMode() == RoundingMode::TowardsMinusInfinity);
}
return FPRound<FPT>(result_value, fpcr, fpsr);
}
template u16 FPRecipStepFused<u16>(u16 op1, u16 op2, FPCR fpcr, FPSR& fpsr);
template u32 FPRecipStepFused<u32>(u32 op1, u32 op2, FPCR fpcr, FPSR& fpsr);
template u64 FPRecipStepFused<u64>(u64 op1, u64 op2, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,16 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
template<typename FPT>
FPT FPRecipStepFused(FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,95 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/info.h"
#include "common/fp/mantissa_util.h"
#include "common/fp/op/FPRoundInt.h"
#include "common/fp/process_exception.h"
#include "common/fp/process_nan.h"
#include "common/fp/rounding_mode.h"
#include "common/fp/unpacked.h"
#include "common/safe_ops.h"
namespace Dynarmic::FP {
template<typename FPT>
u64 FPRoundInt(FPT op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr) {
ASSERT(rounding != RoundingMode::ToOdd);
auto [type, sign, value] = FPUnpack<FPT>(op, fpcr, fpsr);
if (type == FPType::SNaN || type == FPType::QNaN) {
return FPProcessNaN(type, op, fpcr, fpsr);
}
if (type == FPType::Infinity) {
return FPInfo<FPT>::Infinity(sign);
}
if (type == FPType::Zero) {
return FPInfo<FPT>::Zero(sign);
}
// Reshift decimal point back to bit zero.
const int exponent = value.exponent - normalized_point_position;
if (exponent >= 0) {
// Guaranteed to be an integer
return op;
}
u64 int_result = sign ? Safe::Negate<u64>(value.mantissa) : static_cast<u64>(value.mantissa);
const ResidualError error = ResidualErrorOnRightShift(int_result, -exponent);
int_result = Safe::ArithmeticShiftLeft(int_result, exponent);
bool round_up = false;
switch (rounding) {
case RoundingMode::ToNearest_TieEven:
round_up = error > ResidualError::Half || (error == ResidualError::Half && Common::Bit<0>(int_result));
break;
case RoundingMode::TowardsPlusInfinity:
round_up = error != ResidualError::Zero;
break;
case RoundingMode::TowardsMinusInfinity:
round_up = false;
break;
case RoundingMode::TowardsZero:
round_up = error != ResidualError::Zero && Common::MostSignificantBit(int_result);
break;
case RoundingMode::ToNearest_TieAwayFromZero:
round_up = error > ResidualError::Half || (error == ResidualError::Half && !Common::MostSignificantBit(int_result));
break;
case RoundingMode::ToOdd:
UNREACHABLE();
}
if (round_up) {
int_result++;
}
const bool new_sign = Common::MostSignificantBit(int_result);
const u64 abs_int_result = new_sign ? Safe::Negate<u64>(int_result) : static_cast<u64>(int_result);
const FPT result = int_result == 0
? FPInfo<FPT>::Zero(sign)
: FPRound<FPT>(FPUnpacked{new_sign, normalized_point_position, abs_int_result}, fpcr, RoundingMode::TowardsZero, fpsr);
if (error != ResidualError::Zero && exact) {
FPProcessException(FPExc::Inexact, fpcr, fpsr);
}
return result;
}
template u64 FPRoundInt<u16>(u16 op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr);
template u64 FPRoundInt<u32>(u32 op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr);
template u64 FPRoundInt<u64>(u64 op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,19 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/common_types.h"
namespace Dynarmic::FP {
class FPCR;
class FPSR;
enum class RoundingMode;
template<typename FPT>
u64 FPRoundInt(FPT op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,101 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/mantissa_util.h"
#include "common/fp/op/FPToFixed.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<typename FPT>
u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) {
ASSERT(rounding != RoundingMode::ToOdd);
ASSERT(ibits <= 64);
ASSERT(fbits <= ibits);
auto [type, sign, value] = FPUnpack<FPT>(op, fpcr, fpsr);
if (type == FPType::SNaN || type == FPType::QNaN) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
}
// Handle zero
if (value.mantissa == 0) {
return 0;
}
if (sign && unsigned_) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
return 0;
}
// value *= 2.0^fbits and reshift the decimal point back to bit zero.
int exponent = value.exponent + static_cast<int>(fbits) - normalized_point_position;
u64 int_result = sign ? Safe::Negate<u64>(value.mantissa) : static_cast<u64>(value.mantissa);
const ResidualError error = ResidualErrorOnRightShift(int_result, -exponent);
int_result = Safe::ArithmeticShiftLeft(int_result, exponent);
bool round_up = false;
switch (rounding) {
case RoundingMode::ToNearest_TieEven:
round_up = error > ResidualError::Half || (error == ResidualError::Half && Common::Bit<0>(int_result));
break;
case RoundingMode::TowardsPlusInfinity:
round_up = error != ResidualError::Zero;
break;
case RoundingMode::TowardsMinusInfinity:
round_up = false;
break;
case RoundingMode::TowardsZero:
round_up = error != ResidualError::Zero && Common::MostSignificantBit(int_result);
break;
case RoundingMode::ToNearest_TieAwayFromZero:
round_up = error > ResidualError::Half || (error == ResidualError::Half && !Common::MostSignificantBit(int_result));
break;
case RoundingMode::ToOdd:
UNREACHABLE();
}
if (round_up) {
int_result++;
}
// Detect Overflow
const int min_exponent_for_overflow = static_cast<int>(ibits) - static_cast<int>(Common::HighestSetBit(value.mantissa + (round_up ? 1 : 0))) - (unsigned_ ? 0 : 1);
if (exponent >= min_exponent_for_overflow) {
// Positive overflow
if (unsigned_ || !sign) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
return Common::Ones<u64>(ibits - (unsigned_ ? 0 : 1));
}
// Negative overflow
const u64 min_value = Safe::Negate<u64>(static_cast<u64>(1) << (ibits - 1));
if (!(exponent == min_exponent_for_overflow && int_result == min_value)) {
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
return static_cast<u64>(1) << (ibits - 1);
}
}
if (error != ResidualError::Zero) {
FPProcessException(FPExc::Inexact, fpcr, fpsr);
}
return int_result & Common::Ones<u64>(ibits);
}
template u64 FPToFixed<u16>(size_t ibits, u16 op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
template u64 FPToFixed<u32>(size_t ibits, u32 op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
template u64 FPToFixed<u64>(size_t ibits, u64 op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,19 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/common_types.h"
namespace Dynarmic::FP {
class FPCR;
class FPSR;
enum class RoundingMode;
template<typename FPT>
u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,57 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/process_exception.h"
namespace Dynarmic::FP {
void FPProcessException(FPExc exception, FPCR fpcr, FPSR& fpsr) {
switch (exception) {
case FPExc::InvalidOp:
if (fpcr.IOE()) {
ASSERT_FALSE("Raising floating point exceptions unimplemented");
}
fpsr.IOC(true);
break;
case FPExc::DivideByZero:
if (fpcr.DZE()) {
ASSERT_FALSE("Raising floating point exceptions unimplemented");
}
fpsr.DZC(true);
break;
case FPExc::Overflow:
if (fpcr.OFE()) {
ASSERT_FALSE("Raising floating point exceptions unimplemented");
}
fpsr.OFC(true);
break;
case FPExc::Underflow:
if (fpcr.UFE()) {
ASSERT_FALSE("Raising floating point exceptions unimplemented");
}
fpsr.UFC(true);
break;
case FPExc::Inexact:
if (fpcr.IXE()) {
ASSERT_FALSE("Raising floating point exceptions unimplemented");
}
fpsr.IXC(true);
break;
case FPExc::InputDenorm:
if (fpcr.IDE()) {
ASSERT_FALSE("Raising floating point exceptions unimplemented");
}
fpsr.IDC(true);
break;
default:
UNREACHABLE();
break;
}
}
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
class FPCR;
class FPSR;
enum class FPExc {
InvalidOp,
DivideByZero,
Overflow,
Underflow,
Inexact,
InputDenorm,
};
void FPProcessException(FPExc exception, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,91 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/fp/info.h"
#include "common/fp/process_exception.h"
#include "common/fp/process_nan.h"
#include "common/fp/unpacked.h"
namespace Dynarmic::FP {
template<typename FPT>
FPT FPProcessNaN(FPType type, FPT op, FPCR fpcr, FPSR& fpsr) {
ASSERT(type == FPType::QNaN || type == FPType::SNaN);
constexpr size_t topfrac = FPInfo<FPT>::explicit_mantissa_width - 1;
FPT result = op;
if (type == FPType::SNaN) {
result = Common::ModifyBit<topfrac>(op, true);
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
}
if (fpcr.DN()) {
result = FPInfo<FPT>::DefaultNaN();
}
return result;
}
template u16 FPProcessNaN<u16>(FPType type, u16 op, FPCR fpcr, FPSR& fpsr);
template u32 FPProcessNaN<u32>(FPType type, u32 op, FPCR fpcr, FPSR& fpsr);
template u64 FPProcessNaN<u64>(FPType type, u64 op, FPCR fpcr, FPSR& fpsr);
template<typename FPT>
std::optional<FPT> FPProcessNaNs(FPType type1, FPType type2, FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr) {
if (type1 == FPType::SNaN) {
return FPProcessNaN<FPT>(type1, op1, fpcr, fpsr);
}
if (type2 == FPType::SNaN) {
return FPProcessNaN<FPT>(type2, op2, fpcr, fpsr);
}
if (type1 == FPType::QNaN) {
return FPProcessNaN<FPT>(type1, op1, fpcr, fpsr);
}
if (type2 == FPType::QNaN) {
return FPProcessNaN<FPT>(type2, op2, fpcr, fpsr);
}
return std::nullopt;
}
template std::optional<u16> FPProcessNaNs<u16>(FPType type1, FPType type2, u16 op1, u16 op2, FPCR fpcr, FPSR& fpsr);
template std::optional<u32> FPProcessNaNs<u32>(FPType type1, FPType type2, u32 op1, u32 op2, FPCR fpcr, FPSR& fpsr);
template std::optional<u64> FPProcessNaNs<u64>(FPType type1, FPType type2, u64 op1, u64 op2, FPCR fpcr, FPSR& fpsr);
template<typename FPT>
std::optional<FPT> FPProcessNaNs3(FPType type1, FPType type2, FPType type3, FPT op1, FPT op2, FPT op3, FPCR fpcr, FPSR& fpsr) {
if (type1 == FPType::SNaN) {
return FPProcessNaN<FPT>(type1, op1, fpcr, fpsr);
}
if (type2 == FPType::SNaN) {
return FPProcessNaN<FPT>(type2, op2, fpcr, fpsr);
}
if (type3 == FPType::SNaN) {
return FPProcessNaN<FPT>(type3, op3, fpcr, fpsr);
}
if (type1 == FPType::QNaN) {
return FPProcessNaN<FPT>(type1, op1, fpcr, fpsr);
}
if (type2 == FPType::QNaN) {
return FPProcessNaN<FPT>(type2, op2, fpcr, fpsr);
}
if (type3 == FPType::QNaN) {
return FPProcessNaN<FPT>(type3, op3, fpcr, fpsr);
}
return std::nullopt;
}
template std::optional<u16> FPProcessNaNs3<u16>(FPType type1, FPType type2, FPType type3, u16 op1, u16 op2, u16 op3, FPCR fpcr, FPSR& fpsr);
template std::optional<u32> FPProcessNaNs3<u32>(FPType type1, FPType type2, FPType type3, u32 op1, u32 op2, u32 op3, FPCR fpcr, FPSR& fpsr);
template std::optional<u64> FPProcessNaNs3<u64>(FPType type1, FPType type2, FPType type3, u64 op1, u64 op2, u64 op3, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,25 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <optional>
namespace Dynarmic::FP {
class FPCR;
class FPSR;
enum class FPType;
template<typename FPT>
FPT FPProcessNaN(FPType type, FPT op, FPCR fpcr, FPSR& fpsr);
template<typename FPT>
std::optional<FPT> FPProcessNaNs(FPType type1, FPType type2, FPT op1, FPT op2, FPCR fpcr, FPSR& fpsr);
template<typename FPT>
std::optional<FPT> FPProcessNaNs3(FPType type1, FPType type2, FPType type3, FPT op1, FPT op2, FPT op3, FPCR fpcr, FPSR& fpsr);
} // namespace Dynarmic::FP

View File

@@ -0,0 +1,27 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
namespace Dynarmic::FP {
/// Ordering of first four values is important as they correspond to bits in FPCR.
enum class RoundingMode {
/// Round to nearest floating point. If there is a tie, round to nearest even digit in required position.
ToNearest_TieEven,
/// Round up towards positive infinity.
TowardsPlusInfinity,
/// Round downwards towards negative infinity.
TowardsMinusInfinity,
/// Truncate towards zero.
TowardsZero,
/// Round to nearest floating point. If there is a tie, round away from zero.
ToNearest_TieAwayFromZero,
/// Von Neumann rounding (as modified by Brent). Also known as sticky rounding.
/// Set the least significant bit to 1 if the result is not exact.
ToOdd,
};
} // namespace Dynarmic::FP

191
externals/dynarmic/src/common/fp/unpacked.cpp vendored Executable file
View File

@@ -0,0 +1,191 @@
/* 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<typename FPT>
std::tuple<FPType, bool, FPUnpacked> FPUnpackBase(FPT op, FPCR fpcr, [[maybe_unused]] FPSR& fpsr) {
constexpr size_t sign_bit = FPInfo<FPT>::exponent_width + FPInfo<FPT>::explicit_mantissa_width;
constexpr size_t exponent_high_bit = FPInfo<FPT>::exponent_width + FPInfo<FPT>::explicit_mantissa_width - 1;
constexpr size_t exponent_low_bit = FPInfo<FPT>::explicit_mantissa_width;
constexpr size_t mantissa_high_bit = FPInfo<FPT>::explicit_mantissa_width - 1;
constexpr size_t mantissa_low_bit = 0;
constexpr int denormal_exponent = FPInfo<FPT>::exponent_min - int(FPInfo<FPT>::explicit_mantissa_width);
constexpr bool is_half_precision = std::is_same_v<FPT, u16>;
const bool sign = Common::Bit<sign_bit>(op);
const FPT exp_raw = Common::Bits<exponent_low_bit, exponent_high_bit>(op);
const FPT frac_raw = Common::Bits<mantissa_low_bit, mantissa_high_bit>(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<FPT>(FPInfo<FPT>::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<mantissa_high_bit>(frac_raw);
return {is_quiet ? FPType::QNaN : FPType::SNaN, sign, {sign, 0, 0}};
}
const int exp = static_cast<int>(exp_raw) - FPInfo<FPT>::exponent_bias;
const u64 frac = static_cast<u64>(frac_raw | FPInfo<FPT>::implicit_leading_bit) << (normalized_point_position - FPInfo<FPT>::explicit_mantissa_width);
return {FPType::Nonzero, sign, {sign, exp, frac}};
}
template std::tuple<FPType, bool, FPUnpacked> FPUnpackBase<u16>(u16 op, FPCR fpcr, FPSR& fpsr);
template std::tuple<FPType, bool, FPUnpacked> FPUnpackBase<u32>(u32 op, FPCR fpcr, FPSR& fpsr);
template std::tuple<FPType, bool, FPUnpacked> FPUnpackBase<u64>(u64 op, FPCR fpcr, FPSR& fpsr);
template<size_t F>
std::tuple<bool, int, u64, ResidualError> 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<int>(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<typename FPT>
FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) {
ASSERT(op.mantissa != 0);
ASSERT(rounding != RoundingMode::ToNearest_TieAwayFromZero);
constexpr int minimum_exp = FPInfo<FPT>::exponent_min;
constexpr size_t E = FPInfo<FPT>::exponent_width;
constexpr size_t F = FPInfo<FPT>::explicit_mantissa_width;
constexpr bool isFP16 = FPInfo<FPT>::total_width == 16;
auto [sign, exponent, mantissa, error] = Normalize<F>(op);
if (((!isFP16 && fpcr.FZ()) || (isFP16 && fpcr.FZ16())) && exponent < minimum_exp) {
fpsr.UFC(true);
return FPInfo<FPT>::Zero(sign);
}
int biased_exp = std::max<int>(exponent - minimum_exp + 1, 0);
if (biased_exp == 0) {
std::tie(sign, exponent, mantissa, error) = Normalize<F>(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<FPT>::mantissa_mask) == FPInfo<FPT>::mantissa_mask) {
// Overflow on rounding up is going to happen
if (mantissa == FPInfo<FPT>::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<FPT>::Infinity(sign) : FPInfo<FPT>::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<FPT>(mantissa) & FPInfo<FPT>::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<FPT>(mantissa) & FPInfo<FPT>::mantissa_mask;
if (error != ResidualError::Zero) {
FPProcessException(FPExc::Inexact, fpcr, fpsr);
}
}
}
return result;
}
template u16 FPRoundBase<u16>(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
template u32 FPRoundBase<u32>(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
template u64 FPRoundBase<u64>(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
} // namespace Dynarmic::FP

88
externals/dynarmic/src/common/fp/unpacked.h vendored Executable file
View File

@@ -0,0 +1,88 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <tuple>
#include "common/common_types.h"
#include "common/fp/fpcr.h"
namespace Dynarmic::FP {
class FPSR;
enum class RoundingMode;
enum class FPType {
Nonzero,
Zero,
Infinity,
QNaN,
SNaN,
};
constexpr size_t normalized_point_position = 62;
/// value = (sign ? -1 : +1) * mantissa/(2^62) * 2^exponent
/// 63rd bit of mantissa is always set (unless value is zero)
struct FPUnpacked {
bool sign;
int exponent;
u64 mantissa;
};
inline bool operator==(const FPUnpacked& a, const FPUnpacked& b) {
return std::tie(a.sign, a.exponent, a.mantissa) == std::tie(b.sign, b.exponent, b.mantissa);
}
/// return value = (sign ? -1 : +1) * value * 2^exponent
constexpr FPUnpacked ToNormalized(bool sign, int exponent, u64 value) {
if (value == 0) {
return {sign, 0, 0};
}
const int highest_bit = Common::HighestSetBit(value);
const int offset = static_cast<int>(normalized_point_position) - highest_bit;
value <<= offset;
exponent -= offset - normalized_point_position;
return {sign, exponent, value};
}
template<typename FPT>
std::tuple<FPType, bool, FPUnpacked> FPUnpackBase(FPT op, FPCR fpcr, FPSR& fpsr);
template<typename FPT>
std::tuple<FPType, bool, FPUnpacked> FPUnpack(FPT op, FPCR fpcr, FPSR& fpsr) {
fpcr.AHP(false);
return FPUnpackBase(op, fpcr, fpsr);
}
template<typename FPT>
std::tuple<FPType, bool, FPUnpacked> FPUnpackCV(FPT op, FPCR fpcr, FPSR& fpsr) {
fpcr.FZ16(false);
return FPUnpackBase(op, fpcr, fpsr);
}
template<typename FPT>
FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
template<typename FPT>
FPT FPRound(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) {
fpcr.AHP(false);
return FPRoundBase<FPT>(op, fpcr, rounding, fpsr);
}
template<typename FPT>
FPT FPRoundCV(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) {
fpcr.FZ16(false);
return FPRoundBase<FPT>(op, fpcr, rounding, fpsr);
}
template<typename FPT>
FPT FPRound(FPUnpacked op, FPCR fpcr, FPSR& fpsr) {
return FPRound<FPT>(op, fpcr, fpcr.RMode(), fpsr);
}
} // namespace Dynarmic::FP

99
externals/dynarmic/src/common/fp/util.h vendored Executable file
View File

@@ -0,0 +1,99 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <optional>
#include "common/fp/fpcr.h"
#include "common/fp/info.h"
namespace Dynarmic::FP {
/// Is floating point value a zero?
template<typename FPT>
inline bool IsZero(FPT value, FPCR fpcr) {
if (fpcr.FZ()) {
return (value & FPInfo<FPT>::exponent_mask) == 0;
}
return (value & ~FPInfo<FPT>::sign_mask) == 0;
}
/// Is floating point value an infinity?
template<typename FPT>
constexpr bool IsInf(FPT value) {
return (value & ~FPInfo<FPT>::sign_mask) == FPInfo<FPT>::Infinity(false);
}
/// Is floating point value a QNaN?
template<typename FPT>
constexpr bool IsQNaN(FPT value) {
constexpr FPT qnan_bits = FPInfo<FPT>::exponent_mask | FPInfo<FPT>::mantissa_msb;
return (value & qnan_bits) == qnan_bits;
}
/// Is floating point value a SNaN?
template<typename FPT>
constexpr bool IsSNaN(FPT value) {
constexpr FPT qnan_bits = FPInfo<FPT>::exponent_mask | FPInfo<FPT>::mantissa_msb;
constexpr FPT snan_bits = FPInfo<FPT>::exponent_mask;
return (value & qnan_bits) == snan_bits && (value & FPInfo<FPT>::mantissa_mask) != 0;
}
/// Is floating point value a NaN?
template<typename FPT>
constexpr bool IsNaN(FPT value) {
return IsQNaN(value) || IsSNaN(value);
}
/// Given a single argument, return the NaN value which would be returned by an ARM processor.
/// If the argument isn't a NaN, returns std::nullopt.
template<typename FPT>
constexpr std::optional<FPT> ProcessNaNs(FPT a) {
if (IsSNaN(a)) {
return a | FPInfo<FPT>::mantissa_msb;
} else if (IsQNaN(a)) {
return a;
}
return std::nullopt;
}
/// Given a pair of arguments, return the NaN value which would be returned by an ARM processor.
/// If neither argument is a NaN, returns std::nullopt.
template<typename FPT>
constexpr std::optional<FPT> ProcessNaNs(FPT a, FPT b) {
if (IsSNaN(a)) {
return a | FPInfo<FPT>::mantissa_msb;
} else if (IsSNaN(b)) {
return b | FPInfo<FPT>::mantissa_msb;
} else if (IsQNaN(a)) {
return a;
} else if (IsQNaN(b)) {
return b;
}
return std::nullopt;
}
/// Given three arguments, return the NaN value which would be returned by an ARM processor.
/// If none of the arguments is a NaN, returns std::nullopt.
template<typename FPT>
constexpr std::optional<FPT> ProcessNaNs(FPT a, FPT b, FPT c) {
if (IsSNaN(a)) {
return a | FPInfo<FPT>::mantissa_msb;
} else if (IsSNaN(b)) {
return b | FPInfo<FPT>::mantissa_msb;
} else if (IsSNaN(c)) {
return c | FPInfo<FPT>::mantissa_msb;
} else if (IsQNaN(a)) {
return a;
} else if (IsQNaN(b)) {
return b;
} else if (IsQNaN(c)) {
return c;
}
return std::nullopt;
}
} // namespace Dynarmic::FP