kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

arm32_decode_test.c (24943B)


      1 /* ARM32 (Thumb-2) structured decode test.
      2  *
      3  * Pins the ArchDecodeOps path: bytes encoded via the isa.h inline encoders
      4  * decode into KitDecodedInsn records, and the formatter renders those records
      5  * back to text. Covers each 16-bit and 32-bit family, the MOVW/MOVT immediate
      6  * split, the BL/B.W split-immediate, IT, CBZ/TBB, and a few data-proc + ldst.
      7  * Mirrors test/arch/rv64_decode_test.c. */
      8 
      9 #include <kit/compile.h>
     10 #include <kit/core.h>
     11 #include <stdarg.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 
     16 #include "arch/arch.h"
     17 #include "arch/arm32/isa.h"
     18 #include "lib/kit_unit.h"
     19 
     20 static KitUnit g_u;
     21 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__)
     22 
     23 static KitCompiler* new_compiler(void) {
     24   /* arm32 is ILP32: pointers are 4 bytes (the shared helper defaults to 8). */
     25   KitTargetSpec t =
     26       kit_unit_target(KIT_ARCH_ARM_32, KIT_OS_FREESTANDING, KIT_OBJ_ELF);
     27   KitCompiler* c = NULL;
     28   t.ptr_size = 4;
     29   t.ptr_align = 4;
     30   if (kit_unit_compiler_new(&g_u, t, &c) != KIT_OK || !c) {
     31     fprintf(stderr, "compiler_new failed\n");
     32     exit(2);
     33   }
     34   return c;
     35 }
     36 
     37 /* Emit a 32-bit Thumb-2 word ((hw1<<16)|hw2) as two LE half-words, hw1 first. */
     38 static void put_t32(unsigned char* b, size_t off, unsigned w) {
     39   unsigned hw1 = (w >> 16) & 0xffffu, hw2 = w & 0xffffu;
     40   b[off + 0] = (unsigned char)(hw1 & 0xffu);
     41   b[off + 1] = (unsigned char)((hw1 >> 8) & 0xffu);
     42   b[off + 2] = (unsigned char)(hw2 & 0xffu);
     43   b[off + 3] = (unsigned char)((hw2 >> 8) & 0xffu);
     44 }
     45 
     46 static void put_t16(unsigned char* b, size_t off, unsigned hw) {
     47   b[off + 0] = (unsigned char)(hw & 0xffu);
     48   b[off + 1] = (unsigned char)((hw >> 8) & 0xffu);
     49 }
     50 
     51 /* Decode one instruction and assert the rendered mnemonic + an operand needle.
     52  * `nbytes_want` is 2 or 4. */
     53 static void expect_text(KitCompiler* pub, const unsigned char* bytes,
     54                         size_t len, u64 pc, u8 nbytes_want, const char* mnem,
     55                         const char* op_needle, const char* what) {
     56   Compiler* c = (Compiler*)pub;
     57   KitDecodedInsn insn;
     58   ArchInsnFormatter* fmt;
     59   KitInsn text;
     60   KitStatus st;
     61   memset(&insn, 0, sizeof insn);
     62   st = arch_decode_one(c, bytes, len, pc, &insn);
     63   EXPECT(st == KIT_OK, "%s: decode_one status %d", what, (int)st);
     64   EXPECT(insn.nbytes == nbytes_want, "%s: nbytes = %u (want %u)", what,
     65          (unsigned)insn.nbytes, (unsigned)nbytes_want);
     66   fmt = arch_insn_formatter_new(c);
     67   EXPECT(fmt != NULL, "%s: formatter_new NULL", what);
     68   if (!fmt) return;
     69   memset(&text, 0, sizeof text);
     70   st = arch_format_insn(fmt, &insn, &text);
     71   EXPECT(st == KIT_OK, "%s: format status %d", what, (int)st);
     72   EXPECT(kit_slice_eq_cstr(text.mnemonic, mnem), "%s: mnemonic = '%.*s' want '%s'",
     73          what, KIT_SLICE_ARG(text.mnemonic), mnem);
     74   if (op_needle && op_needle[0]) {
     75     EXPECT(text.operands.s && strstr(text.operands.s, op_needle),
     76            "%s: operands '%.*s' missing '%s'", what, KIT_SLICE_ARG(text.operands),
     77            op_needle);
     78   }
     79   arch_insn_formatter_free(fmt);
     80 }
     81 
     82 static void decode_movw_movt(KitCompiler* pub) {
     83   unsigned char b[4];
     84   put_t32(b, 0, arm_movw(ARM_R0, 0x1234));
     85   expect_text(pub, b, 4, 0x1000, 4, "movw", "r0", "movw imm");
     86   expect_text(pub, b, 4, 0x1000, 4, "movw", "4660", "movw value"); /* 0x1234 */
     87   put_t32(b, 0, arm_movt(ARM_R0, 0xabcd));
     88   expect_text(pub, b, 4, 0x1000, 4, "movt", "r0", "movt");
     89 
     90   /* Confirm the operand-decode path recovers the immediate. */
     91   {
     92     Compiler* c = (Compiler*)pub;
     93     KitDecodedInsn insn;
     94     put_t32(b, 0, arm_movw(ARM_R3, 0xbeef));
     95     memset(&insn, 0, sizeof insn);
     96     EXPECT(arch_decode_one(c, b, 4, 0, &insn) == KIT_OK, "movw decode");
     97     EXPECT(insn.noperands == 2, "movw noperands = %u", (unsigned)insn.noperands);
     98     EXPECT(insn.operands[0].kind == KIT_DECOP_REG && insn.operands[0].reg == 3,
     99            "movw rd");
    100     EXPECT(insn.operands[1].kind == KIT_DECOP_IMM &&
    101                insn.operands[1].imm == 0xbeef,
    102            "movw imm = 0x%llx", (unsigned long long)insn.operands[1].imm);
    103   }
    104 }
    105 
    106 static void decode_dp(KitCompiler* pub) {
    107   unsigned char b[4];
    108   put_t32(b, 0, arm_add_reg(ARM_R0, ARM_R1, ARM_R2));
    109   expect_text(pub, b, 4, 0, 4, "add.w", "r0, r1, r2", "add.w reg");
    110   put_t32(b, 0, arm_sub_reg(ARM_R3, ARM_R4, ARM_R5));
    111   expect_text(pub, b, 4, 0, 4, "sub.w", "r3, r4, r5", "sub.w reg");
    112   put_t32(b, 0, arm_orr_reg(ARM_R0, ARM_R1, ARM_R2));
    113   expect_text(pub, b, 4, 0, 4, "orr.w", "r0, r1, r2", "orr.w reg");
    114   {
    115     u32 out12;
    116     EXPECT(thumb_expand_imm_encode(0xff, &out12), "encode 0xff modimm");
    117     put_t32(b, 0, arm_dp_imm(8u, 0u, ARM_R0, ARM_R1, out12)); /* add.w r0,r1,#255 */
    118     expect_text(pub, b, 4, 0, 4, "add.w", "#255", "add.w imm");
    119   }
    120   put_t32(b, 0, arm_mov_reg(ARM_R0, ARM_R1));
    121   expect_text(pub, b, 4, 0, 4, "mov.w", "r0, r1", "mov.w reg");
    122   put_t32(b, 0, arm_cmp_reg(ARM_R2, ARM_R3));
    123   expect_text(pub, b, 4, 0, 4, "cmp.w", "r2, r3", "cmp.w reg");
    124 }
    125 
    126 /* The Thumb-2 shifted-MOV family (0xea4f....) is the ORR-based MOV-with-shift;
    127  * it disassembles (per llvm-objdump --triple=thumbv7em) to the shift mnemonic
    128  * (lsl/lsr/asr/ror, or rrx for ROR #0) — except a plain LSL #0, which stays
    129  * `mov.w`. The MVN-with-shift form (0xea6f....) keeps its `mvn.w` mnemonic and
    130  * appends a `, <sh> #n` suffix. No direct encoder, so raw words via put_t32. */
    131 static void decode_shifts(KitCompiler* pub) {
    132   unsigned char b[4];
    133   /* LSL #0 = plain mov.w (regression guard). */
    134   put_t32(b, 0, 0xea4f0001u);
    135   expect_text(pub, b, 4, 0, 4, "mov.w", "r0, r1", "mov-shift lsl#0 = mov.w");
    136   /* LSL #3 -> lsl.w. */
    137   put_t32(b, 0, 0xea4f00c1u);
    138   expect_text(pub, b, 4, 0, 4, "lsl.w", "r0, r1, #3", "mov-shift lsl#3");
    139   /* LSR #0 means #32. */
    140   put_t32(b, 0, 0xea4f0011u);
    141   expect_text(pub, b, 4, 0, 4, "lsr.w", "r0, r1, #32", "mov-shift lsr#0->32");
    142   /* ASR #0 means #32. */
    143   put_t32(b, 0, 0xea4f0021u);
    144   expect_text(pub, b, 4, 0, 4, "asr.w", "r0, r1, #32", "mov-shift asr#0->32");
    145   /* ROR #5. */
    146   put_t32(b, 0, 0xea4f1071u);
    147   expect_text(pub, b, 4, 0, 4, "ror.w", "r0, r1, #5", "mov-shift ror#5");
    148   /* ROR #0 -> rrx (two operands, no amount, no .w). */
    149   put_t32(b, 0, 0xea4f0031u);
    150   expect_text(pub, b, 4, 0, 4, "rrx", "r0, r1", "mov-shift ror#0 = rrx");
    151   /* MVN LSL #3 keeps mvn.w + appends the shift suffix. */
    152   put_t32(b, 0, 0xea6f00c1u);
    153   expect_text(pub, b, 4, 0, 4, "mvn.w", "r0, r1, lsl #3", "mvn-shift lsl#3");
    154   /* MVN LSL #0 = plain mvn.w (regression guard). */
    155   put_t32(b, 0, 0xea6f0001u);
    156   expect_text(pub, b, 4, 0, 4, "mvn.w", "r0, r1", "mvn lsl#0 = mvn.w");
    157 }
    158 
    159 static void decode_mul_div_ext(KitCompiler* pub) {
    160   unsigned char b[4];
    161   put_t32(b, 0, arm_mul(ARM_R0, ARM_R1, ARM_R2));
    162   expect_text(pub, b, 4, 0, 4, "mul", "r0, r1, r2", "mul");
    163   put_t32(b, 0, arm_sdiv(ARM_R0, ARM_R1, ARM_R2));
    164   expect_text(pub, b, 4, 0, 4, "sdiv", "r0, r1, r2", "sdiv");
    165   put_t32(b, 0, arm_udiv(ARM_R3, ARM_R4, ARM_R5));
    166   expect_text(pub, b, 4, 0, 4, "udiv", "r3, r4, r5", "udiv");
    167   put_t32(b, 0, arm_umull(ARM_R0, ARM_R1, ARM_R2, ARM_R3));
    168   expect_text(pub, b, 4, 0, 4, "umull", "r0, r1, r2, r3", "umull");
    169   put_t32(b, 0, arm_sxtb(ARM_R0, ARM_R1));
    170   expect_text(pub, b, 4, 0, 4, "sxtb", "r0, r1", "sxtb");
    171   put_t32(b, 0, arm_uxth(ARM_R2, ARM_R3));
    172   expect_text(pub, b, 4, 0, 4, "uxth", "r2, r3", "uxth");
    173 }
    174 
    175 static void decode_rev_clz_bitfield(KitCompiler* pub) {
    176   unsigned char b[4];
    177   /* kit emits the 16-bit REV (arm_rev returns a u16, 0xba__); the disassembler
    178    * decodes both the 16-bit (T1) and 32-bit (T2) forms via separate table rows. */
    179   put_t16(b, 0, arm_rev(ARM_R0, ARM_R1));
    180   expect_text(pub, b, 2, 0, 2, "rev", "r0, r1", "rev");
    181   put_t32(b, 0, arm_clz(ARM_R2, ARM_R3));
    182   expect_text(pub, b, 4, 0, 4, "clz", "r2, r3", "clz");
    183   put_t32(b, 0, arm_bfi(ARM_R0, ARM_R1, 4, 8));
    184   expect_text(pub, b, 4, 0, 4, "bfi", "r0, r1, #4, #8", "bfi");
    185   put_t32(b, 0, arm_ubfx(ARM_R0, ARM_R1, 4, 8));
    186   expect_text(pub, b, 4, 0, 4, "ubfx", "r0, r1, #4, #8", "ubfx");
    187   put_t32(b, 0, arm_sbfx(ARM_R2, ARM_R3, 0, 5));
    188   expect_text(pub, b, 4, 0, 4, "sbfx", "r2, r3, #0, #5", "sbfx");
    189 }
    190 
    191 static void decode_ldst(KitCompiler* pub) {
    192   unsigned char b[4];
    193   put_t32(b, 0, arm_ldr_imm(ARM_R0, ARM_R1, 4));
    194   expect_text(pub, b, 4, 0, 4, "ldr.w", "[r1, #4]", "ldr.w T3");
    195   put_t32(b, 0, arm_str_imm(ARM_R2, ARM_R3, 8));
    196   expect_text(pub, b, 4, 0, 4, "str.w", "[r3, #8]", "str.w T3");
    197   put_t32(b, 0, arm_ldrb_imm(ARM_R0, ARM_R1, 1));
    198   expect_text(pub, b, 4, 0, 4, "ldrb.w", "[r1, #1]", "ldrb.w T3");
    199   /* T4 negative offset. */
    200   put_t32(b, 0, arm_ldst_t4(0xf850u, ARM_R0, ARM_R1, 4, 0));
    201   expect_text(pub, b, 4, 0, 4, "ldr.w", "[r1, #-4]", "ldr.w T4 -imm8");
    202   /* Structured operand path on the T3 load. */
    203   {
    204     Compiler* c = (Compiler*)pub;
    205     KitDecodedInsn insn;
    206     put_t32(b, 0, arm_ldr_imm(ARM_R5, ARM_R6, 12));
    207     memset(&insn, 0, sizeof insn);
    208     EXPECT(arch_decode_one(c, b, 4, 0, &insn) == KIT_OK, "ldr decode");
    209     EXPECT((insn.flags & KIT_DECODE_MEMORY) != 0, "ldr is memory");
    210     EXPECT(insn.operands[1].kind == KIT_DECOP_MEM &&
    211                insn.operands[1].reg == 6 && insn.operands[1].imm == 12,
    212            "ldr mem operand");
    213   }
    214 }
    215 
    216 static void decode_pushpop(KitCompiler* pub) {
    217   unsigned char b[4];
    218   put_t32(b, 0, arm_push_w((1u << 4) | (1u << 14))); /* push {r4, lr} */
    219   expect_text(pub, b, 4, 0, 4, "push.w", "r4", "push.w");
    220   expect_text(pub, b, 4, 0, 4, "push.w", "lr", "push.w lr");
    221   put_t32(b, 0, arm_pop_w((1u << 4) | (1u << 15))); /* pop {r4, pc} */
    222   expect_text(pub, b, 4, 0, 4, "pop.w", "pc", "pop.w");
    223 }
    224 
    225 static void decode_branches(KitCompiler* pub) {
    226   unsigned char b[4];
    227   /* BL +8: the 24-bit split immediate {S,I1,I2,imm10,imm11} encodes the byte
    228    * offset (target - pc - 4). For +8 with S=0: I1=I2=1 (so J1=J2=1), imm11=2. */
    229   put_t32(b, 0, 0xf000f802u);
    230   expect_text(pub, b, 4, 0x1000, 4, "bl", "0x1008", "bl +8");
    231 
    232   /* B.W +8 (T4): hw2[15:14]=10, hw2[12]=1, J1=J2=1, imm11=2. */
    233   put_t32(b, 0, 0xf000b802u);
    234   expect_text(pub, b, 4, 0x1000, 4, "b.w", "0x1008", "b.w +8");
    235 
    236   /* B<cond>.W +8 (T3): cond eq in hw1[9:6]; hw2[15:14]=10, hw2[12]=0. */
    237   put_t32(b, 0, 0xf0008002u);
    238   expect_text(pub, b, 4, 0x1000, 4, "beq.w", "0x1008", "beq.w +8");
    239 
    240   /* 16-bit Bcc (T1): 0xDxyy, cond=NE (1), imm8=2 -> off = 4 + 4 = 8. */
    241   put_t16(b, 0, 0xd102u);
    242   expect_text(pub, b, 2, 0x1000, 2, "bne", "0x1008", "bne 16-bit");
    243 
    244   /* 16-bit B (T2): 0xE002 -> off = 4 + 4 = 8. */
    245   put_t16(b, 0, 0xe002u);
    246   expect_text(pub, b, 2, 0x1000, 2, "b", "0x1008", "b 16-bit");
    247 
    248   /* BX lr / BLX r3. */
    249   put_t16(b, 0, arm_bx(ARM_LR));
    250   expect_text(pub, b, 2, 0, 2, "bx", "lr", "bx lr");
    251   put_t16(b, 0, arm_blx_reg(ARM_R3));
    252   expect_text(pub, b, 2, 0, 2, "blx", "r3", "blx r3");
    253 }
    254 
    255 static void decode_cbz_tbb(KitCompiler* pub) {
    256   unsigned char b[4];
    257   /* CBZ r0, +8: imm6 = (8-4)/2 = 2. */
    258   put_t16(b, 0, arm_cbz(ARM_R0, 2));
    259   expect_text(pub, b, 2, 0x1000, 2, "cbz", "r0", "cbz r0");
    260   expect_text(pub, b, 2, 0x1000, 2, "cbz", "0x1008", "cbz target");
    261   put_t16(b, 0, arm_cbnz(ARM_R1, 2));
    262   expect_text(pub, b, 2, 0x1000, 2, "cbnz", "r1", "cbnz r1");
    263   /* TBB [r0, r1]. */
    264   put_t32(b, 0, arm_tbb(ARM_R0, ARM_R1));
    265   expect_text(pub, b, 4, 0, 4, "tbb", "[r0, r1]", "tbb");
    266   put_t32(b, 0, arm_tbh(ARM_R2, ARM_R3));
    267   expect_text(pub, b, 4, 0, 4, "tbh", "[r2, r3", "tbh");
    268 }
    269 
    270 static void decode_misc16(KitCompiler* pub) {
    271   unsigned char b[4];
    272   put_t16(b, 0, arm_nop16());
    273   expect_text(pub, b, 2, 0, 2, "nop", "", "nop");
    274   put_t16(b, 0, arm_bkpt(0xab));
    275   expect_text(pub, b, 2, 0, 2, "bkpt", "#171", "bkpt");
    276   put_t16(b, 0, arm_mov_hi(ARM_R7, ARM_SP)); /* mov r7, sp */
    277   expect_text(pub, b, 2, 0, 2, "mov", "r7, sp", "mov r7, sp");
    278 }
    279 
    280 static void decode_barriers(KitCompiler* pub) {
    281   unsigned char b[4];
    282   put_t32(b, 0, arm_dmb(0xf));
    283   expect_text(pub, b, 4, 0, 4, "dmb", "sy", "dmb sy");
    284   put_t32(b, 0, arm_dsb(0xf));
    285   expect_text(pub, b, 4, 0, 4, "dsb", "sy", "dsb sy");
    286   put_t32(b, 0, arm_isb(0xf));
    287   expect_text(pub, b, 4, 0, 4, "isb", "sy", "isb sy");
    288 }
    289 
    290 /* ARMv7E-M DSP saturating ops: SSAT/USAT (Rd, #sat, Rm) + the QADD family
    291  * (Rd, Rm, Rn — note Rn lands in hw1[3:0], Rm in hw2[3:0]). Golden words pinned
    292  * to llvm-mc (`--triple=thumbv7em-none-eabi --mattr=+dsp -show-encoding`). */
    293 static void decode_dsp_sat(KitCompiler* pub) {
    294   unsigned char b[4];
    295 
    296   /* Direct encoder -> word equality, pinned to the oracle bytes. */
    297   EXPECT(arm_ssat(ARM_R0, 16, ARM_R1) == 0xf301000fu, "ssat encode = %#x",
    298          arm_ssat(ARM_R0, 16, ARM_R1));
    299   /* llvm-mc bytes [0x06,0xf3,0x1f,0x05] -> hw1=0xf306, hw2=0x051f. (The task
    300    * spec quoted 0xf3061f05 with hw2's bytes transposed; the real word is
    301    * 0xf306051f, hw2 = (rd<<8)|(sat-1) = (5<<8)|31.) */
    302   EXPECT(arm_ssat(ARM_R5, 32, ARM_R6) == 0xf306051fu, "ssat32 encode = %#x",
    303          arm_ssat(ARM_R5, 32, ARM_R6));
    304   EXPECT(arm_usat(ARM_R0, 15, ARM_R1) == 0xf381000fu, "usat encode = %#x",
    305          arm_usat(ARM_R0, 15, ARM_R1));
    306   EXPECT(arm_qadd(ARM_R3, ARM_R1, ARM_R2) == 0xfa82f381u, "qadd encode = %#x",
    307          arm_qadd(ARM_R3, ARM_R1, ARM_R2));
    308   EXPECT(arm_qadd(ARM_R0, ARM_R2, ARM_R1) == 0xfa81f082u, "qadd2 encode = %#x",
    309          arm_qadd(ARM_R0, ARM_R2, ARM_R1));
    310   EXPECT(arm_qsub(ARM_R3, ARM_R1, ARM_R2) == 0xfa82f3a1u, "qsub encode = %#x",
    311          arm_qsub(ARM_R3, ARM_R1, ARM_R2));
    312   EXPECT(arm_qdadd(ARM_R3, ARM_R1, ARM_R2) == 0xfa82f391u, "qdadd encode = %#x",
    313          arm_qdadd(ARM_R3, ARM_R1, ARM_R2));
    314   EXPECT(arm_qdsub(ARM_R3, ARM_R1, ARM_R2) == 0xfa82f3b1u, "qdsub encode = %#x",
    315          arm_qdsub(ARM_R3, ARM_R1, ARM_R2));
    316 
    317   /* Disassembly round-trips. */
    318   put_t32(b, 0, arm_ssat(ARM_R0, 16, ARM_R1));
    319   expect_text(pub, b, 4, 0, 4, "ssat", "r0, #16, r1", "ssat");
    320   put_t32(b, 0, arm_usat(ARM_R0, 15, ARM_R1));
    321   expect_text(pub, b, 4, 0, 4, "usat", "r0, #15, r1", "usat");
    322   put_t32(b, 0, arm_qadd(ARM_R3, ARM_R1, ARM_R2));
    323   expect_text(pub, b, 4, 0, 4, "qadd", "r3, r1, r2", "qadd");
    324   put_t32(b, 0, arm_qsub(ARM_R3, ARM_R1, ARM_R2));
    325   expect_text(pub, b, 4, 0, 4, "qsub", "r3, r1, r2", "qsub");
    326   put_t32(b, 0, arm_qdadd(ARM_R3, ARM_R1, ARM_R2));
    327   expect_text(pub, b, 4, 0, 4, "qdadd", "r3, r1, r2", "qdadd");
    328   put_t32(b, 0, arm_qdsub(ARM_R3, ARM_R1, ARM_R2));
    329   expect_text(pub, b, 4, 0, 4, "qdsub", "r3, r1, r2", "qdsub");
    330 
    331   /* Structured-operand check: SSAT decodes to (reg, imm=16, reg). */
    332   {
    333     Compiler* c = (Compiler*)pub;
    334     KitDecodedInsn insn;
    335     put_t32(b, 0, arm_ssat(ARM_R0, 16, ARM_R1));
    336     memset(&insn, 0, sizeof insn);
    337     EXPECT(arch_decode_one(c, b, 4, 0, &insn) == KIT_OK, "ssat decode");
    338     EXPECT(insn.noperands == 3, "ssat noperands = %u",
    339            (unsigned)insn.noperands);
    340     EXPECT(insn.operands[0].kind == KIT_DECOP_REG && insn.operands[0].reg == 0,
    341            "ssat rd");
    342     EXPECT(insn.operands[1].kind == KIT_DECOP_IMM && insn.operands[1].imm == 16,
    343            "ssat sat = %lld", (long long)insn.operands[1].imm);
    344     EXPECT(insn.operands[2].kind == KIT_DECOP_REG && insn.operands[2].reg == 1,
    345            "ssat rm");
    346   }
    347 }
    348 
    349 static void decode_ldrex_strex(KitCompiler* pub) {
    350   unsigned char b[4];
    351   put_t32(b, 0, arm_ldrex(ARM_R0, ARM_R1, 0));
    352   expect_text(pub, b, 4, 0, 4, "ldrex", "[r1]", "ldrex");
    353   put_t32(b, 0, arm_strex(ARM_R0, ARM_R1, ARM_R2, 0));
    354   expect_text(pub, b, 4, 0, 4, "strex", "r0, r1, [r2]", "strex");
    355 }
    356 
    357 /* The IT block renders its firstcond suffix on the following instruction. */
    358 static void decode_it_block(KitCompiler* pub) {
    359   Compiler* c = (Compiler*)pub;
    360   unsigned char b[8];
    361   KitDecodedInsn insts[2];
    362   ArchInsnFormatter* fmt;
    363   KitInsn t0, t1;
    364   u32 n = 0;
    365   u32 out12;
    366 
    367   put_t16(b, 0, arm_it(ARM_CC_EQ, 0x8)); /* it eq */
    368   thumb_expand_imm_encode(1, &out12);
    369   put_t32(b, 2, arm_mov_imm(ARM_R0, out12)); /* mov.w r0, #1 -> moveq */
    370 
    371   memset(insts, 0, sizeof insts);
    372   EXPECT(arch_decode_block(c, b, sizeof b, 0x2000, insts, 2, &n) == KIT_OK,
    373          "it block decode_block");
    374   EXPECT(n == 2, "it block n = %u", (unsigned)n);
    375   EXPECT(insts[0].nbytes == 2, "it nbytes = %u", (unsigned)insts[0].nbytes);
    376   EXPECT(insts[0].opcode == ARM32_DEC_IT, "it opcode = %u",
    377          (unsigned)insts[0].opcode);
    378 
    379   /* Format both through one formatter so the IT-state threads across. */
    380   fmt = arch_insn_formatter_new(c);
    381   EXPECT(fmt != NULL, "it formatter");
    382   if (!fmt) return;
    383   memset(&t0, 0, sizeof t0);
    384   memset(&t1, 0, sizeof t1);
    385   EXPECT(arch_format_insn(fmt, &insts[0], &t0) == KIT_OK, "format it");
    386   EXPECT(kit_slice_eq_cstr(t0.mnemonic, "it"), "it mnemonic = '%.*s'",
    387          KIT_SLICE_ARG(t0.mnemonic));
    388   EXPECT(t0.operands.s && strstr(t0.operands.s, "eq"), "it operand 'eq': %.*s",
    389          KIT_SLICE_ARG(t0.operands));
    390   EXPECT(arch_format_insn(fmt, &insts[1], &t1) == KIT_OK, "format moveq");
    391   EXPECT(kit_slice_eq_cstr(t1.mnemonic, "moveq.w"),
    392          "conditional mnemonic = '%.*s' (want moveq.w)",
    393          KIT_SLICE_ARG(t1.mnemonic));
    394   arch_insn_formatter_free(fmt);
    395 }
    396 
    397 /* decode_block stops at a terminator (a B.W). */
    398 static void decode_block_terminates(KitCompiler* pub) {
    399   Compiler* c = (Compiler*)pub;
    400   unsigned char b[12];
    401   KitDecodedInsn insts[4];
    402   u32 n = 0;
    403   put_t32(b, 0, arm_add_reg(ARM_R0, ARM_R1, ARM_R2));
    404   put_t32(b, 4, 0xf000b802u); /* b.w +8 (terminator) */
    405   put_t32(b, 8, arm_add_reg(ARM_R3, ARM_R4, ARM_R5));
    406   memset(insts, 0, sizeof insts);
    407   EXPECT(arch_decode_block(c, b, sizeof b, 0, insts, 4, &n) == KIT_OK,
    408          "block decode");
    409   EXPECT(n == 2, "block stops at b.w: n = %u", (unsigned)n);
    410   EXPECT((insts[1].flags & KIT_DECODE_TERMINATOR) != 0, "b.w terminates");
    411 }
    412 
    413 /* The new 16-bit narrow encoders (density pass) round-trip against the existing
    414  * decoder. Each asserts the rendered mnemonic + an operand needle; a couple also
    415  * pin the raw 16-bit word so a bit-layout regression is caught at the encoder. */
    416 static void decode_narrow_16(KitCompiler* pub) {
    417   unsigned char b[4];
    418 
    419   /* PUSH {r4, r7, lr} = 0xb590 (list8=0x90, bit8=lr); POP {r7, pc} = 0xbd80. */
    420   EXPECT(arm_push16((1u << 4) | (1u << 7), 1u) == 0xb590u, "push16 bits = 0x%x",
    421          (unsigned)arm_push16((1u << 4) | (1u << 7), 1u));
    422   put_t16(b, 0, arm_push16((1u << 7), 1u)); /* push {r7, lr} */
    423   expect_text(pub, b, 2, 0, 2, "push", "r7", "push16 r7");
    424   expect_text(pub, b, 2, 0, 2, "push", "lr", "push16 lr");
    425   EXPECT(arm_pop16((1u << 7), 1u) == 0xbd80u, "pop16 bits = 0x%x",
    426          (unsigned)arm_pop16((1u << 7), 1u));
    427   put_t16(b, 0, arm_pop16((1u << 7), 1u)); /* pop {r7, pc} */
    428   expect_text(pub, b, 2, 0, 2, "pop", "pc", "pop16 pc");
    429 
    430   /* LDR/STR [rn, #imm5*scale]: ldr r0,[r1,#4] = 0x6848 (imm5=1, ×4). */
    431   EXPECT(arm_ldst_i5_16(0x6800u, ARM_R0, ARM_R1, 1u) == 0x6848u, "ldr i5 bits");
    432   put_t16(b, 0, arm_ldst_i5_16(0x6800u, ARM_R0, ARM_R1, 1u));
    433   expect_text(pub, b, 2, 0, 2, "ldr", "[r1, #4]", "ldr i5");
    434   put_t16(b, 0, arm_ldst_i5_16(0x6000u, ARM_R2, ARM_R3, 2u)); /* str r2,[r3,#8] */
    435   expect_text(pub, b, 2, 0, 2, "str", "[r3, #8]", "str i5");
    436   put_t16(b, 0, arm_ldst_i5_16(0x7800u, ARM_R0, ARM_R1, 1u)); /* ldrb r0,[r1,#1] */
    437   expect_text(pub, b, 2, 0, 2, "ldrb", "[r1, #1]", "ldrb i5");
    438   put_t16(b, 0, arm_ldst_i5_16(0x8800u, ARM_R0, ARM_R1, 1u)); /* ldrh r0,[r1,#2] */
    439   expect_text(pub, b, 2, 0, 2, "ldrh", "[r1, #2]", "ldrh i5");
    440 
    441   /* LDR/STR [sp, #imm8*4]: ldr r0,[sp,#4] = 0x9801. */
    442   EXPECT(arm_ldst_sp_16(0x9800u, ARM_R0, 1u) == 0x9801u, "ldr sp bits");
    443   put_t16(b, 0, arm_ldst_sp_16(0x9800u, ARM_R0, 1u));
    444   expect_text(pub, b, 2, 0, 2, "ldr", "[sp, #4]", "ldr sp");
    445   put_t16(b, 0, arm_ldst_sp_16(0x9000u, ARM_R2, 3u)); /* str r2,[sp,#12] */
    446   expect_text(pub, b, 2, 0, 2, "str", "[sp, #12]", "str sp");
    447 
    448   /* ADD rd, sp, #imm8*4: add r0, sp, #16 = 0xa804. */
    449   put_t16(b, 0, arm_add_sp_imm16(ARM_R0, 4u));
    450   expect_text(pub, b, 2, 0, 2, "add", "sp", "add rd,sp");
    451 
    452   /* ADD/SUB sp, sp, #imm7*4: sub sp,sp,#16 = 0xb084. */
    453   EXPECT(arm_sub_sp_sp_imm16(4u) == 0xb084u, "sub sp bits");
    454   put_t16(b, 0, arm_sub_sp_sp_imm16(4u));
    455   expect_text(pub, b, 2, 0, 2, "sub", "sp, sp", "sub sp,sp");
    456   put_t16(b, 0, arm_add_sp_sp_imm16(2u)); /* add sp,sp,#8 */
    457   expect_text(pub, b, 2, 0, 2, "add", "sp, sp", "add sp,sp");
    458 
    459   /* MOVS/ADDS/SUBS rdn, #imm8. */
    460   put_t16(b, 0, arm_movs_imm8(ARM_R0, 5u));
    461   expect_text(pub, b, 2, 0, 2, "movs", "#5", "movs imm8");
    462   put_t16(b, 0, arm_adds_imm8(ARM_R1, 7u));
    463   expect_text(pub, b, 2, 0, 2, "adds", "#7", "adds imm8");
    464   put_t16(b, 0, arm_subs_imm8(ARM_R2, 9u));
    465   expect_text(pub, b, 2, 0, 2, "subs", "#9", "subs imm8");
    466 
    467   /* ADDS/SUBS rd, rn, #imm3 and rd, rn, rm. */
    468   put_t16(b, 0, arm_adds_imm3(ARM_R0, ARM_R1, 3u));
    469   expect_text(pub, b, 2, 0, 2, "adds", "r0, r1, #3", "adds imm3");
    470   put_t16(b, 0, arm_subs_imm3(ARM_R0, ARM_R1, 2u));
    471   expect_text(pub, b, 2, 0, 2, "subs", "r0, r1, #2", "subs imm3");
    472   put_t16(b, 0, arm_adds_reg16(ARM_R0, ARM_R1, ARM_R2));
    473   expect_text(pub, b, 2, 0, 2, "adds", "r0, r1, r2", "adds reg16");
    474   put_t16(b, 0, arm_subs_reg16(ARM_R3, ARM_R4, ARM_R5));
    475   expect_text(pub, b, 2, 0, 2, "subs", "r3, r4, r5", "subs reg16");
    476 
    477   /* LSLS/LSRS/ASRS rd, rm, #imm5. */
    478   put_t16(b, 0, arm_lsls_imm16(ARM_R0, ARM_R1, 3u));
    479   expect_text(pub, b, 2, 0, 2, "lsls", "r0, r1, #3", "lsls imm5");
    480   put_t16(b, 0, arm_lsrs_imm16(ARM_R0, ARM_R1, 4u));
    481   expect_text(pub, b, 2, 0, 2, "lsrs", "r0, r1, #4", "lsrs imm5");
    482   put_t16(b, 0, arm_asrs_imm16(ARM_R2, ARM_R3, 5u));
    483   expect_text(pub, b, 2, 0, 2, "asrs", "r2, r3, #5", "asrs imm5");
    484 }
    485 
    486 /* The 32-bit shifted-register operand encoders round-trip. The shift TEXT (lsl
    487  * #k) rendering is owned by a concurrent disasm fix, so assert only the base
    488  * mnemonic + rd/rn/rm and pin the raw encoding fields (hw1/hw2 + decoded shift)
    489  * directly — those are this item's contract. */
    490 static void decode_shifted_reg(KitCompiler* pub) {
    491   Compiler* c = (Compiler*)pub;
    492   unsigned char b[4];
    493   KitDecodedInsn insn;
    494 
    495   /* add.w r0, r1, r2, lsl #3 -> hw1 0xeb01, hw2 imm3=0(0..2 of sh=3 -> imm2=3),
    496    * encoded: arm_add_reg_lsl(r0,r1,r2,3) = 0xeb01 00c2. */
    497   EXPECT(arm_add_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 3u) == 0xeb0100c2u,
    498          "add_reg_lsl bits = 0x%08x",
    499          (unsigned)arm_add_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 3u));
    500   put_t32(b, 0, arm_add_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 3u));
    501   expect_text(pub, b, 4, 0, 4, "add.w", "r0, r1, r2", "add.w shifted reg");
    502   EXPECT(arm_sub_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 2u) == 0xeba10082u,
    503          "sub_reg_lsl bits = 0x%08x",
    504          (unsigned)arm_sub_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 2u));
    505   put_t32(b, 0, arm_sub_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 2u));
    506   expect_text(pub, b, 4, 0, 4, "sub.w", "r0, r1, r2", "sub.w shifted reg");
    507   put_t32(b, 0, arm_and_reg_lsl(ARM_R3, ARM_R4, ARM_R5, 1u));
    508   expect_text(pub, b, 4, 0, 4, "and.w", "r3, r4, r5", "and.w shifted reg");
    509   put_t32(b, 0, arm_orr_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 4u));
    510   expect_text(pub, b, 4, 0, 4, "orr.w", "r0, r1, r2", "orr.w shifted reg");
    511   put_t32(b, 0, arm_eor_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 2u));
    512   expect_text(pub, b, 4, 0, 4, "eor.w", "r0, r1, r2", "eor.w shifted reg");
    513 
    514   /* The structured operand path recovers rd/rn/rm (the shift rider lives in the
    515    * encoding fields, pinned above; the textual `lsl #k` render is owned by the
    516    * concurrent disasm fix, so it is not asserted here). */
    517   put_t32(b, 0, arm_orr_reg_lsl(ARM_R0, ARM_R1, ARM_R2, 3u));
    518   memset(&insn, 0, sizeof insn);
    519   EXPECT(arch_decode_one(c, b, 4, 0, &insn) == KIT_OK, "shifted reg decode");
    520   EXPECT(insn.noperands == 3, "shifted reg noperands = %u",
    521          (unsigned)insn.noperands);
    522   EXPECT(insn.operands[2].kind == KIT_DECOP_REG && insn.operands[2].reg == 2,
    523          "shifted reg rm = r%u", (unsigned)insn.operands[2].reg);
    524 }
    525 
    526 /* Undecodable bytes fall back to .inst / .hword. */
    527 static void decode_unknown_fallback(KitCompiler* pub) {
    528   unsigned char b[4];
    529   /* A 32-bit-marked but undefined word (top5 = 11111, garbage body). */
    530   put_t32(b, 0, 0xfff0ffffu);
    531   expect_text(pub, b, 4, 0, 4, ".inst", "0x", ".inst fallback");
    532   /* A 16-bit undefined halfword (0xDExx is UDF). */
    533   put_t16(b, 0, 0xde01u);
    534   /* 0xDE.. matches B<cond> with cond=0xE (AL) which is actually UDF; our table
    535    * maps cond 0xE to "" so it renders as a bare "b" — acceptable. We instead
    536    * use a clearly-unmapped 16-bit value. */
    537   put_t16(b, 0, 0xb600u); /* CPS/SETEND space, not in the table */
    538   expect_text(pub, b, 2, 0, 2, ".hword", "0x", ".hword fallback");
    539 }
    540 
    541 int main(void) {
    542   KitCompiler* c;
    543   kit_unit_init(&g_u);
    544   c = new_compiler();
    545   decode_movw_movt(c);
    546   decode_dp(c);
    547   decode_shifts(c);
    548   decode_mul_div_ext(c);
    549   decode_rev_clz_bitfield(c);
    550   decode_ldst(c);
    551   decode_pushpop(c);
    552   decode_branches(c);
    553   decode_cbz_tbb(c);
    554   decode_misc16(c);
    555   decode_barriers(c);
    556   decode_ldrex_strex(c);
    557   decode_dsp_sat(c);
    558   decode_narrow_16(c);
    559   decode_shifted_reg(c);
    560   decode_it_block(c);
    561   decode_block_terminates(c);
    562   decode_unknown_fallback(c);
    563   kit_compiler_free(c);
    564   kit_unit_summary(&g_u, "arm32_decode_test");
    565   return kit_unit_status(&g_u);
    566 }