commit c7fed8abc537edbc37702a44e41072127053dd46
parent 8198e7ccb0d435aa17347041a18771fd3c9f7359
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Fri, 29 May 2026 15:15:33 -0700
aa64 disasm: decode signed sub-word loads (ldrsb/ldrsh/ldrsw)
Adds the 5 unsigned-imm12 decode rows (opc=10 -> X dst, opc=11 -> W dst) and
makes print_ldst_uimm derive the destination width from opc. These previously
rendered as .inst. Corpus: decode/aa64_ldrs_subword.
Diffstat:
4 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/src/arch/aa64/isa.c b/src/arch/aa64/isa.c
@@ -25,6 +25,14 @@
/* Mnemonic Slice literal for a static table row (compile-time length). */
#define MN(s) {{(s)}, sizeof(s) - 1}
+/* Load/store unsigned-imm12 opc field values for the V=0 signed sub-word
+ * loads. opc=00/01 are STR/LDR (AA64_LDST_OPC_STR/LDR in isa.h); the signed
+ * loads reuse the same size/imm12 layout but sign-extend the loaded value:
+ * opc=10 (LDRS_X) sign-extends to the 64-bit X destination,
+ * opc=11 (LDRS_W) sign-extends to the 32-bit W destination. */
+#define AA64_LDST_OPC_LDRS_X 2u
+#define AA64_LDST_OPC_LDRS_W 3u
+
const AA64InsnDesc aa64_insn_table[] = {
/* ----- Move-wide immediate (MOVN / MOVZ / MOVK) ----- */
{MN("movn"), 0x12800000u, 0x7F800000u, AA64_FMT_MOVEWIDE, 0, {0, 0}},
@@ -176,7 +184,21 @@ const AA64InsnDesc aa64_insn_table[] = {
{MN("subs"), 0x71000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}},
/* ----- Load/store, unsigned 12-bit immediate (scaled) -----
- * Mask: family bits 29:27 + 25:24 + size(31:30) + V(26) + opc(23:22). */
+ * Mask: family bits 29:27 + 25:24 + size(31:30) + V(26) + opc(23:22).
+ *
+ * Signed sub-word loads (LDRSB/LDRSH/LDRSW) share this exact format but
+ * select opc=10 (sign-extend to X) or opc=11 (sign-extend to W). They
+ * are listed BEFORE the unsigned STR/LDR rows so first-match-wins picks
+ * the signed spelling (the masks are disjoint by opc, so the relative
+ * order is not load-bearing, but keeping them first documents intent).
+ * size=00 opc=10 LDRSB Xt ; size=00 opc=11 LDRSB Wt
+ * size=01 opc=10 LDRSH Xt ; size=01 opc=11 LDRSH Wt
+ * size=10 opc=10 LDRSW Xt (no Wt form: that opc=11 slot is PRFUM). */
+ {MN("ldrsb"), 0x39800000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
+ {MN("ldrsb"), 0x39C00000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
+ {MN("ldrsh"), 0x79800000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
+ {MN("ldrsh"), 0x79C00000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
+ {MN("ldrsw"), 0xB9800000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
{MN("strb"), 0x39000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
{MN("ldrb"), 0x39400000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
{MN("strh"), 0x79000000u, 0xFFC00000u, AA64_FMT_LDST_UIMM, 0, {0, 0}},
@@ -660,9 +682,16 @@ static u32 ldst_log2_size(const AA64InsnDesc* d, u32 size_field) {
static void print_ldst_uimm(StrBuf* sb, u32 w, const AA64InsnDesc* d) {
AA64LdStUimm f = aa64_ldst_uimm_unpack(w);
u32 sz = ldst_log2_size(d, f.size);
- /* Pick reg prefix: V=0 picks W/X by size; V=1 picks B/H/S/D by size. */
+ /* Pick reg prefix: V=0 picks W/X; V=1 picks B/H/S/D by size.
+ * For V=0 the destination width follows opc: the signed sub-word loads
+ * (opc=10 LDRS_X, opc=11 LDRS_W) name an X or W register independent of
+ * the access size; the plain STR/LDR (opc<=01) use X only for size=11. */
if (f.V == 0) {
- emit_reg(sb, f.Rt, /*sf=*/(int)(sz == 3u), 0);
+ int sf = (f.opc == AA64_LDST_OPC_LDRS_X) ? 1
+ : (f.opc == AA64_LDST_OPC_LDRS_W)
+ ? 0
+ : (int)(sz == 3u);
+ emit_reg(sb, f.Rt, sf, 0);
} else {
char p = (sz == 0u) ? 'b' : (sz == 1u) ? 'h' : (sz == 2u) ? 's' : 'd';
emit_vreg(sb, f.Rt, p);
diff --git a/test/asm/decode/aa64_ldrs_subword.expected.txt b/test/asm/decode/aa64_ldrs_subword.expected.txt
@@ -0,0 +1,5 @@
+0: ldrsb x10, [x11]
+4: ldrsb w12, [x13, #3]
+8: ldrsh x14, [x15, #8]
+c: ldrsw x16, [x17, #4]
+10: ldrsh w0, [x1, #2]
diff --git a/test/asm/decode/aa64_ldrs_subword.hex b/test/asm/decode/aa64_ldrs_subword.hex
@@ -0,0 +1 @@
+6a 01 80 39 ac 0d c0 39 ee 11 80 79 30 06 80 b9 20 04 c0 79
diff --git a/test/asm/decode/aa64_ldrs_subword.targets b/test/asm/decode/aa64_ldrs_subword.targets
@@ -0,0 +1 @@
+aa64