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