From 0b15fc9755d44f6195acc6523cb6312e6cc17e35 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 26 Apr 2019 07:06:10 -0400 Subject: [PATCH] a32/fuzz_arm: Use same fuzzing mechanism as AArch64 Introduces the same fuzzing mechanism used by the AArch64 code for fuzzing instruction implementations, getting rid of the need to manually specify the instruction generator sequences--replacing it with an instruction blacklist instead. Much of this change originates from a previous patch made by Mary. This just makes it interact nicely with the alterations made to get Unicorn to cooperate properly. --- tests/A32/fuzz_arm.cpp | 1456 ++++++++-------------------------------- 1 file changed, 267 insertions(+), 1189 deletions(-) diff --git a/tests/A32/fuzz_arm.cpp b/tests/A32/fuzz_arm.cpp index a0ae4cfa..837ff1dc 100644 --- a/tests/A32/fuzz_arm.cpp +++ b/tests/A32/fuzz_arm.cpp @@ -6,1250 +6,328 @@ #include #include -#include #include -#include #include #include #include #include - #include -#include "common/bit_util.h" #include "common/common_types.h" +#include "common/fp/fpcr.h" +#include "common/fp/fpsr.h" #include "common/scope_exit.h" #include "frontend/A32/disassembler/disassembler.h" -#include "frontend/A32/FPSCR.h" #include "frontend/A32/location_descriptor.h" -#include "frontend/A32/PSR.h" #include "frontend/A32/translate/translate.h" #include "frontend/A32/types.h" #include "frontend/ir/basic_block.h" #include "frontend/ir/location_descriptor.h" -#include "ir_opt/passes.h" +#include "frontend/ir/opcodes.h" +#include "fuzz_util.h" #include "rand_int.h" #include "testenv.h" #include "unicorn_emu/a32_unicorn.h" -using Dynarmic::Common::Bits; +// Must be declared last for all necessary operator<< to be declared prior to this. +#include +#include namespace { -Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) { +using namespace Dynarmic; + +bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) { + const A32::LocationDescriptor location{pc, {}, {}}; + IR::Block block{location}; + const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction); + + if (!should_continue && !is_last_inst) { + return false; + } + + if (auto terminal = block.GetTerminal(); boost::get(&terminal)) { + return false; + } + + for (const auto& ir_inst : block) { + switch (ir_inst.GetOpcode()) { + case IR::Opcode::A32ExceptionRaised: + case IR::Opcode::A32CallSupervisor: + case IR::Opcode::A32CoprocInternalOperation: + case IR::Opcode::A32CoprocSendOneWord: + case IR::Opcode::A32CoprocSendTwoWords: + case IR::Opcode::A32CoprocGetOneWord: + case IR::Opcode::A32CoprocGetTwoWords: + case IR::Opcode::A32CoprocLoadWords: + case IR::Opcode::A32CoprocStoreWords: + return false; + default: + continue; + } + } + + return true; +} + +u32 GenRandomInst(u32 pc, bool is_last_inst) { + static const struct InstructionGeneratorInfo { + std::vector generators; + std::vector invalid; + } instructions = []{ + const std::vector> list { +#define INST(fn, name, bitstring) {#fn, bitstring}, +#include "frontend/A32/decoder/arm.inc" +#include "frontend/A32/decoder/vfp2.inc" +#undef INST + }; + + std::vector generators; + std::vector invalid; + + // List of instructions not to test + static constexpr std::array do_not_test { + // Translating load/stores + "arm_LDRBT", "arm_LDRBT", "arm_LDRHT", "arm_LDRHT", "arm_LDRSBT", "arm_LDRSBT", "arm_LDRSHT", "arm_LDRSHT", "arm_LDRT", "arm_LDRT", + "arm_STRBT", "arm_STRBT", "arm_STRHT", "arm_STRHT", "arm_STRT", "arm_STRT", + // Exclusive load/stores + "arm_LDREXB", "arm_LDREXD", "arm_LDREXH", "arm_LDREX", + "arm_STREXB", "arm_STREXD", "arm_STREXH", "arm_STREX", + "arm_SWP", "arm_SWPB", + // Elevated load/store multiple instructions. + "arm_LDM_eret", "arm_LDM_usr", + "arm_STM_usr", + // Hint instructions + "arm_NOP", "arm_PLD", "arm_SEV", "arm_WFE", "arm_WFI", "arm_YIELD", + // E, T, J + "arm_BLX_reg", "arm_BLX_imm", "arm_BXJ", "arm_SETEND", + // Coprocessor + "arm_CDP", "arm_LDC", "arm_MCR", "arm_MCRR", "arm_MRC", "arm_MRRC", "arm_STC", + // System + "arm_CPS", "arm_RFE", "arm_SRS", + // Undefined + "arm_UDF", + + // Behavior differs from Qemu + "arm_MSR_reg", "arm_MSR_imm", "arm_MRS", + }; + + for (const auto& [fn, bitstring] : list) { + 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(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; + } + } +} + +Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) { Dynarmic::A32::UserConfig user_config; user_config.enable_fast_dispatch = false; - user_config.callbacks = testenv; + user_config.callbacks = &testenv; return user_config; } -struct InstructionGenerator final { -public: - InstructionGenerator(const char* format, std::function is_valid = [](u32){ return true; }) : is_valid(is_valid) { - REQUIRE(strlen(format) == 32); +static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn& uni, + ArmTestEnv& jit_env, ArmTestEnv& uni_env, + const A32Unicorn::RegisterArray& regs, + const A32Unicorn::ExtRegArray& vecs, + const std::vector& instructions, const u32 cpsr, const u32 fpscr) { + const u32 initial_pc = regs[15]; + const u32 num_words = initial_pc / sizeof(u32); + const u32 code_mem_size = num_words + instructions.size(); - for (int i = 0; i < 32; i++) { - const u32 bit = 1u << (31 - i); - switch (format[i]) { - case '0': - mask |= bit; - break; - case '1': - bits |= bit; - mask |= bit; - break; - default: - // Do nothing - break; + jit_env.code_mem.resize(code_mem_size + 1); + uni_env.code_mem.resize(code_mem_size + 1); + + std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words); + std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + num_words); + jit_env.code_mem.back() = 0xEAFFFFFE; // B . + uni_env.code_mem.back() = 0xEAFFFFFE; // B . + jit_env.modified_memory.clear(); + uni_env.modified_memory.clear(); + jit_env.interrupts.clear(); + uni_env.interrupts.clear(); + + jit.Regs() = regs; + jit.ExtRegs() = vecs; + jit.SetFpscr(fpscr); + jit.SetCpsr(cpsr); + jit.ClearCache(); + uni.SetRegisters(regs); + uni.SetExtRegs(vecs); + uni.SetFpscr(fpscr); + uni.EnableFloatingPointAccess(); + uni.SetCpsr(cpsr); + 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, A32::DisassembleArm(instruction)); + } + fmt::print("\n"); + + fmt::print("Initial register listing:\n"); + for (size_t i = 0; i < regs.size(); ++i) { + fmt::print("{:3s}: {:08x}\n", static_cast(i), regs[i]); + } + for (size_t i = 0; i < vecs.size(); ++i) { + fmt::print("{:3s}: {:08x}\n", static_cast(i), vecs[i]); + } + fmt::print("cpsr {:08x}\n", cpsr); + fmt::print("fpcr {:08x}\n", fpscr); + fmt::print("fpcr.AHP {}\n", FP::FPCR{fpscr}.AHP()); + fmt::print("fpcr.DN {}\n", FP::FPCR{fpscr}.DN()); + fmt::print("fpcr.FZ {}\n", FP::FPCR{fpscr}.FZ()); + fmt::print("fpcr.RMode {}\n", static_cast(FP::FPCR{fpscr}.RMode())); + fmt::print("fpcr.FZ16 {}\n", FP::FPCR{fpscr}.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}: {:08x} {:08x} {}\n", static_cast(i), uni_regs[i], jit.Regs()[i], uni_regs[i] != jit.Regs()[i] ? "*" : ""); + } + const auto uni_ext_regs = uni.GetExtRegs(); + for (size_t i = 0; i < vecs.size(); ++i) { + fmt::print("s{:2d}: {:08x} {:08x} {}\n", static_cast(i), uni_ext_regs[i], jit.ExtRegs()[i], uni_ext_regs[i] != jit.ExtRegs()[i] ? "*" : ""); + } + fmt::print("cpsr {:08x} {:08x} {}\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : ""); + fmt::print("fpsr {:08x} {:08x} {}\n", uni.GetFpscr(), jit.Fpscr(), (uni.GetFpscr() & 0xF0000000) != (jit.Fpscr() & 0xF0000000) ? "*" : ""); + 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("{:08x}: {: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("{:08x}: {:02x} *\n", uni_iter->first, uni_iter->second); + uni_iter++; + } else if (uni_iter->first == jit_iter->first) { + fmt::print("{:08x}: {:02x} {:02x} {}\n", uni_iter->first, uni_iter->second, jit_iter->second, uni_iter->second != jit_iter->second ? "*" : ""); + uni_iter++; + jit_iter++; } } - } - u32 Generate(bool condition = true) const { - u32 inst; - do { - u32 random = RandInt(0, 0xFFFFFFFF); - if (condition) - random &= ~(0xF << 28); - inst = bits | (random & ~mask); - } while (!is_valid(inst)); + fmt::print("\n"); - if (condition) { - // Have a one-in-twenty-five chance of actually having a cond. - if (RandInt(1, 25) == 1) - inst |= RandInt(0x0, 0xD) << 28; - else - inst |= 0xE << 28; + fmt::print("x86_64:\n"); + fmt::print("{}\n", jit.Disassemble(A32::LocationDescriptor{initial_pc, A32::PSR{cpsr}, A32::FPSCR{fpscr}})); + + fmt::print("Interrupts:\n"); + for (const auto& i : uni_env.interrupts) { + std::puts(i.c_str()); } + }; - return inst; + REQUIRE(uni_env.code_mem_modified_by_guest == jit_env.code_mem_modified_by_guest); + if (uni_env.code_mem_modified_by_guest) { + return; } - u32 Bits() const { return bits; } - u32 Mask() const { return mask; } - bool IsValid(u32 inst) const { return is_valid(inst); } -private: - u32 bits = 0; - u32 mask = 0; - std::function is_valid; -}; -using WriteRecords = std::map; - -bool DoesBehaviorMatch(const A32Unicorn& uni, const Dynarmic::A32::Jit& jit, - const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) { - return uni.GetRegisters() == jit.Regs() && - uni.GetExtRegs() == jit.ExtRegs() && - uni.GetCpsr() == jit.Cpsr() && - // uni.GetFpscr() == jit.Fpscr() && - interp_write_records == jit_write_records; -} - -void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function instruction_generator) { - ArmTestEnv test_env; - - // Prepare memory - test_env.code_mem.resize(instruction_count + 1); - test_env.code_mem.back() = 0xEAFFFFFE; // b +#0 - - // Prepare test subjects - A32Unicorn uni{test_env}; - Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; - - for (size_t run_number = 0; run_number < run_count; run_number++) { - uni.ClearPageCache(); - jit.ClearCache(); - - // Setup initial state - - const u32 initial_cpsr = 0x000001D0; - - ArmTestEnv::RegisterArray initial_regs; - std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt(0, 0xFFFFFFFF); }); - initial_regs[15] = 0; - - ArmTestEnv::ExtRegsArray initial_extregs; - std::generate(initial_extregs.begin(), initial_extregs.end(), - []{ return RandInt(0, 0xFFFFFFFF); }); - - const u32 initial_fpscr = 0x01000000 | (RandInt(0, 3) << 22); - - uni.SetCpsr(initial_cpsr); - uni.SetRegisters(initial_regs); - uni.SetExtRegs(initial_extregs); - uni.SetFpscr(initial_fpscr); - uni.EnableFloatingPointAccess(); - jit.Reset(); - jit.SetCpsr(initial_cpsr); - jit.Regs() = initial_regs; - jit.ExtRegs() = initial_extregs; - jit.SetFpscr(initial_fpscr); - - std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator); - - WriteRecords interp_write_records, jit_write_records; - - SCOPE_FAIL { - printf("\nInstruction Listing: \n"); - for (size_t i = 0; i < instruction_count; i++) { - printf("%x: %s\n", test_env.code_mem[i], Dynarmic::A32::DisassembleArm(test_env.code_mem[i]).c_str()); - } - - printf("\nInitial Register Listing: \n"); - for (size_t i = 0; i < initial_regs.size(); i++) { - const auto reg = Dynarmic::A32::RegToString(static_cast(i)); - printf("%4s: %08x\n", reg, initial_regs[i]); - } - printf("CPSR: %08x\n", initial_cpsr); - printf("FPSCR:%08x\n", initial_fpscr); - for (size_t i = 0; i < initial_extregs.size(); i++) { - printf("S%3zu: %08x\n", i, initial_extregs[i]); - } - - printf("\nFinal Register Listing: \n"); - printf(" unicorn jit\n"); - const auto uni_registers = uni.GetRegisters(); - for (size_t i = 0; i < uni_registers.size(); i++) { - const auto reg = Dynarmic::A32::RegToString(static_cast(i)); - printf("%4s: %08x %08x %s\n", reg, uni_registers[i], jit.Regs()[i], uni_registers[i] != jit.Regs()[i] ? "*" : ""); - } - printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : ""); - printf("FPSCR:%08x %08x %s\n", uni.GetFpscr(), jit.Fpscr(), uni.GetFpscr() != jit.Fpscr() ? "*" : ""); - const auto uni_ext_regs = uni.GetExtRegs(); - for (size_t i = 0; i < uni_ext_regs.size(); i++) { - printf("S%3zu: %08x %08x %s\n", i, uni_ext_regs[i], jit.ExtRegs()[i], uni_ext_regs[i] != jit.ExtRegs()[i] ? "*" : ""); - } - - printf("\nInterp Write Records:\n"); - for (const auto& record : interp_write_records) { - printf("[%08x] = %02x\n", record.first, record.second); - } - - printf("\nJIT Write Records:\n"); - for (const auto& record : jit_write_records) { - printf("[%08x] = %02x\n", record.first, record.second); - } - - size_t num_insts = 0; - while (num_insts < instructions_to_execute_count) { - Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), Dynarmic::A32::PSR{}, Dynarmic::A32::FPSCR{}}; - Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, [&test_env](u32 vaddr) { return test_env.MemoryReadCode(vaddr); }, {}); - Dynarmic::Optimization::A32GetSetElimination(ir_block); - Dynarmic::Optimization::DeadCodeElimination(ir_block); - Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, &test_env); - Dynarmic::Optimization::ConstantPropagation(ir_block); - Dynarmic::Optimization::DeadCodeElimination(ir_block); - Dynarmic::Optimization::VerificationPass(ir_block); - printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str()); - printf("\n\nx86_64:\n%s", jit.Disassemble(descriptor).c_str()); - num_insts += ir_block.CycleCount(); - } - - fflush(stdout); - }; - - // Run interpreter - test_env.modified_memory.clear(); - test_env.ticks_left = instructions_to_execute_count; - uni.Run(); - interp_write_records = test_env.modified_memory; - { - const bool T = Dynarmic::Common::Bit<5>(uni.GetCpsr()); - const u32 mask = T ? 0xFFFFFFFE : 0xFFFFFFFC; - const u32 new_pc = uni.GetPC() & mask; - uni.SetPC(new_pc); - } - - // Run jit - test_env.modified_memory.clear(); - test_env.ticks_left = instructions_to_execute_count; - jit.Run(); - jit_write_records = test_env.modified_memory; - - REQUIRE(DoesBehaviorMatch(uni, jit, interp_write_records, jit_write_records)); + // Qemu doesn't do Thumb transitions?? + { + const u32 uni_pc = uni.GetPC(); + const bool is_thumb = (jit.Cpsr() & (1 << 5)) != 0; + const u32 new_uni_pc = uni_pc & (is_thumb ? 0xFFFFFFFE : 0xFFFFFFFC); + uni.SetPC(new_uni_pc); } + + REQUIRE(uni.GetRegisters() == jit.Regs()); + REQUIRE(uni.GetExtRegs() == jit.ExtRegs()); + REQUIRE((uni.GetCpsr() & ~(1 << 5)) == (jit.Cpsr() & ~(1 << 5))); + REQUIRE((uni.GetFpscr() & 0xF0000000) == (jit.Fpscr() & 0xF0000000)); + REQUIRE(uni_env.modified_memory == jit_env.modified_memory); + REQUIRE(uni_env.interrupts.empty()); } } // Anonymous namespace -TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[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. +TEST_CASE("A32: Single random instruction", "[arm]") { + ArmTestEnv jit_env{}; + ArmTestEnv uni_env{}; - ArmTestEnv test_env; - Dynarmic::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 - }; + Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; + A32Unicorn uni{uni_env}; - jit.Regs() = { - 0x6973b6bb, 0x267ea626, 0x69debf49, 0x8f976895, 0x4ecd2d0d, 0xcf89b8c7, 0xb6713f85, 0x15e2aa5, - 0xcd14336a, 0xafca0f3e, 0xace2efd9, 0x68fb82cd, 0x775447c0, 0xc9e1f8cd, 0xebe0e626, 0x0 - }; - jit.SetCpsr(0x000001d0); // User-mode + A32Unicorn::RegisterArray regs; + A32Unicorn::ExtRegArray ext_reg; + std::vector instructions(1); - test_env.ticks_left = 6; - jit.Run(); + for (size_t iteration = 0; iteration < 100000; ++iteration) { + std::generate(regs.begin(), regs.end(), [] { return RandInt(0, ~u32(0)); }); + std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt(0, ~u32(0)); }); - 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); -} + instructions[0] = GenRandomInst(0, true); -TEST_CASE( "arm: shsax r11, sp, r9 (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. + const u32 start_address = 100; + const u32 cpsr = (RandInt(0, 0xF) << 28) | 0x13; + const u32 fpcr = RandomFpcr(); - ArmTestEnv test_env; - Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; - test_env.code_mem = { - 0xe63dbf59, // shsax r11, sp, r9 - 0xeafffffe, // b +#0 - }; + INFO("Instruction: 0x" << std::hex << instructions[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; - Dynarmic::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); -} - -struct VfpTest { - u32 initial_fpscr; - u32 a; - u32 b; - u32 result; - u32 final_fpscr; -}; - -static void RunVfpTests(u32 instr, std::vector tests) { - ArmTestEnv test_env; - Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; - test_env.code_mem = { - instr, - 0xeafffffe, // b +#0 - }; - - printf("vfp test 0x%08x\r", instr); - - for (const auto& test : tests) { - jit.Regs()[15] = 0; - jit.SetCpsr(0x000001d0); - jit.ExtRegs()[4] = test.a; - jit.ExtRegs()[6] = test.b; - jit.SetFpscr(test.initial_fpscr); - - test_env.ticks_left = 2; - jit.Run(); - - const auto check = [&test, &jit](bool p) { - if (!p) { - printf("Failed test:\n"); - printf("initial_fpscr: 0x%08x\n", test.initial_fpscr); - printf("a: 0x%08x (jit: 0x%08x)\n", test.a, jit.ExtRegs()[4]); - printf("b: 0x%08x (jit: 0x%08x)\n", test.b, jit.ExtRegs()[6]); - printf("result: 0x%08x (jit: 0x%08x)\n", test.result, jit.ExtRegs()[2]); - printf("final_fpscr: 0x%08x (jit: 0x%08x)\n", test.final_fpscr, jit.Fpscr()); - FAIL(); - } - }; - - REQUIRE(jit.Regs()[15] == 4); - REQUIRE(jit.Cpsr() == 0x000001d0); - check(jit.ExtRegs()[2] == test.result); - check(jit.ExtRegs()[4] == test.a); - check(jit.ExtRegs()[6] == test.b); - //check(jit.Fpscr() == test.final_fpscr); + regs[15] = start_address; + RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr); } } -TEST_CASE("vfp: vadd", "[.vfp][A32]") { - // vadd.f32 s2, s4, s6 - RunVfpTests(0xEE321A03, { -#include "vfp_vadd_f32.inc" - }); -} +TEST_CASE("A32: Small random block", "[arm]") { + ArmTestEnv jit_env{}; + ArmTestEnv uni_env{}; -TEST_CASE("vfp: vsub", "[.vfp][A32]") { - // vsub.f32 s2, s4, s6 - RunVfpTests(0xEE321A43, { -#include "vfp_vsub_f32.inc" - }); -} + Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; + A32Unicorn uni{uni_env}; -TEST_CASE("VFP: VMOV", "[JitX64][.vfp][A32]") { - const auto is_valid = [](u32 instr) -> bool { - return Bits<0, 6>(instr) != 0b111111 - && Bits<12, 15>(instr) != 0b1111 - && Bits<16, 19>(instr) != 0b1111 - && Bits<12, 15>(instr) != Bits<16, 19>(instr); - }; + A32Unicorn::RegisterArray regs; + A32Unicorn::ExtRegArray ext_reg; + std::vector instructions(5); - const std::array instructions = { - InstructionGenerator("cccc11100000ddddtttt1011D0010000", is_valid), - InstructionGenerator("cccc11100001nnnntttt1011N0010000", is_valid), - InstructionGenerator("cccc11100000nnnntttt1010N0010000", is_valid), - InstructionGenerator("cccc11100001nnnntttt1010N0010000", is_valid), - InstructionGenerator("cccc11000100uuuutttt101000M1mmmm", is_valid), - InstructionGenerator("cccc11000101uuuutttt101000M1mmmm", is_valid), - InstructionGenerator("cccc11000100uuuutttt101100M1mmmm", is_valid), - InstructionGenerator("cccc11000101uuuutttt101100M1mmmm", is_valid), - }; + for (size_t iteration = 0; iteration < 100000; ++iteration) { + std::generate(regs.begin(), regs.end(), [] { return RandInt(0, ~u32(0)); }); + std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt(0, ~u32(0)); }); - FuzzJitArm(1, 1, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} + 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); -TEST_CASE("VFP: VMOV (reg), VLDR, VSTR", "[JitX64][.vfp][A32]") { - const std::array instructions = { - InstructionGenerator("1111000100000001000000e000000000"), // SETEND - InstructionGenerator("cccc11101D110000dddd101z01M0mmmm"), // VMOV (reg) - InstructionGenerator("cccc1101UD01nnnndddd101zvvvvvvvv"), // VLDR - InstructionGenerator("cccc1101UD00nnnndddd101zvvvvvvvv"), // VSTR - }; + const u32 start_address = 100; + const u32 cpsr = (RandInt(0, 0xF) << 28) | 0x13; + const u32 fpcr = RandomFpcr(); - FuzzJitArm(5, 6, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} + 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]); -TEST_CASE("VFP: VCMP", "[JitX64][.vfp][A32]") { - const std::array instructions = { - InstructionGenerator("cccc11101D110100dddd101zE1M0mmmm"), // VCMP - InstructionGenerator("cccc11101D110101dddd101zE1000000"), // VCMP (zero) - }; - - FuzzJitArm(5, 6, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} - -TEST_CASE("Fuzz ARM data processing instructions", "[JitX64][A32]") { - const std::array imm_instructions = { - InstructionGenerator("cccc0010101Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0010100Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0010000Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0011110Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc00110111nnnn0000rrrrvvvvvvvv"), - InstructionGenerator("cccc00110101nnnn0000rrrrvvvvvvvv"), - InstructionGenerator("cccc0010001Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0011101S0000ddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0011111S0000ddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0011100Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0010011Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0010111Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0010110Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc0010010Snnnnddddrrrrvvvvvvvv"), - InstructionGenerator("cccc00110011nnnn0000rrrrvvvvvvvv"), - InstructionGenerator("cccc00110001nnnn0000rrrrvvvvvvvv"), - }; - - const std::array reg_instructions = { - InstructionGenerator("cccc0000101Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0000100Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0000000Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0001110Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc00010111nnnn0000vvvvvrr0mmmm"), - InstructionGenerator("cccc00010101nnnn0000vvvvvrr0mmmm"), - InstructionGenerator("cccc0000001Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0001101S0000ddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0001111S0000ddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0001100Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0000011Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0000111Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0000110Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc0000010Snnnnddddvvvvvrr0mmmm"), - InstructionGenerator("cccc00010011nnnn0000vvvvvrr0mmmm"), - InstructionGenerator("cccc00010001nnnn0000vvvvvrr0mmmm"), - }; - - const std::array rsr_instructions = { - InstructionGenerator("cccc0000101Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0000100Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0000000Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0001110Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc00010111nnnn0000ssss0rr1mmmm"), - InstructionGenerator("cccc00010101nnnn0000ssss0rr1mmmm"), - InstructionGenerator("cccc0000001Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0001101S0000ddddssss0rr1mmmm"), - InstructionGenerator("cccc0001111S0000ddddssss0rr1mmmm"), - InstructionGenerator("cccc0001100Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0000011Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0000111Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0000110Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc0000010Snnnnddddssss0rr1mmmm"), - InstructionGenerator("cccc00010011nnnn0000ssss0rr1mmmm"), - InstructionGenerator("cccc00010001nnnn0000ssss0rr1mmmm"), - }; - - const auto instruction_select = [&](bool Rd_can_be_r15) -> auto { - return [&, Rd_can_be_r15]() -> u32 { - const size_t instruction_set = RandInt(0, 2); - - u32 cond = 0xE; - // Have a one-in-twenty-five chance of actually having a cond. - if (RandInt(1, 25) == 1) { - cond = RandInt(0x0, 0xD); - } - - u32 S = RandInt(0, 1); - - switch (instruction_set) { - case 0: { - const InstructionGenerator& instruction = imm_instructions[RandInt(0, imm_instructions.size() - 1)]; - const u32 Rd = RandInt(0, Rd_can_be_r15 ? 15 : 14); - if (Rd == 15) { - S = false; - } - const u32 Rn = RandInt(0, 15); - const u32 shifter_operand = RandInt(0, 0xFFF); - const u32 assemble_randoms = (shifter_operand << 0) | (Rd << 12) | (Rn << 16) | (S << 20) | (cond << 28); - return instruction.Bits() | (assemble_randoms & ~instruction.Mask()); - } - case 1: { - const InstructionGenerator& instruction = reg_instructions[RandInt(0, reg_instructions.size() - 1)]; - const u32 Rd = RandInt(0, Rd_can_be_r15 ? 15 : 14); - if (Rd == 15) { - S = false; - } - const u32 Rn = RandInt(0, 15); - const u32 shifter_operand = RandInt(0, 0xFFF); - const u32 assemble_randoms = - (shifter_operand << 0) | (Rd << 12) | (Rn << 16) | (S << 20) | (cond << 28); - return instruction.Bits() | (assemble_randoms & ~instruction.Mask()); - } - case 2: { - const InstructionGenerator& instruction = rsr_instructions[RandInt(0, rsr_instructions.size() - 1)]; - const u32 Rd = RandInt(0, 14); // Rd can never be 15. - const u32 Rn = RandInt(0, 14); - const u32 Rs = RandInt(0, 14); - const int rotate = RandInt(0, 3); - const u32 Rm = RandInt(0, 14); - const u32 assemble_randoms = - (Rm << 0) | (rotate << 5) | (Rs << 8) | (Rd << 12) | (Rn << 16) | (S << 20) | (cond << 28); - return instruction.Bits() | (assemble_randoms & ~instruction.Mask()); - } - } - return 0; - }; - }; - - SECTION("single instructions") { - FuzzJitArm(1, 2, 10000, instruction_select(/*Rd_can_be_r15=*/false)); - } - - SECTION("short blocks") { - FuzzJitArm(5, 6, 10000, instruction_select(/*Rd_can_be_r15=*/false)); - } - - SECTION("long blocks") { - FuzzJitArm(1024, 1025, 200, instruction_select(/*Rd_can_be_r15=*/false)); - } - - SECTION("R15") { - FuzzJitArm(1, 1, 10000, instruction_select(/*Rd_can_be_r15=*/true)); + regs[15] = start_address; + RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr); } } - -TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64][A32]") { - const auto EXD_valid = [](u32 inst) -> bool { - return Bits<0, 3>(inst) % 2 == 0 && Bits<0, 3>(inst) != 14 && Bits<12, 15>(inst) != (Bits<0, 3>(inst) + 1); - }; - - const auto STREX_valid = [](u32 inst) -> bool { - return Bits<12, 15>(inst) != Bits<16, 19>(inst) && Bits<12, 15>(inst) != Bits<0, 3>(inst); - }; - - const auto SWP_valid = [](u32 inst) -> bool { - return Bits<12, 15>(inst) != Bits<16, 19>(inst) && Bits<16, 19>(inst) != Bits<0, 3>(inst); - }; - - const auto LDREXD_valid = [](u32 inst) -> bool { - return Bits<12, 15>(inst) != 14; - }; - - const auto D_valid = [](u32 inst) -> bool { - const u32 Rn = Bits<16, 19>(inst); - const u32 Rd = Bits<12, 15>(inst); - const u32 Rm = Bits<0, 3>(inst); - return Rn % 2 == 0 && Rd % 2 == 0 && Rm != Rd && Rm != Rd + 1 && Rd != 14; - }; - - const std::array instructions = { - InstructionGenerator("cccc010pu0w1nnnnddddvvvvvvvvvvvv"), // LDR_imm - InstructionGenerator("cccc011pu0w1nnnnddddvvvvvrr0mmmm"), // LDR_reg - InstructionGenerator("cccc010pu1w1nnnnddddvvvvvvvvvvvv"), // LDRB_imm - InstructionGenerator("cccc011pu1w1nnnnddddvvvvvrr0mmmm"), // LDRB_reg - InstructionGenerator("cccc000pu1w0nnnnddddvvvv1101vvvv", D_valid), // LDRD_imm - InstructionGenerator("cccc000pu0w0nnnndddd00001101mmmm", D_valid), // LDRD_reg - InstructionGenerator("cccc010pu0w0nnnnddddvvvvvvvvvvvv"), // STR_imm - InstructionGenerator("cccc011pu0w0nnnnddddvvvvvrr0mmmm"), // STR_reg - InstructionGenerator("cccc010pu1w0nnnnddddvvvvvvvvvvvv"), // STRB_imm - InstructionGenerator("cccc011pu1w0nnnnddddvvvvvrr0mmmm"), // STRB_reg - InstructionGenerator("cccc000pu1w0nnnnddddvvvv1111vvvv", D_valid), // STRD_imm - InstructionGenerator("cccc000pu0w0nnnndddd00001111mmmm", D_valid), // STRD_reg - InstructionGenerator("cccc000pu1w1nnnnddddvvvv1011vvvv"), // LDRH_imm - InstructionGenerator("cccc000pu0w1nnnndddd00001011mmmm"), // LDRH_reg - InstructionGenerator("cccc000pu1w1nnnnddddvvvv1101vvvv"), // LDRSB_imm - InstructionGenerator("cccc000pu0w1nnnndddd00001101mmmm"), // LDRSB_reg - InstructionGenerator("cccc000pu1w1nnnnddddvvvv1111vvvv"), // LDRSH_imm - InstructionGenerator("cccc000pu0w1nnnndddd00001111mmmm"), // LDRSH_reg - InstructionGenerator("cccc000pu1w0nnnnddddvvvv1011vvvv"), // STRH_imm - InstructionGenerator("cccc000pu0w0nnnndddd00001011mmmm"), // STRH_reg - InstructionGenerator("1111000100000001000000e000000000"), // SETEND - InstructionGenerator("11110101011111111111000000011111"), // CLREX - InstructionGenerator("cccc00011001nnnndddd111110011111"), // LDREX - InstructionGenerator("cccc00011101nnnndddd111110011111"), // LDREXB - InstructionGenerator("cccc00011011nnnndddd111110011111", LDREXD_valid), // LDREXD - InstructionGenerator("cccc00011111nnnndddd111110011111"), // LDREXH - InstructionGenerator("cccc00011000nnnndddd11111001mmmm", STREX_valid), // STREX - InstructionGenerator("cccc00011100nnnndddd11111001mmmm", STREX_valid), // STREXB - InstructionGenerator("cccc00011010nnnndddd11111001mmmm", - [=](u32 inst) { return EXD_valid(inst) && STREX_valid(inst); }), // STREXD - InstructionGenerator("cccc00011110nnnndddd11111001mmmm", STREX_valid), // STREXH - InstructionGenerator("cccc00010000nnnntttt00001001uuuu", SWP_valid), // SWP - InstructionGenerator("cccc00010100nnnntttt00001001uuuu", SWP_valid), // SWPB - }; - - const auto instruction_select = [&]() -> u32 { - const size_t inst_index = RandInt(0, instructions.size() - 1); - - while (true) { - u32 cond = 0xE; - // Have a one-in-twenty-five chance of actually having a cond. - if (RandInt(1, 25) == 1) { - cond = RandInt(0x0, 0xD); - } - - u32 Rn = RandInt(0, 14); - u32 Rd = RandInt(0, 14); - u32 W = 0; - const u32 P = RandInt(0, 1); - if (P) { - W = RandInt(0, 1); - } - const u32 U = RandInt(0, 1); - const u32 rand = RandInt(0, 0xFF); - const u32 Rm = RandInt(0, 14); - - if (!P || W) { - while (Rn == Rd) { - Rn = RandInt(0, 14); - Rd = RandInt(0, 14); - } - } - - const u32 assemble_randoms = (Rm << 0) | (rand << 4) | (Rd << 12) | (Rn << 16) | (W << 21) | (U << 23) | (P << 24) | (cond << 28); - const u32 inst = instructions[inst_index].Bits() | (assemble_randoms & (~instructions[inst_index].Mask())); - if (instructions[inst_index].IsValid(inst)) { - return inst; - } - } - }; - - SECTION("short blocks") { - FuzzJitArm(5, 6, 30000, instruction_select); - } -} - -TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64][A32]") { - const std::array instructions = { - InstructionGenerator("cccc100pu0w1nnnnxxxxxxxxxxxxxxxx"), // LDM - InstructionGenerator("cccc100pu0w0nnnnxxxxxxxxxxxxxxxx"), // STM - }; - - const auto instruction_select = [&]() -> u32 { - const size_t inst_index = RandInt(0, instructions.size() - 1); - - u32 cond = 0xE; - // Have a one-in-twenty-five chance of actually having a cond. - if (RandInt(1, 25) == 1) { - cond = RandInt(0x0, 0xD); - } - - u32 reg_list = RandInt(1, 0xFFFF); - u32 Rn = RandInt(0, 14); - u32 flags = RandInt(0, 0xF); - - while (true) { - if (inst_index == 1 && (flags & 2)) { - if (reg_list & (1 << Rn)) { - reg_list &= ~((1 << Rn) - 1); - } - } else if (inst_index == 0 && (flags & 2)) { - reg_list &= ~(1 << Rn); - } - - if (reg_list) { - break; - } - - reg_list = RandInt(1, 0xFFFF); - } - - const u32 assemble_randoms = (reg_list << 0) | (Rn << 16) | (flags << 24) | (cond << 28); - - return instructions[inst_index].Bits() | (assemble_randoms & (~instructions[inst_index].Mask())); - }; - - FuzzJitArm(1, 1, 10000, instruction_select); -} - -TEST_CASE("Fuzz ARM branch instructions", "[JitX64][A32]") { - const std::array instructions = { - InstructionGenerator("1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), - InstructionGenerator("cccc000100101111111111110011mmmm", - [](u32 instr) { return Bits<0, 3>(instr) != 0b1111; }), // R15 is UNPREDICTABLE - InstructionGenerator("cccc1010vvvvvvvvvvvvvvvvvvvvvvvv"), - InstructionGenerator("cccc1011vvvvvvvvvvvvvvvvvvvvvvvv"), - InstructionGenerator("cccc000100101111111111110001mmmm"), - InstructionGenerator("cccc000100101111111111110010mmmm"), - }; - FuzzJitArm(1, 1, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} - -TEST_CASE("Fuzz ARM reversal instructions", "[JitX64][A32]") { - const auto is_valid = [](u32 instr) -> bool { - // R15 is UNPREDICTABLE - return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111; - }; - - const std::array rev_instructions = { - InstructionGenerator("cccc011011111111dddd11110011mmmm", is_valid), // RBIT - InstructionGenerator("cccc011010111111dddd11110011mmmm", is_valid), // REV - InstructionGenerator("cccc011010111111dddd11111011mmmm", is_valid), // REV16 - InstructionGenerator("cccc011011111111dddd11111011mmmm", is_valid), // REVSH - }; - - SECTION("Reverse tests") { - FuzzJitArm(1, 1, 10000, [&rev_instructions]() -> u32 { - return rev_instructions[RandInt(0, rev_instructions.size() - 1)].Generate(); - }); - } -} - -TEST_CASE("Fuzz ARM extension instructions", "[JitX64][A32]") { - const auto is_valid = [](u32 instr) -> bool { - // R15 as Rd or Rm is UNPREDICTABLE - return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111; - }; - - const std::array signed_instructions = { - InstructionGenerator("cccc011010101111ddddrr000111mmmm", is_valid), - InstructionGenerator("cccc011010001111ddddrr000111mmmm", is_valid), - InstructionGenerator("cccc011010111111ddddrr000111mmmm", is_valid), - InstructionGenerator("cccc01101010nnnnddddrr000111mmmm", is_valid), - InstructionGenerator("cccc01101000nnnnddddrr000111mmmm", is_valid), - InstructionGenerator("cccc01101011nnnnddddrr000111mmmm", is_valid), - }; - - const std::array unsigned_instructions = { - InstructionGenerator("cccc011011101111ddddrr000111mmmm", is_valid), - InstructionGenerator("cccc011011001111ddddrr000111mmmm", is_valid), - InstructionGenerator("cccc011011111111ddddrr000111mmmm", is_valid), - InstructionGenerator("cccc01101110nnnnddddrr000111mmmm", is_valid), - InstructionGenerator("cccc01101100nnnnddddrr000111mmmm", is_valid), //UXTAB16 - InstructionGenerator("cccc01101111nnnnddddrr000111mmmm", is_valid), - }; - - SECTION("Signed extension") { - FuzzJitArm(1, 1, 10000, [&signed_instructions]() -> u32 { - return signed_instructions[RandInt(0, signed_instructions.size() - 1)].Generate(); - }); - } - - SECTION("Unsigned extension") { - FuzzJitArm(1, 1, 10000, [&unsigned_instructions]() -> u32 { - return unsigned_instructions[RandInt(0, unsigned_instructions.size() - 1)].Generate(); - }); - } -} - -TEST_CASE("Fuzz ARM divide instructions", "[JitX64][A32]") { - const auto is_valid = [](u32 instr) { - return Bits<0, 3>(instr) != 0b1111 && - Bits<8, 11>(instr) != 0b1111 && - Bits<16, 19>(instr) != 0b1111; - }; - - const std::array instructions = { - InstructionGenerator("cccc01110001dddd1111mmmm0001nnnn", is_valid), // SDIV - InstructionGenerator("cccc01110011dddd1111mmmm0001nnnn", is_valid), // UDIV - }; - - FuzzJitArm(1, 1, 5000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} - - -TEST_CASE("Fuzz ARM multiply instructions", "[JitX64][A32]") { - const auto validate_d_m_n = [](u32 inst) -> bool { - return Bits<16, 19>(inst) != 15 && - Bits<8, 11>(inst) != 15 && - Bits<0, 3>(inst) != 15; - }; - const auto validate_d_a_m_n = [&](u32 inst) -> bool { - return validate_d_m_n(inst) && - Bits<12, 15>(inst) != 15; - }; - const auto validate_h_l_m_n = [&](u32 inst) -> bool { - return validate_d_a_m_n(inst) && - Bits<12, 15>(inst) != Bits<16, 19>(inst); - }; - - const std::array instructions = { - InstructionGenerator("cccc0000001Sddddaaaammmm1001nnnn", validate_d_a_m_n), // MLA - InstructionGenerator("cccc00000110ddddaaaammmm1001nnnn", validate_d_a_m_n), // MLS - InstructionGenerator("cccc0000000Sdddd0000mmmm1001nnnn", validate_d_m_n), // MUL - - InstructionGenerator("cccc0000111Sddddaaaammmm1001nnnn", validate_h_l_m_n), // SMLAL - InstructionGenerator("cccc0000110Sddddaaaammmm1001nnnn", validate_h_l_m_n), // SMULL - InstructionGenerator("cccc00000100ddddaaaammmm1001nnnn", validate_h_l_m_n), // UMAAL - InstructionGenerator("cccc0000101Sddddaaaammmm1001nnnn", validate_h_l_m_n), // UMLAL - InstructionGenerator("cccc0000100Sddddaaaammmm1001nnnn", validate_h_l_m_n), // UMULL - - InstructionGenerator("cccc00010100ddddaaaammmm1xy0nnnn", validate_h_l_m_n), // SMLALxy - InstructionGenerator("cccc00010000ddddaaaammmm1xy0nnnn", validate_d_a_m_n), // SMLAxy - InstructionGenerator("cccc00010110dddd0000mmmm1xy0nnnn", validate_d_m_n), // SMULxy - - InstructionGenerator("cccc00010010ddddaaaammmm1y00nnnn", validate_d_a_m_n), // SMLAWy - InstructionGenerator("cccc00010010dddd0000mmmm1y10nnnn", validate_d_m_n), // SMULWy - - InstructionGenerator("cccc01110101dddd1111mmmm00R1nnnn", validate_d_m_n), // SMMUL - InstructionGenerator("cccc01110101ddddaaaammmm00R1nnnn", validate_d_a_m_n), // SMMLA - InstructionGenerator("cccc01110101ddddaaaammmm11R1nnnn", validate_d_a_m_n), // SMMLS - InstructionGenerator("cccc01110000ddddaaaammmm00M1nnnn", validate_d_a_m_n), // SMLAD - InstructionGenerator("cccc01110100ddddaaaammmm00M1nnnn", validate_h_l_m_n), // SMLALD - InstructionGenerator("cccc01110000ddddaaaammmm01M1nnnn", validate_d_a_m_n), // SMLSD - InstructionGenerator("cccc01110100ddddaaaammmm01M1nnnn", validate_h_l_m_n), // SMLSLD - InstructionGenerator("cccc01110000dddd1111mmmm00M1nnnn", validate_d_m_n), // SMUAD - InstructionGenerator("cccc01110000dddd1111mmmm01M1nnnn", validate_d_m_n), // SMUSD - }; - - SECTION("Multiply") { - FuzzJitArm(1, 1, 10000, [&]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); - } -} - -TEST_CASE("Fuzz ARM parallel instructions", "[JitX64][parallel][A32]") { - const auto is_valid = [](u32 instr) -> bool { - // R15 as Rd, Rn, or Rm is UNPREDICTABLE - return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111 && Bits<16, 19>(instr) != 0b1111; - }; - - const auto is_sel_valid = [](u32 instr) -> bool { - // R15 as Rd, Rn, or Rm is UNPREDICTABLE - return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111 && Bits<16, 19>(instr) != 0b1111; - }; - - const auto is_msr_valid = [](u32 instr) -> bool { - // Mask can not be 0 - return Bits<18, 19>(instr) != 0b00; - }; - - const InstructionGenerator cpsr_setter = InstructionGenerator("11100011001001001111rrrrvvvvvvvv", is_msr_valid); // MSR_Imm write GE - const InstructionGenerator sel_instr = InstructionGenerator("111001101000nnnndddd11111011mmmm", is_sel_valid); // SEL - - const std::array modulo_add_instructions = { - InstructionGenerator("cccc01100001nnnndddd11111001mmmm", is_valid), // SADD8 - InstructionGenerator("cccc01100001nnnndddd11110001mmmm", is_valid), // SADD16 - InstructionGenerator("cccc01100101nnnndddd11111001mmmm", is_valid), // UADD8 - InstructionGenerator("cccc01100101nnnndddd11110001mmmm", is_valid), // UADD16 - }; - - const std::array modulo_sub_instructions = { - InstructionGenerator("cccc01100001nnnndddd11111111mmmm", is_valid), // SSUB8 - InstructionGenerator("cccc01100001nnnndddd11110111mmmm", is_valid), // SSUB16 - InstructionGenerator("cccc01100101nnnndddd11111111mmmm", is_valid), // USUB8 - InstructionGenerator("cccc01100101nnnndddd11110111mmmm", is_valid), // USUB16 - }; - - const std::array modulo_exchange_instructions = { - InstructionGenerator("cccc01100001nnnndddd11110011mmmm", is_valid), // SASX - InstructionGenerator("cccc01100001nnnndddd11110101mmmm", is_valid), // SSAX - InstructionGenerator("cccc01100101nnnndddd11110011mmmm", is_valid), // UASX - InstructionGenerator("cccc01100101nnnndddd11110101mmmm", is_valid), // USAX - }; - - const std::array saturating_instructions = { - InstructionGenerator("cccc01100010nnnndddd11111001mmmm", is_valid), // QADD8 - InstructionGenerator("cccc01100010nnnndddd11111111mmmm", is_valid), // QSUB8 - InstructionGenerator("cccc01100110nnnndddd11111001mmmm", is_valid), // UQADD8 - InstructionGenerator("cccc01100110nnnndddd11111111mmmm", is_valid), // UQSUB8 - InstructionGenerator("cccc01100010nnnndddd11110001mmmm", is_valid), // QADD16 - InstructionGenerator("cccc01100010nnnndddd11110111mmmm", is_valid), // QSUB16 - InstructionGenerator("cccc01100110nnnndddd11110001mmmm", is_valid), // UQADD16 - InstructionGenerator("cccc01100110nnnndddd11110111mmmm", is_valid), // UQSUB16 - InstructionGenerator("cccc01100010nnnndddd11110011mmmm", is_valid), // QASX - InstructionGenerator("cccc01100010nnnndddd11110101mmmm", is_valid), // QSAX - InstructionGenerator("cccc01100110nnnndddd11110011mmmm", is_valid), // UQASX - InstructionGenerator("cccc01100110nnnndddd11110101mmmm", is_valid), // UQSAX - }; - - const std::array halving_instructions = { - InstructionGenerator("cccc01100011nnnndddd11111001mmmm", is_valid), // SHADD8 - InstructionGenerator("cccc01100011nnnndddd11110001mmmm", is_valid), // SHADD16 - InstructionGenerator("cccc01100011nnnndddd11110011mmmm", is_valid), // SHASX - InstructionGenerator("cccc01100011nnnndddd11110101mmmm", is_valid), // SHSAX - InstructionGenerator("cccc01100011nnnndddd11111111mmmm", is_valid), // SHSUB8 - InstructionGenerator("cccc01100011nnnndddd11110111mmmm", is_valid), // SHSUB16 - InstructionGenerator("cccc01100111nnnndddd11111001mmmm", is_valid), // UHADD8 - InstructionGenerator("cccc01100111nnnndddd11110001mmmm", is_valid), // UHADD16 - InstructionGenerator("cccc01100111nnnndddd11110011mmmm", is_valid), // UHASX - InstructionGenerator("cccc01100111nnnndddd11110101mmmm", is_valid), // UHSAX - InstructionGenerator("cccc01100111nnnndddd11111111mmmm", is_valid), // UHSUB8 - InstructionGenerator("cccc01100111nnnndddd11110111mmmm", is_valid), // UHSUB16 - }; - - size_t index = 0; - const auto also_test_sel = [&](u32 inst) -> u32 { - switch (index++ % 3) { - case 1: - return cpsr_setter.Generate(false); - case 2: - return sel_instr.Generate(false); - } - return inst; - }; - - SECTION("Parallel Add (Modulo)") { - FuzzJitArm(4, 5, 10000, [&]() -> u32 { - return also_test_sel(modulo_add_instructions[RandInt(0, modulo_add_instructions.size() - 1)].Generate()); - }); - } - - SECTION("Parallel Subtract (Modulo)") { - FuzzJitArm(4, 5, 10000, [&]() -> u32 { - return also_test_sel(modulo_sub_instructions[RandInt(0, modulo_sub_instructions.size() - 1)].Generate()); - }); - } - - SECTION("Parallel Exchange (Modulo)") { - FuzzJitArm(4, 5, 10000, [&]() -> u32 { - return also_test_sel(modulo_exchange_instructions[RandInt(0, modulo_exchange_instructions.size() - 1)].Generate()); - }); - } - - SECTION("Parallel Add/Subtract (Saturating)") { - FuzzJitArm(4, 5, 10000, [&]() -> u32 { - return also_test_sel(saturating_instructions[RandInt(0, saturating_instructions.size() - 1)].Generate()); - }); - } - - SECTION("Parallel Add/Subtract (Halving)") { - FuzzJitArm(4, 5, 10000, [&]() -> u32 { - return also_test_sel(halving_instructions[RandInt(0, halving_instructions.size() - 1)].Generate()); - }); - } - - SECTION("Fuzz SEL") { - // Alternate between a SEL and a MSR to change the CPSR, thus changing the expected result of the next SEL - bool set_cpsr = true; - FuzzJitArm(5, 6, 10000, [&sel_instr, &cpsr_setter, &set_cpsr]() -> u32 { - set_cpsr ^= true; - if (set_cpsr) { - return cpsr_setter.Generate(false); - } - return sel_instr.Generate(false); - }); - } -} - -TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64][A32]") { - const auto validate_d_m_n = [](u32 inst) -> bool { - return Bits<16, 19>(inst) != 15 && - Bits<8, 11>(inst) != 15 && - Bits<0, 3>(inst) != 15; - }; - const auto validate_d_a_m_n = [&](u32 inst) -> bool { - return validate_d_m_n(inst) && - Bits<12, 15>(inst) != 15; - }; - - const std::array differences_instructions = { - InstructionGenerator("cccc01111000dddd1111mmmm0001nnnn", validate_d_m_n), // USAD8 - InstructionGenerator("cccc01111000ddddaaaammmm0001nnnn", validate_d_a_m_n), // USADA8 - }; - - SECTION("Sum of Absolute Differences (Differences)") { - FuzzJitArm(1, 1, 10000, [&differences_instructions]() -> u32 { - return differences_instructions[RandInt(0, differences_instructions.size() - 1)].Generate(); - }); - } -} - -TEST_CASE("SMUAD", "[JitX64][A32]") { - ArmTestEnv test_env; - Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; - test_env.code_mem = { - 0xE700F211 // smuad r0, r1, r2 - }; - - 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 = 6; - jit.Run(); - - REQUIRE(jit.Regs()[0] == 0x80000000); - REQUIRE(jit.Regs()[1] == 0x80008000); - REQUIRE(jit.Regs()[2] == 0x80008000); - REQUIRE(jit.Cpsr() == 0x080001d0); -} - -TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][.vfp][A32]") { - const auto is_valid = [](u32 instr) -> bool { - const auto regs = (instr & 0x100) ? (Bits<0, 7>(instr) >> 1) : Bits<0, 7>(instr); - const auto base = Bits<12, 15>(instr); - unsigned d; - if (instr & 0x100) { - d = (base + ((instr & 0x400000) ? 16 : 0)); - } else { - d = ((base << 1) + ((instr & 0x400000) ? 1 : 0)); - } - // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE - return regs != 0 && regs <= 16 && (d + regs) <= 32; - }; - - const std::array instructions = { - InstructionGenerator("cccc11010D101101dddd101zvvvvvvvv", is_valid), // VPUSH - InstructionGenerator("cccc11001D111101dddd1010vvvvvvvv", is_valid), // VPOP - }; - - FuzzJitArm(5, 6, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} - -TEST_CASE("Test ARM misc instructions", "[JitX64][A32]") { - const auto is_bfc_bfi_valid = [](u32 instr) { - if (Bits<12, 15>(instr) == 0b1111) { - // Destination register may not be the PC. - return false; - } - // msb must be greater than or equal to the lsb, - // otherwise the instruction is UNPREDICTABLE. - return Bits<16, 20>(instr) >= Bits<7, 11>(instr); - }; - const auto is_clz_valid = [](u32 instr) { - // R15 as Rd, or Rm is UNPREDICTABLE - return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111; - }; - const auto is_extract_valid = [](u32 instr) { - const u32 lsb = Bits<7, 11>(instr); - const u32 widthm1 = Bits<16, 20>(instr); - const u32 msb = lsb + widthm1; - - // Rd or Rn as R15 or the case where msb > 32 is UNPREDICTABLE. - return Bits<0, 3>(instr) != 0b1111 && - Bits<12, 15>(instr) != 0b1111 && - msb < Dynarmic::Common::BitSize(); - }; - const auto is_movt_valid = [](u32 instr) { - return Bits<12, 15>(instr) != 0b1111; - }; - - const std::array instructions = { - InstructionGenerator("cccc0111110vvvvvddddvvvvv0011111", is_bfc_bfi_valid), // BFC - InstructionGenerator("cccc0111110vvvvvddddvvvvv001nnnn", is_bfc_bfi_valid), // BFI - InstructionGenerator("cccc000101101111dddd11110001mmmm", is_clz_valid), // CLZ - InstructionGenerator("cccc00110100vvvvddddvvvvvvvvvvvv", is_movt_valid), // MOVT - InstructionGenerator("cccc0111101wwwwwddddvvvvv101nnnn", is_extract_valid), // SBFX - InstructionGenerator("cccc0111111wwwwwddddvvvvv101nnnn", is_extract_valid), // UBFX - }; - - FuzzJitArm(1, 1, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} - -TEST_CASE("Test ARM MSR instructions", "[JitX64][A32]") { - const auto is_msr_valid = [](u32 instr) -> bool { - return Bits<16, 19>(instr) != 0; - }; - - const auto is_msr_reg_valid = [&is_msr_valid](u32 instr) -> bool { - return is_msr_valid(instr) && Bits<0, 3>(instr) != 15; - }; - - const auto is_mrs_valid = [&](u32 inst) -> bool { - return Bits<12, 15>(inst) != 15; - }; - - const std::array instructions = { - InstructionGenerator("cccc00110010mmmm1111rrrrvvvvvvvv", is_msr_valid), // MSR (imm) - InstructionGenerator("cccc00010010mmmm111100000000nnnn", is_msr_reg_valid), // MSR (reg) - InstructionGenerator("cccc000100001111dddd000000000000", is_mrs_valid), // MRS - }; - - SECTION("Ones") { - FuzzJitArm(1, 2, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); - } - - SECTION("Fives") { - FuzzJitArm(5, 6, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); - } -} - -TEST_CASE("Fuzz ARM saturated add/sub instructions", "[JitX64][A32]") { - const auto is_valid = [](u32 inst) -> bool { - // R15 as Rd, Rn, or Rm is UNPREDICTABLE - return Bits<16, 19>(inst) != 0b1111 && - Bits<12, 15>(inst) != 0b1111 && - Bits<0, 3>(inst) != 0b1111; - }; - - const std::array instructions = { - InstructionGenerator("cccc00010000nnnndddd00000101mmmm", is_valid), // QADD - InstructionGenerator("cccc00010010nnnndddd00000101mmmm", is_valid), // QSUB - InstructionGenerator("cccc00010100nnnndddd00000101mmmm", is_valid), // QDADD - InstructionGenerator("cccc00010110nnnndddd00000101mmmm", is_valid), // QDSUB - }; - - SECTION("Saturated") { - FuzzJitArm(4, 5, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); - } -} - -TEST_CASE("Fuzz ARM saturation instructions", "[JitX64][A32]") { - const auto is_valid = [](u32 inst) -> bool { - // R15 as Rd or Rn is UNPREDICTABLE - return Bits<12, 15>(inst) != 0b1111 && - Bits<0, 3>(inst) != 0b1111; - }; - - const std::array instructions = { - InstructionGenerator("cccc0110101vvvvvddddvvvvvr01nnnn", is_valid), // SSAT - InstructionGenerator("cccc01101010vvvvdddd11110011nnnn", is_valid), // SSAT16 - InstructionGenerator("cccc0110111vvvvvddddvvvvvr01nnnn", is_valid), // USAT - InstructionGenerator("cccc01101110vvvvdddd11110011nnnn", is_valid), // USAT16 - }; - - FuzzJitArm(4, 5, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); -} - -TEST_CASE("Fuzz ARM packing instructions", "[JitX64][A32]") { - const auto is_pkh_valid = [](u32 inst) -> bool { - // R15 as Rd, Rn, or Rm is UNPREDICTABLE - return Bits<16, 19>(inst) != 0b1111 && - Bits<12, 15>(inst) != 0b1111 && - Bits<0, 3>(inst) != 0b1111; - }; - - const std::array instructions = { - InstructionGenerator("cccc01101000nnnnddddvvvvv001mmmm", is_pkh_valid), // PKHBT - InstructionGenerator("cccc01101000nnnnddddvvvvv101mmmm", is_pkh_valid), // PKHTB - }; - - SECTION("Packing") { - FuzzJitArm(1, 1, 10000, [&instructions]() -> u32 { - return instructions[RandInt(0, instructions.size() - 1)].Generate(); - }); - } -} - -TEST_CASE("arm: Test InvalidateCacheRange", "[arm][A32]") { - ArmTestEnv test_env; - Dynarmic::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); -}