diff --git a/Source/UnitTests/Core/CMakeLists.txt b/Source/UnitTests/Core/CMakeLists.txt index 621cfd3f61..894310e126 100644 --- a/Source/UnitTests/Core/CMakeLists.txt +++ b/Source/UnitTests/Core/CMakeLists.txt @@ -13,3 +13,7 @@ add_dolphin_test(DSPAssemblyTest add_dolphin_test(ESFormatsTest IOS/ES/FormatsTest.cpp IOS/ES/TestBinaryData.cpp) add_dolphin_test(FileSystemTest IOS/FS/FileSystemTest.cpp) + +if(_M_X86) + add_dolphin_test(PowerPCTest PowerPC/Jit64Common/Frsqrte.cpp) +endif() diff --git a/Source/UnitTests/Core/PowerPC/Jit64Common/Frsqrte.cpp b/Source/UnitTests/Core/PowerPC/Jit64Common/Frsqrte.cpp new file mode 100644 index 0000000000..472bfdf450 --- /dev/null +++ b/Source/UnitTests/Core/PowerPC/Jit64Common/Frsqrte.cpp @@ -0,0 +1,101 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/BitUtils.h" +#include "Common/CommonTypes.h" +#include "Common/FloatUtils.h" +#include "Common/x64ABI.h" +#include "Core/PowerPC/Gekko.h" +#include "Core/PowerPC/Jit64Common/Jit64AsmCommon.h" +#include "Core/PowerPC/Jit64Common/Jit64Base.h" +#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" + +#include + +class TestCommonAsmRoutines : public CommonAsmRoutines +{ +public: + TestCommonAsmRoutines() + { + using namespace Gen; + + AllocCodeSpace(4096); + m_const_pool.Init(AllocChildCodeSpace(1024), 1024); + + const auto raw_frsqrte = reinterpret_cast(AlignCode4()); + GenFrsqrte(); + + wrapped_frsqrte = reinterpret_cast(AlignCode4()); + ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8, 16); + + // We know the frsqrte implementation only accesses the fpscr. We manufacture a + // PPCSTATE pointer so we read/write to our provided fpscr argument instead. + XOR(32, R(RPPCSTATE), R(RPPCSTATE)); + LEA(64, RSCRATCH, PPCSTATE(fpscr)); + SUB(64, R(ABI_PARAM2), R(RSCRATCH)); + MOV(64, R(RPPCSTATE), R(ABI_PARAM2)); + + // Call + MOVQ_xmm(XMM0, R(ABI_PARAM1)); + ABI_CallFunction(raw_frsqrte); + MOVQ_xmm(R(ABI_RETURN), XMM0); + + ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8, 16); + RET(); + } + + u64 (*wrapped_frsqrte)(u64, UReg_FPSCR&); +}; + +TEST(Jit64, Frsqrte) +{ + TestCommonAsmRoutines routines; + + const std::vector special_values{ + 0x0000'0000'0000'0000, // positive zero + 0x0000'0000'0000'0001, // smallest positive denormal + 0x0000'0000'0100'0000, + 0x000F'FFFF'FFFF'FFFF, // largest positive denormal + 0x0010'0000'0000'0000, // smallest positive normal + 0x0010'0000'0000'0002, + 0x3FF0'0000'0000'0000, // 1.0 + 0x7FEF'FFFF'FFFF'FFFF, // largest positive normal + 0x7FF0'0000'0000'0000, // positive infinity + 0x7FF0'0000'0000'0001, // first positive SNaN + 0x7FF7'FFFF'FFFF'FFFF, // last positive SNaN + 0x7FF8'0000'0000'0000, // first positive QNaN + 0x7FFF'FFFF'FFFF'FFFF, // last positive QNaN + 0x8000'0000'0000'0000, // negative zero + 0x8000'0000'0000'0001, // smallest negative denormal + 0x8000'0000'0100'0000, + 0x800F'FFFF'FFFF'FFFF, // largest negative denormal + 0x8010'0000'0000'0000, // smallest negative normal + 0x8010'0000'0000'0002, + 0xBFF0'0000'0000'0000, // -1.0 + 0xFFEF'FFFF'FFFF'FFFF, // largest negative normal + 0xFFF0'0000'0000'0000, // negative infinity + 0xFFF0'0000'0000'0001, // first negative SNaN + 0xFFF7'FFFF'FFFF'FFFF, // last negative SNaN + 0xFFF8'0000'0000'0000, // first negative QNaN + 0xFFFF'FFFF'FFFF'FFFF, // last negative QNaN + }; + + UReg_FPSCR fpscr; + + for (u64 ivalue : special_values) + { + double dvalue = Common::BitCast(ivalue); + + u64 expected = Common::BitCast(Common::ApproximateReciprocalSquareRoot(dvalue)); + + u64 actual = routines.wrapped_frsqrte(ivalue, fpscr); + + printf("%016llx -> %016llx == %016llx\n", ivalue, actual, expected); + + EXPECT_EQ(expected, actual); + } +}