early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

636
externals/dynarmic/tests/A64/a64.cpp vendored Executable file
View File

@@ -0,0 +1,636 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <catch.hpp>
#include <dynarmic/exclusive_monitor.h>
#include "common/fp/fpsr.h"
#include "testenv.h"
using namespace Dynarmic;
TEST_CASE("A64: ADD", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x8b020020); // ADD X0, X1, X2
env.code_mem.emplace_back(0x14000000); // B .
jit.SetRegister(0, 0);
jit.SetRegister(1, 1);
jit.SetRegister(2, 2);
jit.SetPC(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetRegister(0) == 3);
REQUIRE(jit.GetRegister(1) == 1);
REQUIRE(jit.GetRegister(2) == 2);
REQUIRE(jit.GetPC() == 4);
}
TEST_CASE("A64: REV", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0xdac00c00); // REV X0, X0
env.code_mem.emplace_back(0x5ac00821); // REV W1, W1
env.code_mem.emplace_back(0x14000000); // B .
jit.SetRegister(0, 0xaabbccddeeff1100);
jit.SetRegister(1, 0xaabbccdd);
jit.SetPC(0);
env.ticks_left = 3;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0x11ffeeddccbbaa);
REQUIRE(jit.GetRegister(1) == 0xddccbbaa);
REQUIRE(jit.GetPC() == 8);
}
TEST_CASE("A64: REV32", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0xdac00800); // REV32 X0, X0
env.code_mem.emplace_back(0x14000000); // B .
jit.SetRegister(0, 0xaabbccddeeff1100);
jit.SetPC(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0xddccbbaa0011ffee);
REQUIRE(jit.GetPC() == 4);
}
TEST_CASE("A64: REV16", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0xdac00400); // REV16 X0, X0
env.code_mem.emplace_back(0x5ac00421); // REV16 W1, W1
env.code_mem.emplace_back(0x14000000); // B .
jit.SetRegister(0, 0xaabbccddeeff1100);
jit.SetRegister(1, 0xaabbccdd);
jit.SetPC(0);
env.ticks_left = 3;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0xbbaaddccffee0011);
REQUIRE(jit.GetRegister(1) == 0xbbaaddcc);
REQUIRE(jit.GetPC() == 8);
}
TEST_CASE("A64: AND", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x8a020020); // AND X0, X1, X2
env.code_mem.emplace_back(0x14000000); // B .
jit.SetRegister(0, 0);
jit.SetRegister(1, 1);
jit.SetRegister(2, 3);
jit.SetPC(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetRegister(0) == 1);
REQUIRE(jit.GetRegister(1) == 1);
REQUIRE(jit.GetRegister(2) == 3);
REQUIRE(jit.GetPC() == 4);
}
TEST_CASE("A64: Bitmasks", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x3200c3e0); // ORR W0, WZR, #0x01010101
env.code_mem.emplace_back(0x320c8fe1); // ORR W1, WZR, #0x00F000F0
env.code_mem.emplace_back(0x320003e2); // ORR W2, WZR, #1
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
env.ticks_left = 4;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0x01010101);
REQUIRE(jit.GetRegister(1) == 0x00F000F0);
REQUIRE(jit.GetRegister(2) == 1);
REQUIRE(jit.GetPC() == 12);
}
TEST_CASE("A64: ANDS NZCV", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x6a020020); // ANDS W0, W1, W2
env.code_mem.emplace_back(0x14000000); // B .
SECTION("N=1, Z=0") {
jit.SetRegister(0, 0);
jit.SetRegister(1, 0xFFFFFFFF);
jit.SetRegister(2, 0xFFFFFFFF);
jit.SetPC(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0xFFFFFFFF);
REQUIRE(jit.GetRegister(1) == 0xFFFFFFFF);
REQUIRE(jit.GetRegister(2) == 0xFFFFFFFF);
REQUIRE(jit.GetPC() == 4);
REQUIRE((jit.GetPstate() & 0xF0000000) == 0x80000000);
}
SECTION("N=0, Z=1") {
jit.SetRegister(0, 0);
jit.SetRegister(1, 0xFFFFFFFF);
jit.SetRegister(2, 0x00000000);
jit.SetPC(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0x00000000);
REQUIRE(jit.GetRegister(1) == 0xFFFFFFFF);
REQUIRE(jit.GetRegister(2) == 0x00000000);
REQUIRE(jit.GetPC() == 4);
REQUIRE((jit.GetPstate() & 0xF0000000) == 0x40000000);
}
SECTION("N=0, Z=0") {
jit.SetRegister(0, 0);
jit.SetRegister(1, 0x12345678);
jit.SetRegister(2, 0x7324a993);
jit.SetPC(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0x12240010);
REQUIRE(jit.GetRegister(1) == 0x12345678);
REQUIRE(jit.GetRegister(2) == 0x7324a993);
REQUIRE(jit.GetPC() == 4);
REQUIRE((jit.GetPstate() & 0xF0000000) == 0x00000000);
}
}
TEST_CASE("A64: CBZ", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x34000060); // 0x00 : CBZ X0, label
env.code_mem.emplace_back(0x320003e2); // 0x04 : MOV X2, 1
env.code_mem.emplace_back(0x14000000); // 0x08 : B.
env.code_mem.emplace_back(0x321f03e2); // 0x0C : label: MOV X2, 2
env.code_mem.emplace_back(0x14000000); // 0x10 : B .
SECTION("no branch") {
jit.SetPC(0);
jit.SetRegister(0, 1);
env.ticks_left = 4;
jit.Run();
REQUIRE(jit.GetRegister(2) == 1);
REQUIRE(jit.GetPC() == 8);
}
SECTION("branch") {
jit.SetPC(0);
jit.SetRegister(0, 0);
env.ticks_left = 4;
jit.Run();
REQUIRE(jit.GetRegister(2) == 2);
REQUIRE(jit.GetPC() == 16);
}
}
TEST_CASE("A64: TBZ", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x36180060); // 0x00 : TBZ X0, 3, label
env.code_mem.emplace_back(0x320003e2); // 0x04 : MOV X2, 1
env.code_mem.emplace_back(0x14000000); // 0x08 : B .
env.code_mem.emplace_back(0x321f03e2); // 0x0C : label: MOV X2, 2
env.code_mem.emplace_back(0x14000000); // 0x10 : B .
SECTION("no branch") {
jit.SetPC(0);
jit.SetRegister(0, 0xFF);
env.ticks_left = 4;
jit.Run();
REQUIRE(jit.GetRegister(2) == 1);
REQUIRE(jit.GetPC() == 8);
}
SECTION("branch with zero") {
jit.SetPC(0);
jit.SetRegister(0, 0);
env.ticks_left = 4;
jit.Run();
REQUIRE(jit.GetRegister(2) == 2);
REQUIRE(jit.GetPC() == 16);
}
SECTION("branch with non-zero") {
jit.SetPC(0);
jit.SetRegister(0, 1);
env.ticks_left = 4;
jit.Run();
REQUIRE(jit.GetRegister(2) == 2);
REQUIRE(jit.GetPC() == 16);
}
}
TEST_CASE("A64: FABD", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x6eb5d556); // FABD.4S V22, V10, V21
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(10, {0xb4858ac77ff39a87, 0x9fce5e14c4873176});
jit.SetVector(21, {0x56d3f085ff890e2b, 0x6e4b0a41801a2d00});
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(22) == Vector{0x56d3f0857fc90e2b, 0x6e4b0a4144873176});
}
TEST_CASE("A64: 128-bit exclusive read/write", "[a64]") {
A64TestEnv env;
ExclusiveMonitor monitor{1};
A64::UserConfig conf;
conf.callbacks = &env;
conf.processor_id = 0;
SECTION("Global Monitor") {
conf.global_monitor = &monitor;
}
A64::Jit jit{conf};
env.code_mem.emplace_back(0xc87f0861); // LDXP X1, X2, [X3]
env.code_mem.emplace_back(0xc8241865); // STXP W4, X5, X6, [X3]
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetRegister(3, 0x1234567812345678);
jit.SetRegister(4, 0xbaadbaadbaadbaad);
jit.SetRegister(5, 0xaf00d1e5badcafe0);
jit.SetRegister(6, 0xd0d0cacad0d0caca);
env.ticks_left = 3;
jit.Run();
REQUIRE(jit.GetRegister(1) == 0x7f7e7d7c7b7a7978);
REQUIRE(jit.GetRegister(2) == 0x8786858483828180);
REQUIRE(jit.GetRegister(4) == 0);
REQUIRE(env.MemoryRead64(0x1234567812345678) == 0xaf00d1e5badcafe0);
REQUIRE(env.MemoryRead64(0x1234567812345680) == 0xd0d0cacad0d0caca);
}
TEST_CASE("A64: CNTPCT_EL0", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0xd53be021); // MRS X1, CNTPCT_EL0
env.code_mem.emplace_back(0xd503201f); // NOP
env.code_mem.emplace_back(0xd503201f); // NOP
env.code_mem.emplace_back(0xd503201f); // NOP
env.code_mem.emplace_back(0xd503201f); // NOP
env.code_mem.emplace_back(0xd503201f); // NOP
env.code_mem.emplace_back(0xd503201f); // NOP
env.code_mem.emplace_back(0xd53be022); // MRS X2, CNTPCT_EL0
env.code_mem.emplace_back(0xcb010043); // SUB X3, X2, X1
env.code_mem.emplace_back(0x14000000); // B .
env.ticks_left = 10;
jit.Run();
REQUIRE(jit.GetRegister(3) == 7);
}
TEST_CASE("A64: FNMSUB 1", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x1f618a9c); // FNMSUB D28, D20, D1, D2
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(20, {0xe73a51346164bd6c, 0x8080000000002b94});
jit.SetVector(1, {0xbf8000007fffffff, 0xffffffff00002b94});
jit.SetVector(2, {0x0000000000000000, 0xc79b271e3f000000});
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(28) == Vector{0x66ca513533ee6076, 0x0000000000000000});
}
TEST_CASE("A64: FNMSUB 2", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x1f2ab88e); // FNMSUB S14, S4, S10, S14
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(4, {0x3c9623b101398437, 0x7ff0abcd0ba98d27});
jit.SetVector(10, {0xffbfffff3eaaaaab, 0x3f0000003f8147ae});
jit.SetVector(14, {0x80000000007fffff, 0xe73a513400000000});
jit.SetFpcr(0x00400000);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(14) == Vector{0x0000000080045284, 0x0000000000000000});
}
TEST_CASE("A64: FMADD", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x1f5e0e4a); // FMADD D10, D18, D30, D3
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(18, {0x8000007600800000, 0x7ff812347f800000});
jit.SetVector(30, {0xff984a3700000000, 0xe73a513480800000});
jit.SetVector(3, {0x3f000000ff7fffff, 0x8139843780000000});
jit.SetFpcr(0x00400000);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(10) == Vector{0x3f059921bf0dbfff, 0x0000000000000000});
}
TEST_CASE("A64: FMLA.4S (denormal)", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x4e2fcccc); // FMLA.4S V12, V6, V15
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(12, {0x3c9623b17ff80000, 0xbff0000080000076});
jit.SetVector(6, {0x7ff80000ff800000, 0x09503366c1200000});
jit.SetVector(15, {0x3ff0000080636d24, 0xbf800000e73a5134});
jit.SetFpcr(0x01000000);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(12) == Vector{0x7ff800007fc00000, 0xbff0000068e8e581});
}
TEST_CASE("A64: FMLA.4S (0x80800000)", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x4e38cc2b); // FMLA.4S V11, V1, V24
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(11, {0xc79b271efff05678, 0xffc0000080800000});
jit.SetVector(1, {0x00636d2400800000, 0x0966320bb26bddee});
jit.SetVector(24, {0x460e8c84fff00000, 0x8ba98d2780800002});
jit.SetFpcr(0x03000000);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(11) == Vector{0xc79b271e7fc00000, 0x7fc0000080000000});
}
// x64 has different rounding behaviour to AArch64.
// AArch64 performs rounding after flushing-to-zero.
// x64 performs rounding before flushing-to-zero.
TEST_CASE("A64: FMADD (0x80800000)", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x1f0f7319); // FMADD S25, S24, S15, S28
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(24, {0x00800000, 0});
jit.SetVector(15, {0x0ba98d27, 0});
jit.SetVector(28, {0x80800000, 0});
jit.SetFpcr(0x01000000);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(25) == Vector{0x80000000, 0});
}
TEST_CASE("A64: FNEG failed to zero upper", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x2ea0fb50); // FNEG.2S V16, V26
env.code_mem.emplace_back(0x2e207a1c); // SQNEG.8B V28, V16
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(26, {0x071286fde8f34a90, 0x837cffa8be382f60});
jit.SetFpcr(0x01000000);
env.ticks_left = 6;
jit.Run();
REQUIRE(jit.GetVector(28) == Vector{0x79ee7a03980db670, 0});
REQUIRE(FP::FPSR{jit.GetFpsr()}.QC() == false);
}
TEST_CASE("A64: FRSQRTS", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x5eb8fcad); // FRSQRTS S13, S5, S24
env.code_mem.emplace_back(0x14000000); // B .
// These particular values result in an intermediate value during
// the calculation that is close to infinity. We want to verify
// that this special case is handled appropriately.
jit.SetPC(0);
jit.SetVector(5, {0xfc6a0206, 0});
jit.SetVector(24, {0xfc6a0206, 0});
jit.SetFpcr(0x00400000);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(13) == Vector{0xff7fffff, 0});
}
TEST_CASE("A64: SQDMULH.8H (saturate)", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x4e62b420); // SQDMULH.8H V0, V1, V2
env.code_mem.emplace_back(0x14000000); // B .
// Make sure that saturating values are tested
jit.SetPC(0);
jit.SetVector(1, {0x7fff80007ffe8001, 0x7fff80007ffe8001});
jit.SetVector(2, {0x7fff80007ffe8001, 0x80007fff80017ffe});
jit.SetFpsr(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(0) == Vector{0x7ffe7fff7ffc7ffe, 0x8001800180028002});
REQUIRE(FP::FPSR{jit.GetFpsr()}.QC() == true);
}
TEST_CASE("A64: SQDMULH.4S (saturate)", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x4ea2b420); // SQDMULH.4S V0, V1, V2
env.code_mem.emplace_back(0x14000000); // B .
// Make sure that saturating values are tested
jit.SetPC(0);
jit.SetVector(1, {0x7fffffff80000000, 0x7fffffff80000000});
jit.SetVector(2, {0x7fffffff80000000, 0x800000007fffffff});
jit.SetFpsr(0);
env.ticks_left = 2;
jit.Run();
REQUIRE(jit.GetVector(0) == Vector{0x7ffffffe7fffffff, 0x8000000180000001});
REQUIRE(FP::FPSR{jit.GetFpsr()}.QC() == true);
}
TEST_CASE("A64: This is an infinite loop if fast dispatch is enabled", "[a64]") {
A64TestEnv env;
A64::UserConfig conf{&env};
conf.optimizations &= ~OptimizationFlag::FastDispatch;
A64::Jit jit{conf};
env.code_mem.emplace_back(0x2ef998fa);
env.code_mem.emplace_back(0x2ef41c11);
env.code_mem.emplace_back(0x0f07fdd8);
env.code_mem.emplace_back(0x9ac90d09);
env.code_mem.emplace_back(0xd63f0120); // BLR X9
env.code_mem.emplace_back(0x14000000); // B .
env.ticks_left = 6;
jit.Run();
}
TEST_CASE("A64: Optimization failure when folding ADD", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0xbc4f84be); // LDR S30, [X5], #248
env.code_mem.emplace_back(0x9a0c00ea); // ADC X10, X7, X12
env.code_mem.emplace_back(0x5a1a0079); // SBC W25, W3, W26
env.code_mem.emplace_back(0x9b0e2be9); // MADD X9, XZR, X14, X10
env.code_mem.emplace_back(0xfa5fe8a9); // CCMP X5, #31, #9, AL
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetRegister(0, 0x46e15845dba57924);
jit.SetRegister(1, 0x6f60d04350581fea);
jit.SetRegister(2, 0x85cface50edcfc03);
jit.SetRegister(3, 0x47e1e8906e10ec5a);
jit.SetRegister(4, 0x70717c9450b6b707);
jit.SetRegister(5, 0x300d83205baeaff4);
jit.SetRegister(6, 0xb7890de7c6fee082);
jit.SetRegister(7, 0xa89fb6d6f1b42f4a);
jit.SetRegister(8, 0x04e36b8aada91d4f);
jit.SetRegister(9, 0xa03bf6bde71c6ac5);
jit.SetRegister(10, 0x319374d14baa83b0);
jit.SetRegister(11, 0x5a78fc0fffca7c5f);
jit.SetRegister(12, 0xc012b5063f43b8ad);
jit.SetRegister(13, 0x821ade159d39fea1);
jit.SetRegister(14, 0x41f97b2f5525c25e);
jit.SetRegister(15, 0xab0cd3653cb93738);
jit.SetRegister(16, 0x50dfcb55a4ebd554);
jit.SetRegister(17, 0x30dd7d18ae52df03);
jit.SetRegister(18, 0x4e53b20d252bf085);
jit.SetRegister(19, 0x013582d71f5fd42a);
jit.SetRegister(20, 0x97a151539dad44e7);
jit.SetRegister(21, 0xa6fcc6bb220a2ad3);
jit.SetRegister(22, 0x4c84d3c84a6c5c5c);
jit.SetRegister(23, 0x1a7596a5ef930dff);
jit.SetRegister(24, 0x06248d96a02ff210);
jit.SetRegister(25, 0xfcb8772aec4b1dfd);
jit.SetRegister(26, 0x63619787b6a17665);
jit.SetRegister(27, 0xbd50c3352d001e40);
jit.SetRegister(28, 0x4e186aae63c81553);
jit.SetRegister(29, 0x57462b7163bd6508);
jit.SetRegister(30, 0xa977c850d16d562c);
jit.SetSP(0x000000da9b761d8c);
jit.SetFpsr(0x03480000);
jit.SetPstate(0x30000000);
env.ticks_left = 6;
jit.Run();
REQUIRE(jit.GetRegister(0) == 0x46e15845dba57924);
REQUIRE(jit.GetRegister(1) == 0x6f60d04350581fea);
REQUIRE(jit.GetRegister(2) == 0x85cface50edcfc03);
REQUIRE(jit.GetRegister(3) == 0x47e1e8906e10ec5a);
REQUIRE(jit.GetRegister(4) == 0x70717c9450b6b707);
REQUIRE(jit.GetRegister(5) == 0x300d83205baeb0ec);
REQUIRE(jit.GetRegister(6) == 0xb7890de7c6fee082);
REQUIRE(jit.GetRegister(7) == 0xa89fb6d6f1b42f4a);
REQUIRE(jit.GetRegister(8) == 0x04e36b8aada91d4f);
REQUIRE(jit.GetRegister(9) == 0x68b26bdd30f7e7f8);
REQUIRE(jit.GetRegister(10) == 0x68b26bdd30f7e7f8);
REQUIRE(jit.GetRegister(11) == 0x5a78fc0fffca7c5f);
REQUIRE(jit.GetRegister(12) == 0xc012b5063f43b8ad);
REQUIRE(jit.GetRegister(13) == 0x821ade159d39fea1);
REQUIRE(jit.GetRegister(14) == 0x41f97b2f5525c25e);
REQUIRE(jit.GetRegister(15) == 0xab0cd3653cb93738);
REQUIRE(jit.GetRegister(16) == 0x50dfcb55a4ebd554);
REQUIRE(jit.GetRegister(17) == 0x30dd7d18ae52df03);
REQUIRE(jit.GetRegister(18) == 0x4e53b20d252bf085);
REQUIRE(jit.GetRegister(19) == 0x013582d71f5fd42a);
REQUIRE(jit.GetRegister(20) == 0x97a151539dad44e7);
REQUIRE(jit.GetRegister(21) == 0xa6fcc6bb220a2ad3);
REQUIRE(jit.GetRegister(22) == 0x4c84d3c84a6c5c5c);
REQUIRE(jit.GetRegister(23) == 0x1a7596a5ef930dff);
REQUIRE(jit.GetRegister(24) == 0x06248d96a02ff210);
REQUIRE(jit.GetRegister(25) == 0x00000000b76f75f5);
REQUIRE(jit.GetRegister(26) == 0x63619787b6a17665);
REQUIRE(jit.GetRegister(27) == 0xbd50c3352d001e40);
REQUIRE(jit.GetRegister(28) == 0x4e186aae63c81553);
REQUIRE(jit.GetRegister(29) == 0x57462b7163bd6508);
REQUIRE(jit.GetRegister(30) == 0xa977c850d16d562c);
REQUIRE(jit.GetPstate() == 0x20000000);
REQUIRE(jit.GetVector(30) == Vector{0xf7f6f5f4, 0});
}

