commit 6ddf35a70674161ac4cccecf126763cc566e8c50
parent 05bca5fe8e103711afaf75836d6aa5af3b483fe1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Fri, 29 May 2026 14:22:09 -0700
aa64 asm: encode bitfield (sbfm/ubfm/bfm) and DP1 (clz/rbit/rev/rev16)
The kTable lacked these even though codegen emits them (sign/zero-extend and
shifts via sbfm/ubfm; clz/ctz/bswap builtins via clz/rbit/rev) and the
disassembler decodes them. Add the encode side via isa.h encoders
aa64_bitfield/aa64_dp1 (shared bit-knowledge with the decode rows; rev picks
opcode2 by width). Byte-identical to clang; adds aa64_bitfield_dp1 corpus case.
Diffstat:
5 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/src/arch/aa64/asm.c b/src/arch/aa64/asm.c
@@ -1261,6 +1261,47 @@ static void p_ucvtf(AsmDriver* d) { p_cvtf(d, AA64_FP_ICVT_UCVTF); }
static void p_fcvtzs(AsmDriver* d) { p_fcvtz(d, AA64_FP_ICVT_FCVTZS); }
static void p_fcvtzu(AsmDriver* d) { p_fcvtz(d, AA64_FP_ICVT_FCVTZU); }
+/* Data-processing (1 source): clz/rbit/rev16, and rev (whose opcode2 is the
+ * width: 2 for 32-bit, 3 for 64-bit). */
+static void p_dp1_op(AsmDriver* d, u32 opcode2) {
+ AA64Reg rd = parse_reg(d);
+ AA64Reg rn;
+ expect_comma(d, "dp1");
+ rn = parse_reg(d);
+ if (rd.is64 != rn.is64) asm_driver_panic(d, "asm: dp1: width mismatch");
+ emit32(d, aa64_dp1(rd.is64, opcode2, rd.num, rn.num));
+}
+static void p_clz(AsmDriver* d) { p_dp1_op(d, AA64_DP1_CLZ); }
+static void p_rbit(AsmDriver* d) { p_dp1_op(d, AA64_DP1_RBIT); }
+static void p_rev16(AsmDriver* d) { p_dp1_op(d, AA64_DP1_REV16); }
+static void p_rev(AsmDriver* d) {
+ AA64Reg rd = parse_reg(d);
+ AA64Reg rn;
+ expect_comma(d, "rev");
+ rn = parse_reg(d);
+ if (rd.is64 != rn.is64) asm_driver_panic(d, "asm: rev: width mismatch");
+ emit32(d, aa64_dp1(rd.is64, rd.is64 ? AA64_DP1_REV64 : AA64_DP1_REV32, rd.num,
+ rn.num));
+}
+
+/* Bitfield move (opc: 0=sbfm, 1=bfm, 2=ubfm): Rd, Rn, #immr, #imms. */
+static void p_bitfield(AsmDriver* d, u32 opc) {
+ AA64Reg rd = parse_reg(d);
+ AA64Reg rn;
+ i64 immr, imms;
+ expect_comma(d, "bitfield");
+ rn = parse_reg(d);
+ expect_comma(d, "bitfield");
+ immr = parse_imm_const(d);
+ expect_comma(d, "bitfield");
+ imms = parse_imm_const(d);
+ if (rd.is64 != rn.is64) asm_driver_panic(d, "asm: bitfield: width mismatch");
+ emit32(d, aa64_bitfield(rd.is64, opc, (u32)immr, (u32)imms, rd.num, rn.num));
+}
+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); }
+
/* 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);
@@ -1297,6 +1338,13 @@ static const AA64Mn kTable[] = {
{"ucvtf", p_ucvtf, 0},
{"fcvtzs", p_fcvtzs, 0},
{"fcvtzu", p_fcvtzu, 0},
+ {"clz", p_clz, 0},
+ {"rbit", p_rbit, 0},
+ {"rev", p_rev, 0},
+ {"rev16", p_rev16, 0},
+ {"sbfm", p_sbfm, 0},
+ {"ubfm", p_ubfm, 0},
+ {"bfm", p_bfm, 0},
{"nop", p_nop, 0},
{"dmb", p_dmb, 0},
{"dsb", p_dsb, 0},
diff --git a/src/arch/aa64/isa.h b/src/arch/aa64/isa.h
@@ -832,6 +832,27 @@ static inline u32 aa64_fp_int_cvt(u32 sf, u32 ftype, u32 opcode, u32 Rd,
((opcode & 0x1fu) << 16) | ((Rn & 0x1fu) << 5) | (Rd & 0x1fu);
}
+/* Bitfield move (opc: 0=SBFM, 1=BFM, 2=UBFM). The N bit tracks sf for the
+ * 32-/64-bit forms. Matches native.c aa_sbfm/aa_ubfm and the BITFIELD row. */
+static inline u32 aa64_bitfield(u32 sf, u32 opc, u32 immr, u32 imms, u32 Rd,
+ u32 Rn) {
+ return ((sf & 1u) << 31) | ((opc & 3u) << 29) | 0x13000000u |
+ ((sf & 1u) << 22) | ((immr & 0x3fu) << 16) | ((imms & 0x3fu) << 10) |
+ ((Rn & 0x1fu) << 5) | (Rd & 0x1fu);
+}
+
+/* Data-processing (1 source). opcode2 (bits[15:10]): RBIT=0, REV16=1,
+ * REV(32)=2, REV(64)=3, CLZ=4. Matches native.c aa_clz/aa_rbit/aa_rev. */
+#define AA64_DP1_RBIT 0x00u
+#define AA64_DP1_REV16 0x01u
+#define AA64_DP1_REV32 0x02u
+#define AA64_DP1_REV64 0x03u
+#define AA64_DP1_CLZ 0x04u
+static inline u32 aa64_dp1(u32 sf, u32 opcode2, u32 Rd, u32 Rn) {
+ return ((sf & 1u) << 31) | 0x5AC00000u | ((opcode2 & 0x3fu) << 10) |
+ ((Rn & 0x1fu) << 5) | (Rd & 0x1fu);
+}
+
/* ====================================================================
* Load/store register pair, pre-indexed (STP / LDP, 64-bit form)
* opc(2) 101 V(1) 010 L(1) imm7(7) Rt2(5) Rn(5) Rt(5)
diff --git a/test/asm/encode/aa64_bitfield_dp1.expected.hex b/test/asm/encode/aa64_bitfield_dp1.expected.hex
@@ -0,0 +1 @@
+2010c0da6210c05aa400c0dae60cc0da2809c05a6a05c0da2050449362280253a44048b3c0035fd6
diff --git a/test/asm/encode/aa64_bitfield_dp1.s b/test/asm/encode/aa64_bitfield_dp1.s
@@ -0,0 +1,12 @@
+.text
+t:
+ clz x0, x1
+ clz w2, w3
+ rbit x4, x5
+ rev x6, x7
+ rev w8, w9
+ rev16 x10, x11
+ sbfm x0, x1, #4, #20
+ ubfm w2, w3, #2, #10
+ bfm x4, x5, #8, #16
+ ret
diff --git a/test/asm/encode/aa64_bitfield_dp1.targets b/test/asm/encode/aa64_bitfield_dp1.targets
@@ -0,0 +1 @@
+aa64
+\ No newline at end of file