early-access version 1255
This commit is contained in:
21
externals/dynarmic/src/common/assert.cpp
vendored
Executable file
21
externals/dynarmic/src/common/assert.cpp
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
[[noreturn]] void Terminate(fmt::string_view msg, fmt::format_args args) {
|
||||
fmt::print(stderr, "dynarmic assertion failed: ");
|
||||
fmt::vprint(stderr, msg, args);
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
69
externals/dynarmic/src/common/assert.h
vendored
Executable file
69
externals/dynarmic/src/common/assert.h
vendored
Executable file
@@ -0,0 +1,69 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/unlikely.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
[[noreturn]] void Terminate(fmt::string_view msg, fmt::format_args args);
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename... Ts>
|
||||
[[noreturn]] void TerminateHelper(fmt::string_view msg, Ts... args) {
|
||||
Terminate(msg, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace Dynarmic::Common
|
||||
|
||||
#if defined(NDEBUG)
|
||||
#if defined(__clang) || defined(__GNUC__)
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#define ASSUME(expr) [&]{ if (!(expr)) __builtin_unreachable(); }()
|
||||
#elif defined(_MSC_VER)
|
||||
#define UNREACHABLE() __assume(0)
|
||||
#define ASSUME(expr) __assume(expr)
|
||||
#else
|
||||
#define UNREACHABLE() ASSERT_FALSE("Unreachable code!")
|
||||
#define ASSUME(expr)
|
||||
#endif
|
||||
#else
|
||||
#define UNREACHABLE() ASSERT_FALSE("Unreachable code!")
|
||||
#define ASSUME(expr)
|
||||
#endif
|
||||
|
||||
#ifdef DYNARMIC_IGNORE_ASSERTS
|
||||
#define ASSERT(expr) ASSUME(expr)
|
||||
#define ASSERT_MSG(expr, ...) ASSUME(expr)
|
||||
#define ASSERT_FALSE(...) UNREACHABLE()
|
||||
#else
|
||||
#define ASSERT(expr) \
|
||||
[&]{ \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
::Dynarmic::Common::detail::TerminateHelper(#expr); \
|
||||
} \
|
||||
}()
|
||||
#define ASSERT_MSG(expr, ...) \
|
||||
[&]{ \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
::Dynarmic::Common::detail::TerminateHelper(#expr "\nMessage: " __VA_ARGS__); \
|
||||
} \
|
||||
}()
|
||||
#define ASSERT_FALSE(...) ::Dynarmic::Common::detail::TerminateHelper("false\nMessage: " __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG) || defined(DYNARMIC_IGNORE_ASSERTS)
|
||||
#define DEBUG_ASSERT(expr) ASSUME(expr)
|
||||
#define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
|
||||
#else
|
||||
#define DEBUG_ASSERT(expr) ASSERT(expr)
|
||||
#define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
|
||||
#endif
|
243
externals/dynarmic/src/common/bit_util.h
vendored
Executable file
243
externals/dynarmic/src/common/bit_util.h
vendored
Executable file
@@ -0,0 +1,243 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
/// The size of a type in terms of bits
|
||||
template<typename T>
|
||||
constexpr size_t BitSize() {
|
||||
return sizeof(T) * CHAR_BIT;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T Ones(size_t count) {
|
||||
ASSERT_MSG(count <= BitSize<T>(), "count larger than bitsize of T");
|
||||
if (count == BitSize<T>())
|
||||
return static_cast<T>(~static_cast<T>(0));
|
||||
return ~(static_cast<T>(~static_cast<T>(0)) << count);
|
||||
}
|
||||
|
||||
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
|
||||
template<typename T>
|
||||
constexpr T Bits(const size_t begin_bit, const size_t end_bit, const T value) {
|
||||
ASSERT_MSG(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
|
||||
ASSERT_MSG(begin_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
|
||||
ASSERT_MSG(end_bit < BitSize<T>(), "end_bit must be smaller than size of T");
|
||||
|
||||
return (value >> begin_bit) & Ones<T>(end_bit - begin_bit + 1);
|
||||
}
|
||||
|
||||
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
|
||||
template<size_t begin_bit, size_t end_bit, typename T>
|
||||
constexpr T Bits(const T value) {
|
||||
static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
|
||||
static_assert(begin_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
|
||||
static_assert(end_bit < BitSize<T>(), "end_bit must be smaller than size of T");
|
||||
|
||||
return (value >> begin_bit) & Ones<T>(end_bit - begin_bit + 1);
|
||||
}
|
||||
|
||||
/// Create a mask of type T for bits [begin_bit, end_bit] inclusive.
|
||||
template<size_t begin_bit, size_t end_bit, typename T>
|
||||
constexpr T Mask() {
|
||||
static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
|
||||
static_assert(begin_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
|
||||
static_assert(end_bit < BitSize<T>(), "end_bit must be smaller than size of T");
|
||||
|
||||
return Ones<T>(end_bit - begin_bit + 1) << begin_bit;
|
||||
}
|
||||
|
||||
/// Clears bits [begin_bit, end_bit] inclusive of value of type T.
|
||||
template<size_t begin_bit, size_t end_bit, typename T>
|
||||
constexpr T ClearBits(const T value) {
|
||||
return value & ~Mask<begin_bit, end_bit, T>();
|
||||
}
|
||||
|
||||
/// Modifies bits [begin_bit, end_bit] inclusive of value of type T.
|
||||
template<size_t begin_bit, size_t end_bit, typename T>
|
||||
constexpr T ModifyBits(const T value, const T new_bits) {
|
||||
return ClearBits<begin_bit, end_bit, T>(value) | ((new_bits << begin_bit) & Mask<begin_bit, end_bit, T>());
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4554)
|
||||
#endif
|
||||
/// Extracts a single bit at bit_position from value of type T.
|
||||
template<typename T>
|
||||
inline bool Bit(size_t bit_position, const T value) {
|
||||
ASSERT_MSG(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||
|
||||
return ((value >> bit_position) & 1) != 0;
|
||||
}
|
||||
|
||||
/// Extracts a single bit at bit_position from value of type T.
|
||||
template<size_t bit_position, typename T>
|
||||
constexpr bool Bit(const T value) {
|
||||
static_assert(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||
|
||||
return Bit<T>(bit_position, value);
|
||||
}
|
||||
|
||||
/// Clears a single bit at bit_position from value of type T.
|
||||
template<typename T>
|
||||
inline T ClearBit(size_t bit_position, const T value) {
|
||||
ASSERT_MSG(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||
|
||||
return value & ~(static_cast<T>(1) << bit_position);
|
||||
}
|
||||
|
||||
/// Clears a single bit at bit_position from value of type T.
|
||||
template<size_t bit_position, typename T>
|
||||
constexpr T ClearBit(const T value) {
|
||||
static_assert(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||
|
||||
return ClearBit<T>(bit_position, value);
|
||||
}
|
||||
|
||||
/// Modifies a single bit at bit_position from value of type T.
|
||||
template<typename T>
|
||||
inline T ModifyBit(size_t bit_position, const T value, bool new_bit) {
|
||||
ASSERT_MSG(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||
|
||||
return ClearBit<T>(bit_position, value) | (static_cast<T>(new_bit) << bit_position);
|
||||
}
|
||||
|
||||
/// Modifies a single bit at bit_position from value of type T.
|
||||
template<size_t bit_position, typename T>
|
||||
constexpr T ModifyBit(const T value, bool new_bit) {
|
||||
static_assert(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
|
||||
|
||||
return ModifyBit<T>(bit_position, value, new_bit);
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
|
||||
template<size_t bit_count, typename T>
|
||||
constexpr T SignExtend(const T value) {
|
||||
static_assert(bit_count <= BitSize<T>(), "bit_count larger than bitsize of T");
|
||||
|
||||
constexpr T mask = static_cast<T>(1ULL << bit_count) - 1;
|
||||
const bool signbit = Bit<bit_count - 1, T>(value);
|
||||
if (signbit) {
|
||||
return value | ~mask;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
|
||||
template<typename T>
|
||||
inline T SignExtend(const size_t bit_count, const T value) {
|
||||
ASSERT_MSG(bit_count <= BitSize<T>(), "bit_count larger than bitsize of T");
|
||||
|
||||
const T mask = static_cast<T>(1ULL << bit_count) - 1;
|
||||
const bool signbit = Bit<T>(bit_count - 1, value);
|
||||
if (signbit) {
|
||||
return value | ~mask;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename Integral>
|
||||
inline size_t BitCount(Integral value) {
|
||||
return std::bitset<BitSize<Integral>()>(value).count();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t CountLeadingZeros(T value) {
|
||||
auto x = static_cast<std::make_unsigned_t<T>>(value);
|
||||
size_t result = BitSize<T>();
|
||||
while (x != 0) {
|
||||
x >>= 1;
|
||||
result--;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr int HighestSetBit(T value) {
|
||||
auto x = static_cast<std::make_unsigned_t<T>>(value);
|
||||
int result = -1;
|
||||
while (x != 0) {
|
||||
x >>= 1;
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t LowestSetBit(T value) {
|
||||
auto x = static_cast<std::make_unsigned_t<T>>(value);
|
||||
if (x == 0)
|
||||
return BitSize<T>();
|
||||
|
||||
size_t result = 0;
|
||||
while ((x & 1) == 0) {
|
||||
x >>= 1;
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool MostSignificantBit(T value) {
|
||||
return Bit<BitSize<T>() - 1, T>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Replicate(T value, size_t element_size) {
|
||||
ASSERT_MSG(BitSize<T>() % element_size == 0, "bitsize of T not divisible by element_size");
|
||||
if (element_size == BitSize<T>())
|
||||
return value;
|
||||
return Replicate(value | (value << element_size), element_size * 2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T RotateRight(T value, size_t amount) {
|
||||
amount %= BitSize<T>();
|
||||
|
||||
if (amount == 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
auto x = static_cast<std::make_unsigned_t<T>>(value);
|
||||
return static_cast<T>((x >> amount) | (x << (BitSize<T>() - amount)));
|
||||
}
|
||||
|
||||
constexpr u16 Swap16(u16 value) {
|
||||
return static_cast<u16>(u32{value} >> 8 | u32{value} << 8);
|
||||
}
|
||||
|
||||
constexpr u32 Swap32(u32 value) {
|
||||
return ((value & 0xFF000000U) >> 24) |
|
||||
((value & 0x00FF0000U) >> 8) |
|
||||
((value & 0x0000FF00U) << 8) |
|
||||
((value & 0x000000FFU) << 24);
|
||||
}
|
||||
|
||||
constexpr u64 Swap64(u64 value) {
|
||||
return ((value & 0xFF00000000000000ULL) >> 56) |
|
||||
((value & 0x00FF000000000000ULL) >> 40) |
|
||||
((value & 0x0000FF0000000000ULL) >> 24) |
|
||||
((value & 0x000000FF00000000ULL) >> 8) |
|
||||
((value & 0x00000000FF000000ULL) << 8) |
|
||||
((value & 0x0000000000FF0000ULL) << 24) |
|
||||
((value & 0x000000000000FF00ULL) << 40) |
|
||||
((value & 0x00000000000000FFULL) << 56);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
45
externals/dynarmic/src/common/cast_util.h
vendored
Executable file
45
externals/dynarmic/src/common/cast_util.h
vendored
Executable file
@@ -0,0 +1,45 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mp/traits/function_info.h>
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
/// Reinterpret objects of one type as another by bit-casting between object representations.
|
||||
template <class Dest, class Source>
|
||||
inline Dest BitCast(const Source& source) noexcept {
|
||||
static_assert(sizeof(Dest) == sizeof(Source), "size of destination and source objects must be equal");
|
||||
static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable_v<Source>, "source type must be trivially copyable");
|
||||
|
||||
std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
|
||||
std::memcpy(&dest, &source, sizeof(dest));
|
||||
return reinterpret_cast<Dest&>(dest);
|
||||
}
|
||||
|
||||
/// Reinterpret objects of any arbitrary type as another type by bit-casting between object representations.
|
||||
/// Note that here we do not verify if source has enough bytes to read from.
|
||||
template <class Dest, class SourcePtr>
|
||||
inline Dest BitCastPointee(const SourcePtr source) noexcept {
|
||||
static_assert(sizeof(SourcePtr) == sizeof(void*), "source pointer must have size of a pointer");
|
||||
static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
|
||||
|
||||
std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
|
||||
std::memcpy(&dest, BitCast<void*>(source), sizeof(dest));
|
||||
return reinterpret_cast<Dest&>(dest);
|
||||
}
|
||||
|
||||
/// Cast a lambda into an equivalent function pointer.
|
||||
template <class Function>
|
||||
inline auto FptrCast(Function f) noexcept {
|
||||
return static_cast<mp::equivalent_function_type<Function>*>(f);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
28
externals/dynarmic/src/common/common_types.h
vendored
Executable file
28
externals/dynarmic/src/common/common_types.h
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using uptr = std::uintptr_t;
|
||||
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
using sptr = std::intptr_t;
|
||||
|
||||
using size_t = std::size_t;
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
static_assert(sizeof(f32) == sizeof(u32), "f32 must be 32 bits wide");
|
||||
static_assert(sizeof(f64) == sizeof(u64), "f64 must be 64 bits wide");
|
181
externals/dynarmic/src/common/crypto/aes.cpp
vendored
Executable file
181
externals/dynarmic/src/common/crypto/aes.cpp
vendored
Executable file
@@ -0,0 +1,181 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/crypto/aes.h"
|
||||
|
||||
namespace Dynarmic::Common::Crypto::AES {
|
||||
|
||||
using SubstitutionTable = std::array<u8, 256>;
|
||||
|
||||
// See section 5.1.1 Figure 7 in FIPS 197
|
||||
constexpr SubstitutionTable substitution_box{{
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
|
||||
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
|
||||
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
||||
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
|
||||
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
|
||||
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
||||
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
|
||||
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
|
||||
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
|
||||
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
|
||||
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
||||
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
|
||||
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
|
||||
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
||||
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
|
||||
}};
|
||||
|
||||
// See section 5.3.2 Figure 14 in FIPS 197
|
||||
constexpr SubstitutionTable inverse_substitution_box{{
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
|
||||
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
|
||||
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
|
||||
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
|
||||
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
|
||||
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
|
||||
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
|
||||
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
|
||||
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
|
||||
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
|
||||
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
|
||||
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
|
||||
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
|
||||
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
|
||||
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
|
||||
}};
|
||||
|
||||
// See section 4.2.1 in FIPS 197.
|
||||
static constexpr u8 xtime(u8 x) {
|
||||
return static_cast<u8>((x << 1) ^ (((x >> 7) & 1) * 0x1B));
|
||||
}
|
||||
|
||||
// Galois Field multiplication.
|
||||
static constexpr u8 Multiply(u8 x, u8 y) {
|
||||
return static_cast<u8>(((y & 1) * x) ^
|
||||
((y >> 1 & 1) * xtime(x)) ^
|
||||
((y >> 2 & 1) * xtime(xtime(x))) ^
|
||||
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
|
||||
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))));
|
||||
}
|
||||
|
||||
static void ShiftRows(State& out_state, const State& state) {
|
||||
// Move zeroth row over
|
||||
out_state[0] = state[0];
|
||||
out_state[4] = state[4];
|
||||
out_state[8] = state[8];
|
||||
out_state[12] = state[12];
|
||||
|
||||
// Rotate first row 1 column left.
|
||||
u8 temp = state[1];
|
||||
out_state[1] = state[5];
|
||||
out_state[5] = state[9];
|
||||
out_state[9] = state[13];
|
||||
out_state[13] = temp;
|
||||
|
||||
// Rotate second row 2 columns left
|
||||
temp = state[2];
|
||||
out_state[2] = state[10];
|
||||
out_state[10] = temp;
|
||||
|
||||
temp = state[6];
|
||||
out_state[6] = state[14];
|
||||
out_state[14] = temp;
|
||||
|
||||
// Rotate third row 3 columns left
|
||||
temp = state[3];
|
||||
out_state[3] = state[15];
|
||||
out_state[15] = state[11];
|
||||
out_state[11] = state[7];
|
||||
out_state[7] = temp;
|
||||
}
|
||||
|
||||
static void InverseShiftRows(State& out_state, const State& state) {
|
||||
// Move zeroth row over
|
||||
out_state[0] = state[0];
|
||||
out_state[4] = state[4];
|
||||
out_state[8] = state[8];
|
||||
out_state[12] = state[12];
|
||||
|
||||
// Rotate first row 1 column right.
|
||||
u8 temp = state[13];
|
||||
out_state[13] = state[9];
|
||||
out_state[9] = state[5];
|
||||
out_state[5] = state[1];
|
||||
out_state[1] = temp;
|
||||
|
||||
// Rotate second row 2 columns right
|
||||
temp = state[2];
|
||||
out_state[2] = state[10];
|
||||
out_state[10] = temp;
|
||||
|
||||
temp = state[6];
|
||||
out_state[6] = state[14];
|
||||
out_state[14] = temp;
|
||||
|
||||
// Rotate third row 3 columns right
|
||||
temp = state[3];
|
||||
out_state[3] = state[7];
|
||||
out_state[7] = state[11];
|
||||
out_state[11] = state[15];
|
||||
out_state[15] = temp;
|
||||
}
|
||||
|
||||
static void SubBytes(State& state, const SubstitutionTable& table) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
state[4 * i + j] = table[state[4 * i + j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecryptSingleRound(State& out_state, const State& state) {
|
||||
InverseShiftRows(out_state, state);
|
||||
SubBytes(out_state, inverse_substitution_box);
|
||||
}
|
||||
|
||||
void EncryptSingleRound(State& out_state, const State& state) {
|
||||
ShiftRows(out_state, state);
|
||||
SubBytes(out_state, substitution_box);
|
||||
}
|
||||
|
||||
void MixColumns(State& out_state, const State& state) {
|
||||
for (size_t i = 0; i < out_state.size(); i += 4) {
|
||||
const u8 a = state[i];
|
||||
const u8 b = state[i + 1];
|
||||
const u8 c = state[i + 2];
|
||||
const u8 d = state[i + 3];
|
||||
|
||||
const u8 tmp = a ^ b ^ c ^ d;
|
||||
|
||||
out_state[i + 0] = a ^ xtime(a ^ b) ^ tmp;
|
||||
out_state[i + 1] = b ^ xtime(b ^ c) ^ tmp;
|
||||
out_state[i + 2] = c ^ xtime(c ^ d) ^ tmp;
|
||||
out_state[i + 3] = d ^ xtime(d ^ a) ^ tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void InverseMixColumns(State& out_state, const State& state) {
|
||||
for (size_t i = 0; i < out_state.size(); i += 4) {
|
||||
const u8 a = state[i];
|
||||
const u8 b = state[i + 1];
|
||||
const u8 c = state[i + 2];
|
||||
const u8 d = state[i + 3];
|
||||
|
||||
out_state[i + 0] = Multiply(a, 0x0E) ^ Multiply(b, 0x0B) ^ Multiply(c, 0x0D) ^ Multiply(d, 0x09);
|
||||
out_state[i + 1] = Multiply(a, 0x09) ^ Multiply(b, 0x0E) ^ Multiply(c, 0x0B) ^ Multiply(d, 0x0D);
|
||||
out_state[i + 2] = Multiply(a, 0x0D) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0E) ^ Multiply(d, 0x0B);
|
||||
out_state[i + 3] = Multiply(a, 0x0B) ^ Multiply(b, 0x0D) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0E);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common::Crypto::AES
|
22
externals/dynarmic/src/common/crypto/aes.h
vendored
Executable file
22
externals/dynarmic/src/common/crypto/aes.h
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Common::Crypto::AES {
|
||||
|
||||
using State = std::array<u8, 16>;
|
||||
|
||||
// Assumes the state has already been XORed by the round key.
|
||||
void DecryptSingleRound(State& out_state, const State& state);
|
||||
void EncryptSingleRound(State& out_state, const State& state);
|
||||
|
||||
void MixColumns(State& out_state, const State& state);
|
||||
void InverseMixColumns(State& out_state, const State& state);
|
||||
|
||||
} // namespace Dynarmic::Common::Crypto::AES
|
169
externals/dynarmic/src/common/crypto/crc32.cpp
vendored
Executable file
169
externals/dynarmic/src/common/crypto/crc32.cpp
vendored
Executable file
@@ -0,0 +1,169 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/crypto/crc32.h"
|
||||
|
||||
namespace Dynarmic::Common::Crypto::CRC32 {
|
||||
|
||||
using CRC32Table = std::array<u32, 256>;
|
||||
|
||||
// CRC32 algorithm that uses polynomial 0x1EDC6F41
|
||||
constexpr CRC32Table castagnoli_table{{
|
||||
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
|
||||
0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
|
||||
0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
|
||||
0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
|
||||
0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
|
||||
0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
|
||||
0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
|
||||
0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
|
||||
0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
|
||||
0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
|
||||
0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
|
||||
0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
|
||||
0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
|
||||
0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
|
||||
0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
|
||||
0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
|
||||
0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
|
||||
0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
|
||||
0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
|
||||
0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
|
||||
0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
|
||||
0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
|
||||
0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
|
||||
0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
|
||||
0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
|
||||
0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
|
||||
0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
|
||||
0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
|
||||
0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
|
||||
0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
|
||||
0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
|
||||
0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
|
||||
0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
|
||||
0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
|
||||
0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
|
||||
0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
|
||||
0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
|
||||
0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
|
||||
0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
|
||||
0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
|
||||
0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
|
||||
0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
|
||||
0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
|
||||
0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
|
||||
0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
|
||||
0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
|
||||
0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
|
||||
0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
|
||||
0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
|
||||
0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
|
||||
0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
|
||||
0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
|
||||
0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
|
||||
0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
|
||||
0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
|
||||
0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
|
||||
0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
|
||||
0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
|
||||
0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
|
||||
0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
|
||||
0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
|
||||
0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
|
||||
0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
|
||||
0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
|
||||
}};
|
||||
|
||||
// CRC32 algorithm that uses polynomial 0x04C11DB7
|
||||
constexpr CRC32Table iso_table{{
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
||||
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
|
||||
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
|
||||
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
|
||||
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
|
||||
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
|
||||
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
|
||||
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
|
||||
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
|
||||
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
|
||||
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
|
||||
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
|
||||
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
|
||||
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
|
||||
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
|
||||
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
|
||||
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
|
||||
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
|
||||
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
|
||||
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
|
||||
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
|
||||
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
}};
|
||||
|
||||
static u32 ComputeCRC32(const CRC32Table& table, u32 crc, const u64 value, int length) {
|
||||
const auto* data = reinterpret_cast<const unsigned char*>(&value);
|
||||
|
||||
while (length-- > 0) {
|
||||
crc = (crc >> 8) ^ table[(crc ^ (*data++)) & 0xFF];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
u32 ComputeCRC32Castagnoli(u32 crc, u64 value, int length) {
|
||||
return ComputeCRC32(castagnoli_table, crc, value, length);
|
||||
}
|
||||
|
||||
u32 ComputeCRC32ISO(u32 crc, u64 value, int length) {
|
||||
return ComputeCRC32(iso_table, crc, value, length);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common::Crypto::CRC32
|
40
externals/dynarmic/src/common/crypto/crc32.h
vendored
Executable file
40
externals/dynarmic/src/common/crypto/crc32.h
vendored
Executable file
@@ -0,0 +1,40 @@
|
||||
/* 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::Common::Crypto::CRC32 {
|
||||
|
||||
/**
|
||||
* Computes a CRC32 value using Castagnoli polynomial (0x1EDC6F41).
|
||||
*
|
||||
* @param crc The initial CRC value
|
||||
* @param value The value to compute the CRC of.
|
||||
* @param length The length of the data to compute.
|
||||
*
|
||||
* @remark @p value is interpreted internally as an array of
|
||||
* unsigned char with @p length data.
|
||||
*
|
||||
* @return The computed CRC32 value.
|
||||
*/
|
||||
u32 ComputeCRC32Castagnoli(u32 crc, u64 value, int length);
|
||||
|
||||
/**
|
||||
* Computes a CRC32 value using the ISO polynomial (0x04C11DB7).
|
||||
*
|
||||
* @param crc The initial CRC value
|
||||
* @param value The value to compute the CRC of.
|
||||
* @param length The length of the data to compute.
|
||||
*
|
||||
* @remark @p value is interpreted internally as an array of
|
||||
* unsigned char with @p length data.
|
||||
*
|
||||
* @return The computed CRC32 value.
|
||||
*/
|
||||
u32 ComputeCRC32ISO(u32 crc, u64 value, int length);
|
||||
|
||||
} // namespace Dynarmic::Common::Crypto::CRC32
|
54
externals/dynarmic/src/common/crypto/sm4.cpp
vendored
Executable file
54
externals/dynarmic/src/common/crypto/sm4.cpp
vendored
Executable file
@@ -0,0 +1,54 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/crypto/sm4.h"
|
||||
|
||||
namespace Dynarmic::Common::Crypto::SM4 {
|
||||
|
||||
using SubstitutionTable = std::array<u8, 256>;
|
||||
|
||||
constexpr SubstitutionTable substitution_box{{
|
||||
0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7,
|
||||
0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
|
||||
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3,
|
||||
0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
|
||||
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A,
|
||||
0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
|
||||
0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95,
|
||||
0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
|
||||
0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA,
|
||||
0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
|
||||
0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B,
|
||||
0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
|
||||
0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2,
|
||||
0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
|
||||
0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52,
|
||||
0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
|
||||
0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5,
|
||||
0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
|
||||
0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55,
|
||||
0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
|
||||
0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60,
|
||||
0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
|
||||
0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F,
|
||||
0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
|
||||
0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F,
|
||||
0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
|
||||
0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD,
|
||||
0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
|
||||
0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E,
|
||||
0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
|
||||
0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20,
|
||||
0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
|
||||
}};
|
||||
|
||||
u8 AccessSubstitutionBox(u8 index) {
|
||||
return substitution_box[index];
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common::Crypto::SM4
|
14
externals/dynarmic/src/common/crypto/sm4.h
vendored
Executable file
14
externals/dynarmic/src/common/crypto/sm4.h
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
/* 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::Common::Crypto::SM4 {
|
||||
|
||||
u8 AccessSubstitutionBox(u8 index);
|
||||
|
||||
} // namespace Dynarmic::Common::Crypto::SM4
|
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
|
375
externals/dynarmic/src/common/intrusive_list.h
vendored
Executable file
375
externals/dynarmic/src/common/intrusive_list.h
vendored
Executable file
@@ -0,0 +1,375 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
template <typename T> class IntrusiveList;
|
||||
template <typename T> class IntrusiveListIterator;
|
||||
|
||||
template <typename T>
|
||||
class IntrusiveListNode {
|
||||
public:
|
||||
bool IsSentinel() const {
|
||||
return is_sentinel;
|
||||
}
|
||||
|
||||
protected:
|
||||
IntrusiveListNode* next = nullptr;
|
||||
IntrusiveListNode* prev = nullptr;
|
||||
bool is_sentinel = false;
|
||||
|
||||
friend class IntrusiveList<T>;
|
||||
friend class IntrusiveListIterator<T>;
|
||||
friend class IntrusiveListIterator<const T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntrusiveListSentinel final : public IntrusiveListNode<T>
|
||||
{
|
||||
using IntrusiveListNode<T>::next;
|
||||
using IntrusiveListNode<T>::prev;
|
||||
using IntrusiveListNode<T>::is_sentinel;
|
||||
|
||||
public:
|
||||
IntrusiveListSentinel() {
|
||||
next = this;
|
||||
prev = this;
|
||||
is_sentinel = true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntrusiveListIterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
// If value_type is const, we want "const IntrusiveListNode<value_type>", not "const IntrusiveListNode<const value_type>"
|
||||
using node_type = std::conditional_t<std::is_const<value_type>::value,
|
||||
const IntrusiveListNode<std::remove_const_t<value_type>>,
|
||||
IntrusiveListNode<value_type>>;
|
||||
using node_pointer = node_type*;
|
||||
using node_reference = node_type&;
|
||||
|
||||
IntrusiveListIterator() = default;
|
||||
IntrusiveListIterator(const IntrusiveListIterator& other) = default;
|
||||
IntrusiveListIterator& operator=(const IntrusiveListIterator& other) = default;
|
||||
|
||||
explicit IntrusiveListIterator(node_pointer list_node) : node(list_node) {
|
||||
}
|
||||
explicit IntrusiveListIterator(pointer data) : node(data) {
|
||||
}
|
||||
explicit IntrusiveListIterator(reference data) : node(&data) {
|
||||
}
|
||||
|
||||
IntrusiveListIterator& operator++() {
|
||||
node = node->next;
|
||||
return *this;
|
||||
}
|
||||
IntrusiveListIterator& operator--() {
|
||||
node = node->prev;
|
||||
return *this;
|
||||
}
|
||||
IntrusiveListIterator operator++(int) {
|
||||
IntrusiveListIterator it(*this);
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
IntrusiveListIterator operator--(int) {
|
||||
IntrusiveListIterator it(*this);
|
||||
--*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
bool operator==(const IntrusiveListIterator& other) const {
|
||||
return node == other.node;
|
||||
}
|
||||
bool operator!=(const IntrusiveListIterator& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
DEBUG_ASSERT(!node->IsSentinel());
|
||||
return static_cast<reference>(*node);
|
||||
}
|
||||
pointer operator->() const {
|
||||
return std::addressof(operator*());
|
||||
}
|
||||
|
||||
node_pointer AsNodePointer() const {
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IntrusiveList<T>;
|
||||
node_pointer node = nullptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntrusiveList {
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = std::size_t;
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = IntrusiveListIterator<value_type>;
|
||||
using const_iterator = IntrusiveListIterator<const value_type>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
/**
|
||||
* Inserts a node at the given location indicated by an iterator.
|
||||
*
|
||||
* @param location The location to insert the node.
|
||||
* @param new_node The node to add.
|
||||
*/
|
||||
iterator insert(iterator location, pointer new_node) {
|
||||
return insert_before(location, new_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a node at the given location, moving the previous
|
||||
* node occupant ahead of the one inserted.
|
||||
*
|
||||
* @param location The location to insert the new node.
|
||||
* @param new_node The node to insert into the list.
|
||||
*/
|
||||
iterator insert_before(iterator location, pointer new_node) {
|
||||
auto existing_node = location.AsNodePointer();
|
||||
|
||||
new_node->next = existing_node;
|
||||
new_node->prev = existing_node->prev;
|
||||
existing_node->prev->next = new_node;
|
||||
existing_node->prev = new_node;
|
||||
|
||||
return iterator(new_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new node into the list ahead of the position indicated.
|
||||
*
|
||||
* @param position Location to insert the node in front of.
|
||||
* @param new_node The node to be inserted into the list.
|
||||
*/
|
||||
iterator insert_after(iterator position, pointer new_node) {
|
||||
if (empty())
|
||||
return insert(begin(), new_node);
|
||||
|
||||
return insert(++position, new_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to the start of the list.
|
||||
* @param node Node to add to the list.
|
||||
*/
|
||||
void push_front(pointer node) {
|
||||
insert(begin(), node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to the end of the list
|
||||
* @param node Node to add to the list.
|
||||
*/
|
||||
void push_back(pointer node) {
|
||||
insert(end(), node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases the node at the front of the list.
|
||||
* @note Must not be called on an empty list.
|
||||
*/
|
||||
void pop_front() {
|
||||
DEBUG_ASSERT(!empty());
|
||||
erase(begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases the node at the back of the list.
|
||||
* @note Must not be called on an empty list.
|
||||
*/
|
||||
void pop_back() {
|
||||
DEBUG_ASSERT(!empty());
|
||||
erase(--end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node from this list
|
||||
* @param it An iterator that points to the node to remove from list.
|
||||
*/
|
||||
pointer remove(iterator& it) {
|
||||
DEBUG_ASSERT(it != end());
|
||||
|
||||
pointer node = &*it++;
|
||||
|
||||
node->prev->next = node->next;
|
||||
node->next->prev = node->prev;
|
||||
#if !defined(NDEBUG)
|
||||
node->next = nullptr;
|
||||
node->prev = nullptr;
|
||||
#endif
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node from this list
|
||||
* @param it A constant iterator that points to the node to remove from list.
|
||||
*/
|
||||
pointer remove(const iterator& it) {
|
||||
iterator copy = it;
|
||||
return remove(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node from this list.
|
||||
* @param node A pointer to the node to remove.
|
||||
*/
|
||||
pointer remove(pointer node) {
|
||||
return remove(iterator(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node from this list.
|
||||
* @param node A reference to the node to remove.
|
||||
*/
|
||||
pointer remove(reference node) {
|
||||
return remove(iterator(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this list empty?
|
||||
* @returns true if there are no nodes in this list.
|
||||
*/
|
||||
bool empty() const {
|
||||
return root->next == root.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of elements within this list.
|
||||
* @return the number of elements in this list.
|
||||
*/
|
||||
size_type size() const {
|
||||
return static_cast<size_type>(std::distance(begin(), end()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a reference to the node at the front of the list.
|
||||
* @note Must not be called on an empty list.
|
||||
*/
|
||||
reference front() {
|
||||
DEBUG_ASSERT(!empty());
|
||||
return *begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a constant reference to the node at the front of the list.
|
||||
* @note Must not be called on an empty list.
|
||||
*/
|
||||
const_reference front() const {
|
||||
DEBUG_ASSERT(!empty());
|
||||
return *begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a reference to the node at the back of the list.
|
||||
* @note Must not be called on an empty list.
|
||||
*/
|
||||
reference back() {
|
||||
DEBUG_ASSERT(!empty());
|
||||
return *--end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a constant reference to the node at the back of the list.
|
||||
* @note Must not be called on an empty list.
|
||||
*/
|
||||
const_reference back() const {
|
||||
DEBUG_ASSERT(!empty());
|
||||
return *--end();
|
||||
}
|
||||
|
||||
// Iterator interface
|
||||
iterator begin() { return iterator(root->next); }
|
||||
const_iterator begin() const { return const_iterator(root->next); }
|
||||
const_iterator cbegin() const { return begin(); }
|
||||
|
||||
iterator end() { return iterator(root.get()); }
|
||||
const_iterator end() const { return const_iterator(root.get()); }
|
||||
const_iterator cend() const { return end(); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
||||
const_reverse_iterator crbegin() const { return rbegin(); }
|
||||
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
||||
const_reverse_iterator crend() const { return rend(); }
|
||||
|
||||
/**
|
||||
* Erases a node from the list, indicated by an iterator.
|
||||
* @param it The iterator that points to the node to erase.
|
||||
*/
|
||||
iterator erase(iterator it) {
|
||||
remove(it);
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases a node from this list.
|
||||
* @param node A pointer to the node to erase from this list.
|
||||
*/
|
||||
iterator erase(pointer node) {
|
||||
return erase(iterator(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases a node from this list.
|
||||
* @param node A reference to the node to erase from this list.
|
||||
*/
|
||||
iterator erase(reference node) {
|
||||
return erase(iterator(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchanges contents of this list with another list instance.
|
||||
* @param other The other list to swap with.
|
||||
*/
|
||||
void swap(IntrusiveList& other) noexcept {
|
||||
root.swap(other.root);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<IntrusiveListNode<T>> root = std::make_shared<IntrusiveListSentinel<T>>();
|
||||
};
|
||||
|
||||
/**
|
||||
* Exchanges contents of an intrusive list with another intrusive list.
|
||||
* @tparam T The type of data being kept track of by the lists.
|
||||
* @param lhs The first list.
|
||||
* @param rhs The second list.
|
||||
*/
|
||||
template <typename T>
|
||||
void swap(IntrusiveList<T>& lhs, IntrusiveList<T>& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
35
externals/dynarmic/src/common/iterator_util.h
vendored
Executable file
35
externals/dynarmic/src/common/iterator_util.h
vendored
Executable file
@@ -0,0 +1,35 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
struct ReverseAdapter {
|
||||
T& iterable;
|
||||
|
||||
constexpr auto begin() {
|
||||
using namespace std;
|
||||
return rbegin(iterable);
|
||||
}
|
||||
|
||||
constexpr auto end() {
|
||||
using namespace std;
|
||||
return rend(iterable);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
constexpr detail::ReverseAdapter<T> Reverse(T&& iterable) {
|
||||
return detail::ReverseAdapter<T>{iterable};
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
103
externals/dynarmic/src/common/llvm_disassemble.cpp
vendored
Executable file
103
externals/dynarmic/src/common/llvm_disassemble.cpp
vendored
Executable file
@@ -0,0 +1,103 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifdef DYNARMIC_USE_LLVM
|
||||
#include <llvm-c/Disassembler.h>
|
||||
#include <llvm-c/Target.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/llvm_disassemble.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
std::string DisassembleX64(const void* begin, const void* end) {
|
||||
std::string result;
|
||||
|
||||
#ifdef DYNARMIC_USE_LLVM
|
||||
LLVMInitializeX86TargetInfo();
|
||||
LLVMInitializeX86TargetMC();
|
||||
LLVMInitializeX86Disassembler();
|
||||
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("x86_64", nullptr, 0, nullptr, nullptr);
|
||||
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
|
||||
|
||||
const u8* pos = reinterpret_cast<const u8*>(begin);
|
||||
size_t remaining = reinterpret_cast<size_t>(end) - reinterpret_cast<size_t>(pos);
|
||||
while (pos < end) {
|
||||
char buffer[80];
|
||||
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(pos), remaining, reinterpret_cast<u64>(pos), buffer, sizeof(buffer));
|
||||
ASSERT(inst_size);
|
||||
for (const u8* i = pos; i < pos + inst_size; i++)
|
||||
result += fmt::format("{:02x} ", *i);
|
||||
for (size_t i = inst_size; i < 10; i++)
|
||||
result += " ";
|
||||
result += buffer;
|
||||
result += '\n';
|
||||
|
||||
pos += inst_size;
|
||||
remaining -= inst_size;
|
||||
}
|
||||
|
||||
LLVMDisasmDispose(llvm_ctx);
|
||||
#else
|
||||
result += fmt::format("(recompile with DYNARMIC_USE_LLVM=ON to disassemble the generated x86_64 code)\n");
|
||||
result += fmt::format("start: {:016x}, end: {:016x}\n", begin, end);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string DisassembleAArch32([[maybe_unused]] u32 instruction, [[maybe_unused]] u64 pc) {
|
||||
std::string result;
|
||||
|
||||
#ifdef DYNARMIC_USE_LLVM
|
||||
LLVMInitializeARMTargetInfo();
|
||||
LLVMInitializeARMTargetMC();
|
||||
LLVMInitializeARMDisassembler();
|
||||
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("armv8-arm", nullptr, 0, nullptr, nullptr);
|
||||
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
|
||||
|
||||
char buffer[80];
|
||||
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer));
|
||||
result = inst_size > 0 ? buffer : "<invalid instruction>";
|
||||
result += '\n';
|
||||
|
||||
LLVMDisasmDispose(llvm_ctx);
|
||||
#else
|
||||
result += fmt::format("(disassembly disabled)\n");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string DisassembleAArch64([[maybe_unused]] u32 instruction, [[maybe_unused]] u64 pc) {
|
||||
std::string result;
|
||||
|
||||
#ifdef DYNARMIC_USE_LLVM
|
||||
LLVMInitializeAArch64TargetInfo();
|
||||
LLVMInitializeAArch64TargetMC();
|
||||
LLVMInitializeAArch64Disassembler();
|
||||
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("aarch64", nullptr, 0, nullptr, nullptr);
|
||||
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
|
||||
|
||||
char buffer[80];
|
||||
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer));
|
||||
result = inst_size > 0 ? buffer : "<invalid instruction>";
|
||||
result += '\n';
|
||||
|
||||
LLVMDisasmDispose(llvm_ctx);
|
||||
#else
|
||||
result += fmt::format("(disassembly disabled)\n");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
18
externals/dynarmic/src/common/llvm_disassemble.h
vendored
Executable file
18
externals/dynarmic/src/common/llvm_disassemble.h
vendored
Executable file
@@ -0,0 +1,18 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
std::string DisassembleX64(const void* pos, const void* end);
|
||||
std::string DisassembleAArch32(u32 instruction, u64 pc = 0);
|
||||
std::string DisassembleAArch64(u32 instruction, u64 pc = 0);
|
||||
|
||||
} // namespace Dynarmic::Common
|
37
externals/dynarmic/src/common/lut_from_list.h
vendored
Executable file
37
externals/dynarmic/src/common/lut_from_list.h
vendored
Executable file
@@ -0,0 +1,37 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mp/metafunction/apply.h>
|
||||
#include <mp/traits/is_instance_of_template.h>
|
||||
#include <mp/typelist/list.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <mp/typelist/head.h>
|
||||
#endif
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
template <typename Function, typename ...Values>
|
||||
inline auto GenerateLookupTableFromList(Function f, mp::list<Values...>) {
|
||||
#ifdef _MSC_VER
|
||||
using PairT = std::invoke_result_t<Function, mp::head<mp::list<Values...>>>;
|
||||
#else
|
||||
using PairT = std::common_type_t<std::invoke_result_t<Function, Values>...>;
|
||||
#endif
|
||||
using MapT = mp::apply<std::map, PairT>;
|
||||
|
||||
static_assert(mp::is_instance_of_template_v<std::pair, PairT>);
|
||||
|
||||
const std::initializer_list<PairT> pair_array{f(Values{})...};
|
||||
return MapT(pair_array.begin(), pair_array.end());
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
15
externals/dynarmic/src/common/macro_util.h
vendored
Executable file
15
externals/dynarmic/src/common/macro_util.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
|
||||
|
||||
#define CONCATENATE_TOKENS(x, y) CONCATENATE_TOKENS_IMPL(x, y)
|
||||
#define CONCATENATE_TOKENS_IMPL(x, y) x ## y
|
||||
|
||||
#ifdef __COUNTER__
|
||||
#define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __COUNTER__)
|
||||
#else
|
||||
#define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __LINE__)
|
||||
#endif
|
67
externals/dynarmic/src/common/math_util.cpp
vendored
Executable file
67
externals/dynarmic/src/common/math_util.cpp
vendored
Executable file
@@ -0,0 +1,67 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include "common/math_util.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
u8 RecipEstimate(u64 a) {
|
||||
using LUT = std::array<u8, 256>;
|
||||
static constexpr u64 lut_offset = 256;
|
||||
|
||||
static const LUT lut = [] {
|
||||
LUT result{};
|
||||
for (u64 i = 0; i < result.size(); i++) {
|
||||
u64 a = i + lut_offset;
|
||||
|
||||
a = a * 2 + 1;
|
||||
u64 b = (1u << 19) / a;
|
||||
result[i] = static_cast<u8>((b + 1) / 2);
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
return lut[a - lut_offset];
|
||||
}
|
||||
|
||||
/// Input is a u0.9 fixed point number. Only values in [0.25, 1.0) are valid.
|
||||
/// Output is a u0.8 fixed point number, with an implied 1 prefixed.
|
||||
/// i.e.: The output is a value in [1.0, 2.0).
|
||||
u8 RecipSqrtEstimate(u64 a) {
|
||||
using LUT = std::array<u8, 512>;
|
||||
|
||||
static const LUT lut = [] {
|
||||
LUT result{};
|
||||
for (u64 i = 128; i < result.size(); i++) {
|
||||
u64 a = i;
|
||||
|
||||
// Convert to u.10 (with 8 significant bits), force to odd
|
||||
if (a < 256) {
|
||||
// [0.25, 0.5)
|
||||
a = a * 2 + 1;
|
||||
} else {
|
||||
// [0.5, 1.0)
|
||||
a = (a | 1) * 2;
|
||||
}
|
||||
|
||||
// Calculate largest b which for which b < 1.0 / sqrt(a).
|
||||
// Start from b = 1.0 (in u.9) since b cannot be smaller.
|
||||
u64 b = 512;
|
||||
// u.10 * u.9 * u.9 -> u.28
|
||||
while (a * (b + 1) * (b + 1) < (1u << 28)) {
|
||||
b++;
|
||||
}
|
||||
|
||||
// Round to nearest u0.8 (with implied set integer bit).
|
||||
result[i] = static_cast<u8>((b + 1) / 2);
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
return lut[a & 0x1FF];
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
47
externals/dynarmic/src/common/math_util.h
vendored
Executable file
47
externals/dynarmic/src/common/math_util.h
vendored
Executable file
@@ -0,0 +1,47 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
/**
|
||||
* This function is a workaround for a bug in MSVC 19.12 where fold expressions
|
||||
* do not work when the /permissive- flag is enabled.
|
||||
*/
|
||||
template<typename T, typename... Ts>
|
||||
constexpr T Sum(T first, Ts&&... rest) {
|
||||
if constexpr (sizeof...(rest) == 0) {
|
||||
return first;
|
||||
} else {
|
||||
return first + Sum(std::forward<Ts>(rest)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Input is a u0.9 fixed point number. Only values in [0.5, 1.0) are valid.
|
||||
* Output is a u0.8 fixed point number, with an implied 1 prefixed.
|
||||
* i.e.: The output is a value in [1.0, 2.0).
|
||||
*
|
||||
* @see RecipEstimate() within the ARMv8 architecture reference manual
|
||||
* for a general overview of the requirements of the algorithm.
|
||||
*/
|
||||
u8 RecipEstimate(u64 a);
|
||||
|
||||
/**
|
||||
* Input is a u0.9 fixed point number. Only values in [0.25, 1.0) are valid.
|
||||
* Output is a u0.8 fixed point number, with an implied 1 prefixed.
|
||||
* i.e.: The output is a value in [1.0, 2.0).
|
||||
*
|
||||
* @see RecipSqrtEstimate() within the ARMv8 architecture reference manual
|
||||
* for a general overview of the requirements of the algorithm.
|
||||
*/
|
||||
u8 RecipSqrtEstimate(u64 a);
|
||||
|
||||
} // namespace Dynarmic::Common
|
43
externals/dynarmic/src/common/memory_pool.cpp
vendored
Executable file
43
externals/dynarmic/src/common/memory_pool.cpp
vendored
Executable file
@@ -0,0 +1,43 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/memory_pool.h"
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
Pool::Pool(size_t object_size, size_t initial_pool_size) : object_size(object_size), slab_size(initial_pool_size) {
|
||||
AllocateNewSlab();
|
||||
}
|
||||
|
||||
Pool::~Pool() {
|
||||
std::free(current_slab);
|
||||
|
||||
for (char* slab : slabs) {
|
||||
std::free(slab);
|
||||
}
|
||||
}
|
||||
|
||||
void* Pool::Alloc() {
|
||||
if (remaining == 0) {
|
||||
slabs.emplace_back(current_slab);
|
||||
AllocateNewSlab();
|
||||
}
|
||||
|
||||
void* ret = static_cast<void*>(current_ptr);
|
||||
current_ptr += object_size;
|
||||
remaining--;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Pool::AllocateNewSlab() {
|
||||
current_slab = static_cast<char*>(std::malloc(object_size * slab_size));
|
||||
current_ptr = current_slab;
|
||||
remaining = slab_size;
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
45
externals/dynarmic/src/common/memory_pool.h
vendored
Executable file
45
externals/dynarmic/src/common/memory_pool.h
vendored
Executable file
@@ -0,0 +1,45 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
class Pool {
|
||||
public:
|
||||
/**
|
||||
* @param object_size Byte-size of objects to construct
|
||||
* @param initial_pool_size Number of objects to have per slab
|
||||
*/
|
||||
Pool(size_t object_size, size_t initial_pool_size);
|
||||
~Pool();
|
||||
|
||||
Pool(const Pool&) = delete;
|
||||
Pool(Pool&&) = delete;
|
||||
|
||||
Pool& operator=(const Pool&) = delete;
|
||||
Pool& operator=(Pool&&) = delete;
|
||||
|
||||
/// Returns a pointer to an `object_size`-bytes block of memory.
|
||||
void* Alloc();
|
||||
|
||||
private:
|
||||
// Allocates a completely new memory slab.
|
||||
// Used when an entirely new slab is needed
|
||||
// due the current one running out of usable space.
|
||||
void AllocateNewSlab();
|
||||
|
||||
size_t object_size;
|
||||
size_t slab_size;
|
||||
char* current_slab;
|
||||
char* current_ptr;
|
||||
size_t remaining;
|
||||
std::vector<char*> slabs;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Common
|
110
externals/dynarmic/src/common/safe_ops.h
vendored
Executable file
110
externals/dynarmic/src/common/safe_ops.h
vendored
Executable file
@@ -0,0 +1,110 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/u128.h"
|
||||
|
||||
namespace Dynarmic::Safe {
|
||||
|
||||
template<typename T> T LogicalShiftLeft(T value, int shift_amount);
|
||||
template<typename T> T LogicalShiftRight(T value, int shift_amount);
|
||||
template<typename T> T ArithmeticShiftLeft(T value, int shift_amount);
|
||||
template<typename T> T ArithmeticShiftRight(T value, int shift_amount);
|
||||
|
||||
template<typename T>
|
||||
T LogicalShiftLeft(T value, int shift_amount) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
|
||||
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (shift_amount < 0) {
|
||||
return LogicalShiftRight(value, -shift_amount);
|
||||
}
|
||||
|
||||
auto unsigned_value = static_cast<std::make_unsigned_t<T>>(value);
|
||||
return static_cast<T>(unsigned_value << shift_amount);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline u128 LogicalShiftLeft(u128 value, int shift_amount) {
|
||||
return value << shift_amount;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T LogicalShiftRight(T value, int shift_amount) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
|
||||
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (shift_amount < 0) {
|
||||
return LogicalShiftLeft(value, -shift_amount);
|
||||
}
|
||||
|
||||
auto unsigned_value = static_cast<std::make_unsigned_t<T>>(value);
|
||||
return static_cast<T>(unsigned_value >> shift_amount);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline u128 LogicalShiftRight(u128 value, int shift_amount) {
|
||||
return value >> shift_amount;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T LogicalShiftRightDouble(T top, T bottom, int shift_amount) {
|
||||
return LogicalShiftLeft(top, int(Common::BitSize<T>()) - shift_amount) | LogicalShiftRight(bottom, shift_amount);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ArithmeticShiftLeft(T value, int shift_amount) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
|
||||
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (shift_amount < 0) {
|
||||
return ArithmeticShiftRight(value, -shift_amount);
|
||||
}
|
||||
|
||||
auto unsigned_value = static_cast<std::make_unsigned_t<T>>(value);
|
||||
return static_cast<T>(unsigned_value << shift_amount);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ArithmeticShiftRight(T value, int shift_amount) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
|
||||
if (shift_amount >= static_cast<int>(Common::BitSize<T>())) {
|
||||
return Common::MostSignificantBit(value) ? ~static_cast<T>(0) : 0;
|
||||
}
|
||||
|
||||
if (shift_amount < 0) {
|
||||
return ArithmeticShiftLeft(value, -shift_amount);
|
||||
}
|
||||
|
||||
auto signed_value = static_cast<std::make_signed_t<T>>(value);
|
||||
return static_cast<T>(signed_value >> shift_amount);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ArithmeticShiftRightDouble(T top, T bottom, int shift_amount) {
|
||||
return ArithmeticShiftLeft(top, int(Common::BitSize<T>()) - shift_amount) | LogicalShiftRight(bottom, shift_amount);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Negate(T value) {
|
||||
return static_cast<T>(~static_cast<std::uintmax_t>(value) + 1);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Safe
|
80
externals/dynarmic/src/common/scope_exit.h
vendored
Executable file
80
externals/dynarmic/src/common/scope_exit.h
vendored
Executable file
@@ -0,0 +1,80 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "common/macro_util.h"
|
||||
|
||||
namespace Dynarmic::detail {
|
||||
|
||||
struct ScopeExitTag {};
|
||||
struct ScopeFailTag {};
|
||||
struct ScopeSuccessTag {};
|
||||
|
||||
template <typename Function>
|
||||
class ScopeExit final {
|
||||
public:
|
||||
explicit ScopeExit(Function&& fn) : function(std::move(fn)) {}
|
||||
~ScopeExit() noexcept {
|
||||
function();
|
||||
}
|
||||
private:
|
||||
Function function;
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
class ScopeFail final {
|
||||
public:
|
||||
explicit ScopeFail(Function&& fn) : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {}
|
||||
~ScopeFail() noexcept {
|
||||
if (std::uncaught_exceptions() > exception_count) {
|
||||
function();
|
||||
}
|
||||
}
|
||||
private:
|
||||
Function function;
|
||||
int exception_count;
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
class ScopeSuccess final {
|
||||
public:
|
||||
explicit ScopeSuccess(Function&& fn) : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {}
|
||||
~ScopeSuccess() {
|
||||
if (std::uncaught_exceptions() <= exception_count) {
|
||||
function();
|
||||
}
|
||||
}
|
||||
private:
|
||||
Function function;
|
||||
int exception_count;
|
||||
};
|
||||
|
||||
// We use ->* here as it has the highest precedence of the operators we can use.
|
||||
|
||||
template <typename Function>
|
||||
auto operator->*(ScopeExitTag, Function&& function) {
|
||||
return ScopeExit<std::decay_t<Function>>{std::forward<Function>(function)};
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
auto operator->*(ScopeFailTag, Function&& function) {
|
||||
return ScopeFail<std::decay_t<Function>>{std::forward<Function>(function)};
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
auto operator->*(ScopeSuccessTag, Function&& function) {
|
||||
return ScopeSuccess<std::decay_t<Function>>{std::forward<Function>(function)};
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::detail
|
||||
|
||||
#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(_SCOPE_EXIT_) = ::Dynarmic::detail::ScopeExitTag{} ->* [&]() noexcept
|
||||
#define SCOPE_FAIL auto ANONYMOUS_VARIABLE(_SCOPE_FAIL_) = ::Dynarmic::detail::ScopeFailTag{} ->* [&]() noexcept
|
||||
#define SCOPE_SUCCESS auto ANONYMOUS_VARIABLE(_SCOPE_FAIL_) = ::Dynarmic::detail::ScopeSuccessTag{} ->* [&]()
|
15
externals/dynarmic/src/common/string_util.h
vendored
Executable file
15
externals/dynarmic/src/common/string_util.h
vendored
Executable file
@@ -0,0 +1,15 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
template <typename T>
|
||||
constexpr char SignToChar(T value) {
|
||||
return value >= 0 ? '+' : '-';
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
141
externals/dynarmic/src/common/u128.cpp
vendored
Executable file
141
externals/dynarmic/src/common/u128.cpp
vendored
Executable file
@@ -0,0 +1,141 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/u128.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
u128 Multiply64To128(u64 a, u64 b) {
|
||||
const u32 a0 = static_cast<u32>(a);
|
||||
const u32 b0 = static_cast<u32>(b);
|
||||
const u32 a1 = static_cast<u32>(a >> 32);
|
||||
const u32 b1 = static_cast<u32>(b >> 32);
|
||||
|
||||
// result = (c2 << 64) + (c1 << 32) + c0
|
||||
// c2 = a1 * b1
|
||||
// c1 = a1 * b0 + a0 * b1
|
||||
// c0 = a0 * b0
|
||||
// noting that these operations may overflow
|
||||
|
||||
const u64 c0 = static_cast<u64>(a0) * b0;
|
||||
const u64 c1_0 = static_cast<u64>(a1) * b0;
|
||||
const u64 c1_1 = static_cast<u64>(a0) * b1;
|
||||
const u64 c2 = static_cast<u64>(a1) * b1;
|
||||
|
||||
const u64 c1 = c1_0 + c1_1;
|
||||
const u64 c1_overflow = c1 < c1_0;
|
||||
|
||||
const u64 lower = c0 + (c1 << 32);
|
||||
const u64 lower_overflow = lower < c0;
|
||||
|
||||
const u64 upper = lower_overflow + (c1 >> 32) + (c1_overflow << 32) + c2;
|
||||
|
||||
u128 result;
|
||||
result.lower = lower;
|
||||
result.upper = upper;
|
||||
return result;
|
||||
}
|
||||
|
||||
u128 operator<<(u128 operand, int amount) {
|
||||
if (amount < 0) {
|
||||
return operand >> -amount;
|
||||
}
|
||||
|
||||
if (amount == 0) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
if (amount < 64) {
|
||||
u128 result;
|
||||
result.lower = (operand.lower << amount);
|
||||
result.upper = (operand.upper << amount) | (operand.lower >> (64 - amount));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (amount < 128) {
|
||||
u128 result;
|
||||
result.upper = operand.lower << (amount - 64);
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
u128 operator>>(u128 operand, int amount) {
|
||||
if (amount < 0) {
|
||||
return operand << -amount;
|
||||
}
|
||||
|
||||
if (amount == 0) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
if (amount < 64) {
|
||||
u128 result;
|
||||
result.lower = (operand.lower >> amount) | (operand.upper << (64 - amount));
|
||||
result.upper = (operand.upper >> amount);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (amount < 128) {
|
||||
u128 result;
|
||||
result.lower = operand.upper >> (amount - 64);
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
u128 StickyLogicalShiftRight(u128 operand, int amount) {
|
||||
if (amount < 0) {
|
||||
return operand << -amount;
|
||||
}
|
||||
|
||||
if (amount == 0) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
if (amount < 64) {
|
||||
u128 result;
|
||||
result.lower = (operand.lower >> amount) | (operand.upper << (64 - amount));
|
||||
result.upper = (operand.upper >> amount);
|
||||
// Sticky bit
|
||||
if ((operand.lower << (64 - amount)) != 0) {
|
||||
result.lower |= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (amount == 64) {
|
||||
u128 result;
|
||||
result.lower = operand.upper;
|
||||
// Sticky bit
|
||||
if (operand.lower != 0) {
|
||||
result.lower |= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (amount < 128) {
|
||||
u128 result;
|
||||
result.lower = operand.upper >> (amount - 64);
|
||||
// Sticky bit
|
||||
if (operand.lower != 0) {
|
||||
result.lower |= 1;
|
||||
}
|
||||
if ((operand.upper << (128 - amount)) != 0) {
|
||||
result.lower |= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (operand.lower != 0 || operand.upper != 0) {
|
||||
return u128(1);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Dynarmic
|
96
externals/dynarmic/src/common/u128.h
vendored
Executable file
96
externals/dynarmic/src/common/u128.h
vendored
Executable file
@@ -0,0 +1,96 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
struct u128 {
|
||||
u128() = default;
|
||||
u128(const u128&) = default;
|
||||
u128(u128&&) = default;
|
||||
u128& operator=(const u128&) = default;
|
||||
u128& operator=(u128&&) = default;
|
||||
|
||||
u128(u64 lower_, u64 upper_) : lower(lower_), upper(upper_) {}
|
||||
|
||||
template<typename T>
|
||||
/* implicit */ u128(T value) : lower(value), upper(0) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
static_assert(Common::BitSize<T>() <= Common::BitSize<u64>());
|
||||
}
|
||||
|
||||
u64 lower = 0;
|
||||
u64 upper = 0;
|
||||
|
||||
template<size_t bit_position>
|
||||
bool Bit() const {
|
||||
static_assert(bit_position < 128);
|
||||
if constexpr (bit_position < 64) {
|
||||
return Common::Bit<bit_position>(lower);
|
||||
} else {
|
||||
return Common::Bit<bit_position - 64>(upper);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(Common::BitSize<u128>() == 128);
|
||||
static_assert(std::is_standard_layout_v<u128>);
|
||||
static_assert(std::is_trivially_copyable_v<u128>);
|
||||
|
||||
u128 Multiply64To128(u64 a, u64 b);
|
||||
|
||||
inline u128 operator+(u128 a, u128 b) {
|
||||
u128 result;
|
||||
result.lower = a.lower + b.lower;
|
||||
result.upper = a.upper + b.upper + (a.lower > result.lower);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u128 operator-(u128 a, u128 b) {
|
||||
u128 result;
|
||||
result.lower = a.lower - b.lower;
|
||||
result.upper = a.upper - b.upper - (a.lower < result.lower);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool operator<(u128 a, u128 b) {
|
||||
return std::tie(a.upper, a.lower) < std::tie(b.upper, b.lower);
|
||||
}
|
||||
|
||||
inline bool operator>(u128 a, u128 b) {
|
||||
return operator<(b, a);
|
||||
}
|
||||
|
||||
inline bool operator<=(u128 a, u128 b) {
|
||||
return !operator>(a, b);
|
||||
}
|
||||
|
||||
inline bool operator>=(u128 a, u128 b) {
|
||||
return !operator<(a, b);
|
||||
}
|
||||
|
||||
inline bool operator==(u128 a, u128 b) {
|
||||
return std::tie(a.upper, a.lower) == std::tie(b.upper, b.lower);
|
||||
}
|
||||
|
||||
inline bool operator!=(u128 a, u128 b) {
|
||||
return !operator==(a, b);
|
||||
}
|
||||
|
||||
u128 operator<<(u128 operand, int amount);
|
||||
u128 operator>>(u128 operand, int amount);
|
||||
|
||||
/// LSB is a "sticky-bit".
|
||||
/// If a 1 is shifted off, the LSB would be set.
|
||||
u128 StickyLogicalShiftRight(u128 operand, int amount);
|
||||
|
||||
} // namespace Dynarmic
|
12
externals/dynarmic/src/common/unlikely.h
vendored
Executable file
12
externals/dynarmic/src/common/unlikely.h
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define UNLIKELY(x) !!(x)
|
||||
#endif
|
29
externals/dynarmic/src/common/variant_util.h
vendored
Executable file
29
externals/dynarmic/src/common/variant_util.h
vendored
Executable file
@@ -0,0 +1,29 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace Dynarmic::Common {
|
||||
namespace detail {
|
||||
|
||||
template <typename ReturnT, typename Lambda>
|
||||
struct VariantVisitor : boost::static_visitor<ReturnT>, Lambda {
|
||||
VariantVisitor(Lambda&& lambda)
|
||||
: Lambda(std::move(lambda))
|
||||
{}
|
||||
|
||||
using Lambda::operator();
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename ReturnT, typename Variant, typename Lambda>
|
||||
inline ReturnT VisitVariant(Variant&& variant, Lambda&& lambda) {
|
||||
return boost::apply_visitor(detail::VariantVisitor<ReturnT, Lambda>(std::move(lambda)), variant);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Common
|
Reference in New Issue
Block a user