mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-31 18:12:35 +01:00
255ee3fdce
This is a little trick I came up with that lets us restructure our float classification code so we can exit earlier when the float is normal, which is the case more often than not. First we shift left by 1 to get rid of the sign bit, and then we count the number of leading sign bits. If the result is less than 10 (for doubles) or 7 (for floats), the float is normal. This is because, if the float isn't normal, the exponent is either all zeroes or all ones.
101 lines
2.9 KiB
C++
101 lines
2.9 KiB
C++
// Copyright 2021 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
#include "Common/Arm64Emitter.h"
|
|
#include "Common/BitUtils.h"
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/ScopeGuard.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h"
|
|
#include "Core/PowerPC/JitArm64/Jit.h"
|
|
#include "Core/PowerPC/PowerPC.h"
|
|
#include "Core/System.h"
|
|
|
|
#include "../TestValues.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace
|
|
{
|
|
using namespace Arm64Gen;
|
|
|
|
class TestFPRF : public JitArm64
|
|
{
|
|
public:
|
|
explicit TestFPRF(Core::System& system) : JitArm64(system)
|
|
{
|
|
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
|
|
|
|
AllocCodeSpace(4096);
|
|
|
|
const u8* raw_fprf_single = GetCodePtr();
|
|
GenerateFPRF(true);
|
|
const u8* raw_fprf_double = GetCodePtr();
|
|
GenerateFPRF(false);
|
|
|
|
auto& ppc_state = system.GetPPCState();
|
|
|
|
fprf_single = Common::BitCast<void (*)(u32)>(GetCodePtr());
|
|
MOV(ARM64Reg::X15, ARM64Reg::X30);
|
|
MOV(ARM64Reg::X14, PPC_REG);
|
|
MOVP2R(PPC_REG, &ppc_state);
|
|
BL(raw_fprf_single);
|
|
MOV(ARM64Reg::X30, ARM64Reg::X15);
|
|
MOV(PPC_REG, ARM64Reg::X14);
|
|
RET();
|
|
|
|
fprf_double = Common::BitCast<void (*)(u64)>(GetCodePtr());
|
|
MOV(ARM64Reg::X15, ARM64Reg::X30);
|
|
MOV(ARM64Reg::X14, PPC_REG);
|
|
MOVP2R(PPC_REG, &ppc_state);
|
|
BL(raw_fprf_double);
|
|
MOV(ARM64Reg::X30, ARM64Reg::X15);
|
|
MOV(PPC_REG, ARM64Reg::X14);
|
|
RET();
|
|
}
|
|
|
|
std::function<void(u32)> fprf_single;
|
|
std::function<void(u64)> fprf_double;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static u32 RunUpdateFPRF(PowerPC::PowerPCState& ppc_state, const std::function<void()>& f)
|
|
{
|
|
ppc_state.fpscr.Hex = 0x12345678;
|
|
f();
|
|
return ppc_state.fpscr.Hex;
|
|
}
|
|
|
|
TEST(JitArm64, FPRF)
|
|
{
|
|
Core::DeclareAsCPUThread();
|
|
Common::ScopeGuard cpu_thread_guard([] { Core::UndeclareAsCPUThread(); });
|
|
|
|
auto& system = Core::System::GetInstance();
|
|
auto& ppc_state = system.GetPPCState();
|
|
TestFPRF test(system);
|
|
|
|
for (const u64 double_input : double_test_values)
|
|
{
|
|
const u32 expected_double = RunUpdateFPRF(
|
|
ppc_state, [&] { ppc_state.UpdateFPRFDouble(Common::BitCast<double>(double_input)); });
|
|
const u32 actual_double = RunUpdateFPRF(ppc_state, [&] { test.fprf_double(double_input); });
|
|
if (expected_double != actual_double)
|
|
fmt::print("{:016x} -> {:08x} == {:08x}\n", double_input, actual_double, expected_double);
|
|
EXPECT_EQ(expected_double, actual_double);
|
|
|
|
const u32 single_input = ConvertToSingle(double_input);
|
|
|
|
const u32 expected_single = RunUpdateFPRF(
|
|
ppc_state, [&] { ppc_state.UpdateFPRFSingle(Common::BitCast<float>(single_input)); });
|
|
const u32 actual_single = RunUpdateFPRF(ppc_state, [&] { test.fprf_single(single_input); });
|
|
if (expected_single != actual_single)
|
|
fmt::print("{:08x} -> {:08x} == {:08x}\n", single_input, actual_single, expected_single);
|
|
EXPECT_EQ(expected_single, actual_single);
|
|
}
|
|
}
|