ed6ca58058
Fairly self-explanatory, we can leverage the existing IR functions for the purpose of these instructions. In the integer case, we can just insert function pointers into an array and index it, given all comparison primitives exist already for the integer side of things.
487 lines
14 KiB
C++
487 lines
14 KiB
C++
/* 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.enable_fast_dispatch = false;
|
|
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.enable_fast_dispatch = true;
|
|
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.enable_fast_dispatch = true;
|
|
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);
|
|
}
|