kit

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

commit 83dac6d332d09a32e1faa6bedcad0f7b23df8733
parent 87eb85cdc69c09fe71ad21e4d29c0f832bf05b77
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 21 May 2026 07:50:29 -0700

Improve AA64 compare and cset disassembly

Diffstat:
Msrc/arch/aa64/asm.c | 53++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/arch/aa64/isa.c | 48+++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/arch/aa64/isa.h | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/arch/aa64_isa_test.c | 14++++++++++++++
Atest/asm/decode/aa64_cmp_cset.expected.txt | 6++++++
Atest/asm/decode/aa64_cmp_cset.hex | 1+
Atest/asm/decode/aa64_cmp_cset.targets | 1+
Atest/asm/encode/aa64_cmp_cset.expected.hex | 1+
Atest/asm/encode/aa64_cmp_cset.s | 7+++++++
Atest/asm/encode/aa64_cmp_cset.targets | 1+
10 files changed, 198 insertions(+), 12 deletions(-)

diff --git a/src/arch/aa64/asm.c b/src/arch/aa64/asm.c @@ -644,21 +644,42 @@ static void p_cmp(AsmDriver* d, int is_neg /* cmn flips op */) { emit32(d, word); } -static void p_csinc(AsmDriver* d) { +static void p_condsel(AsmDriver* d, u32 op, u32 op2, const char* what) { AA64Reg rd = parse_reg(d); - expect_comma(d, "csinc"); + expect_comma(d, what); AA64Reg rn = parse_reg(d); - expect_comma(d, "csinc"); + expect_comma(d, what); AA64Reg rm = parse_reg(d); - expect_comma(d, "csinc"); - u32 cond = parse_cond(d, "csinc"); + expect_comma(d, what); + u32 cond = parse_cond(d, what); if (rd.is_sp || rn.is_sp || rm.is_sp) - asm_driver_panic(d, "asm: csinc: SP register not allowed"); + asm_driver_panic(d, "asm: %s: SP register not allowed", what); if (rd.is64 != rn.is64 || rd.is64 != rm.is64) - asm_driver_panic(d, "asm: csinc: width mismatch"); - u32 word = 0x1A800400u | ((u32)rd.is64 << 31) | ((rm.num & 0x1fu) << 16) | - ((cond & 0xfu) << 12) | ((rn.num & 0x1fu) << 5) | - (rd.num & 0x1fu); + asm_driver_panic(d, "asm: %s: width mismatch", what); + u32 word = aa64_condsel_pack((AA64CondSel){.sf = (u32)rd.is64, + .op = op, + .S = 0, + .Rm = rm.num, + .cond = cond, + .op2 = op2, + .Rn = rn.num, + .Rd = rd.num}); + emit32(d, word); +} + +static void p_cset_like(AsmDriver* d, u32 op, u32 op2, const char* what) { + AA64Reg rd = parse_reg(d); + expect_comma(d, what); + u32 cond = parse_cond(d, what); + if (rd.is_sp) asm_driver_panic(d, "asm: %s: SP register not allowed", what); + u32 word = aa64_condsel_pack((AA64CondSel){.sf = (u32)rd.is64, + .op = op, + .S = 0, + .Rm = AA64_ZR, + .cond = cond ^ 1u, + .op2 = op2, + .Rn = AA64_ZR, + .Rd = rd.num}); emit32(d, word); } @@ -959,7 +980,12 @@ static void p_addsub_sub(AsmDriver* d) { p_addsub(d, 1, 0); } static void p_addsub_subs(AsmDriver* d) { p_addsub(d, 1, 1); } static void p_cmp_w(AsmDriver* d) { p_cmp(d, 0); } static void p_cmn_w(AsmDriver* d) { p_cmp(d, 1); } -static void p_csinc_(AsmDriver* d) { p_csinc(d); } +static void p_csel_(AsmDriver* d) { p_condsel(d, 0, 0, "csel"); } +static void p_csinc_(AsmDriver* d) { p_condsel(d, 0, 1, "csinc"); } +static void p_csinv_(AsmDriver* d) { p_condsel(d, 1, 0, "csinv"); } +static void p_csneg_(AsmDriver* d) { p_condsel(d, 1, 1, "csneg"); } +static void p_cset_(AsmDriver* d) { p_cset_like(d, 0, 1, "cset"); } +static void p_csetm_(AsmDriver* d) { p_cset_like(d, 1, 0, "csetm"); } static void p_neg_w(AsmDriver* d) { p_neg(d, 0); } static void p_negs_w(AsmDriver* d) { p_neg(d, 1); } static void p_and_w(AsmDriver* d) { p_log_sr(d, AA64_LOG_AND_OPC, 0); } @@ -1038,7 +1064,12 @@ static const AA64Mn kTable[] = { {"subs", p_addsub_subs, 0}, {"cmp", p_cmp_w, 0}, {"cmn", p_cmn_w, 0}, + {"csel", p_csel_, 0}, {"csinc", p_csinc_, 0}, + {"csinv", p_csinv_, 0}, + {"csneg", p_csneg_, 0}, + {"cset", p_cset_, 0}, + {"csetm", p_csetm_, 0}, {"neg", p_neg_w, 0}, {"negs", p_negs_w, 0}, {"and", p_and_w, 0}, diff --git a/src/arch/aa64/isa.c b/src/arch/aa64/isa.c @@ -79,6 +79,20 @@ const AA64InsnDesc aa64_insn_table[] = { {"asrv", 0x1AC02800u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, {"rorv", 0x1AC02C00u, 0x5FE0FC00u, AA64_FMT_DP2, 0, {0, 0}}, + /* ----- Conditional select ----- + * CSET Rd, cond ≡ CSINC Rd, ZR, ZR, invert(cond). + * CSETM Rd, cond ≡ CSINV Rd, ZR, ZR, invert(cond). + * These aliases are expressible as fixed Rn/Rm=ZR masks, so put them + * before the canonical conditional-select rows. */ + {"cset", 0x1A9F07E0u, 0x7FE00C00u | (0x1Fu << 16) | (0x1Fu << 5), + AA64_FMT_CONDSEL, AA64_ASMFL_ALIAS, {0, 0}}, + {"csetm", 0x5A9F03E0u, 0x7FE00C00u | (0x1Fu << 16) | (0x1Fu << 5), + AA64_FMT_CONDSEL, AA64_ASMFL_ALIAS, {0, 0}}, + {"csel", 0x1A800000u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, + {"csinc", 0x1A800400u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, + {"csinv", 0x5A800000u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, + {"csneg", 0x5A800400u, 0x7FE00C00u, AA64_FMT_CONDSEL, 0, {0, 0}}, + /* ----- Unconditional branch (register) ----- * RET aliases its no-operand spelling to RET X30 (Rn=11110). The * tighter row matches when Rn=30 and prints "ret" without operands; @@ -93,7 +107,12 @@ const AA64InsnDesc aa64_insn_table[] = { {"adr", 0x10000000u, 0x9F000000u, AA64_FMT_PCREL_ADR, 0, {0, 0}}, {"adrp", 0x90000000u, 0x9F000000u, AA64_FMT_PCREL_ADR, 0, {0, 0}}, - /* ----- Add/Sub immediate ----- */ + /* ----- Add/Sub immediate ----- + * CMP/CMN immediate are the Rd=ZR aliases of SUBS/ADDS immediate. */ + {"cmn", 0x3100001Fu, 0x7F00001Fu, AA64_FMT_ADDSUB_IMM, AA64_ASMFL_ALIAS, + {0, 0}}, + {"cmp", 0x7100001Fu, 0x7F00001Fu, AA64_FMT_ADDSUB_IMM, AA64_ASMFL_ALIAS, + {0, 0}}, {"add", 0x11000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}}, {"adds", 0x31000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}}, {"sub", 0x51000000u, 0x7F000000u, AA64_FMT_ADDSUB_IMM, 0, {0, 0}}, @@ -352,6 +371,24 @@ static void print_dp2(StrBuf* sb, u32 w) { emit_reg(sb, f.Rm, (int)f.sf, 0); } +static void print_condsel(StrBuf* sb, u32 w, const AA64InsnDesc* d) { + AA64CondSel f = aa64_condsel_unpack(w); + if (d->flags & AA64_ASMFL_ALIAS) { + /* CSET / CSETM: Rd, cond. Encoded condition is inverted. */ + emit_reg(sb, f.Rd, (int)f.sf, 0); + strbuf_puts(sb, ", "); + emit_cond(sb, f.cond ^ 1u); + return; + } + emit_reg(sb, f.Rd, (int)f.sf, 0); + strbuf_puts(sb, ", "); + emit_reg(sb, f.Rn, (int)f.sf, 0); + strbuf_puts(sb, ", "); + emit_reg(sb, f.Rm, (int)f.sf, 0); + strbuf_puts(sb, ", "); + emit_cond(sb, f.cond); +} + static void print_brreg(StrBuf* sb, u32 w, const AA64InsnDesc* d) { AA64BrReg f = aa64_brreg_unpack(w); if (d->flags & AA64_ASMFL_NORN) return; /* RET (with implicit X30) */ @@ -375,6 +412,14 @@ static void print_pcrel(StrBuf* sb, u32 w, u64 vaddr) { static void print_addsubimm(StrBuf* sb, u32 w) { AA64AddSubImm f = aa64_addsubimm_unpack(w); + if (f.S && f.Rd == AA64_ZR) { + /* CMP/CMN immediate aliases: Rn|SP, #imm. */ + emit_reg(sb, f.Rn, (int)f.sf, 1); + strbuf_puts(sb, ", #"); + strbuf_put_u64(sb, (u64)f.imm12); + if (f.sh) strbuf_puts(sb, ", lsl #12"); + return; + } /* For these encodings, Rd/Rn=31 means SP. */ emit_reg(sb, f.Rd, (int)f.sf, 1); strbuf_puts(sb, ", "); @@ -575,6 +620,7 @@ void aa64_print_operands(StrBuf* sb, const AA64InsnDesc* desc, u32 word, case AA64_FMT_ADDSUB_SR: print_addsubsr(sb, word, desc); break; case AA64_FMT_DP3: print_dp3(sb, word, desc); break; case AA64_FMT_DP2: print_dp2(sb, word); break; + case AA64_FMT_CONDSEL: print_condsel(sb, word, desc); break; case AA64_FMT_BR_REG: print_brreg(sb, word, desc); break; case AA64_FMT_PCREL_ADR: print_pcrel(sb, word, vaddr); break; case AA64_FMT_ADDSUB_IMM: print_addsubimm(sb, word); break; diff --git a/src/arch/aa64/isa.h b/src/arch/aa64/isa.h @@ -40,6 +40,7 @@ typedef enum AA64Format { AA64_FMT_ADDSUB_SR, /* add/sub, shifted register */ AA64_FMT_DP3, /* data-processing, 3 source */ AA64_FMT_DP2, /* data-processing, 2 source */ + AA64_FMT_CONDSEL, /* conditional select (CSEL / CSINC / aliases) */ AA64_FMT_BR_REG, /* unconditional branch (register) */ AA64_FMT_PCREL_ADR, /* PC-relative ADR / ADRP */ AA64_FMT_ADDSUB_IMM, /* add/sub, immediate */ @@ -472,6 +473,83 @@ static inline u32 aa64_rorv(u32 sf, u32 Rd, u32 Rn, u32 Rm) { } /* ==================================================================== + * Conditional select (CSEL / CSINC / CSINV / CSNEG) + * sf op S 11010100 Rm(5) cond(4) op2(2) Rn(5) Rd(5) + * 31 30 29 28..21 20..16 15..12 11..10 9..5 4..0 + * + * The integer forms this backend emits keep S=0. Aliases such as CSET + * are descriptor-table rows over this same encoding family. */ + +#define AA64_CONDSEL_FAMILY_MATCH 0x1A800000u +#define AA64_CONDSEL_FAMILY_MASK 0x1FE00000u /* bits 28:21 fixed */ + +typedef struct AA64CondSel { + u32 sf, op, S, Rm, cond, op2, Rn, Rd; +} AA64CondSel; + +static inline u32 aa64_condsel_pack(AA64CondSel f) { + return ((f.sf & 1u) << 31) | ((f.op & 1u) << 30) | + ((f.S & 1u) << 29) | AA64_CONDSEL_FAMILY_MATCH | + ((f.Rm & 0x1fu) << 16) | ((f.cond & 0xfu) << 12) | + ((f.op2 & 3u) << 10) | ((f.Rn & 0x1fu) << 5) | + (f.Rd & 0x1fu); +} + +static inline AA64CondSel aa64_condsel_unpack(u32 w) { + AA64CondSel f; + f.sf = (w >> 31) & 1u; + f.op = (w >> 30) & 1u; + f.S = (w >> 29) & 1u; + f.Rm = (w >> 16) & 0x1fu; + f.cond = (w >> 12) & 0xfu; + f.op2 = (w >> 10) & 3u; + f.Rn = (w >> 5) & 0x1fu; + f.Rd = w & 0x1fu; + return f; +} + +static inline u32 aa64_csel_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { + return aa64_condsel_pack((AA64CondSel){.sf = sf, + .op = 0, + .S = 0, + .Rm = Rm, + .cond = cond, + .op2 = 0, + .Rn = Rn, + .Rd = Rd}); +} +static inline u32 aa64_csinc_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { + return aa64_condsel_pack((AA64CondSel){.sf = sf, + .op = 0, + .S = 0, + .Rm = Rm, + .cond = cond, + .op2 = 1, + .Rn = Rn, + .Rd = Rd}); +} +static inline u32 aa64_csinv_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { + return aa64_condsel_pack((AA64CondSel){.sf = sf, + .op = 1, + .S = 0, + .Rm = Rm, + .cond = cond, + .op2 = 0, + .Rn = Rn, + .Rd = Rd}); +} +static inline u32 aa64_csneg_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { + return aa64_condsel_pack((AA64CondSel){.sf = sf, + .op = 1, + .S = 0, + .Rm = Rm, + .cond = cond, + .op2 = 1, + .Rn = Rn, + .Rd = Rd}); +} + +/* ==================================================================== * Unconditional branch (register) — BR / BLR / RET * 1101011 opc(4) op2(5)=11111 op3(6)=000000 Rn(5) op4(5)=00000 * 31..25 24..21 20..16 15..10 9..5 4..0 diff --git a/test/arch/aa64_isa_test.c b/test/arch/aa64_isa_test.c @@ -90,6 +90,11 @@ int main(void) { check(aa64_udiv(1, 1, 2, 3), "udiv", "x1, x2, x3"); check(aa64_lslv(0, 1, 2, 3), "lslv", "w1, w2, w3"); + /* CONDSEL: CSEL X1, X2, X3, EQ and CSET W4, NE. */ + check(aa64_csel_enc(1, 1, 2, 3, 0), "csel", "x1, x2, x3, eq"); + check(aa64_csinc_enc(0, 4, AA64_ZR, AA64_ZR, 0), "cset", "w4, ne"); + check(aa64_csinv_enc(1, 5, AA64_ZR, AA64_ZR, 1), "csetm", "x5, eq"); + /* BR_REG alias: RET (with implicit X30). */ check(aa64_ret(/*Rn=*/30), "ret", NULL); /* BR_REG: BR X16 */ @@ -103,6 +108,15 @@ int main(void) { /* ADDSUB_IMM: ADD X1, X2, #0x10 */ check(aa64_add_imm(1, 1, 2, 0x10, 0), "add", "x1, x2, #16"); check(aa64_sub_imm(1, 1, 2, 0x10, 0), "sub", "x1, x2, #16"); + check(aa64_subs_imm12(1, AA64_ZR, 2, 0x10, 0), "cmp", "x2, #16"); + check(aa64_addsubimm_pack((AA64AddSubImm){.sf = 0, + .op = 0, + .S = 1, + .sh = 1, + .imm12 = 1, + .Rn = AA64_SP, + .Rd = AA64_ZR}), + "cmn", "sp, #1, lsl #12"); /* LDST_UIMM (size=11, V=0): LDR X1, [X2, #8] (encoded imm12=1, scale=8) */ check(aa64_ldr64_uimm12(/*Rt=*/1, /*Rn=*/2, /*imm12_scaled=*/1), "ldr", diff --git a/test/asm/decode/aa64_cmp_cset.expected.txt b/test/asm/decode/aa64_cmp_cset.expected.txt @@ -0,0 +1,6 @@ +0: cmp x2, #16 +4: cmn sp, #1, lsl #12 +8: cset w4, ne +c: csetm x5, eq +10: csel x1, x2, x3, eq +14: csinc x6, x7, x8, lt diff --git a/test/asm/decode/aa64_cmp_cset.hex b/test/asm/decode/aa64_cmp_cset.hex @@ -0,0 +1 @@ +5f4000f1ff0740b1e4079f1ae5139fda4100839ae6b4889a diff --git a/test/asm/decode/aa64_cmp_cset.targets b/test/asm/decode/aa64_cmp_cset.targets @@ -0,0 +1 @@ +aa64 diff --git a/test/asm/encode/aa64_cmp_cset.expected.hex b/test/asm/encode/aa64_cmp_cset.expected.hex @@ -0,0 +1 @@ +5f4000f1ff0740b1e4079f1ae5139fda4100839ae6b4889a diff --git a/test/asm/encode/aa64_cmp_cset.s b/test/asm/encode/aa64_cmp_cset.s @@ -0,0 +1,7 @@ +.text + cmp x2, #16 + cmn sp, #1, lsl #12 + cset w4, ne + csetm x5, eq + csel x1, x2, x3, eq + csinc x6, x7, x8, lt diff --git a/test/asm/encode/aa64_cmp_cset.targets b/test/asm/encode/aa64_cmp_cset.targets @@ -0,0 +1 @@ +aa64