early-access version 1255
This commit is contained in:
207
externals/dynarmic/src/common/fp/fpcr.h
vendored
Executable file
207
externals/dynarmic/src/common/fp/fpcr.h
vendored
Executable 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
159
externals/dynarmic/src/common/fp/fpsr.h
vendored
Executable 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
89
externals/dynarmic/src/common/fp/fused.cpp
vendored
Executable 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
15
externals/dynarmic/src/common/fp/fused.h
vendored
Executable 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
138
externals/dynarmic/src/common/fp/info.h
vendored
Executable 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
|
46
externals/dynarmic/src/common/fp/mantissa_util.h
vendored
Executable file
46
externals/dynarmic/src/common/fp/mantissa_util.h
vendored
Executable 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
17
externals/dynarmic/src/common/fp/op.h
vendored
Executable 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"
|
40
externals/dynarmic/src/common/fp/op/FPCompare.cpp
vendored
Executable file
40
externals/dynarmic/src/common/fp/op/FPCompare.cpp
vendored
Executable 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
|
16
externals/dynarmic/src/common/fp/op/FPCompare.h
vendored
Executable file
16
externals/dynarmic/src/common/fp/op/FPCompare.h
vendored
Executable 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
|
89
externals/dynarmic/src/common/fp/op/FPConvert.cpp
vendored
Executable file
89
externals/dynarmic/src/common/fp/op/FPConvert.cpp
vendored
Executable 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
|
17
externals/dynarmic/src/common/fp/op/FPConvert.h
vendored
Executable file
17
externals/dynarmic/src/common/fp/op/FPConvert.h
vendored
Executable 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
|
79
externals/dynarmic/src/common/fp/op/FPMulAdd.cpp
vendored
Executable file
79
externals/dynarmic/src/common/fp/op/FPMulAdd.cpp
vendored
Executable 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
|
16
externals/dynarmic/src/common/fp/op/FPMulAdd.h
vendored
Executable file
16
externals/dynarmic/src/common/fp/op/FPMulAdd.h
vendored
Executable 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
17
externals/dynarmic/src/common/fp/op/FPNeg.h
vendored
Executable 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
|
56
externals/dynarmic/src/common/fp/op/FPRSqrtEstimate.cpp
vendored
Executable file
56
externals/dynarmic/src/common/fp/op/FPRSqrtEstimate.cpp
vendored
Executable 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
|
16
externals/dynarmic/src/common/fp/op/FPRSqrtEstimate.h
vendored
Executable file
16
externals/dynarmic/src/common/fp/op/FPRSqrtEstimate.h
vendored
Executable 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
|
56
externals/dynarmic/src/common/fp/op/FPRSqrtStepFused.cpp
vendored
Executable file
56
externals/dynarmic/src/common/fp/op/FPRSqrtStepFused.cpp
vendored
Executable 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
|
16
externals/dynarmic/src/common/fp/op/FPRSqrtStepFused.h
vendored
Executable file
16
externals/dynarmic/src/common/fp/op/FPRSqrtStepFused.h
vendored
Executable 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
|
98
externals/dynarmic/src/common/fp/op/FPRecipEstimate.cpp
vendored
Executable file
98
externals/dynarmic/src/common/fp/op/FPRecipEstimate.cpp
vendored
Executable 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
|
16
externals/dynarmic/src/common/fp/op/FPRecipEstimate.h
vendored
Executable file
16
externals/dynarmic/src/common/fp/op/FPRecipEstimate.h
vendored
Executable 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
|
57
externals/dynarmic/src/common/fp/op/FPRecipExponent.cpp
vendored
Executable file
57
externals/dynarmic/src/common/fp/op/FPRecipExponent.cpp
vendored
Executable 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
|
16
externals/dynarmic/src/common/fp/op/FPRecipExponent.h
vendored
Executable file
16
externals/dynarmic/src/common/fp/op/FPRecipExponent.h
vendored
Executable 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
|
55
externals/dynarmic/src/common/fp/op/FPRecipStepFused.cpp
vendored
Executable file
55
externals/dynarmic/src/common/fp/op/FPRecipStepFused.cpp
vendored
Executable 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
|
16
externals/dynarmic/src/common/fp/op/FPRecipStepFused.h
vendored
Executable file
16
externals/dynarmic/src/common/fp/op/FPRecipStepFused.h
vendored
Executable 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
|
95
externals/dynarmic/src/common/fp/op/FPRoundInt.cpp
vendored
Executable file
95
externals/dynarmic/src/common/fp/op/FPRoundInt.cpp
vendored
Executable 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
|
19
externals/dynarmic/src/common/fp/op/FPRoundInt.h
vendored
Executable file
19
externals/dynarmic/src/common/fp/op/FPRoundInt.h
vendored
Executable 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
|
101
externals/dynarmic/src/common/fp/op/FPToFixed.cpp
vendored
Executable file
101
externals/dynarmic/src/common/fp/op/FPToFixed.cpp
vendored
Executable 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
|
19
externals/dynarmic/src/common/fp/op/FPToFixed.h
vendored
Executable file
19
externals/dynarmic/src/common/fp/op/FPToFixed.h
vendored
Executable 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
|
57
externals/dynarmic/src/common/fp/process_exception.cpp
vendored
Executable file
57
externals/dynarmic/src/common/fp/process_exception.cpp
vendored
Executable 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
|
24
externals/dynarmic/src/common/fp/process_exception.h
vendored
Executable file
24
externals/dynarmic/src/common/fp/process_exception.h
vendored
Executable 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
|
91
externals/dynarmic/src/common/fp/process_nan.cpp
vendored
Executable file
91
externals/dynarmic/src/common/fp/process_nan.cpp
vendored
Executable 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
|
25
externals/dynarmic/src/common/fp/process_nan.h
vendored
Executable file
25
externals/dynarmic/src/common/fp/process_nan.h
vendored
Executable 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
|
27
externals/dynarmic/src/common/fp/rounding_mode.h
vendored
Executable file
27
externals/dynarmic/src/common/fp/rounding_mode.h
vendored
Executable 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
191
externals/dynarmic/src/common/fp/unpacked.cpp
vendored
Executable 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
88
externals/dynarmic/src/common/fp/unpacked.h
vendored
Executable 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
99
externals/dynarmic/src/common/fp/util.h
vendored
Executable 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
|
Reference in New Issue
Block a user