fuzz_arm.cpp
1 /* This file is part of the dynarmic project. 2 * Copyright (c) 2016 MerryMage 3 * SPDX-License-Identifier: 0BSD 4 */ 5 6 #include <algorithm> 7 #include <array> 8 #include <cstdio> 9 #include <functional> 10 #include <tuple> 11 #include <type_traits> 12 #include <vector> 13 14 #include <catch2/catch_test_macros.hpp> 15 #include <mcl/bit/bit_count.hpp> 16 #include <mcl/bit/swap.hpp> 17 #include <mcl/scope_exit.hpp> 18 #include <mcl/stdint.hpp> 19 20 #include "../fuzz_util.h" 21 #include "../rand_int.h" 22 #include "../unicorn_emu/a32_unicorn.h" 23 #include "./testenv.h" 24 #include "dynarmic/common/fp/fpcr.h" 25 #include "dynarmic/common/fp/fpsr.h" 26 #include "dynarmic/common/llvm_disassemble.h" 27 #include "dynarmic/common/variant_util.h" 28 #include "dynarmic/frontend/A32/ITState.h" 29 #include "dynarmic/frontend/A32/a32_location_descriptor.h" 30 #include "dynarmic/frontend/A32/a32_types.h" 31 #include "dynarmic/frontend/A32/translate/a32_translate.h" 32 #include "dynarmic/interface/A32/a32.h" 33 #include "dynarmic/ir/basic_block.h" 34 #include "dynarmic/ir/location_descriptor.h" 35 #include "dynarmic/ir/opcodes.h" 36 37 // Must be declared last for all necessary operator<< to be declared prior to this. 38 #include <fmt/format.h> 39 #include <fmt/ostream.h> 40 41 namespace { 42 using namespace Dynarmic; 43 44 template<typename Fn> 45 bool AnyLocationDescriptorForTerminalHas(IR::Terminal terminal, Fn fn) { 46 return Common::VisitVariant<bool>(terminal, [&](auto t) -> bool { 47 using T = std::decay_t<decltype(t)>; 48 if constexpr (std::is_same_v<T, IR::Term::Invalid>) { 49 return false; 50 } else if constexpr (std::is_same_v<T, IR::Term::ReturnToDispatch>) { 51 return false; 52 } else if constexpr (std::is_same_v<T, IR::Term::LinkBlock>) { 53 return fn(t.next); 54 } else if constexpr (std::is_same_v<T, IR::Term::LinkBlockFast>) { 55 return fn(t.next); 56 } else if constexpr (std::is_same_v<T, IR::Term::PopRSBHint>) { 57 return false; 58 } else if constexpr (std::is_same_v<T, IR::Term::Interpret>) { 59 return fn(t.next); 60 } else if constexpr (std::is_same_v<T, IR::Term::FastDispatchHint>) { 61 return false; 62 } else if constexpr (std::is_same_v<T, IR::Term::If>) { 63 return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn); 64 } else if constexpr (std::is_same_v<T, IR::Term::CheckBit>) { 65 return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn); 66 } else if constexpr (std::is_same_v<T, IR::Term::CheckHalt>) { 67 return AnyLocationDescriptorForTerminalHas(t.else_, fn); 68 } else { 69 ASSERT_MSG(false, "Invalid terminal type"); 70 return false; 71 } 72 }); 73 } 74 75 bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) { 76 const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb).SetIT(it_state); 77 IR::Block block{location}; 78 const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction); 79 80 if (!should_continue && !is_last_inst) { 81 return false; 82 } 83 84 if (auto terminal = block.GetTerminal(); boost::get<IR::Term::Interpret>(&terminal)) { 85 return false; 86 } 87 88 if (AnyLocationDescriptorForTerminalHas(block.GetTerminal(), [&](IR::LocationDescriptor ld) { return A32::LocationDescriptor{ld}.PC() <= pc; })) { 89 return false; 90 } 91 92 for (const auto& ir_inst : block) { 93 switch (ir_inst.GetOpcode()) { 94 case IR::Opcode::A32ExceptionRaised: 95 case IR::Opcode::A32CallSupervisor: 96 case IR::Opcode::A32CoprocInternalOperation: 97 case IR::Opcode::A32CoprocSendOneWord: 98 case IR::Opcode::A32CoprocSendTwoWords: 99 case IR::Opcode::A32CoprocGetOneWord: 100 case IR::Opcode::A32CoprocGetTwoWords: 101 case IR::Opcode::A32CoprocLoadWords: 102 case IR::Opcode::A32CoprocStoreWords: 103 return false; 104 // Currently unimplemented in Unicorn 105 case IR::Opcode::FPVectorRecipEstimate16: 106 case IR::Opcode::FPVectorRSqrtEstimate16: 107 case IR::Opcode::VectorPolynomialMultiplyLong64: 108 return false; 109 default: 110 continue; 111 } 112 } 113 114 return true; 115 } 116 117 u32 GenRandomArmInst(u32 pc, bool is_last_inst) { 118 static const struct InstructionGeneratorInfo { 119 std::vector<InstructionGenerator> generators; 120 std::vector<InstructionGenerator> invalid; 121 } instructions = [] { 122 const std::vector<std::tuple<std::string, const char*>> list{ 123 #define INST(fn, name, bitstring) {#fn, bitstring}, 124 #include "dynarmic/frontend/A32/decoder/arm.inc" 125 #include "dynarmic/frontend/A32/decoder/asimd.inc" 126 #include "dynarmic/frontend/A32/decoder/vfp.inc" 127 #undef INST 128 }; 129 130 std::vector<InstructionGenerator> generators; 131 std::vector<InstructionGenerator> invalid; 132 133 // List of instructions not to test 134 static constexpr std::array do_not_test{ 135 // Translating load/stores 136 "arm_LDRBT", "arm_LDRBT", "arm_LDRHT", "arm_LDRHT", "arm_LDRSBT", "arm_LDRSBT", "arm_LDRSHT", "arm_LDRSHT", "arm_LDRT", "arm_LDRT", 137 "arm_STRBT", "arm_STRBT", "arm_STRHT", "arm_STRHT", "arm_STRT", "arm_STRT", 138 // Exclusive load/stores 139 "arm_LDREXB", "arm_LDREXD", "arm_LDREXH", "arm_LDREX", "arm_LDAEXB", "arm_LDAEXD", "arm_LDAEXH", "arm_LDAEX", 140 "arm_STREXB", "arm_STREXD", "arm_STREXH", "arm_STREX", "arm_STLEXB", "arm_STLEXD", "arm_STLEXH", "arm_STLEX", 141 "arm_SWP", "arm_SWPB", 142 // Elevated load/store multiple instructions. 143 "arm_LDM_eret", "arm_LDM_usr", 144 "arm_STM_usr", 145 // Hint instructions 146 "arm_NOP", "arm_PLD_imm", "arm_PLD_reg", "arm_SEV", 147 "arm_WFE", "arm_WFI", "arm_YIELD", 148 // E, T, J 149 "arm_BLX_reg", "arm_BLX_imm", "arm_BXJ", "arm_SETEND", 150 // Coprocessor 151 "arm_CDP", "arm_LDC", "arm_MCR", "arm_MCRR", "arm_MRC", "arm_MRRC", "arm_STC", 152 // System 153 "arm_CPS", "arm_RFE", "arm_SRS", 154 // Undefined 155 "arm_UDF", 156 // FPSCR is inaccurate 157 "vfp_VMRS", 158 // Incorrect Unicorn implementations 159 "asimd_VRECPS", // Unicorn does not fuse the multiply and subtraction, resulting in being off by 1ULP. 160 "asimd_VRSQRTS", // Unicorn does not fuse the multiply and subtraction, resulting in being off by 1ULP. 161 "vfp_VCVT_from_fixed", // Unicorn does not do round-to-nearest-even for this instruction correctly. 162 }; 163 164 for (const auto& [fn, bitstring] : list) { 165 if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { 166 invalid.emplace_back(InstructionGenerator{bitstring}); 167 continue; 168 } 169 generators.emplace_back(InstructionGenerator{bitstring}); 170 } 171 return InstructionGeneratorInfo{generators, invalid}; 172 }(); 173 174 while (true) { 175 const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1); 176 const u32 inst = instructions.generators[index].Generate(); 177 178 if ((instructions.generators[index].Mask() & 0xF0000000) == 0 && (inst & 0xF0000000) == 0xF0000000) { 179 continue; 180 } 181 182 if (ShouldTestInst(inst, pc, false, is_last_inst)) { 183 return inst; 184 } 185 } 186 } 187 188 std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst, A32::ITState it_state = {}) { 189 static const struct InstructionGeneratorInfo { 190 std::vector<InstructionGenerator> generators; 191 std::vector<InstructionGenerator> invalid; 192 } instructions = [] { 193 const std::vector<std::tuple<std::string, const char*>> list{ 194 #define INST(fn, name, bitstring) {#fn, bitstring}, 195 #include "dynarmic/frontend/A32/decoder/thumb16.inc" 196 #include "dynarmic/frontend/A32/decoder/thumb32.inc" 197 #undef INST 198 }; 199 200 const std::vector<std::tuple<std::string, const char*>> vfp_list{ 201 #define INST(fn, name, bitstring) {#fn, bitstring}, 202 #include "dynarmic/frontend/A32/decoder/vfp.inc" 203 #undef INST 204 }; 205 206 const std::vector<std::tuple<std::string, const char*>> asimd_list{ 207 #define INST(fn, name, bitstring) {#fn, bitstring}, 208 #include "dynarmic/frontend/A32/decoder/asimd.inc" 209 #undef INST 210 }; 211 212 std::vector<InstructionGenerator> generators; 213 std::vector<InstructionGenerator> invalid; 214 215 // List of instructions not to test 216 static constexpr std::array do_not_test{ 217 "thumb16_BKPT", 218 "thumb16_IT", 219 "thumb16_SETEND", 220 221 // Exclusive load/stores 222 "thumb32_LDREX", 223 "thumb32_LDREXB", 224 "thumb32_LDREXD", 225 "thumb32_LDREXH", 226 "thumb32_STREX", 227 "thumb32_STREXB", 228 "thumb32_STREXD", 229 "thumb32_STREXH", 230 231 // FPSCR is inaccurate 232 "vfp_VMRS", 233 234 // Unicorn is incorrect? 235 "thumb32_MRS_reg", 236 "thumb32_MSR_reg", 237 238 // Unicorn has incorrect implementation (incorrect rounding and unsets CPSR.T??) 239 "vfp_VCVT_to_fixed", 240 "vfp_VCVT_from_fixed", 241 "asimd_VRECPS", // Unicorn does not fuse the multiply and subtraction, resulting in being off by 1ULP. 242 "asimd_VRSQRTS", // Unicorn does not fuse the multiply and subtraction, resulting in being off by 1ULP. 243 244 // Coprocessor 245 "thumb32_CDP", 246 "thumb32_LDC", 247 "thumb32_MCR", 248 "thumb32_MCRR", 249 "thumb32_MRC", 250 "thumb32_MRRC", 251 "thumb32_STC", 252 }; 253 254 for (const auto& [fn, bitstring] : list) { 255 if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { 256 invalid.emplace_back(InstructionGenerator{bitstring}); 257 continue; 258 } 259 generators.emplace_back(InstructionGenerator{bitstring}); 260 } 261 for (const auto& [fn, bs] : vfp_list) { 262 std::string bitstring = bs; 263 if (bitstring.substr(0, 4) == "cccc" || bitstring.substr(0, 4) == "----") { 264 bitstring.replace(0, 4, "1110"); 265 } 266 if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { 267 invalid.emplace_back(InstructionGenerator{bitstring.c_str()}); 268 continue; 269 } 270 generators.emplace_back(InstructionGenerator{bitstring.c_str()}); 271 } 272 for (const auto& [fn, bs] : asimd_list) { 273 std::string bitstring = bs; 274 if (bitstring.substr(0, 7) == "1111001") { 275 const char U = bitstring[7]; 276 bitstring.replace(0, 8, "111-1111"); 277 bitstring[3] = U; 278 } else if (bitstring.substr(0, 8) == "11110100") { 279 bitstring.replace(0, 8, "11111001"); 280 } else { 281 ASSERT_FALSE("Unhandled ASIMD instruction: {} {}", fn, bs); 282 } 283 if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { 284 invalid.emplace_back(InstructionGenerator{bitstring.c_str()}); 285 continue; 286 } 287 generators.emplace_back(InstructionGenerator{bitstring.c_str()}); 288 } 289 return InstructionGeneratorInfo{generators, invalid}; 290 }(); 291 292 while (true) { 293 const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1); 294 const u32 inst = instructions.generators[index].Generate(); 295 const bool is_four_bytes = (inst >> 16) != 0; 296 297 if (ShouldTestInst(is_four_bytes ? mcl::bit::swap_halves_32(inst) : inst, pc, true, is_last_inst, it_state)) { 298 if (is_four_bytes) 299 return {static_cast<u16>(inst >> 16), static_cast<u16>(inst)}; 300 return {static_cast<u16>(inst)}; 301 } 302 } 303 } 304 305 template<typename TestEnv> 306 Dynarmic::A32::UserConfig GetUserConfig(TestEnv& testenv) { 307 Dynarmic::A32::UserConfig user_config; 308 user_config.optimizations &= ~OptimizationFlag::FastDispatch; 309 user_config.callbacks = &testenv; 310 user_config.always_little_endian = true; 311 return user_config; 312 } 313 314 template<typename TestEnv> 315 static void RunTestInstance(Dynarmic::A32::Jit& jit, 316 A32Unicorn<TestEnv>& uni, 317 TestEnv& jit_env, 318 TestEnv& uni_env, 319 const typename A32Unicorn<TestEnv>::RegisterArray& regs, 320 const typename A32Unicorn<TestEnv>::ExtRegArray& vecs, 321 const std::vector<typename TestEnv::InstructionType>& instructions, 322 const u32 cpsr, 323 const u32 fpscr, 324 const size_t ticks_left) { 325 const u32 initial_pc = regs[15]; 326 const u32 num_words = initial_pc / sizeof(typename TestEnv::InstructionType); 327 const u32 code_mem_size = num_words + static_cast<u32>(instructions.size()); 328 const u32 expected_end_pc = code_mem_size * sizeof(typename TestEnv::InstructionType); 329 330 jit_env.code_mem.resize(code_mem_size); 331 uni_env.code_mem.resize(code_mem_size); 332 std::fill(jit_env.code_mem.begin(), jit_env.code_mem.end(), TestEnv::infinite_loop); 333 std::fill(uni_env.code_mem.begin(), uni_env.code_mem.end(), TestEnv::infinite_loop); 334 335 std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words); 336 std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + num_words); 337 jit_env.PadCodeMem(); 338 uni_env.PadCodeMem(); 339 jit_env.modified_memory.clear(); 340 uni_env.modified_memory.clear(); 341 jit_env.interrupts.clear(); 342 uni_env.interrupts.clear(); 343 344 jit.Regs() = regs; 345 jit.ExtRegs() = vecs; 346 jit.SetFpscr(fpscr); 347 jit.SetCpsr(cpsr); 348 jit.ClearCache(); 349 uni.SetRegisters(regs); 350 uni.SetExtRegs(vecs); 351 uni.SetFpscr(fpscr); 352 uni.EnableFloatingPointAccess(); 353 uni.SetCpsr(cpsr); 354 uni.ClearPageCache(); 355 356 jit_env.ticks_left = ticks_left; 357 jit.Run(); 358 359 uni_env.ticks_left = instructions.size(); // Unicorn counts thumb instructions weirdly. 360 uni.Run(); 361 362 SCOPE_FAIL { 363 fmt::print("Instruction Listing:\n"); 364 fmt::print("{}\n", Common::DisassembleAArch32(std::is_same_v<TestEnv, ThumbTestEnv>, initial_pc, (const u8*)instructions.data(), instructions.size() * sizeof(instructions[0]))); 365 366 fmt::print("Initial register listing:\n"); 367 for (size_t i = 0; i < regs.size(); ++i) { 368 fmt::print("{:3s}: {:08x}\n", static_cast<A32::Reg>(i), regs[i]); 369 } 370 for (size_t i = 0; i < vecs.size(); ++i) { 371 fmt::print("{:3s}: {:08x}\n", static_cast<A32::ExtReg>(i), vecs[i]); 372 } 373 fmt::print("cpsr {:08x}\n", cpsr); 374 fmt::print("fpcr {:08x}\n", fpscr); 375 fmt::print("fpcr.AHP {}\n", FP::FPCR{fpscr}.AHP()); 376 fmt::print("fpcr.DN {}\n", FP::FPCR{fpscr}.DN()); 377 fmt::print("fpcr.FZ {}\n", FP::FPCR{fpscr}.FZ()); 378 fmt::print("fpcr.RMode {}\n", static_cast<size_t>(FP::FPCR{fpscr}.RMode())); 379 fmt::print("fpcr.FZ16 {}\n", FP::FPCR{fpscr}.FZ16()); 380 fmt::print("\n"); 381 382 fmt::print("Final register listing:\n"); 383 fmt::print(" unicorn dynarmic\n"); 384 const auto uni_regs = uni.GetRegisters(); 385 for (size_t i = 0; i < regs.size(); ++i) { 386 fmt::print("{:3s}: {:08x} {:08x} {}\n", static_cast<A32::Reg>(i), uni_regs[i], jit.Regs()[i], uni_regs[i] != jit.Regs()[i] ? "*" : ""); 387 } 388 const auto uni_ext_regs = uni.GetExtRegs(); 389 for (size_t i = 0; i < vecs.size(); ++i) { 390 fmt::print("s{:2d}: {:08x} {:08x} {}\n", static_cast<size_t>(i), uni_ext_regs[i], jit.ExtRegs()[i], uni_ext_regs[i] != jit.ExtRegs()[i] ? "*" : ""); 391 } 392 fmt::print("cpsr {:08x} {:08x} {}\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : ""); 393 fmt::print("fpsr {:08x} {:08x} {}\n", uni.GetFpscr(), jit.Fpscr(), (uni.GetFpscr() & 0xF0000000) != (jit.Fpscr() & 0xF0000000) ? "*" : ""); 394 fmt::print("\n"); 395 396 fmt::print("Modified memory:\n"); 397 fmt::print(" uni dyn\n"); 398 auto uni_iter = uni_env.modified_memory.begin(); 399 auto jit_iter = jit_env.modified_memory.begin(); 400 while (uni_iter != uni_env.modified_memory.end() || jit_iter != jit_env.modified_memory.end()) { 401 if (uni_iter == uni_env.modified_memory.end() || (jit_iter != jit_env.modified_memory.end() && uni_iter->first > jit_iter->first)) { 402 fmt::print("{:08x}: {:02x} *\n", jit_iter->first, jit_iter->second); 403 jit_iter++; 404 } else if (jit_iter == jit_env.modified_memory.end() || jit_iter->first > uni_iter->first) { 405 fmt::print("{:08x}: {:02x} *\n", uni_iter->first, uni_iter->second); 406 uni_iter++; 407 } else if (uni_iter->first == jit_iter->first) { 408 fmt::print("{:08x}: {:02x} {:02x} {}\n", uni_iter->first, uni_iter->second, jit_iter->second, uni_iter->second != jit_iter->second ? "*" : ""); 409 uni_iter++; 410 jit_iter++; 411 } 412 } 413 fmt::print("\n"); 414 415 fmt::print("x86_64:\n"); 416 jit.DumpDisassembly(); 417 418 fmt::print("Interrupts:\n"); 419 for (const auto& i : uni_env.interrupts) { 420 std::puts(i.c_str()); 421 } 422 }; 423 424 REQUIRE(uni_env.code_mem_modified_by_guest == jit_env.code_mem_modified_by_guest); 425 if (uni_env.code_mem_modified_by_guest) { 426 return; 427 } 428 429 // Qemu doesn't do Thumb transitions?? 430 { 431 const u32 uni_pc = uni.GetPC(); 432 const bool is_thumb = (jit.Cpsr() & (1 << 5)) != 0; 433 const u32 new_uni_pc = uni_pc & (is_thumb ? 0xFFFFFFFE : 0xFFFFFFFC); 434 uni.SetPC(new_uni_pc); 435 } 436 437 if (uni.GetRegisters()[15] > jit.Regs()[15]) { 438 int trials = 0; 439 while (jit.Regs()[15] >= initial_pc && jit.Regs()[15] < expected_end_pc && trials++ < 100 && uni.GetRegisters()[15] != jit.Regs()[15]) { 440 fmt::print("Warning: Possible unicorn overrrun, attempt recovery\n"); 441 jit.Step(); 442 } 443 } 444 445 REQUIRE(uni.GetRegisters() == jit.Regs()); 446 REQUIRE(uni.GetExtRegs() == jit.ExtRegs()); 447 REQUIRE((uni.GetCpsr() & 0xFFFFFDDF) == (jit.Cpsr() & 0xFFFFFDDF)); 448 REQUIRE((uni.GetFpscr() & 0xF8000000) == (jit.Fpscr() & 0xF8000000)); 449 REQUIRE(uni_env.modified_memory == jit_env.modified_memory); 450 REQUIRE(uni_env.interrupts.empty()); 451 } 452 } // Anonymous namespace 453 454 TEST_CASE("A32: Single random arm instruction", "[arm]") { 455 ArmTestEnv jit_env{}; 456 ArmTestEnv uni_env{}; 457 458 Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; 459 A32Unicorn<ArmTestEnv> uni{uni_env}; 460 461 A32Unicorn<ArmTestEnv>::RegisterArray regs; 462 A32Unicorn<ArmTestEnv>::ExtRegArray ext_reg; 463 std::vector<u32> instructions(1); 464 465 for (size_t iteration = 0; iteration < 100000; ++iteration) { 466 std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 467 std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 468 469 const u32 start_address = 100; 470 const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10; 471 const u32 fpcr = RandomFpcr(); 472 473 instructions[0] = GenRandomArmInst(start_address, true); 474 475 INFO("Instruction: 0x" << std::hex << instructions[0]); 476 477 regs[15] = start_address; 478 RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1); 479 } 480 } 481 482 TEST_CASE("A32: Small random arm block", "[arm]") { 483 ArmTestEnv jit_env{}; 484 ArmTestEnv uni_env{}; 485 486 Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; 487 A32Unicorn<ArmTestEnv> uni{uni_env}; 488 489 A32Unicorn<ArmTestEnv>::RegisterArray regs; 490 A32Unicorn<ArmTestEnv>::ExtRegArray ext_reg; 491 std::vector<u32> instructions(5); 492 493 for (size_t iteration = 0; iteration < 100000; ++iteration) { 494 std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 495 std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 496 497 const u32 start_address = 100; 498 const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10; 499 const u32 fpcr = RandomFpcr(); 500 501 instructions[0] = GenRandomArmInst(start_address + 0, false); 502 instructions[1] = GenRandomArmInst(start_address + 4, false); 503 instructions[2] = GenRandomArmInst(start_address + 8, false); 504 instructions[3] = GenRandomArmInst(start_address + 12, false); 505 instructions[4] = GenRandomArmInst(start_address + 16, true); 506 507 INFO("Instruction 1: 0x" << std::hex << instructions[0]); 508 INFO("Instruction 2: 0x" << std::hex << instructions[1]); 509 INFO("Instruction 3: 0x" << std::hex << instructions[2]); 510 INFO("Instruction 4: 0x" << std::hex << instructions[3]); 511 INFO("Instruction 5: 0x" << std::hex << instructions[4]); 512 513 regs[15] = start_address; 514 RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5); 515 } 516 } 517 518 TEST_CASE("A32: Large random arm block", "[arm]") { 519 ArmTestEnv jit_env{}; 520 ArmTestEnv uni_env{}; 521 522 Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; 523 A32Unicorn<ArmTestEnv> uni{uni_env}; 524 525 A32Unicorn<ArmTestEnv>::RegisterArray regs; 526 A32Unicorn<ArmTestEnv>::ExtRegArray ext_reg; 527 528 constexpr size_t instruction_count = 100; 529 std::vector<u32> instructions(instruction_count); 530 531 for (size_t iteration = 0; iteration < 10000; ++iteration) { 532 std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 533 std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 534 535 const u64 start_address = 100; 536 const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10; 537 const u32 fpcr = RandomFpcr(); 538 539 for (size_t j = 0; j < instruction_count; ++j) { 540 instructions[j] = GenRandomArmInst(start_address + j * 4, j == instruction_count - 1); 541 } 542 543 regs[15] = start_address; 544 RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 100); 545 } 546 } 547 548 TEST_CASE("A32: Single random thumb instruction", "[thumb]") { 549 ThumbTestEnv jit_env{}; 550 ThumbTestEnv uni_env{}; 551 552 Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; 553 A32Unicorn<ThumbTestEnv> uni{uni_env}; 554 555 A32Unicorn<ThumbTestEnv>::RegisterArray regs; 556 A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg; 557 std::vector<u16> instructions; 558 559 for (size_t iteration = 0; iteration < 100000; ++iteration) { 560 std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 561 std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 562 563 const u32 start_address = 100; 564 const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0; 565 const u32 fpcr = RandomFpcr(); 566 567 instructions = GenRandomThumbInst(start_address, true); 568 569 INFO("Instruction: 0x" << std::hex << instructions[0]); 570 571 regs[15] = start_address; 572 RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1); 573 } 574 } 575 576 TEST_CASE("A32: Single random thumb instruction (offset)", "[thumb]") { 577 ThumbTestEnv jit_env{}; 578 ThumbTestEnv uni_env{}; 579 580 Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; 581 A32Unicorn<ThumbTestEnv> uni{uni_env}; 582 583 A32Unicorn<ThumbTestEnv>::RegisterArray regs; 584 A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg; 585 std::vector<u16> instructions; 586 587 for (size_t iteration = 0; iteration < 100000; ++iteration) { 588 std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 589 std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 590 591 const u32 start_address = 100; 592 const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0; 593 const u32 fpcr = RandomFpcr(); 594 595 instructions.clear(); 596 instructions.push_back(0xbf00); // NOP 597 const std::vector<u16> inst = GenRandomThumbInst(start_address + 2, true); 598 instructions.insert(instructions.end(), inst.begin(), inst.end()); 599 600 INFO("Instruction: 0x" << std::hex << inst[0]); 601 602 regs[15] = start_address; 603 RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 2); 604 } 605 } 606 607 TEST_CASE("A32: Small random thumb block", "[thumb]") { 608 ThumbTestEnv jit_env{}; 609 ThumbTestEnv uni_env{}; 610 611 Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; 612 A32Unicorn<ThumbTestEnv> uni{uni_env}; 613 614 A32Unicorn<ThumbTestEnv>::RegisterArray regs; 615 A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg; 616 std::vector<u16> instructions; 617 618 for (size_t iteration = 0; iteration < 100000; ++iteration) { 619 std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 620 std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 621 622 const u32 start_address = 100; 623 const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0; 624 const u32 fpcr = RandomFpcr(); 625 626 instructions.clear(); 627 for (size_t i = 0; i < 5; i++) { 628 const std::vector<u16> inst = GenRandomThumbInst(start_address + instructions.size() * 2, i == 4); 629 instructions.insert(instructions.end(), inst.begin(), inst.end()); 630 } 631 632 regs[15] = start_address; 633 RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5); 634 } 635 } 636 637 TEST_CASE("A32: Test thumb IT instruction", "[thumb]") { 638 ThumbTestEnv jit_env{}; 639 ThumbTestEnv uni_env{}; 640 641 Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; 642 A32Unicorn<ThumbTestEnv> uni{uni_env}; 643 644 A32Unicorn<ThumbTestEnv>::RegisterArray regs; 645 A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg; 646 std::vector<u16> instructions; 647 648 for (size_t iteration = 0; iteration < 100000; ++iteration) { 649 std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 650 std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); 651 652 const size_t pre_instructions = RandInt<size_t>(0, 3); 653 const size_t post_instructions = RandInt<size_t>(5, 8); 654 655 const u32 start_address = 100; 656 const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0; 657 const u32 fpcr = RandomFpcr(); 658 659 instructions.clear(); 660 661 for (size_t i = 0; i < pre_instructions; i++) { 662 const std::vector<u16> inst = GenRandomThumbInst(start_address + instructions.size() * 2, false); 663 instructions.insert(instructions.end(), inst.begin(), inst.end()); 664 } 665 666 // Emit IT instruction 667 A32::ITState it_state = [&] { 668 while (true) { 669 const u16 imm8 = RandInt<u16>(0, 0xFF); 670 if (mcl::bit::get_bits<0, 3>(imm8) == 0b0000 || mcl::bit::get_bits<4, 7>(imm8) == 0b1111 || (mcl::bit::get_bits<4, 7>(imm8) == 0b1110 && mcl::bit::count_ones(mcl::bit::get_bits<0, 3>(imm8)) != 1)) { 671 continue; 672 } 673 instructions.push_back(0b1011111100000000 | imm8); 674 return A32::ITState{static_cast<u8>(imm8)}; 675 } 676 }(); 677 678 for (size_t i = 0; i < post_instructions; i++) { 679 const std::vector<u16> inst = GenRandomThumbInst(start_address + instructions.size() * 2, i == post_instructions - 1, it_state); 680 instructions.insert(instructions.end(), inst.begin(), inst.end()); 681 it_state = it_state.Advance(); 682 } 683 684 regs[15] = start_address; 685 RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, pre_instructions + 1 + post_instructions); 686 } 687 }