View File

@@ -0,0 +1,505 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <catch.hpp>
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/llvm_disassemble.h"
#include "common/scope_exit.h"
#include "frontend/A64/decoder/a64.h"
#include "frontend/A64/location_descriptor.h"
#include "frontend/A64/translate/translate.h"
#include "frontend/A64/types.h"
#include "frontend/ir/basic_block.h"
#include "frontend/ir/opcodes.h"
#include "fuzz_util.h"
#include "ir_opt/passes.h"
#include "rand_int.h"
#include "testenv.h"
#include "unicorn_emu/a64_unicorn.h"
// Must be declared last for all necessary operator<< to be declared prior to this.
#include <fmt/format.h>
#include <fmt/ostream.h>
using namespace Dynarmic;
static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) {
const A64::LocationDescriptor location{pc, {}};
IR::Block block{location};
bool should_continue = A64::TranslateSingleInstruction(block, location, instruction);
if (!should_continue && !is_last_inst)
return false;
if (auto terminal = block.GetTerminal(); boost::get<IR::Term::Interpret>(&terminal))
return false;
for (const auto& ir_inst : block) {
switch (ir_inst.GetOpcode()) {
case IR::Opcode::A64ExceptionRaised:
case IR::Opcode::A64CallSupervisor:
case IR::Opcode::A64DataCacheOperationRaised:
case IR::Opcode::A64GetCNTPCT:
return false;
default:
continue;
}
}
return true;
}
static u32 GenRandomInst(u64 pc, bool is_last_inst) {
static const struct InstructionGeneratorInfo {
std::vector<InstructionGenerator> generators;
std::vector<InstructionGenerator> invalid;
} instructions = []{
const std::vector<std::tuple<std::string, const char*>> list {
#define INST(fn, name, bitstring) {#fn, bitstring},
#include "frontend/A64/decoder/a64.inc"
#undef INST
};
std::vector<InstructionGenerator> generators;
std::vector<InstructionGenerator> invalid;
// List of instructions not to test
const std::vector<std::string> do_not_test {
// Unimplemented in QEMU
"STLLR",
// Unimplemented in QEMU
"LDLAR",
// Dynarmic and QEMU currently differ on how the exclusive monitor's address range works.
"STXR", "STLXR", "STXP", "STLXP", "LDXR", "LDAXR", "LDXP", "LDAXP",
// Behaviour differs from QEMU
"MSR_reg", "MSR_imm", "MRS",
};
for (const auto& [fn, bitstring] : list) {
if (fn == "UnallocatedEncoding") {
continue;
}
if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) {
invalid.emplace_back(InstructionGenerator{bitstring});
continue;
}
generators.emplace_back(InstructionGenerator{bitstring});
}
return InstructionGeneratorInfo{generators, invalid};
}();
while (true) {
const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1);
const u32 inst = instructions.generators[index].Generate();
if (std::any_of(instructions.invalid.begin(), instructions.invalid.end(), [inst](const auto& invalid) { return invalid.Match(inst); })) {
continue;
}
if (ShouldTestInst(inst, pc, is_last_inst)) {
return inst;
}
}
}
static u32 GenFloatInst(u64 pc, bool is_last_inst) {
static const std::vector<InstructionGenerator> instruction_generators = []{
const std::vector<std::tuple<std::string, std::string, const char*>> list {
#define INST(fn, name, bitstring) {#fn, #name, bitstring},
#include "frontend/A64/decoder/a64.inc"
#undef INST
};
// List of instructions not to test
const std::vector<std::string> do_not_test {
};
std::vector<InstructionGenerator> result;
for (const auto& [fn, name, bitstring] : list) {
(void)name;
if (fn[0] != 'F') {
continue;
} else if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) {
continue;
}
result.emplace_back(InstructionGenerator{bitstring});
}
return result;
}();
while (true) {
const size_t index = RandInt<size_t>(0, instruction_generators.size() - 1);
const u32 instruction = instruction_generators[index].Generate();
if ((instruction & 0x00800000) == 0 && ShouldTestInst(instruction, pc, is_last_inst)) {
return instruction;
}
}
}
static Dynarmic::A64::UserConfig GetUserConfig(A64TestEnv& jit_env) {
Dynarmic::A64::UserConfig jit_user_config{&jit_env};
jit_user_config.optimizations &= ~OptimizationFlag::FastDispatch;
// The below corresponds to the settings for qemu's aarch64_max_initfn
jit_user_config.dczid_el0 = 7;
jit_user_config.ctr_el0 = 0x80038003;
return jit_user_config;
}
static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv& jit_env, A64TestEnv& uni_env,
const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start,
const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) {
jit_env.code_mem = instructions;
uni_env.code_mem = instructions;
jit_env.code_mem.emplace_back(0x14000000); // B .
uni_env.code_mem.emplace_back(0x14000000); // B .
jit_env.code_mem_start_address = instructions_start;
uni_env.code_mem_start_address = instructions_start;
jit_env.modified_memory.clear();
uni_env.modified_memory.clear();
jit_env.interrupts.clear();
uni_env.interrupts.clear();
const u64 initial_sp = RandInt<u64>(0x30'0000'0000, 0x40'0000'0000) * 4;
jit.SetRegisters(regs);
jit.SetVectors(vecs);
jit.SetPC(instructions_start);
jit.SetSP(initial_sp);
jit.SetFpcr(fpcr);
jit.SetFpsr(0);
jit.SetPstate(pstate);
jit.ClearCache();
uni.SetRegisters(regs);
uni.SetVectors(vecs);
uni.SetPC(instructions_start);
uni.SetSP(initial_sp);
uni.SetFpcr(fpcr);
uni.SetFpsr(0);
uni.SetPstate(pstate);
uni.ClearPageCache();
jit_env.ticks_left = instructions.size();
jit.Run();
uni_env.ticks_left = instructions.size();
uni.Run();
SCOPE_FAIL {
fmt::print("Instruction Listing:\n");
for (u32 instruction : instructions) {
fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch64(instruction));
}
fmt::print("\n");
fmt::print("Initial register listing:\n");
for (size_t i = 0; i < regs.size(); ++i) {
fmt::print("{:3s}: {:016x}\n", static_cast<A64::Reg>(i), regs[i]);
}
for (size_t i = 0; i < vecs.size(); ++i) {
fmt::print("{:3s}: {}\n", static_cast<A64::Vec>(i), vecs[i]);
}
fmt::print("sp : {:016x}\n", initial_sp);
fmt::print("pc : {:016x}\n", instructions_start);
fmt::print("p : {:08x}\n", pstate);
fmt::print("fpcr {:08x}\n", fpcr);
fmt::print("fpcr.AHP {}\n", FP::FPCR{fpcr}.AHP());
fmt::print("fpcr.DN {}\n", FP::FPCR{fpcr}.DN());
fmt::print("fpcr.FZ {}\n", FP::FPCR{fpcr}.FZ());
fmt::print("fpcr.RMode {}\n", static_cast<size_t>(FP::FPCR{fpcr}.RMode()));
fmt::print("fpcr.FZ16 {}\n", FP::FPCR{fpcr}.FZ16());
fmt::print("\n");
fmt::print("Final register listing:\n");
fmt::print(" unicorn dynarmic\n");
const auto uni_regs = uni.GetRegisters();
for (size_t i = 0; i < regs.size(); ++i) {
fmt::print("{:3s}: {:016x} {:016x} {}\n", static_cast<A64::Reg>(i), uni_regs[i], jit.GetRegisters()[i], uni_regs[i] != jit.GetRegisters()[i] ? "*" : "");
}
const auto uni_vecs = uni.GetVectors();
for (size_t i = 0; i < vecs.size(); ++i) {
fmt::print("{:3s}: {} {} {}\n", static_cast<A64::Vec>(i), uni_vecs[i], jit.GetVectors()[i], uni_vecs[i] != jit.GetVectors()[i] ? "*" : "");
}
fmt::print("sp : {:016x} {:016x} {}\n", uni.GetSP(), jit.GetSP(), uni.GetSP() != jit.GetSP() ? "*" : "");
fmt::print("pc : {:016x} {:016x} {}\n", uni.GetPC(), jit.GetPC(), uni.GetPC() != jit.GetPC() ? "*" : "");
fmt::print("p : {:08x} {:08x} {}\n", uni.GetPstate(), jit.GetPstate(), (uni.GetPstate() & 0xF0000000) != (jit.GetPstate() & 0xF0000000) ? "*" : "");
fmt::print("qc : {:08x} {:08x} {}\n", uni.GetFpsr(), jit.GetFpsr(), FP::FPSR{uni.GetFpsr()}.QC() != FP::FPSR{jit.GetFpsr()}.QC() ? "*" : "");
fmt::print("\n");
fmt::print("Modified memory:\n");
fmt::print(" uni dyn\n");
auto uni_iter = uni_env.modified_memory.begin();
auto jit_iter = jit_env.modified_memory.begin();
while (uni_iter != uni_env.modified_memory.end() || jit_iter != jit_env.modified_memory.end()) {
if (uni_iter == uni_env.modified_memory.end() || (jit_iter != jit_env.modified_memory.end() && uni_iter->first > jit_iter->first)) {
fmt::print("{:016x}: {:02x} *\n", jit_iter->first, jit_iter->second);
jit_iter++;
} else if (jit_iter == jit_env.modified_memory.end() || jit_iter->first > uni_iter->first) {
fmt::print("{:016x}: {:02x} *\n", uni_iter->first, uni_iter->second);
uni_iter++;
} else if (uni_iter->first == jit_iter->first) {
fmt::print("{:016x}: {:02x} {:02x} {}\n", uni_iter->first, uni_iter->second, jit_iter->second, uni_iter->second != jit_iter->second ? "*" : "");
uni_iter++;
jit_iter++;
}
}
fmt::print("\n");
const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); };
IR::Block ir_block = A64::Translate({instructions_start, FP::FPCR{fpcr}}, get_code, {});
Optimization::A64CallbackConfigPass(ir_block, GetUserConfig(jit_env));
fmt::print("IR:\n");
fmt::print("{}\n", IR::DumpBlock(ir_block));
Optimization::A64GetSetElimination(ir_block);
Optimization::DeadCodeElimination(ir_block);
Optimization::ConstantPropagation(ir_block);
Optimization::DeadCodeElimination(ir_block);
fmt::print("Optimized IR:\n");
fmt::print("{}\n", IR::DumpBlock(ir_block));
fmt::print("x86_64:\n");
fmt::print("{}\n", jit.Disassemble());
fmt::print("Interrupts:\n");
for (auto& i : uni_env.interrupts) {
puts(i.c_str());
}
};
REQUIRE(uni_env.code_mem_modified_by_guest == jit_env.code_mem_modified_by_guest);
if (uni_env.code_mem_modified_by_guest) {
return;
}
REQUIRE(uni.GetPC() == jit.GetPC());
REQUIRE(uni.GetRegisters() == jit.GetRegisters());
REQUIRE(uni.GetVectors() == jit.GetVectors());
REQUIRE(uni.GetSP() == jit.GetSP());
REQUIRE((uni.GetPstate() & 0xF0000000) == (jit.GetPstate() & 0xF0000000));
REQUIRE(uni_env.modified_memory == jit_env.modified_memory);
REQUIRE(uni_env.interrupts.empty());
REQUIRE(FP::FPSR{uni.GetFpsr()}.QC() == FP::FPSR{jit.GetFpsr()}.QC());
}
TEST_CASE("A64: Single random instruction", "[a64]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
A64Unicorn::VectorArray vecs;
std::vector<u32> instructions(1);
for (size_t iteration = 0; iteration < 100000; ++iteration) {
std::generate(regs.begin(), regs.end(), []{ return RandInt<u64>(0, ~u64(0)); });
std::generate(vecs.begin(), vecs.end(), RandomVector);
instructions[0] = GenRandomInst(0, true);
const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4;
const u32 pstate = RandInt<u32>(0, 0xF) << 28;
const u32 fpcr = RandomFpcr();
INFO("Instruction: 0x" << std::hex << instructions[0]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
TEST_CASE("A64: Floating point instructions", "[a64]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
A64Unicorn uni{uni_env};
static constexpr std::array<u64, 80> float_numbers {
0x00000000, // positive zero
0x00000001, // smallest positive denormal
0x00000076, //
0x00002b94, //
0x00636d24, //
0x007fffff, // largest positive denormal
0x00800000, // smallest positive normalised real
0x00800002, //
0x01398437, //
0x0ba98d27, //
0x0ba98d7a, //
0x751f853a, //
0x7f7ffff0, //
0x7f7fffff, // largest positive normalised real
0x7f800000, // positive infinity
0x7f800001, // first positive SNaN
0x7f984a37, //
0x7fbfffff, // last positive SNaN
0x7fc00000, // first positive QNaN
0x7fd9ba98, //
0x7fffffff, // last positive QNaN
0x80000000, // negative zero
0x80000001, // smallest negative denormal
0x80000076, //
0x80002b94, //
0x80636d24, //
0x807fffff, // largest negative denormal
0x80800000, // smallest negative normalised real
0x80800002, //
0x81398437, //
0x8ba98d27, //
0x8ba98d7a, //
0xf51f853a, //
0xff7ffff0, //
0xff7fffff, // largest negative normalised real
0xff800000, // negative infinity
0xff800001, // first negative SNaN
0xff984a37, //
0xffbfffff, // last negative SNaN
0xffc00000, // first negative QNaN
0xffd9ba98, //
0xffffffff, // last negative QNaN
// some random numbers follow
0x4f3495cb,
0xe73a5134,
0x7c994e9e,
0x6164bd6c,
0x09503366,
0xbf5a97c9,
0xe6ff1a14,
0x77f31e2f,
0xaab4d7d8,
0x0966320b,
0xb26bddee,
0xb5c8e5d3,
0x317285d3,
0x3c9623b1,
0x51fd2c7c,
0x7b906a6c,
0x3f800000,
0x3dcccccd,
0x3f000000,
0x42280000,
0x3eaaaaab,
0xc1200000,
0xbf800000,
0xbf8147ae,
0x3f8147ae,
0x415df525,
0xc79b271e,
0x460e8c84,
// some 64-bit-float upper-halves
0x7ff00000, // +SNaN / +Inf
0x7ff0abcd, // +SNaN
0x7ff80000, // +QNaN
0x7ff81234, // +QNaN
0xfff00000, // -SNaN / -Inf
0xfff05678, // -SNaN
0xfff80000, // -QNaN
0xfff809ef, // -QNaN
0x3ff00000, // Number near +1.0
0xbff00000, // Number near -1.0
};
const auto gen_float = [&]{
return float_numbers[RandInt<size_t>(0, float_numbers.size() - 1)];
};
const auto gen_vector = [&]{
u64 upper = (gen_float() << 32) | gen_float();
u64 lower = (gen_float() << 32) | gen_float();
return Vector{lower, upper};
};
A64Unicorn::RegisterArray regs;
A64Unicorn::VectorArray vecs;
std::vector<u32> instructions(1);
for (size_t iteration = 0; iteration < 100000; ++iteration) {
std::generate(regs.begin(), regs.end(), gen_float);
std::generate(vecs.begin(), vecs.end(), gen_vector);
instructions[0] = GenFloatInst(0, true);
const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4;
const u32 pstate = RandInt<u32>(0, 0xF) << 28;
const u32 fpcr = RandomFpcr();
INFO("Instruction: 0x" << std::hex << instructions[0]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
TEST_CASE("A64: Small random block", "[a64]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
A64Unicorn::VectorArray vecs;
std::vector<u32> instructions(5);
for (size_t iteration = 0; iteration < 100000; ++iteration) {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u64>(0, ~u64(0)); });
std::generate(vecs.begin(), vecs.end(), RandomVector);
instructions[0] = GenRandomInst(0, false);
instructions[1] = GenRandomInst(4, false);
instructions[2] = GenRandomInst(8, false);
instructions[3] = GenRandomInst(12, false);
instructions[4] = GenRandomInst(16, true);
const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4;
const u32 pstate = RandInt<u32>(0, 0xF) << 28;
const u32 fpcr = RandomFpcr();
INFO("Instruction 1: 0x" << std::hex << instructions[0]);
INFO("Instruction 2: 0x" << std::hex << instructions[1]);
INFO("Instruction 3: 0x" << std::hex << instructions[2]);
INFO("Instruction 4: 0x" << std::hex << instructions[3]);
INFO("Instruction 5: 0x" << std::hex << instructions[4]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
TEST_CASE("A64: Large random block", "[a64]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
A64Unicorn::VectorArray vecs;
constexpr size_t instruction_count = 100;
std::vector<u32> instructions(instruction_count);
for (size_t iteration = 0; iteration < 500; ++iteration) {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u64>(0, ~u64(0)); });
std::generate(vecs.begin(), vecs.end(), RandomVector);
for (size_t j = 0; j < instruction_count; ++j) {
instructions[j] = GenRandomInst(j * 4, j == instruction_count - 1);
}
const u64 start_address = RandInt<u64>(0, 0x10'0000'0000) * 4;
const u32 pstate = RandInt<u32>(0, 0xF) << 28;
const u32 fpcr = RandomFpcr();
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
}
}

View File

@@ -0,0 +1,30 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <catch.hpp>
#include <dynarmic/A64/a64.h>
#include "testenv.h"
TEST_CASE("misaligned load/store do not use page_table when detect_misaligned_access_via_page_table is set", "[a64]") {
A64TestEnv env;
Dynarmic::A64::UserConfig conf{&env};
conf.page_table = nullptr;
conf.detect_misaligned_access_via_page_table = 128;
conf.only_detect_misalignment_via_page_table_on_page_boundary = true;
Dynarmic::A64::Jit jit{conf};
env.code_mem.emplace_back(0x3c800400); // STR Q0, [X0], #0
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetRegister(0, 0x000000000b0afff8);
env.ticks_left = 2;
jit.Run();
// If we don't crash we're fine.
}

127
externals/dynarmic/tests/A64/testenv.h vendored Executable file
View File

@@ -0,0 +1,127 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <array>
#include <map>
#include <dynarmic/A64/a64.h>
#include "common/assert.h"
#include "common/common_types.h"
using Vector = Dynarmic::A64::Vector;
class A64TestEnv final : public Dynarmic::A64::UserCallbacks {
public:
u64 ticks_left = 0;
bool code_mem_modified_by_guest = false;
u64 code_mem_start_address = 0;
std::vector<u32> code_mem;
std::map<u64, u8> modified_memory;
std::vector<std::string> interrupts;
bool IsInCodeMem(u64 vaddr) const {
return vaddr >= code_mem_start_address && vaddr < code_mem_start_address + code_mem.size() * 4;
}
std::uint32_t MemoryReadCode(u64 vaddr) override {
if (!IsInCodeMem(vaddr)) {
return 0x14000000; // B .
}
const size_t index = (vaddr - code_mem_start_address) / 4;
return code_mem[index];
}
std::uint8_t MemoryRead8(u64 vaddr) override {
if (IsInCodeMem(vaddr)) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address];
}
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
return iter->second;
}
return static_cast<u8>(vaddr);
}
std::uint16_t MemoryRead16(u64 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u64 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
Vector MemoryRead128(u64 vaddr) override {
return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)};
}
void MemoryWrite8(u64 vaddr, std::uint8_t value) override {
if (IsInCodeMem(vaddr)) {
code_mem_modified_by_guest = true;
}
modified_memory[vaddr] = value;
}
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
}
void MemoryWrite128(u64 vaddr, Vector value) override {
MemoryWrite64(vaddr, value[0]);
MemoryWrite64(vaddr + 8, value[1]);
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value);
return true;
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value);
return true;
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value);
return true;
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value);
return true;
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
MemoryWrite128(vaddr, value);
return true;
}
void InterpreterFallback(u64 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:016x}, {})", pc, num_instructions); }
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:016x})", pc); }
void AddTicks(std::uint64_t ticks) override {
if (ticks > ticks_left) {
ticks_left = 0;
return;
}
ticks_left -= ticks;
}
std::uint64_t GetTicksRemaining() override {
return ticks_left;
}
std::uint64_t GetCNTPCT() override {
return 0x10000000000 - ticks_left;
}
};

