kit

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

commit 81b4f3f49ae6eddfb4e460f85a323ea47715141f
parent 5bf772ce3ffc905db1ca33fd61b7fa24fea4678b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 29 May 2026 13:48:33 -0700

aa64 disasm: decode FP, bitfield, DP1, and register-offset ldst

The aa64 backend emits scalar FP data-processing, bitfield (sbfm/ubfm),
1-source DP (clz/rbit/rev/rev16), and register-offset loads/stores, but
none had rows in aa64_insn_table, so the disassembler rendered them all as
'.inst 0xXXXX'. Any aa64 function touching float/double, an int<->long cast
or shift, a clz/ctz builtin, or array indexing showed opaque .inst lines in
objdump and the JIT debugger — on all three aa64 OSes.

Add the formats + descriptor rows + operand printers (single source of bit
knowledge, mirroring native.c's encoders):
  - FP_DP2  fmul/fdiv/fadd/fsub/fmax/fmin/fnmul (s/d/h via ftype)
  - FP_DP1  fmov/fabs/fneg/fsqrt
  - FP_CMP  fcmp
  - FP_CVT  fcvt (precision change; src ftype, dst opc)
  - FP_INT_CVT  scvtf/ucvtf/fcvtzs/fcvtzu + fmov gpr<->fp (direction from opcode)
  - DP1  rbit/rev16/rev(32&64)/clz
  - BITFIELD  sbfm/ubfm (raw form; alias display is a separate nicety)
  - LDST_REGOFF  ldr/str/ldrb/ldrh/strb/strh [Xn, Xm{, LSL #s}]

Verified byte-for-byte against llvm-objdump across a float/cast/shift/index C
program (0 .inst/.byte) and a hand-assembled corpus of every new family; the
only display delta is cfree's existing raw-form/decimal-immediate convention
(ubfm vs ubfx, #16 vs #0x10). Adds an aa64 decode corpus case to the default
suite. test-isa + test-asm green.

Diffstat:
Msrc/arch/aa64/isa.c | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/arch/aa64/isa.h | 9+++++++++
Atest/asm/decode/aa64_fp_bitfield_dp1.expected.txt | 21+++++++++++++++++++++
Atest/asm/decode/aa64_fp_bitfield_dp1.hex | 2++
Atest/asm/decode/aa64_fp_bitfield_dp1.targets | 2++
5 files changed, 232 insertions(+), 0 deletions(-)

diff --git a/src/arch/aa64/isa.c b/src/arch/aa64/isa.c @@ -378,6 +378,63 @@ const AA64InsnDesc aa64_insn_table[] = { {MN("dsb"), 0xD503309Fu, 0xFFFFF0FFu, AA64_FMT_BARRIER, 0, {0, 0}}, {MN("isb"), 0xD50330DFu, 0xFFFFF0FFu, AA64_FMT_BARRIER, 0, {0, 0}}, {MN("clrex"), 0xD503305Fu, 0xFFFFF0FFu, AA64_FMT_BARRIER, 0, {0, 0}}, + + /* ----- Data-processing (1 source): RBIT / REV16 / REV / CLZ ----- + * sf (bit31) free; opcode2 in bits[15:10] selects the operation. REV has + * a 32-bit (opcode2=000010) and 64-bit (000011) encoding, both "rev". */ + {MN("rbit"), 0x5AC00000u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, + {MN("rev16"), 0x5AC00400u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, + {MN("rev"), 0x5AC00800u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, + {MN("rev"), 0x5AC00C00u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, + {MN("clz"), 0x5AC01000u, 0x7FFFFC00u, AA64_FMT_DP1, 0, {0, 0}}, + + /* ----- Bitfield move (SBFM / UBFM) ----- + * sf/N free (read for W/X); opc (bits30:29) selects sbfm vs ubfm. Aliases + * (lsl/lsr/asr/ubfx/sxtb/...) decode to the raw sbfm/ubfm form. */ + {MN("sbfm"), 0x13000000u, 0x7F800000u, AA64_FMT_BITFIELD, 0, {0, 0}}, + {MN("ubfm"), 0x53000000u, 0x7F800000u, AA64_FMT_BITFIELD, 0, {0, 0}}, + + /* ----- Load/store, register offset [Xn, Xm{, LSL #s}] (V=0) ----- + * Family bits[29:24]=111000, bit21=1, bits[11:10]=10; per-size opc rows. */ + {MN("strb"), 0x38200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldrb"), 0x38600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("strh"), 0x78200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldrh"), 0x78600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("str"), 0xB8200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldr"), 0xB8600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("str"), 0xF8200800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + {MN("ldr"), 0xF8600800u, 0xFFE00C00u, AA64_FMT_LDST_REGOFF, 0, {0, 0}}, + + /* ----- FP data-processing (2 source): FMUL / FDIV / FADD / FSUB ----- + * ftype (bits23:22) free, read for the s/d/h register prefix. */ + {MN("fmul"), 0x1E200800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, + {MN("fdiv"), 0x1E201800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, + {MN("fadd"), 0x1E202800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, + {MN("fsub"), 0x1E203800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, + {MN("fmax"), 0x1E204800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, + {MN("fmin"), 0x1E205800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, + {MN("fnmul"), 0x1E208800u, 0xFF20FC00u, AA64_FMT_FP_DP2, 0, {0, 0}}, + + /* ----- FP compare (FCMP, register form) ----- */ + {MN("fcmp"), 0x1E202000u, 0xFF20FC1Fu, AA64_FMT_FP_CMP, 0, {0, 0}}, + + /* ----- FP precision convert (FCVT single<->double<->half) ----- */ + {MN("fcvt"), 0x1E224000u, 0xFF3E7C00u, AA64_FMT_FP_CVT, 0, {0, 0}}, + + /* ----- FP data-processing (1 source): FMOV / FABS / FNEG / FSQRT ----- */ + {MN("fmov"), 0x1E204000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, + {MN("fabs"), 0x1E20C000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, + {MN("fneg"), 0x1E214000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, + {MN("fsqrt"), 0x1E21C000u, 0xFF3FFC00u, AA64_FMT_FP_DP1, 0, {0, 0}}, + + /* ----- FP<->int convert + FMOV gpr<->fp ----- + * sf (bit31) and ftype free; opcode (bits20:16) selects op + direction. */ + {MN("scvtf"), 0x1E220000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, + {MN("ucvtf"), 0x1E230000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, + {MN("fcvtzs"), 0x1E380000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, + {MN("fcvtzu"), 0x1E390000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, + {MN("fmov"), 0x1E260000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, + {MN("fmov"), 0x1E270000u, 0x7F3FFC00u, AA64_FMT_FP_INT_CVT, 0, {0, 0}}, }; #undef MN @@ -820,6 +877,123 @@ static void print_barrier(StrBuf* sb, u32 w, const AA64InsnDesc* desc) { } } +/* FP scalar size from the 2-bit ftype field: 00=single, 01=double, 11=half. */ +static char aa64_ftype_prefix(u32 ftype) { + return ftype == 0u ? 's' : (ftype == 1u ? 'd' : 'h'); +} + +/* FADD/FSUB/FMUL/FDIV: Vd, Vn, Vm (all the same ftype). */ +static void print_fp_dp2(StrBuf* sb, u32 w) { + char p = aa64_ftype_prefix((w >> 22) & 3u); + emit_vreg(sb, w & 0x1fu, p); + strbuf_puts(sb, ", "); + emit_vreg(sb, (w >> 5) & 0x1fu, p); + strbuf_puts(sb, ", "); + emit_vreg(sb, (w >> 16) & 0x1fu, p); +} + +/* FMOV(reg)/FNEG/FABS/FSQRT: Vd, Vn (same ftype). */ +static void print_fp_dp1(StrBuf* sb, u32 w) { + char p = aa64_ftype_prefix((w >> 22) & 3u); + emit_vreg(sb, w & 0x1fu, p); + strbuf_puts(sb, ", "); + emit_vreg(sb, (w >> 5) & 0x1fu, p); +} + +/* FCMP: Vn, Vm. */ +static void print_fp_cmp(StrBuf* sb, u32 w) { + char p = aa64_ftype_prefix((w >> 22) & 3u); + emit_vreg(sb, (w >> 5) & 0x1fu, p); + strbuf_puts(sb, ", "); + emit_vreg(sb, (w >> 16) & 0x1fu, p); +} + +/* FCVT precision change: Vd has the destination type (opc, bits 16:15), Vn the + * source type (ftype, bits 23:22). */ +static void print_fp_cvt(StrBuf* sb, u32 w) { + char src = aa64_ftype_prefix((w >> 22) & 3u); + char dst = aa64_ftype_prefix((w >> 15) & 3u); + emit_vreg(sb, w & 0x1fu, dst); + strbuf_puts(sb, ", "); + emit_vreg(sb, (w >> 5) & 0x1fu, src); +} + +/* SCVTF/UCVTF/FCVTZS/FCVTZU and FMOV gpr<->fp. The opcode (bits 20:16) + * selects the direction: fcvtzs/fcvtzu and fmov-to-gpr produce a GPR dst with + * an FP src; scvtf/ucvtf and fmov-to-fp produce an FP dst with a GPR src. + * sf (bit 31) is the GPR width, ftype (bits 23:22) the FP size. */ +static void print_fp_int_cvt(StrBuf* sb, u32 w) { + u32 opcode = (w >> 16) & 0x1fu; + int sf = (int)((w >> 31) & 1u); + char fp = aa64_ftype_prefix((w >> 22) & 3u); + u32 rd = w & 0x1fu, rn = (w >> 5) & 0x1fu; + int gpr_dst = (opcode == 0x18u /*fcvtzs*/ || opcode == 0x19u /*fcvtzu*/ || + opcode == 0x06u /*fmov fp->gpr*/); + if (gpr_dst) { + emit_reg(sb, rd, sf, 0); + strbuf_puts(sb, ", "); + emit_vreg(sb, rn, fp); + } else { + emit_vreg(sb, rd, fp); + strbuf_puts(sb, ", "); + emit_reg(sb, rn, sf, 0); + } +} + +/* RBIT/REV16/REV32/REV/CLZ: Rd, Rn (sf = bit 31). */ +static void print_dp1(StrBuf* sb, u32 w) { + int sf = (int)((w >> 31) & 1u); + emit_reg(sb, w & 0x1fu, sf, 0); + strbuf_puts(sb, ", "); + emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); +} + +/* SBFM/UBFM: Rd, Rn, #immr, #imms. */ +static void print_bitfield(StrBuf* sb, u32 w) { + int sf = (int)((w >> 31) & 1u); + emit_reg(sb, w & 0x1fu, sf, 0); + strbuf_puts(sb, ", "); + emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); + strbuf_puts(sb, ", #"); + strbuf_put_u64(sb, (u64)((w >> 16) & 0x3fu)); + strbuf_puts(sb, ", #"); + strbuf_put_u64(sb, (u64)((w >> 10) & 0x3fu)); +} + +/* Register-offset load/store: Rt, [Xn, Xm{, LSL #s}]. Rt width follows the + * size field (X for 64-bit accesses, W otherwise); the index extend is LSL + * for option=011 (UXTX), with shift amount S ? size : 0. */ +static void print_ldst_regoff(StrBuf* sb, u32 w) { + u32 size = (w >> 30) & 3u; + u32 option = (w >> 13) & 7u; + u32 s_bit = (w >> 12) & 1u; + int xt = (size == 3u); + emit_reg(sb, w & 0x1fu, xt, 0); + strbuf_puts(sb, ", ["); + emit_reg(sb, (w >> 5) & 0x1fu, /*sf=*/1, /*sp_means_sp=*/1); + strbuf_puts(sb, ", "); + /* UXTW/SXTW use a W index; UXTX/SXTX (LSL) use an X index. */ + emit_reg(sb, (w >> 16) & 0x1fu, (option & 1u) ? 1 : 0, 0); + if (option != 3u) { + /* Register-offset extends: only UXTW(010)/SXTW(110)/SXTX(111) are valid + * besides LSL/UXTX(011). */ + static const char* ext[8] = {0, 0, "uxtw", 0, 0, 0, "sxtw", "sxtx"}; + const char* e = ext[option]; + if (e) { + strbuf_puts(sb, ", "); + strbuf_puts(sb, e); + if (s_bit) { + strbuf_puts(sb, " #"); + strbuf_put_u64(sb, (u64)size); + } + } + } else if (s_bit) { + strbuf_puts(sb, ", lsl #"); + strbuf_put_u64(sb, (u64)size); + } + strbuf_putc(sb, ']'); +} + void aa64_print_operands(StrBuf* sb, const AA64InsnDesc* desc, u32 word, u64 vaddr) { switch ((AA64Format)desc->fmt) { @@ -882,6 +1056,30 @@ void aa64_print_operands(StrBuf* sb, const AA64InsnDesc* desc, u32 word, case AA64_FMT_BARRIER: print_barrier(sb, word, desc); break; + case AA64_FMT_DP1: + print_dp1(sb, word); + break; + case AA64_FMT_BITFIELD: + print_bitfield(sb, word); + break; + case AA64_FMT_LDST_REGOFF: + print_ldst_regoff(sb, word); + break; + case AA64_FMT_FP_DP2: + print_fp_dp2(sb, word); + break; + case AA64_FMT_FP_DP1: + print_fp_dp1(sb, word); + break; + case AA64_FMT_FP_CMP: + print_fp_cmp(sb, word); + break; + case AA64_FMT_FP_CVT: + print_fp_cvt(sb, word); + break; + case AA64_FMT_FP_INT_CVT: + print_fp_int_cvt(sb, word); + break; } } diff --git a/src/arch/aa64/isa.h b/src/arch/aa64/isa.h @@ -57,6 +57,15 @@ typedef enum AA64Format { AA64_FMT_EXCEPT, /* exception generation (BRK / SVC / HVC / ...) */ AA64_FMT_HINT, /* hint (NOP / YIELD / ...) */ AA64_FMT_BARRIER, /* memory barrier (DMB / DSB / ISB / CLREX) */ + AA64_FMT_DP1, /* data-processing, 1 source (RBIT/REV/REV16/CLZ) */ + AA64_FMT_BITFIELD, /* bitfield move (SBFM / UBFM): Rd, Rn, #immr, #imms */ + AA64_FMT_LDST_REGOFF, /* load/store, register offset [Xn, Xm{, LSL #s}] */ + AA64_FMT_FP_DP2, /* FP data-processing 2-source (FADD/FSUB/FMUL/FDIV) */ + AA64_FMT_FP_DP1, /* FP data-processing 1-source (FMOV/FNEG/FABS/FSQRT) */ + AA64_FMT_FP_CMP, /* FP compare (FCMP) */ + AA64_FMT_FP_CVT, /* FP precision convert (FCVT single<->double) */ + AA64_FMT_FP_INT_CVT, /* FP<->int convert + FMOV gpr<->fp + * (SCVTF/UCVTF/FCVTZS/FCVTZU/FMOV) */ } AA64Format; /* ---- AsmFlags column on AA64InsnDesc ---- diff --git a/test/asm/decode/aa64_fp_bitfield_dp1.expected.txt b/test/asm/decode/aa64_fp_bitfield_dp1.expected.txt @@ -0,0 +1,21 @@ +0: clz x0, x1 +4: clz w0, w1 +8: rbit x2, x3 +c: rev x4, x5 +10: rev w6, w7 +14: rev16 x8, x9 +18: fmov d0, d1 +1c: fmov s2, s3 +20: fabs d4, d5 +24: fsqrt s6, s7 +28: fcmp d0, d1 +2c: fcmp s2, s3 +30: fmov x0, d8 +34: fmov d9, x10 +38: fmov w11, s12 +3c: fmov s13, w14 +40: scvtf s0, w1 +44: ucvtf d2, x3 +48: fcvtzu w4, d5 +4c: ubfm x0, x1, #4, #20 +50: ret diff --git a/test/asm/decode/aa64_fp_bitfield_dp1.hex b/test/asm/decode/aa64_fp_bitfield_dp1.hex @@ -0,0 +1 @@ +2010c0da2010c05a6200c0daa40cc0dae608c05a2805c0da2040601e6240201ea4c0601ee6c0211e0020611e4020231e0001669e4901679e8b01261ecd01271e2000221e6200639ea400791e205044d3c0035fd6 +\ No newline at end of file diff --git a/test/asm/decode/aa64_fp_bitfield_dp1.targets b/test/asm/decode/aa64_fp_bitfield_dp1.targets @@ -0,0 +1 @@ +aa64 +\ No newline at end of file