kit

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

commit a8052633f6775e1cba26a4b7a6db19f237e98c22
parent f7fbbc9db78d92ef2796ccc4def521332e77e50e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu,  4 Jun 2026 10:45:21 -0700

aa64: support bitfield extension aliases

Diffstat:
Msrc/arch/aa64/asm.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/arch/aa64/disasm.c | 8++++++--
Msrc/arch/aa64/isa.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/arch/aa64/isa.h | 8++++++++
Atest/asm/decode/aa64_bitfield_aliases.expected.txt | 15+++++++++++++++
Atest/asm/decode/aa64_bitfield_aliases.hex | 1+
Atest/asm/decode/aa64_bitfield_aliases.targets | 1+
Mtest/asm/decode/aa64_fp_bitfield_dp1.expected.txt | 2+-
Mtest/asm/encode/aa64_bitfield_dp1.expected.hex | 2+-
Mtest/asm/encode/aa64_bitfield_dp1.s | 14++++++++++++++
10 files changed, 189 insertions(+), 12 deletions(-)

diff --git a/src/arch/aa64/asm.c b/src/arch/aa64/asm.c @@ -1134,8 +1134,7 @@ static void p_log_sr(AsmDriver* d, u32 opc, u32 N) { * ANDS uses ZR. Rn is always a GPR (caller's parse_reg already enforced * GP for the two register operands). */ if (N) asm_driver_panic(d, "asm: logical: immediate form has no negation"); - if (rd.is64 != rn.is64) - asm_driver_panic(d, "asm: logical: width mismatch"); + if (rd.is64 != rn.is64) asm_driver_panic(d, "asm: logical: width mismatch"); u64 imm = (u64)parse_imm_const(d); u32 bN = 0, immr = 0, imms = 0; if (!aa64_logimm_encode(imm, rd.is64, &bN, &immr, &imms)) @@ -2089,6 +2088,74 @@ static void p_sbfm(AsmDriver* d) { p_bitfield(d, 0u); } static void p_bfm(AsmDriver* d) { p_bitfield(d, 1u); } static void p_ubfm(AsmDriver* d) { p_bitfield(d, 2u); } +static void p_bfx(AsmDriver* d, u32 opc, const char* what) { + AA64Reg rd = parse_reg(d); + AA64Reg rn; + i64 lsb, width; + u32 reg_width; + expect_comma(d, what); + rn = parse_reg(d); + reject_sp_reg(d, rd, what); + reject_sp_reg(d, rn, what); + if (rd.is64 != rn.is64) + asm_driver_panic(d, "asm: %.*s: width mismatch", + SLICE_ARG(slice_from_cstr(what))); + expect_comma(d, what); + lsb = parse_imm_const(d); + expect_comma(d, what); + width = parse_imm_const(d); + reg_width = rd.is64 ? 64u : 32u; + if (lsb < 0 || width <= 0 || (u64)lsb >= reg_width || + (u64)width > (u64)reg_width - (u64)lsb) { + asm_driver_panic(d, "asm: %.*s: bit range out of bounds", + SLICE_ARG(slice_from_cstr(what))); + } + emit32(d, aa64_bitfield(rd.is64, opc, (u32)lsb, (u32)(lsb + width - 1), + rd.num, rn.num)); +} + +static void p_sbfx(AsmDriver* d) { p_bfx(d, 0u, "sbfx"); } +static void p_ubfx(AsmDriver* d) { p_bfx(d, 2u, "ubfx"); } + +static void p_sxt(AsmDriver* d, u32 bits, const char* what) { + AA64Reg rd = parse_reg(d); + AA64Reg rn; + expect_comma(d, what); + rn = parse_reg(d); + reject_sp_reg(d, rd, what); + reject_sp_reg(d, rn, what); + if (rn.is64) + asm_driver_panic(d, "asm: %.*s: source must be a W register", + SLICE_ARG(slice_from_cstr(what))); + if (bits == 32u && !rd.is64) + asm_driver_panic(d, "asm: sxtw: destination must be an X register"); + emit32(d, aa64_bitfield(rd.is64, 0u, 0u, bits - 1u, rd.num, rn.num)); +} + +static void p_uxt(AsmDriver* d, u32 bits, const char* what) { + AA64Reg rd = parse_reg(d); + AA64Reg rn; + u32 sf; + expect_comma(d, what); + rn = parse_reg(d); + reject_sp_reg(d, rd, what); + reject_sp_reg(d, rn, what); + if (rn.is64) + asm_driver_panic(d, "asm: %.*s: source must be a W register", + SLICE_ARG(slice_from_cstr(what))); + if (bits == 32u && !rd.is64) + asm_driver_panic(d, "asm: uxtw: destination must be an X register"); + sf = bits == 32u ? 1u : 0u; + emit32(d, aa64_bitfield(sf, 2u, 0u, bits - 1u, rd.num, rn.num)); +} + +static void p_sxtb(AsmDriver* d) { p_sxt(d, 8u, "sxtb"); } +static void p_sxth(AsmDriver* d) { p_sxt(d, 16u, "sxth"); } +static void p_sxtw(AsmDriver* d) { p_sxt(d, 32u, "sxtw"); } +static void p_uxtb(AsmDriver* d) { p_uxt(d, 8u, "uxtb"); } +static void p_uxth(AsmDriver* d) { p_uxt(d, 16u, "uxth"); } +static void p_uxtw(AsmDriver* d) { p_uxt(d, 32u, "uxtw"); } + /* fmov: Vd,Vn (FP reg move) | Rd,Vn (fp->gpr) | Vd,Rn (gpr->fp). */ static void p_fmov(AsmDriver* d) { FpOrGpr a = parse_fp_or_gpr(d); @@ -2327,6 +2394,14 @@ static const AA64Mn kTable[] = { {"sbfm", p_sbfm, 0}, {"ubfm", p_ubfm, 0}, {"bfm", p_bfm, 0}, + {"sbfx", p_sbfx, 0}, + {"ubfx", p_ubfx, 0}, + {"sxtb", p_sxtb, 0}, + {"sxth", p_sxth, 0}, + {"sxtw", p_sxtw, 0}, + {"uxtb", p_uxtb, 0}, + {"uxth", p_uxth, 0}, + {"uxtw", p_uxtw, 0}, {"nop", p_nop, 0}, {"dmb", p_dmb, 0}, {"dsb", p_dsb, 0}, diff --git a/src/arch/aa64/disasm.c b/src/arch/aa64/disasm.c @@ -52,10 +52,14 @@ static void aa64_write_mnemonic(AA64Disasm* d, const AA64InsnDesc* desc, return; } if (desc->fmt == AA64_FMT_BITFIELD) { - /* SBFM/UBFM disassemble to their lsl/lsr/asr shift aliases when the + /* SBFM/UBFM disassemble to their preferred aliases when the * fields match; aa64_print_operands renders the matching operands. */ - u32 shift; + u32 shift, lsb, width; const char* alias = aa64_bitfield_shift_alias(word, &shift); + if (!alias) alias = aa64_bitfield_extend_alias(word); + if (!alias) alias = aa64_bitfield_extract_alias(word, &lsb, &width); + (void)lsb; + (void)width; if (alias) { strbuf_puts(&d->mnem, alias); return; diff --git a/src/arch/aa64/isa.c b/src/arch/aa64/isa.c @@ -543,7 +543,7 @@ const AA64InsnDesc aa64_insn_table[] = { /* ----- 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. */ + * (lsl/lsr/asr/ubfx/sxtb/...) are selected by the bitfield printer. */ {MN("sbfm"), 0x13000000u, 0x7F800000u, AA64_FMT_BITFIELD, 0, {0, 0}}, {MN("ubfm"), 0x53000000u, 0x7F800000u, AA64_FMT_BITFIELD, 0, {0, 0}}, @@ -1183,16 +1183,24 @@ static void print_dp1(StrBuf* sb, u32 w) { } /* SBFM/UBFM: Rd, Rn, #immr, #imms. */ +static int bitfield_width(u32 word, u32* width) { + u32 sf = (word >> 31) & 1u; + u32 N = (word >> 22) & 1u; + if (N != sf) return 0; + *width = sf ? 64u : 32u; + return 1; +} + /* SBFM/UBFM shift aliases. UBFM is LSR when imms is the top bit index and LSL * when immr == imms+1 (the encoder's `lsl #s` form); SBFM is ASR when imms is - * the top bit index. The other bitfield aliases (UBFX/SBFX/UXTB/SBFIZ/...) are - * left as the raw mnemonic. */ + * the top bit index. */ const char* aa64_bitfield_shift_alias(u32 word, u32* shift) { - u32 sf = (word >> 31) & 1u; u32 opc = (word >> 29) & 3u; /* 0 = SBFM, 2 = UBFM */ u32 immr = (word >> 16) & 0x3fu; u32 imms = (word >> 10) & 0x3fu; - u32 top = sf ? 63u : 31u; + u32 width, top; + if (!bitfield_width(word, &width)) return NULL; + top = width - 1u; if (opc == 2u) { /* UBFM */ if (imms == top) { *shift = immr; @@ -1211,9 +1219,42 @@ const char* aa64_bitfield_shift_alias(u32 word, u32* shift) { return NULL; } +const char* aa64_bitfield_extend_alias(u32 word) { + u32 sf = (word >> 31) & 1u; + u32 opc = (word >> 29) & 3u; /* 0 = SBFM, 2 = UBFM */ + u32 immr = (word >> 16) & 0x3fu; + u32 imms = (word >> 10) & 0x3fu; + u32 width; + if (!bitfield_width(word, &width) || immr != 0u) return NULL; + (void)width; + if (opc == 0u) { + if (imms == 7u) return "sxtb"; + if (imms == 15u) return "sxth"; + if (sf && imms == 31u) return "sxtw"; + } else if (opc == 2u && !sf) { + if (imms == 7u) return "uxtb"; + if (imms == 15u) return "uxth"; + } + return NULL; +} + +const char* aa64_bitfield_extract_alias(u32 word, u32* lsb, u32* width_out) { + u32 opc = (word >> 29) & 3u; /* 0 = SBFM, 2 = UBFM */ + u32 immr = (word >> 16) & 0x3fu; + u32 imms = (word >> 10) & 0x3fu; + u32 width; + if (!bitfield_width(word, &width)) return NULL; + (void)width; + if (opc != 0u && opc != 2u) return NULL; + if (immr > imms) return NULL; + *lsb = immr; + *width_out = imms - immr + 1u; + return opc == 0u ? "sbfx" : "ubfx"; +} + static void print_bitfield(StrBuf* sb, u32 w) { int sf = (int)((w >> 31) & 1u); - u32 shift; + u32 shift, lsb, width; if (aa64_bitfield_shift_alias(w, &shift)) { /* lsl/lsr/asr Rd, Rn, #shift (mnemonic chosen by the disasm mnemonic * writer via the same helper). */ @@ -1224,6 +1265,24 @@ static void print_bitfield(StrBuf* sb, u32 w) { strbuf_put_u64(sb, (u64)shift); return; } + if (aa64_bitfield_extend_alias(w)) { + /* sxtb/sxth/sxtw/uxtb/uxth Rd, Wn. The source is spelled Wn even when + * the underlying SBFM has sf=1 (sxtb/sxth/sxtw into Xd). */ + emit_reg(sb, w & 0x1fu, sf, 0); + strbuf_puts(sb, ", "); + emit_reg(sb, (w >> 5) & 0x1fu, 0, 0); + return; + } + if (aa64_bitfield_extract_alias(w, &lsb, &width)) { + 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)lsb); + strbuf_puts(sb, ", #"); + strbuf_put_u64(sb, (u64)width); + return; + } emit_reg(sb, w & 0x1fu, sf, 0); strbuf_puts(sb, ", "); emit_reg(sb, (w >> 5) & 0x1fu, sf, 0); diff --git a/src/arch/aa64/isa.h b/src/arch/aa64/isa.h @@ -1591,6 +1591,14 @@ void aa64_print_operands(StrBuf* sb, const AA64InsnDesc* desc, u32 word, * operand printers so the alias decision lives in one place. */ const char* aa64_bitfield_shift_alias(u32 word, u32* shift); +/* Preferred SBFM/UBFM extension aliases: sxtb/sxth/sxtw/uxtb/uxth. + * Returns NULL when the bitfield does not match one of those forms. */ +const char* aa64_bitfield_extend_alias(u32 word); + +/* Preferred SBFM/UBFM extract aliases: sbfx/ubfx. Writes the least-significant + * source bit and extracted width when an alias is available. */ +const char* aa64_bitfield_extract_alias(u32 word, u32* lsb, u32* width); + /* Returns 1 on success, 0 on parse error. Phase 2 stub returns 0 for * every format; phase 3 fills in the bodies. */ int aa64_parse_operands(struct AA64AsmTok* tok, const AA64InsnDesc* desc, diff --git a/test/asm/decode/aa64_bitfield_aliases.expected.txt b/test/asm/decode/aa64_bitfield_aliases.expected.txt @@ -0,0 +1,15 @@ +0: sxtb w6, w7 +4: sxtb x8, w9 +8: sxth w10, w11 +c: sxth x12, w13 +10: sxtw x14, w15 +14: uxtb w16, w17 +18: uxtb w18, w19 +1c: uxth w20, w21 +20: uxth w22, w23 +24: ubfx x24, x25, #0, #32 +28: sbfx w26, w27, #3, #5 +2c: sbfx x28, x29, #4, #20 +30: ubfx w0, w1, #5, #9 +34: ubfx x2, x3, #6, #33 +38: ret diff --git a/test/asm/decode/aa64_bitfield_aliases.hex b/test/asm/decode/aa64_bitfield_aliases.hex @@ -0,0 +1 @@ +e61c0013281d40936a3d0013ac3d4093ee7d4093301e0053721e0053b43e0053f63e0053387f40d37a1f0313bc5f449320340553629846d3c0035fd6 diff --git a/test/asm/decode/aa64_bitfield_aliases.targets b/test/asm/decode/aa64_bitfield_aliases.targets @@ -0,0 +1 @@ +aa64 diff --git a/test/asm/decode/aa64_fp_bitfield_dp1.expected.txt b/test/asm/decode/aa64_fp_bitfield_dp1.expected.txt @@ -17,5 +17,5 @@ c: rev x4, x5 40: scvtf s0, w1 44: ucvtf d2, x3 48: fcvtzu w4, d5 -4c: ubfm x0, x1, #4, #20 +4c: ubfx x0, x1, #4, #17 50: ret diff --git a/test/asm/encode/aa64_bitfield_dp1.expected.hex b/test/asm/encode/aa64_bitfield_dp1.expected.hex @@ -1 +1 @@ -2010c0da6210c05aa400c0dae60cc0da2809c05a6a05c0da2050449362280253a44048b3c0035fd6 +2010c0da6210c05aa400c0dae60cc0da2809c05a6a05c0da2050449362280253a44048b3e61c0013281d40936a3d0013ac3d4093ee7d4093301e0053721e0053b43e0053f63e0053387f40d37a1f0313bc5f449320340553629846d3c0035fd6 diff --git a/test/asm/encode/aa64_bitfield_dp1.s b/test/asm/encode/aa64_bitfield_dp1.s @@ -9,4 +9,18 @@ t: sbfm x0, x1, #4, #20 ubfm w2, w3, #2, #10 bfm x4, x5, #8, #16 + sxtb w6, w7 + sxtb x8, w9 + sxth w10, w11 + sxth x12, w13 + sxtw x14, w15 + uxtb w16, w17 + uxtb x18, w19 + uxth w20, w21 + uxth x22, w23 + uxtw x24, w25 + sbfx w26, w27, #3, #5 + sbfx x28, x29, #4, #20 + ubfx w0, w1, #5, #9 + ubfx x2, x3, #6, #33 ret