View File

@@ -0,0 +1,81 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <array>
#include <catch.hpp>
#include "rand_int.h"
#include "testenv.h"
#include "unicorn_emu/a64_unicorn.h"
using namespace Dynarmic;
TEST_CASE("Unicorn: Sanity test", "[a64]") {
A64TestEnv env;
env.code_mem.emplace_back(0x8b020020); // ADD X0, X1, X2
env.code_mem.emplace_back(0x14000000); // B .
constexpr A64Unicorn::RegisterArray regs{
0, 1, 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
};
A64Unicorn unicorn{env};
unicorn.SetRegisters(regs);
unicorn.SetPC(0);
env.ticks_left = 2;
unicorn.Run();
REQUIRE(unicorn.GetRegisters()[0] == 3);
REQUIRE(unicorn.GetRegisters()[1] == 1);
REQUIRE(unicorn.GetRegisters()[2] == 2);
REQUIRE(unicorn.GetPC() == 4);
}
TEST_CASE("Unicorn: Ensure 0xFFFF'FFFF'FFFF'FFFF is readable", "[a64]") {
A64TestEnv env;
env.code_mem.emplace_back(0x385fed99); // LDRB W25, [X12, #0xfffffffffffffffe]!
env.code_mem.emplace_back(0x14000000); // B .
A64Unicorn::RegisterArray regs{};
regs[12] = 1;
A64Unicorn unicorn{env};
unicorn.SetRegisters(regs);
unicorn.SetPC(0);
env.ticks_left = 2;
unicorn.Run();
REQUIRE(unicorn.GetPC() == 4);
}
TEST_CASE("Unicorn: Ensure is able to read across page boundaries", "[a64]") {
A64TestEnv env;
env.code_mem.emplace_back(0xb85f93d9); // LDUR W25, [X30, #0xfffffffffffffff9]
env.code_mem.emplace_back(0x14000000); // B .
A64Unicorn::RegisterArray regs{};
regs[30] = 4;
A64Unicorn unicorn{env};
unicorn.SetRegisters(regs);
unicorn.SetPC(0);
env.ticks_left = 2;
unicorn.Run();
REQUIRE(unicorn.GetPC() == 4);
}