/ tests / A32 / fuzz_arm.cpp
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  }