yuzu/externals/dynarmic/tests/A32/test_arm_instructions.cpp

508 lines
14 KiB
C++
Executable File

/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <catch.hpp>
#include <dynarmic/A32/a32.h>
#include "A32/testenv.h"
#include "frontend/A32/location_descriptor.h"
using namespace Dynarmic;
static A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
A32::UserConfig user_config;
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
user_config.callbacks = testenv;
return user_config;
}
TEST_CASE("arm: Opt Failure: Const folding in MostSignificantWord", "[arm][A32]") {
// This was a randomized test-case that was failing.
// This was due to constant folding for MostSignificantWord
// failing to take into account an associated GetCarryFromOp
// pseudoinstruction.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe30ad071, // movw, sp, #41073
0xe75efd3d, // smmulr lr, sp, sp
0xa637af1e, // shadd16ge r10, r7, lr
0xf57ff01f, // clrex
0x86b98879, // sxtahhi r8, r9, r9, ror #16
0xeafffffe, // b +#0
};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 6;
jit.Run();
// If we don't trigger the GetCarryFromOp ASSERT, we're fine.
}
TEST_CASE("arm: Unintended modification in SetCFlag", "[arm][A32]") {
// This was a randomized test-case that was failing.
//
// IR produced for location {12, !T, !E} was:
// %0 = GetRegister r1
// %1 = SubWithCarry %0, #0x3e80000, #1
// %2 = GetCarryFromOp %1
// %3 = GetOverflowFromOp %1
// %4 = MostSignificantBit %1
// SetNFlag %4
// %6 = IsZero %1
// SetZFlag %6
// SetCFlag %2
// SetVFlag %3
// %10 = GetRegister r5
// %11 = AddWithCarry %10, #0x8a00, %2
// SetRegister r4, %11
//
// The reference to %2 in instruction %11 was the issue, because instruction %8
// told the register allocator it was a Use but then modified the value.
// Changing the EmitSet*Flag instruction to declare their arguments as UseScratch
// solved this bug.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe35f0cd9, // cmp pc, #55552
0xe11c0474, // tst r12, r4, ror r4
0xe1a006a7, // mov r0, r7, lsr #13
0xe35107fa, // cmp r1, #0x3E80000
0xe2a54c8a, // adc r4, r5, #35328
0xeafffffe, // b +#0
};
jit.Regs() = {
0x6973b6bb, 0x267ea626, 0x69debf49, 0x8f976895, 0x4ecd2d0d, 0xcf89b8c7, 0xb6713f85, 0x15e2aa5,
0xcd14336a, 0xafca0f3e, 0xace2efd9, 0x68fb82cd, 0x775447c0, 0xc9e1f8cd, 0xebe0e626, 0x0
};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 6;
jit.Run();
REQUIRE(jit.Regs()[0] == 0x00000af1);
REQUIRE(jit.Regs()[1] == 0x267ea626);
REQUIRE(jit.Regs()[2] == 0x69debf49);
REQUIRE(jit.Regs()[3] == 0x8f976895);
REQUIRE(jit.Regs()[4] == 0xcf8a42c8);
REQUIRE(jit.Regs()[5] == 0xcf89b8c7);
REQUIRE(jit.Regs()[6] == 0xb6713f85);
REQUIRE(jit.Regs()[7] == 0x015e2aa5);
REQUIRE(jit.Regs()[8] == 0xcd14336a);
REQUIRE(jit.Regs()[9] == 0xafca0f3e);
REQUIRE(jit.Regs()[10] == 0xace2efd9);
REQUIRE(jit.Regs()[11] == 0x68fb82cd);
REQUIRE(jit.Regs()[12] == 0x775447c0);
REQUIRE(jit.Regs()[13] == 0xc9e1f8cd);
REQUIRE(jit.Regs()[14] == 0xebe0e626);
REQUIRE(jit.Regs()[15] == 0x00000014);
REQUIRE(jit.Cpsr() == 0x200001d0);
}
TEST_CASE( "arm: shsax (Edge-case)", "[arm][A32]" ) {
// This was a randomized test-case that was failing.
//
// The issue here was one of the words to be subtracted was 0x8000.
// When the 2s complement was calculated by (~a + 1), it was 0x8000.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe63dbf59, // shsax r11, sp, r9
0xeafffffe, // b +#0
};
jit.Regs() = {
0x3a3b8b18, 0x96156555, 0xffef039f, 0xafb946f2, 0x2030a69a, 0xafe09b2a, 0x896823c8, 0xabde0ded,
0x9825d6a6, 0x17498000, 0x999d2c95, 0x8b812a59, 0x209bdb58, 0x2f7fb1d4, 0x0f378107, 0x00000000
};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.Regs()[0] == 0x3a3b8b18);
REQUIRE(jit.Regs()[1] == 0x96156555);
REQUIRE(jit.Regs()[2] == 0xffef039f);
REQUIRE(jit.Regs()[3] == 0xafb946f2);
REQUIRE(jit.Regs()[4] == 0x2030a69a);
REQUIRE(jit.Regs()[5] == 0xafe09b2a);
REQUIRE(jit.Regs()[6] == 0x896823c8);
REQUIRE(jit.Regs()[7] == 0xabde0ded);
REQUIRE(jit.Regs()[8] == 0x9825d6a6);
REQUIRE(jit.Regs()[9] == 0x17498000);
REQUIRE(jit.Regs()[10] == 0x999d2c95);
REQUIRE(jit.Regs()[11] == 0x57bfe48e);
REQUIRE(jit.Regs()[12] == 0x209bdb58);
REQUIRE(jit.Regs()[13] == 0x2f7fb1d4);
REQUIRE(jit.Regs()[14] == 0x0f378107);
REQUIRE(jit.Regs()[15] == 0x00000004);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE( "arm: uasx (Edge-case)", "[arm][A32]" ) {
// UASX's Rm<31:16> == 0x0000.
// An implementation that depends on addition overflow to detect
// if diff >= 0 will fail this testcase.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe6549f35, // uasx r9, r4, r5
0xeafffffe, // b +#0
};
jit.Regs()[4] = 0x8ed38f4c;
jit.Regs()[5] = 0x0000261d;
jit.Regs()[15] = 0x00000000;
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.Regs()[4] == 0x8ed38f4c);
REQUIRE(jit.Regs()[5] == 0x0000261d);
REQUIRE(jit.Regs()[9] == 0xb4f08f4c);
REQUIRE(jit.Regs()[15] == 0x00000004);
REQUIRE(jit.Cpsr() == 0x000301d0);
}
TEST_CASE("arm: smuad (Edge-case)", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xE700F211, // smuad r0, r1, r2
0xeafffffe, // b +#0
};
jit.Regs() = {
0, // Rd
0x80008000, // Rn
0x80008000, // Rm
0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.Regs()[0] == 0x80000000);
REQUIRE(jit.Regs()[1] == 0x80008000);
REQUIRE(jit.Regs()[2] == 0x80008000);
REQUIRE(jit.Cpsr() == 0x080001d0);
}
TEST_CASE("arm: Test InvalidateCacheRange", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe3a00005, // mov r0, #5
0xe3a0100D, // mov r1, #13
0xe0812000, // add r2, r1, r0
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs() = {};
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 4;
jit.Run();
REQUIRE(jit.Regs()[0] == 5);
REQUIRE(jit.Regs()[1] == 13);
REQUIRE(jit.Regs()[2] == 18);
REQUIRE(jit.Regs()[15] == 0x0000000c);
REQUIRE(jit.Cpsr() == 0x000001d0);
// Change the code
test_env.code_mem[1] = 0xe3a01007; // mov r1, #7
jit.InvalidateCacheRange(/*start_memory_location = */ 4, /* length_in_bytes = */ 4);
// Reset position of PC
jit.Regs()[15] = 0;
test_env.ticks_left = 4;
jit.Run();
REQUIRE(jit.Regs()[0] == 5);
REQUIRE(jit.Regs()[1] == 7);
REQUIRE(jit.Regs()[2] == 12);
REQUIRE(jit.Regs()[15] == 0x0000000c);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Step blx", "[arm]") {
ArmTestEnv test_env;
A32::UserConfig config = GetUserConfig(&test_env);
config.optimizations |= OptimizationFlag::FastDispatch;
Dynarmic::A32::Jit jit{config};
test_env.code_mem = {
0xe12fff30, // blx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 8;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[0] == 8);
REQUIRE(jit.Regs()[14] == 4);
REQUIRE(jit.Regs()[15] == 8);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Step bx", "[arm]") {
ArmTestEnv test_env;
A32::UserConfig config = GetUserConfig(&test_env);
config.optimizations |= OptimizationFlag::FastDispatch;
Dynarmic::A32::Jit jit{config};
test_env.code_mem = {
0xe12fff10, // bx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 8;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[0] == 8);
REQUIRE(jit.Regs()[15] == 8);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Test stepping", "[arm]") {
ArmTestEnv test_env;
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 8;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
for (size_t i = 0; i < 5; ++i) {
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[15] == (i + 1) * 4);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
test_env.ticks_left = 20;
jit.Run();
REQUIRE(jit.Regs()[15] == 80);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Test stepping 2", "[arm]") {
ArmTestEnv test_env;
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe12fff10, // bx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 4;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
for (size_t i = 0; i < 5; ++i) {
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[15] == (i + 1) * 4);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
test_env.ticks_left = 20;
jit.Run();
REQUIRE(jit.Regs()[15] == 80);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: Test stepping 3", "[arm]") {
ArmTestEnv test_env;
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xe12fff10, // bx r0
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
0xeafffffe, // b +#0 (infinite loop)
};
jit.Regs()[0] = 4;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 10;
jit.Step();
REQUIRE(jit.Regs()[15] == 4);
REQUIRE(jit.Cpsr() == 0x000001d0);
test_env.ticks_left = 20;
jit.Run();
REQUIRE(jit.Regs()[15] == 20);
REQUIRE(jit.Cpsr() == 0x000001d0);
}
TEST_CASE("arm: PackedAbsDiffSumS8", "[arm][A32]") {
// This was a randomized test-case that was failing.
// In circumstances there were cases when the upper 32 bits of an argument to psadbw were not zero.
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0x87414354, // smlsldhi r4, r1, r4, r3
0xe7886412, // usad8a r8, r2, r4, r6
0xeafffffe, // b +#0
};
jit.Regs() = {
0xea85297c, 0x417ad918, 0x64f8b70b, 0xcca0373e, 0xbc722361, 0xc528c69e, 0xca926de8, 0xd665d210,
0xb5650555, 0x4a24b25b, 0xaed44144, 0xe87230b2, 0x98e391de, 0x126efc0c, 0xe591fd11, 0x00000000,
};
jit.SetCpsr(0xb0000010);
test_env.ticks_left = 3;
jit.Run();
REQUIRE(jit.Regs()[0] == 0xea85297c);
REQUIRE(jit.Regs()[1] == 0x417ad918);
REQUIRE(jit.Regs()[2] == 0x64f8b70b);
REQUIRE(jit.Regs()[3] == 0xcca0373e);
REQUIRE(jit.Regs()[4] == 0xb685ec9f);
REQUIRE(jit.Regs()[5] == 0xc528c69e);
REQUIRE(jit.Regs()[6] == 0xca926de8);
REQUIRE(jit.Regs()[7] == 0xd665d210);
REQUIRE(jit.Regs()[8] == 0xca926f76);
REQUIRE(jit.Regs()[9] == 0x4a24b25b);
REQUIRE(jit.Regs()[10] == 0xaed44144);
REQUIRE(jit.Regs()[11] == 0xe87230b2);
REQUIRE(jit.Regs()[12] == 0x98e391de);
REQUIRE(jit.Regs()[13] == 0x126efc0c);
REQUIRE(jit.Regs()[14] == 0xe591fd11);
REQUIRE(jit.Regs()[15] == 0x00000008);
REQUIRE(jit.Cpsr() == 0xb0000010);
}
TEST_CASE("arm: vclt.f32 with zero", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xf3b93628, // vclt.f32 d3, d24, #0
0xeafffffe, // b +#0
};
jit.ExtRegs()[48] = 0x3a87d9f1;
jit.ExtRegs()[49] = 0x80796dc0;
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.ExtRegs()[6] == 0x00000000);
REQUIRE(jit.ExtRegs()[7] == 0x00000000);
}
TEST_CASE("arm: vcvt.s16.f64", "[arm][A32]") {
ArmTestEnv test_env;
A32::Jit jit{GetUserConfig(&test_env)};
test_env.code_mem = {
0xeebe8b45, // vcvt.s16.f64 d8, d8, #6
0xeafffffe, // b +#0
};
jit.ExtRegs()[16] = 0x9a7110b0;
jit.ExtRegs()[17] = 0xcd78f4e7;
jit.SetCpsr(0x000001d0); // User-mode
test_env.ticks_left = 2;
jit.Run();
REQUIRE(jit.ExtRegs()[16] == 0xffff8000);
REQUIRE(jit.ExtRegs()[17] == 0xffffffff);
}