fuzz_thumb.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /* This file is part of the dynarmic project.
  2. * Copyright (c) 2016 MerryMage
  3. * SPDX-License-Identifier: 0BSD
  4. */
  5. #include <algorithm>
  6. #include <array>
  7. #include <cinttypes>
  8. #include <cstdio>
  9. #include <cstring>
  10. #include <functional>
  11. #include <string_view>
  12. #include <tuple>
  13. #include <catch2/catch_test_macros.hpp>
  14. #include <mcl/bit/bit_field.hpp>
  15. #include <mcl/stdint.hpp>
  16. #include "../rand_int.h"
  17. #include "../unicorn_emu/a32_unicorn.h"
  18. #include "./testenv.h"
  19. #include "dynarmic/frontend/A32/FPSCR.h"
  20. #include "dynarmic/frontend/A32/PSR.h"
  21. #include "dynarmic/frontend/A32/a32_location_descriptor.h"
  22. #include "dynarmic/frontend/A32/disassembler/disassembler.h"
  23. #include "dynarmic/frontend/A32/translate/a32_translate.h"
  24. #include "dynarmic/interface/A32/a32.h"
  25. #include "dynarmic/ir/basic_block.h"
  26. #include "dynarmic/ir/opt/passes.h"
  27. using namespace Dynarmic;
  28. static A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
  29. A32::UserConfig user_config;
  30. user_config.optimizations &= ~OptimizationFlag::FastDispatch;
  31. user_config.callbacks = testenv;
  32. return user_config;
  33. }
  34. using WriteRecords = std::map<u32, u8>;
  35. struct ThumbInstGen final {
  36. public:
  37. ThumbInstGen(
  38. std::string_view format, std::function<bool(u32)> is_valid = [](u32) { return true; })
  39. : is_valid(is_valid) {
  40. REQUIRE((format.size() == 16 || format.size() == 32));
  41. const auto bit_size = format.size();
  42. for (size_t i = 0; i < bit_size; i++) {
  43. const u32 bit = 1U << (bit_size - 1 - i);
  44. switch (format[i]) {
  45. case '0':
  46. mask |= bit;
  47. break;
  48. case '1':
  49. bits |= bit;
  50. mask |= bit;
  51. break;
  52. default:
  53. // Do nothing
  54. break;
  55. }
  56. }
  57. }
  58. u16 Generate16() const {
  59. u32 inst;
  60. do {
  61. const auto random = RandInt<u16>(0, 0xFFFF);
  62. inst = bits | (random & ~mask);
  63. } while (!is_valid(inst));
  64. ASSERT((inst & mask) == bits);
  65. return static_cast<u16>(inst);
  66. }
  67. u32 Generate32() const {
  68. u32 inst;
  69. do {
  70. const auto random = RandInt<u32>(0, 0xFFFFFFFF);
  71. inst = bits | (random & ~mask);
  72. } while (!is_valid(inst));
  73. ASSERT((inst & mask) == bits);
  74. return inst;
  75. }
  76. private:
  77. u32 bits = 0;
  78. u32 mask = 0;
  79. std::function<bool(u32)> is_valid;
  80. };
  81. static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const A32::Jit& jit, const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
  82. const auto interp_regs = uni.GetRegisters();
  83. const auto jit_regs = jit.Regs();
  84. return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) && uni.GetCpsr() == jit.Cpsr() && interp_write_records == jit_write_records;
  85. }
  86. static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<ThumbTestEnv>& uni, A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
  87. uni.ClearPageCache();
  88. jit.ClearCache();
  89. // Setup initial state
  90. uni.SetCpsr(0x000001F0);
  91. uni.SetRegisters(initial_regs);
  92. jit.SetCpsr(0x000001F0);
  93. jit.Regs() = initial_regs;
  94. // Run interpreter
  95. test_env.modified_memory.clear();
  96. test_env.ticks_left = instructions_to_execute_count;
  97. uni.SetPC(uni.GetPC() | 1);
  98. uni.Run();
  99. const bool uni_code_memory_modified = test_env.code_mem_modified_by_guest;
  100. const auto interp_write_records = test_env.modified_memory;
  101. // Run jit
  102. test_env.code_mem_modified_by_guest = false;
  103. test_env.modified_memory.clear();
  104. test_env.ticks_left = instructions_to_execute_count;
  105. jit.Run();
  106. const bool jit_code_memory_modified = test_env.code_mem_modified_by_guest;
  107. const auto jit_write_records = test_env.modified_memory;
  108. test_env.code_mem_modified_by_guest = false;
  109. REQUIRE(uni_code_memory_modified == jit_code_memory_modified);
  110. if (uni_code_memory_modified) {
  111. return;
  112. }
  113. // Compare
  114. if (!DoesBehaviorMatch(uni, jit, interp_write_records, jit_write_records)) {
  115. printf("Failed at execution number %zu\n", run_number);
  116. printf("\nInstruction Listing: \n");
  117. for (size_t i = 0; i < instruction_count; i++) {
  118. printf("%04x %s\n", test_env.code_mem[i], A32::DisassembleThumb16(test_env.code_mem[i]).c_str());
  119. }
  120. printf("\nInitial Register Listing: \n");
  121. for (size_t i = 0; i < initial_regs.size(); i++) {
  122. printf("%4zu: %08x\n", i, initial_regs[i]);
  123. }
  124. printf("\nFinal Register Listing: \n");
  125. printf(" unicorn jit\n");
  126. const auto uni_registers = uni.GetRegisters();
  127. for (size_t i = 0; i < uni_registers.size(); i++) {
  128. printf("%4zu: %08x %08x %s\n", i, uni_registers[i], jit.Regs()[i], uni_registers[i] != jit.Regs()[i] ? "*" : "");
  129. }
  130. printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : "");
  131. printf("\nUnicorn Write Records:\n");
  132. for (const auto& record : interp_write_records) {
  133. printf("[%08x] = %02x\n", record.first, record.second);
  134. }
  135. printf("\nJIT Write Records:\n");
  136. for (const auto& record : jit_write_records) {
  137. printf("[%08x] = %02x\n", record.first, record.second);
  138. }
  139. A32::PSR cpsr;
  140. cpsr.T(true);
  141. size_t num_insts = 0;
  142. while (num_insts < instructions_to_execute_count) {
  143. A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
  144. IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
  145. Optimization::NamingPass(ir_block);
  146. Optimization::A32GetSetElimination(ir_block, {.convert_nz_to_nzc = true});
  147. Optimization::DeadCodeElimination(ir_block);
  148. Optimization::A32ConstantMemoryReads(ir_block, &test_env);
  149. Optimization::ConstantPropagation(ir_block);
  150. Optimization::DeadCodeElimination(ir_block);
  151. Optimization::VerificationPass(ir_block);
  152. printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
  153. printf("\n\nx86_64:\n");
  154. jit.DumpDisassembly();
  155. num_insts += ir_block.CycleCount();
  156. }
  157. #ifdef _MSC_VER
  158. __debugbreak();
  159. #endif
  160. FAIL();
  161. }
  162. }
  163. void FuzzJitThumb16(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
  164. ThumbTestEnv test_env;
  165. // Prepare memory.
  166. test_env.code_mem.resize(instruction_count + 1);
  167. test_env.code_mem.back() = 0xE7FE; // b +#0
  168. // Prepare test subjects
  169. A32Unicorn uni{test_env};
  170. A32::Jit jit{GetUserConfig(&test_env)};
  171. for (size_t run_number = 0; run_number < run_count; run_number++) {
  172. ThumbTestEnv::RegisterArray initial_regs;
  173. std::generate_n(initial_regs.begin(), initial_regs.size() - 1, [] { return RandInt<u32>(0, 0xFFFFFFFF); });
  174. initial_regs[15] = 0;
  175. std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
  176. RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
  177. }
  178. }
  179. void FuzzJitThumb32(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) {
  180. ThumbTestEnv test_env;
  181. // Prepare memory.
  182. // A Thumb-32 instruction is 32-bits so we multiply our count
  183. test_env.code_mem.resize(instruction_count * 2 + 1);
  184. test_env.code_mem.back() = 0xE7FE; // b +#0
  185. // Prepare test subjects
  186. A32Unicorn uni{test_env};
  187. A32::Jit jit{GetUserConfig(&test_env)};
  188. for (size_t run_number = 0; run_number < run_count; run_number++) {
  189. ThumbTestEnv::RegisterArray initial_regs;
  190. std::generate_n(initial_regs.begin(), initial_regs.size() - 1, [] { return RandInt<u32>(0, 0xFFFFFFFF); });
  191. initial_regs[15] = 0;
  192. for (size_t i = 0; i < instruction_count; i++) {
  193. const auto instruction = instruction_generator();
  194. const auto first_halfword = static_cast<u16>(mcl::bit::get_bits<0, 15>(instruction));
  195. const auto second_halfword = static_cast<u16>(mcl::bit::get_bits<16, 31>(instruction));
  196. test_env.code_mem[i * 2 + 0] = second_halfword;
  197. test_env.code_mem[i * 2 + 1] = first_halfword;
  198. }
  199. RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
  200. }
  201. }
  202. TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb][Thumb16]") {
  203. const std::array instructions = {
  204. ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
  205. ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
  206. ThumbInstGen("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
  207. ThumbInstGen("000110oxxxxxxxxx"), // ADD/SUB_reg
  208. ThumbInstGen("000111oxxxxxxxxx"), // ADD/SUB_imm
  209. ThumbInstGen("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm
  210. ThumbInstGen("010000ooooxxxxxx"), // Data Processing
  211. ThumbInstGen("010001000hxxxxxx"), // ADD (high registers)
  212. ThumbInstGen("0100010101xxxxxx", // CMP (high registers)
  213. [](u32 inst) { return mcl::bit::get_bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
  214. ThumbInstGen("0100010110xxxxxx", // CMP (high registers)
  215. [](u32 inst) { return mcl::bit::get_bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
  216. ThumbInstGen("010001100hxxxxxx"), // MOV (high registers)
  217. ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer
  218. ThumbInstGen("10110010ooxxxxxx"), // SXT/UXT
  219. ThumbInstGen("1011101000xxxxxx"), // REV
  220. ThumbInstGen("1011101001xxxxxx"), // REV16
  221. ThumbInstGen("1011101011xxxxxx"), // REVSH
  222. ThumbInstGen("01001xxxxxxxxxxx"), // LDR Rd, [PC, #]
  223. ThumbInstGen("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm]
  224. ThumbInstGen("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #]
  225. ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
  226. ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
  227. ThumbInstGen("1011010xxxxxxxxx", // PUSH
  228. [](u32 inst) { return mcl::bit::get_bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
  229. ThumbInstGen("10111100xxxxxxxx", // POP (P = 0)
  230. [](u32 inst) { return mcl::bit::get_bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
  231. ThumbInstGen("1100xxxxxxxxxxxx", // STMIA/LDMIA
  232. [](u32 inst) {
  233. // Ensure that the architecturally undefined case of
  234. // the base register being within the list isn't hit.
  235. const u32 rn = mcl::bit::get_bits<8, 10>(inst);
  236. return (inst & (1U << rn)) == 0 && mcl::bit::get_bits<0, 7>(inst) != 0;
  237. }),
  238. // TODO: We should properly test against swapped
  239. // endianness cases, however Unicorn doesn't
  240. // expose the intended endianness of a load/store
  241. // operation to memory through its hooks.
  242. #if 0
  243. ThumbInstGen("101101100101x000"), // SETEND
  244. #endif
  245. };
  246. const auto instruction_select = [&]() -> u16 {
  247. const auto inst_index = RandInt<size_t>(0, instructions.size() - 1);
  248. return instructions[inst_index].Generate16();
  249. };
  250. SECTION("single instructions") {
  251. FuzzJitThumb16(1, 2, 10000, instruction_select);
  252. }
  253. SECTION("short blocks") {
  254. FuzzJitThumb16(5, 6, 3000, instruction_select);
  255. }
  256. // TODO: Test longer blocks when Unicorn can consistently
  257. // run these without going into an infinite loop.
  258. #if 0
  259. SECTION("long blocks") {
  260. FuzzJitThumb16(1024, 1025, 1000, instruction_select);
  261. }
  262. #endif
  263. }
  264. TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb][Thumb16]") {
  265. const std::array instructions = {
  266. // TODO: We currently can't test BX/BLX as we have
  267. // no way of preventing the unpredictable
  268. // condition from occurring with the current interface.
  269. // (bits zero and one within the specified register
  270. // must not be address<1:0> == '10'.
  271. #if 0
  272. ThumbInstGen("01000111xmmmm000", // BLX/BX
  273. [](u32 inst){
  274. const u32 Rm = mcl::bit::get_bits<3, 6>(inst);
  275. return Rm != 15;
  276. }),
  277. #endif
  278. ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp
  279. ThumbInstGen("11100xxxxxxxxxxx"), // B
  280. ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
  281. ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
  282. ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
  283. [](u32 inst) {
  284. const u32 c = mcl::bit::get_bits<9, 12>(inst);
  285. return c < 0b1110; // Don't want SWI or undefined instructions.
  286. }),
  287. ThumbInstGen("1011o0i1iiiiinnn"), // CBZ/CBNZ
  288. ThumbInstGen("10110110011x0xxx"), // CPS
  289. // TODO: We currently have no control over the generated
  290. // values when creating new pages, so we can't
  291. // reliably test this yet.
  292. #if 0
  293. ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1)
  294. #endif
  295. };
  296. const auto instruction_select = [&]() -> u16 {
  297. const auto inst_index = RandInt<size_t>(0, instructions.size() - 1);
  298. return instructions[inst_index].Generate16();
  299. };
  300. FuzzJitThumb16(1, 1, 10000, instruction_select);
  301. }
  302. TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
  303. const auto three_reg_not_r15 = [](u32 inst) {
  304. const auto d = mcl::bit::get_bits<8, 11>(inst);
  305. const auto m = mcl::bit::get_bits<0, 3>(inst);
  306. const auto n = mcl::bit::get_bits<16, 19>(inst);
  307. return d != 15 && m != 15 && n != 15;
  308. };
  309. const std::array instructions = {
  310. ThumbInstGen("111110101011nnnn1111dddd1000mmmm", // CLZ
  311. [](u32 inst) {
  312. const auto d = mcl::bit::get_bits<8, 11>(inst);
  313. const auto m = mcl::bit::get_bits<0, 3>(inst);
  314. const auto n = mcl::bit::get_bits<16, 19>(inst);
  315. return m == n && d != 15 && m != 15;
  316. }),
  317. ThumbInstGen("111110101000nnnn1111dddd1000mmmm", // QADD
  318. three_reg_not_r15),
  319. ThumbInstGen("111110101000nnnn1111dddd0001mmmm", // QADD8
  320. three_reg_not_r15),
  321. ThumbInstGen("111110101001nnnn1111dddd0001mmmm", // QADD16
  322. three_reg_not_r15),
  323. ThumbInstGen("111110101010nnnn1111dddd0001mmmm", // QASX
  324. three_reg_not_r15),
  325. ThumbInstGen("111110101000nnnn1111dddd1001mmmm", // QDADD
  326. three_reg_not_r15),
  327. ThumbInstGen("111110101000nnnn1111dddd1011mmmm", // QDSUB
  328. three_reg_not_r15),
  329. ThumbInstGen("111110101110nnnn1111dddd0001mmmm", // QSAX
  330. three_reg_not_r15),
  331. ThumbInstGen("111110101000nnnn1111dddd1010mmmm", // QSUB
  332. three_reg_not_r15),
  333. ThumbInstGen("111110101100nnnn1111dddd0001mmmm", // QSUB8
  334. three_reg_not_r15),
  335. ThumbInstGen("111110101101nnnn1111dddd0001mmmm", // QSUB16
  336. three_reg_not_r15),
  337. ThumbInstGen("111110101001nnnn1111dddd1010mmmm", // RBIT
  338. [](u32 inst) {
  339. const auto d = mcl::bit::get_bits<8, 11>(inst);
  340. const auto m = mcl::bit::get_bits<0, 3>(inst);
  341. const auto n = mcl::bit::get_bits<16, 19>(inst);
  342. return m == n && d != 15 && m != 15;
  343. }),
  344. ThumbInstGen("111110101001nnnn1111dddd1000mmmm", // REV
  345. [](u32 inst) {
  346. const auto d = mcl::bit::get_bits<8, 11>(inst);
  347. const auto m = mcl::bit::get_bits<0, 3>(inst);
  348. const auto n = mcl::bit::get_bits<16, 19>(inst);
  349. return m == n && d != 15 && m != 15;
  350. }),
  351. ThumbInstGen("111110101001nnnn1111dddd1001mmmm", // REV16
  352. [](u32 inst) {
  353. const auto d = mcl::bit::get_bits<8, 11>(inst);
  354. const auto m = mcl::bit::get_bits<0, 3>(inst);
  355. const auto n = mcl::bit::get_bits<16, 19>(inst);
  356. return m == n && d != 15 && m != 15;
  357. }),
  358. ThumbInstGen("111110101001nnnn1111dddd1011mmmm", // REVSH
  359. [](u32 inst) {
  360. const auto d = mcl::bit::get_bits<8, 11>(inst);
  361. const auto m = mcl::bit::get_bits<0, 3>(inst);
  362. const auto n = mcl::bit::get_bits<16, 19>(inst);
  363. return m == n && d != 15 && m != 15;
  364. }),
  365. ThumbInstGen("111110101000nnnn1111dddd0000mmmm", // SADD8
  366. three_reg_not_r15),
  367. ThumbInstGen("111110101001nnnn1111dddd0000mmmm", // SADD16
  368. three_reg_not_r15),
  369. ThumbInstGen("111110101010nnnn1111dddd0000mmmm", // SASX
  370. three_reg_not_r15),
  371. ThumbInstGen("111110101010nnnn1111dddd1000mmmm", // SEL
  372. three_reg_not_r15),
  373. ThumbInstGen("111110101000nnnn1111dddd0010mmmm", // SHADD8
  374. three_reg_not_r15),
  375. ThumbInstGen("111110101001nnnn1111dddd0010mmmm", // SHADD16
  376. three_reg_not_r15),
  377. ThumbInstGen("111110101010nnnn1111dddd0010mmmm", // SHASX
  378. three_reg_not_r15),
  379. ThumbInstGen("111110101110nnnn1111dddd0010mmmm", // SHSAX
  380. three_reg_not_r15),
  381. ThumbInstGen("111110101100nnnn1111dddd0010mmmm", // SHSUB8
  382. three_reg_not_r15),
  383. ThumbInstGen("111110101101nnnn1111dddd0010mmmm", // SHSUB16
  384. three_reg_not_r15),
  385. ThumbInstGen("111110101110nnnn1111dddd0000mmmm", // SSAX
  386. three_reg_not_r15),
  387. ThumbInstGen("111110101100nnnn1111dddd0000mmmm", // SSUB8
  388. three_reg_not_r15),
  389. ThumbInstGen("111110101101nnnn1111dddd0000mmmm", // SSUB16
  390. three_reg_not_r15),
  391. ThumbInstGen("111110101000nnnn1111dddd0100mmmm", // UADD8
  392. three_reg_not_r15),
  393. ThumbInstGen("111110101001nnnn1111dddd0100mmmm", // UADD16
  394. three_reg_not_r15),
  395. ThumbInstGen("111110101010nnnn1111dddd0100mmmm", // UASX
  396. three_reg_not_r15),
  397. ThumbInstGen("111110101000nnnn1111dddd0110mmmm", // UHADD8
  398. three_reg_not_r15),
  399. ThumbInstGen("111110101001nnnn1111dddd0110mmmm", // UHADD16
  400. three_reg_not_r15),
  401. ThumbInstGen("111110101010nnnn1111dddd0110mmmm", // UHASX
  402. three_reg_not_r15),
  403. ThumbInstGen("111110101110nnnn1111dddd0110mmmm", // UHSAX
  404. three_reg_not_r15),
  405. ThumbInstGen("111110101100nnnn1111dddd0110mmmm", // UHSUB8
  406. three_reg_not_r15),
  407. ThumbInstGen("111110101101nnnn1111dddd0110mmmm", // UHSUB16
  408. three_reg_not_r15),
  409. ThumbInstGen("111110101000nnnn1111dddd0101mmmm", // UQADD8
  410. three_reg_not_r15),
  411. ThumbInstGen("111110101001nnnn1111dddd0101mmmm", // UQADD16
  412. three_reg_not_r15),
  413. ThumbInstGen("111110101010nnnn1111dddd0101mmmm", // UQASX
  414. three_reg_not_r15),
  415. ThumbInstGen("111110101110nnnn1111dddd0101mmmm", // UQSAX
  416. three_reg_not_r15),
  417. ThumbInstGen("111110101100nnnn1111dddd0101mmmm", // UQSUB8
  418. three_reg_not_r15),
  419. ThumbInstGen("111110101101nnnn1111dddd0101mmmm", // UQSUB16
  420. three_reg_not_r15),
  421. ThumbInstGen("111110101110nnnn1111dddd0100mmmm", // USAX
  422. three_reg_not_r15),
  423. ThumbInstGen("111110101100nnnn1111dddd0100mmmm", // USUB8
  424. three_reg_not_r15),
  425. ThumbInstGen("111110101101nnnn1111dddd0100mmmm", // USUB16
  426. three_reg_not_r15),
  427. };
  428. const auto instruction_select = [&]() -> u32 {
  429. const auto inst_index = RandInt<size_t>(0, instructions.size() - 1);
  430. return instructions[inst_index].Generate32();
  431. };
  432. SECTION("single instructions") {
  433. FuzzJitThumb32(1, 2, 10000, instruction_select);
  434. }
  435. SECTION("short blocks") {
  436. FuzzJitThumb32(5, 6, 3000, instruction_select);
  437. }
  438. }
  439. TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") {
  440. ThumbTestEnv test_env;
  441. // Prepare test subjects
  442. A32Unicorn<ThumbTestEnv> uni{test_env};
  443. A32::Jit jit{GetUserConfig(&test_env)};
  444. constexpr ThumbTestEnv::RegisterArray initial_regs{
  445. 0xe90ecd70,
  446. 0x3e3b73c3,
  447. 0x571616f9,
  448. 0x0b1ef45a,
  449. 0xb3a829f2,
  450. 0x915a7a6a,
  451. 0x579c38f4,
  452. 0xd9ffe391,
  453. 0x55b6682b,
  454. 0x458d8f37,
  455. 0x8f3eb3dc,
  456. 0xe18c0e7d,
  457. 0x6752657a,
  458. 0x00001766,
  459. 0xdbbf23e3,
  460. 0x00000000,
  461. };
  462. test_env.code_mem = {
  463. 0x40B8, // lsls r0, r7, #0
  464. 0x01CA, // lsls r2, r1, #7
  465. 0x83A1, // strh r1, [r4, #28]
  466. 0x708A, // strb r2, [r1, #2]
  467. 0xBCC4, // pop {r2, r6, r7}
  468. 0xE7FE, // b +#0
  469. };
  470. RunInstance(1, test_env, uni, jit, initial_regs, 5, 5);
  471. }