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 }