isa.h (61921B)
1 #ifndef KIT_ARCH_AA64_ISA_H 2 #define KIT_ARCH_AA64_ISA_H 3 4 /* AArch64 ISA descriptors — single source of truth for every instruction 5 * the encoder, decoder, and disassembler all need to agree on. 6 * 7 * Each format declares: 8 * - A field struct naming every encoded bitfield. 9 * - {pack, unpack} pure functions that round-trip through a u32 word. 10 * - A {family_match, family_mask} pair identifying the format. 11 * - Per-instruction inline wrappers that bake in the opc bits and 12 * return the encoded word; callers emit it via MCEmitter. 13 * 14 * A descriptor table at the bottom (aa64_insn_table) maps mnemonic → 15 * (match, mask, AA64Format), so the disassembler matches a raw word with 16 * for (i=0; i<N; ++i) if ((word & desc[i].mask) == desc[i].match) ... 17 * and then calls the format's unpack to recover the operand fields. 18 * 19 * Conventions: 20 * - sf = 0 selects the 32-bit (W) form, sf = 1 selects 64-bit (X). 21 * - Reg values are the raw 5-bit encoding (0..30 + 31 for ZR/SP). 22 * - All wrappers take Rd first, then Rn, Rm, Ra to match the AAPCS 23 * "destination first" convention used in the AArch64 manual. 24 * 25 * New instructions land as one entry in the table and (typically) one 26 * inline wrapper in the relevant format section. */ 27 28 #include "core/core.h" 29 #include "core/slice.h" 30 #include "core/strbuf.h" 31 32 /* ---- common register names ---- */ 33 #define AA64_ZR 31u /* WZR / XZR */ 34 #define AA64_SP 31u /* SP at Rd/Rn slot */ 35 #define AA64_LR 30u /* X30 / link register */ 36 37 /* ---- format kinds ---- */ 38 typedef enum AA64Format { 39 AA64_FMT_MOVEWIDE, 40 AA64_FMT_LOG_SR, /* logical, shifted register */ 41 AA64_FMT_ADDSUB_SR, /* add/sub, shifted register */ 42 AA64_FMT_DP3, /* data-processing, 3 source */ 43 AA64_FMT_DP2, /* data-processing, 2 source */ 44 AA64_FMT_CONDSEL, /* conditional select (CSEL / CSINC / aliases) */ 45 AA64_FMT_BR_REG, /* unconditional branch (register) */ 46 AA64_FMT_PCREL_ADR, /* PC-relative ADR / ADRP */ 47 AA64_FMT_ADDSUB_IMM, /* add/sub, immediate */ 48 AA64_FMT_LDST_UIMM, /* load/store, unsigned 12-bit immediate offset */ 49 AA64_FMT_LDSTP_PRE, /* load/store pair, pre-indexed */ 50 AA64_FMT_LDSTP_SOFF, /* load/store pair, signed-offset */ 51 AA64_FMT_LDSTP_POST, /* load/store pair, post-indexed */ 52 AA64_FMT_LDST_SIMM9, /* load/store, unscaled 9-bit signed offset 53 (LDUR / STUR, V=0 and V=1) */ 54 AA64_FMT_BR_IMM, /* unconditional branch (immediate) — B / BL */ 55 AA64_FMT_BR_COND, /* B.cond (imm19) */ 56 AA64_FMT_CB, /* compare-and-branch (CBZ / CBNZ) */ 57 AA64_FMT_EXCEPT, /* exception generation (BRK / SVC / HVC / ...) */ 58 AA64_FMT_HINT, /* hint (NOP / YIELD / ...) */ 59 AA64_FMT_BARRIER, /* memory barrier (DMB / DSB / ISB / CLREX) */ 60 AA64_FMT_DP1, /* data-processing, 1 source (RBIT/REV/REV16/CLZ) */ 61 AA64_FMT_BITFIELD, /* bitfield move (SBFM / UBFM): Rd, Rn, #immr, #imms */ 62 AA64_FMT_LDST_REGOFF, /* load/store, register offset [Xn, Xm{, LSL #s}] */ 63 AA64_FMT_FP_DP2, /* FP data-processing 2-source (FADD/FSUB/FMUL/FDIV) */ 64 AA64_FMT_FP_DP1, /* FP data-processing 1-source (FMOV/FNEG/FABS/FSQRT) */ 65 AA64_FMT_FP_CMP, /* FP compare (FCMP) */ 66 AA64_FMT_FP_CVT, /* FP precision convert (FCVT single<->double) */ 67 AA64_FMT_FP_INT_CVT, /* FP<->int convert + FMOV gpr<->fp 68 * (SCVTF/UCVTF/FCVTZS/FCVTZU/FMOV) */ 69 AA64_FMT_LDST_EXCL, /* load/store exclusive + acquire/release ordered 70 * (LDXR/LDAXR/STXR/STLXR/LDAR/STLR + b/h) */ 71 AA64_FMT_LOG_IMM, /* logical, immediate (AND/ORR/EOR/ANDS #bitmask) */ 72 AA64_FMT_SYSREG, /* system-register move (MRS Xt,<reg> / MSR <reg>,Xt) */ 73 } AA64Format; 74 75 /* ---- AsmFlags column on AA64InsnDesc ---- 76 * 77 * Per-row metadata that varies across same-format members. Most rows 78 * carry 0. When the disassembler matches a row whose ALIAS bit is set, 79 * that's the spelling it prints; the assembler also accepts both the 80 * alias and the canonical form because both rows live in the table. */ 81 #define AA64_ASMFL_ALIAS \ 82 0x01u /* row is an alias (e.g. MOV → ORR Rd, ZR, Rm) \ 83 */ 84 #define AA64_ASMFL_SF1 0x02u /* 64-bit form only (sf hard-wired) */ 85 #define AA64_ASMFL_NORN \ 86 0x04u /* hide Rn operand in print (e.g. RET when Rn=30) */ 87 88 /* ==================================================================== 89 * Move-wide immediate (MOVN / MOVZ / MOVK) 90 * sf opc(2) 100101 hw(2) imm16(16) Rd(5) 91 * 31 30..29 28..23 22..21 20..5 4..0 92 * ==================================================================== */ 93 94 #define AA64_MOVN_OPC 0u 95 #define AA64_MOVZ_OPC 2u 96 #define AA64_MOVK_OPC 3u 97 98 #define AA64_MOVEWIDE_FAMILY_MATCH 0x12800000u 99 #define AA64_MOVEWIDE_FAMILY_MASK 0x1F800000u /* bits 28:23 */ 100 101 typedef struct AA64MoveWide { 102 u32 sf, opc, hw, imm16, Rd; 103 } AA64MoveWide; 104 105 static inline u32 aa64_movewide_pack(AA64MoveWide f) { 106 return ((f.sf & 1u) << 31) | ((f.opc & 3u) << 29) | 107 AA64_MOVEWIDE_FAMILY_MATCH | ((f.hw & 3u) << 21) | 108 ((f.imm16 & 0xffffu) << 5) | (f.Rd & 0x1fu); 109 } 110 111 static inline AA64MoveWide aa64_movewide_unpack(u32 w) { 112 AA64MoveWide f; 113 f.sf = (w >> 31) & 1u; 114 f.opc = (w >> 29) & 3u; 115 f.hw = (w >> 21) & 3u; 116 f.imm16 = (w >> 5) & 0xffffu; 117 f.Rd = w & 0x1fu; 118 return f; 119 } 120 121 static inline u32 aa64_movz(u32 sf, u32 Rd, u32 imm16, u32 hw) { 122 return aa64_movewide_pack((AA64MoveWide){ 123 .sf = sf, .opc = AA64_MOVZ_OPC, .hw = hw, .imm16 = imm16, .Rd = Rd}); 124 } 125 static inline u32 aa64_movn(u32 sf, u32 Rd, u32 imm16, u32 hw) { 126 return aa64_movewide_pack((AA64MoveWide){ 127 .sf = sf, .opc = AA64_MOVN_OPC, .hw = hw, .imm16 = imm16, .Rd = Rd}); 128 } 129 static inline u32 aa64_movk(u32 sf, u32 Rd, u32 imm16, u32 hw) { 130 return aa64_movewide_pack((AA64MoveWide){ 131 .sf = sf, .opc = AA64_MOVK_OPC, .hw = hw, .imm16 = imm16, .Rd = Rd}); 132 } 133 134 /* ==================================================================== 135 * Logical, shifted register (AND / ORR / EOR / ANDS, with N inverting 136 * Rm to BIC / ORN / EON / BICS). 137 * sf opc(2) 01010 shift(2) N(1) Rm(5) imm6(6) Rn(5) Rd(5) 138 * 31 30..29 28..24 23..22 21 20..16 15..10 9..5 4..0 139 * ==================================================================== */ 140 141 #define AA64_LOG_AND_OPC 0u 142 #define AA64_LOG_ORR_OPC 1u 143 #define AA64_LOG_EOR_OPC 2u 144 #define AA64_LOG_ANDS_OPC 3u 145 146 #define AA64_LOGSR_FAMILY_MATCH 0x0A000000u 147 #define AA64_LOGSR_FAMILY_MASK 0x1F000000u /* bits 28:24 */ 148 149 typedef struct AA64LogSR { 150 u32 sf, opc, shift, N, Rm, imm6, Rn, Rd; 151 } AA64LogSR; 152 153 static inline u32 aa64_logsr_pack(AA64LogSR f) { 154 return ((f.sf & 1u) << 31) | ((f.opc & 3u) << 29) | AA64_LOGSR_FAMILY_MATCH | 155 ((f.shift & 3u) << 22) | ((f.N & 1u) << 21) | ((f.Rm & 0x1fu) << 16) | 156 ((f.imm6 & 0x3fu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rd & 0x1fu); 157 } 158 159 static inline AA64LogSR aa64_logsr_unpack(u32 w) { 160 AA64LogSR f; 161 f.sf = (w >> 31) & 1u; 162 f.opc = (w >> 29) & 3u; 163 f.shift = (w >> 22) & 3u; 164 f.N = (w >> 21) & 1u; 165 f.Rm = (w >> 16) & 0x1fu; 166 f.imm6 = (w >> 10) & 0x3fu; 167 f.Rn = (w >> 5) & 0x1fu; 168 f.Rd = w & 0x1fu; 169 return f; 170 } 171 172 static inline u32 aa64_and(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 173 return aa64_logsr_pack((AA64LogSR){ 174 .sf = sf, .opc = AA64_LOG_AND_OPC, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 175 } 176 static inline u32 aa64_orr(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 177 return aa64_logsr_pack((AA64LogSR){ 178 .sf = sf, .opc = AA64_LOG_ORR_OPC, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 179 } 180 static inline u32 aa64_eor(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 181 return aa64_logsr_pack((AA64LogSR){ 182 .sf = sf, .opc = AA64_LOG_EOR_OPC, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 183 } 184 static inline u32 aa64_orn(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 185 return aa64_logsr_pack((AA64LogSR){ 186 .sf = sf, .opc = AA64_LOG_ORR_OPC, .N = 1, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 187 } 188 189 /* MOV Wd, Wm ≡ ORR Wd, WZR, Wm */ 190 static inline u32 aa64_mov_reg(u32 sf, u32 Rd, u32 Rm) { 191 return aa64_orr(sf, Rd, AA64_ZR, Rm); 192 } 193 /* MVN Wd, Wm ≡ ORN Wd, WZR, Wm */ 194 static inline u32 aa64_mvn(u32 sf, u32 Rd, u32 Rm) { 195 return aa64_orn(sf, Rd, AA64_ZR, Rm); 196 } 197 198 /* ==================================================================== 199 * Logical, immediate (AND / ORR / EOR / ANDS, bitmask-imm form) 200 * sf opc(2) 100100 N(1) immr(6) imms(6) Rn(5) Rd(5) 201 * 31 30..29 28..23 22 21..16 15..10 9..5 4..0 202 * 203 * N:immr:imms encodes a repeated-pattern bitmask. The encoder 204 * aa64_logimm_encode below computes those fields from a literal value; 205 * this pack just lays the bits out. For 32-bit ops (sf=0), N must be 0; 206 * for 64-bit ops N can be 0 or 1 and selects whether the pattern 207 * element is 64 bits (N=1) or 2..32 bits (N=0). 208 * ==================================================================== */ 209 210 #define AA64_LOGIMM_FAMILY_MATCH 0x12000000u 211 #define AA64_LOGIMM_FAMILY_MASK 0x1F800000u /* bits 28:23 */ 212 213 typedef struct AA64LogImm { 214 u32 sf, opc, N, immr, imms, Rn, Rd; 215 } AA64LogImm; 216 217 static inline u32 aa64_logimm_pack(AA64LogImm f) { 218 return ((f.sf & 1u) << 31) | ((f.opc & 3u) << 29) | AA64_LOGIMM_FAMILY_MATCH | 219 ((f.N & 1u) << 22) | ((f.immr & 0x3fu) << 16) | 220 ((f.imms & 0x3fu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rd & 0x1fu); 221 } 222 223 static inline u32 aa64_and_imm(u32 sf, u32 Rd, u32 Rn, u32 N, u32 immr, 224 u32 imms) { 225 return aa64_logimm_pack((AA64LogImm){.sf = sf, 226 .opc = AA64_LOG_AND_OPC, 227 .N = N, 228 .immr = immr, 229 .imms = imms, 230 .Rn = Rn, 231 .Rd = Rd}); 232 } 233 static inline u32 aa64_orr_imm(u32 sf, u32 Rd, u32 Rn, u32 N, u32 immr, 234 u32 imms) { 235 return aa64_logimm_pack((AA64LogImm){.sf = sf, 236 .opc = AA64_LOG_ORR_OPC, 237 .N = N, 238 .immr = immr, 239 .imms = imms, 240 .Rn = Rn, 241 .Rd = Rd}); 242 } 243 static inline u32 aa64_eor_imm(u32 sf, u32 Rd, u32 Rn, u32 N, u32 immr, 244 u32 imms) { 245 return aa64_logimm_pack((AA64LogImm){.sf = sf, 246 .opc = AA64_LOG_EOR_OPC, 247 .N = N, 248 .immr = immr, 249 .imms = imms, 250 .Rn = Rn, 251 .Rd = Rd}); 252 } 253 254 /* Bitmask-immediate predicate + encoder. Returns 1 and writes N/immr/imms 255 * if `imm` is encodable as an AArch64 logical immediate of width 256 * (sf ? 64 : 32); returns 0 otherwise (caller materializes into a 257 * scratch and uses the shifted-register form). 258 * 259 * Algorithm (inverse of ARM ARM "DecodeBitMasks"): an encodable value 260 * is a non-zero, non-all-ones bitmask made of a repeated `size`-bit 261 * element (size ∈ {2,4,8,16,32,64}); within one element the pattern is 262 * a rotation of (0…0 1…1). Find size by detecting the smallest 263 * repeating period; find the rotation that places the 1-run at the 264 * LSB; encode size and ones-count into imms per the standard scheme 265 * (top bits of imms inverted-encode size, low bits are ones-count-1). */ 266 static inline int aa64_logimm_encode(u64 imm, u32 sf, u32* N_out, u32* immr_out, 267 u32* imms_out) { 268 if (!sf) { 269 u64 lo = imm & 0xFFFFFFFFu; 270 u64 hi = imm >> 32; 271 if (hi != 0 && hi != lo) return 0; 272 imm = lo | (lo << 32); 273 } 274 if (imm == 0 || imm == ~(u64)0) return 0; 275 276 u32 size = 64; 277 for (u32 s = 32; s >= 2; s >>= 1) { 278 u64 mask = ((u64)1 << s) - 1u; 279 if ((imm & mask) != ((imm >> s) & mask)) break; 280 size = s; 281 } 282 u64 elt_mask = (size == 64) ? ~(u64)0 : (((u64)1 << size) - 1u); 283 u64 elt = imm & elt_mask; 284 if (elt == 0 || elt == elt_mask) return 0; 285 286 u32 ones = 0; 287 for (u64 x = elt; x; x >>= 1) ones += (u32)(x & 1u); 288 if (ones == 0 || ones >= size) return 0; 289 290 u64 aligned = ((u64)1 << ones) - 1u; 291 u32 rotation = 0xFFFFFFFFu; 292 for (u32 r = 0; r < size; r++) { 293 u64 rotated = 294 r == 0 ? elt : (((elt >> r) | (elt << (size - r))) & elt_mask); 295 if (rotated == aligned) { 296 rotation = r; 297 break; 298 } 299 } 300 if (rotation == 0xFFFFFFFFu) return 0; 301 302 if (size == 64) { 303 *N_out = 1u; 304 *imms_out = (ones - 1u) & 0x3Fu; 305 } else { 306 *N_out = 0u; 307 u32 neg_size_shl1 = ((u32)(-(i32)size) << 1) & 0x3Fu; 308 *imms_out = neg_size_shl1 | ((ones - 1u) & 0x3Fu); 309 } 310 *immr_out = rotation ? (size - rotation) & (size - 1u) : 0u; 311 return 1; 312 } 313 314 /* Shift-by-immediate field generators for LSL/LSR/ASR (encoded via 315 * UBFM/SBFM). Predicate: shift < width. The aa64_ubfm / aa64_sbfm 316 * encoders live in aarch64.c; callers pair these (immr, imms) with the 317 * matching pack. */ 318 static inline int aa64_lsl_imm_fields(u32 shift, u32 sf, u32* immr_out, 319 u32* imms_out) { 320 u32 width = sf ? 64u : 32u; 321 if (shift >= width) return 0; 322 *immr_out = (width - shift) & (width - 1u); 323 *imms_out = width - 1u - shift; 324 return 1; 325 } 326 static inline int aa64_lsr_imm_fields(u32 shift, u32 sf, u32* immr_out, 327 u32* imms_out) { 328 u32 width = sf ? 64u : 32u; 329 if (shift >= width) return 0; 330 *immr_out = shift; 331 *imms_out = width - 1u; 332 return 1; 333 } 334 static inline int aa64_asr_imm_fields(u32 shift, u32 sf, u32* immr_out, 335 u32* imms_out) { 336 u32 width = sf ? 64u : 32u; 337 if (shift >= width) return 0; 338 *immr_out = shift; 339 *imms_out = width - 1u; 340 return 1; 341 } 342 343 /* ==================================================================== 344 * Add/Sub, shifted register (ADD / SUB / ADDS / SUBS) 345 * sf op(1) S(1) 01011 shift(2) 0 Rm(5) imm6(6) Rn(5) Rd(5) 346 * 31 30 29 28..24 23..22 21 20..16 15..10 9..5 4..0 347 * ==================================================================== */ 348 349 #define AA64_ADDSUBSR_FAMILY_MATCH 0x0B000000u 350 #define AA64_ADDSUBSR_FAMILY_MASK 0x1F200000u /* bits 28:24 + bit 21 */ 351 352 typedef struct AA64AddSubSR { 353 u32 sf, op, S, shift, Rm, imm6, Rn, Rd; 354 } AA64AddSubSR; 355 356 static inline u32 aa64_addsubsr_pack(AA64AddSubSR f) { 357 return ((f.sf & 1u) << 31) | ((f.op & 1u) << 30) | ((f.S & 1u) << 29) | 358 AA64_ADDSUBSR_FAMILY_MATCH | ((f.shift & 3u) << 22) | 359 ((f.Rm & 0x1fu) << 16) | ((f.imm6 & 0x3fu) << 10) | 360 ((f.Rn & 0x1fu) << 5) | (f.Rd & 0x1fu); 361 } 362 363 static inline AA64AddSubSR aa64_addsubsr_unpack(u32 w) { 364 AA64AddSubSR f; 365 f.sf = (w >> 31) & 1u; 366 f.op = (w >> 30) & 1u; 367 f.S = (w >> 29) & 1u; 368 f.shift = (w >> 22) & 3u; 369 f.Rm = (w >> 16) & 0x1fu; 370 f.imm6 = (w >> 10) & 0x3fu; 371 f.Rn = (w >> 5) & 0x1fu; 372 f.Rd = w & 0x1fu; 373 return f; 374 } 375 376 static inline u32 aa64_add(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 377 return aa64_addsubsr_pack( 378 (AA64AddSubSR){.sf = sf, .op = 0, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 379 } 380 static inline u32 aa64_sub(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 381 return aa64_addsubsr_pack( 382 (AA64AddSubSR){.sf = sf, .op = 1, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 383 } 384 385 /* NEG Wd, Wm ≡ SUB Wd, WZR, Wm */ 386 static inline u32 aa64_neg(u32 sf, u32 Rd, u32 Rm) { 387 return aa64_sub(sf, Rd, AA64_ZR, Rm); 388 } 389 390 /* ==================================================================== 391 * Data-processing, 3-source (MADD / MSUB / SMULL / UMULL / ...) 392 * sf op54(2) 11011 op31(3) Rm(5) o0(1) Ra(5) Rn(5) Rd(5) 393 * 31 30..29 28..24 23..21 20..16 15 14..10 9..5 4..0 394 * ==================================================================== */ 395 396 #define AA64_DP3_FAMILY_MATCH 0x1B000000u 397 #define AA64_DP3_FAMILY_MASK 0x1F000000u /* bits 28:24 */ 398 399 typedef struct AA64DP3 { 400 u32 sf, op54, op31, Rm, o0, Ra, Rn, Rd; 401 } AA64DP3; 402 403 static inline u32 aa64_dp3_pack(AA64DP3 f) { 404 return ((f.sf & 1u) << 31) | ((f.op54 & 3u) << 29) | AA64_DP3_FAMILY_MATCH | 405 ((f.op31 & 7u) << 21) | ((f.Rm & 0x1fu) << 16) | ((f.o0 & 1u) << 15) | 406 ((f.Ra & 0x1fu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rd & 0x1fu); 407 } 408 409 static inline AA64DP3 aa64_dp3_unpack(u32 w) { 410 AA64DP3 f; 411 f.sf = (w >> 31) & 1u; 412 f.op54 = (w >> 29) & 3u; 413 f.op31 = (w >> 21) & 7u; 414 f.Rm = (w >> 16) & 0x1fu; 415 f.o0 = (w >> 15) & 1u; 416 f.Ra = (w >> 10) & 0x1fu; 417 f.Rn = (w >> 5) & 0x1fu; 418 f.Rd = w & 0x1fu; 419 return f; 420 } 421 422 static inline u32 aa64_madd(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 Ra) { 423 return aa64_dp3_pack((AA64DP3){ 424 .sf = sf, .op31 = 0, .o0 = 0, .Rm = Rm, .Ra = Ra, .Rn = Rn, .Rd = Rd}); 425 } 426 static inline u32 aa64_msub(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 Ra) { 427 return aa64_dp3_pack((AA64DP3){ 428 .sf = sf, .op31 = 0, .o0 = 1, .Rm = Rm, .Ra = Ra, .Rn = Rn, .Rd = Rd}); 429 } 430 /* MUL Wd, Wn, Wm ≡ MADD Wd, Wn, Wm, WZR */ 431 static inline u32 aa64_mul(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 432 return aa64_madd(sf, Rd, Rn, Rm, AA64_ZR); 433 } 434 435 /* ==================================================================== 436 * Data-processing, 2-source (UDIV / SDIV / LSLV / LSRV / ASRV / RORV) 437 * sf 0 S(1) 11010110 Rm(5) opcode(6) Rn(5) Rd(5) 438 * 31 30 29 28..21 20..16 15..10 9..5 4..0 439 * ==================================================================== */ 440 441 #define AA64_DP2_UDIV_OP 0x02u 442 #define AA64_DP2_SDIV_OP 0x03u 443 #define AA64_DP2_LSLV_OP 0x08u 444 #define AA64_DP2_LSRV_OP 0x09u 445 #define AA64_DP2_ASRV_OP 0x0Au 446 #define AA64_DP2_RORV_OP 0x0Bu 447 448 #define AA64_DP2_FAMILY_MATCH 0x1AC00000u 449 #define AA64_DP2_FAMILY_MASK 0x5FE00000u /* bit 30 + bits 28:21 */ 450 451 typedef struct AA64DP2 { 452 u32 sf, S, opcode, Rm, Rn, Rd; 453 } AA64DP2; 454 455 static inline u32 aa64_dp2_pack(AA64DP2 f) { 456 return ((f.sf & 1u) << 31) | ((f.S & 1u) << 29) | AA64_DP2_FAMILY_MATCH | 457 ((f.Rm & 0x1fu) << 16) | ((f.opcode & 0x3fu) << 10) | 458 ((f.Rn & 0x1fu) << 5) | (f.Rd & 0x1fu); 459 } 460 461 static inline AA64DP2 aa64_dp2_unpack(u32 w) { 462 AA64DP2 f; 463 f.sf = (w >> 31) & 1u; 464 f.S = (w >> 29) & 1u; 465 f.Rm = (w >> 16) & 0x1fu; 466 f.opcode = (w >> 10) & 0x3fu; 467 f.Rn = (w >> 5) & 0x1fu; 468 f.Rd = w & 0x1fu; 469 return f; 470 } 471 472 static inline u32 aa64_udiv(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 473 return aa64_dp2_pack((AA64DP2){ 474 .sf = sf, .opcode = AA64_DP2_UDIV_OP, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 475 } 476 static inline u32 aa64_sdiv(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 477 return aa64_dp2_pack((AA64DP2){ 478 .sf = sf, .opcode = AA64_DP2_SDIV_OP, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 479 } 480 static inline u32 aa64_lslv(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 481 return aa64_dp2_pack((AA64DP2){ 482 .sf = sf, .opcode = AA64_DP2_LSLV_OP, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 483 } 484 static inline u32 aa64_lsrv(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 485 return aa64_dp2_pack((AA64DP2){ 486 .sf = sf, .opcode = AA64_DP2_LSRV_OP, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 487 } 488 static inline u32 aa64_asrv(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 489 return aa64_dp2_pack((AA64DP2){ 490 .sf = sf, .opcode = AA64_DP2_ASRV_OP, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 491 } 492 static inline u32 aa64_rorv(u32 sf, u32 Rd, u32 Rn, u32 Rm) { 493 return aa64_dp2_pack((AA64DP2){ 494 .sf = sf, .opcode = AA64_DP2_RORV_OP, .Rm = Rm, .Rn = Rn, .Rd = Rd}); 495 } 496 497 /* ==================================================================== 498 * Conditional select (CSEL / CSINC / CSINV / CSNEG) 499 * sf op S 11010100 Rm(5) cond(4) op2(2) Rn(5) Rd(5) 500 * 31 30 29 28..21 20..16 15..12 11..10 9..5 4..0 501 * 502 * The integer forms this backend emits keep S=0. Aliases such as CSET 503 * are descriptor-table rows over this same encoding family. */ 504 505 #define AA64_CONDSEL_FAMILY_MATCH 0x1A800000u 506 #define AA64_CONDSEL_FAMILY_MASK 0x1FE00000u /* bits 28:21 fixed */ 507 508 typedef struct AA64CondSel { 509 u32 sf, op, S, Rm, cond, op2, Rn, Rd; 510 } AA64CondSel; 511 512 static inline u32 aa64_condsel_pack(AA64CondSel f) { 513 return ((f.sf & 1u) << 31) | ((f.op & 1u) << 30) | ((f.S & 1u) << 29) | 514 AA64_CONDSEL_FAMILY_MATCH | ((f.Rm & 0x1fu) << 16) | 515 ((f.cond & 0xfu) << 12) | ((f.op2 & 3u) << 10) | 516 ((f.Rn & 0x1fu) << 5) | (f.Rd & 0x1fu); 517 } 518 519 static inline AA64CondSel aa64_condsel_unpack(u32 w) { 520 AA64CondSel f; 521 f.sf = (w >> 31) & 1u; 522 f.op = (w >> 30) & 1u; 523 f.S = (w >> 29) & 1u; 524 f.Rm = (w >> 16) & 0x1fu; 525 f.cond = (w >> 12) & 0xfu; 526 f.op2 = (w >> 10) & 3u; 527 f.Rn = (w >> 5) & 0x1fu; 528 f.Rd = w & 0x1fu; 529 return f; 530 } 531 532 static inline u32 aa64_csel_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { 533 return aa64_condsel_pack((AA64CondSel){.sf = sf, 534 .op = 0, 535 .S = 0, 536 .Rm = Rm, 537 .cond = cond, 538 .op2 = 0, 539 .Rn = Rn, 540 .Rd = Rd}); 541 } 542 static inline u32 aa64_csinc_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { 543 return aa64_condsel_pack((AA64CondSel){.sf = sf, 544 .op = 0, 545 .S = 0, 546 .Rm = Rm, 547 .cond = cond, 548 .op2 = 1, 549 .Rn = Rn, 550 .Rd = Rd}); 551 } 552 static inline u32 aa64_csinv_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { 553 return aa64_condsel_pack((AA64CondSel){.sf = sf, 554 .op = 1, 555 .S = 0, 556 .Rm = Rm, 557 .cond = cond, 558 .op2 = 0, 559 .Rn = Rn, 560 .Rd = Rd}); 561 } 562 static inline u32 aa64_csneg_enc(u32 sf, u32 Rd, u32 Rn, u32 Rm, u32 cond) { 563 return aa64_condsel_pack((AA64CondSel){.sf = sf, 564 .op = 1, 565 .S = 0, 566 .Rm = Rm, 567 .cond = cond, 568 .op2 = 1, 569 .Rn = Rn, 570 .Rd = Rd}); 571 } 572 573 /* ==================================================================== 574 * Unconditional branch (register) — BR / BLR / RET 575 * 1101011 opc(4) op2(5)=11111 op3(6)=000000 Rn(5) op4(5)=00000 576 * 31..25 24..21 20..16 15..10 9..5 4..0 577 * ==================================================================== */ 578 579 #define AA64_BR_OP_BR 0u 580 #define AA64_BR_OP_BLR 1u 581 #define AA64_BR_OP_RET 2u 582 583 #define AA64_BR_REG_FAMILY_MATCH 0xD61F0000u 584 #define AA64_BR_REG_FAMILY_MASK \ 585 0xFE1FFC1Fu /* everything fixed except opc + Rn */ 586 587 typedef struct AA64BrReg { 588 u32 opc, Rn; 589 } AA64BrReg; 590 591 static inline u32 aa64_brreg_pack(AA64BrReg f) { 592 return AA64_BR_REG_FAMILY_MATCH | ((f.opc & 0xfu) << 21) | 593 ((f.Rn & 0x1fu) << 5); 594 } 595 596 static inline AA64BrReg aa64_brreg_unpack(u32 w) { 597 AA64BrReg f; 598 f.opc = (w >> 21) & 0xfu; 599 f.Rn = (w >> 5) & 0x1fu; 600 return f; 601 } 602 603 static inline u32 aa64_br(u32 Rn) { 604 return aa64_brreg_pack((AA64BrReg){.opc = AA64_BR_OP_BR, .Rn = Rn}); 605 } 606 static inline u32 aa64_blr(u32 Rn) { 607 return aa64_brreg_pack((AA64BrReg){.opc = AA64_BR_OP_BLR, .Rn = Rn}); 608 } 609 static inline u32 aa64_ret(u32 Rn) { 610 return aa64_brreg_pack((AA64BrReg){.opc = AA64_BR_OP_RET, .Rn = Rn}); 611 } 612 613 /* ==================================================================== 614 * PC-relative addressing (ADR / ADRP) 615 * op(1) immlo(2) 10000 immhi(19) Rd(5) 616 * 31 30..29 28..24 23..5 4..0 617 * 618 * op = 0 → ADR (PC + sign_extend(immhi:immlo)) 619 * op = 1 → ADRP (page(PC) + sign_extend(immhi:immlo) << 12) 620 * 621 * The two immediate halves stay split because the linker's 622 * R_AARCH64_ADR_PREL_PG_HI21 reloc patches them in place; keeping the 623 * field layout symmetric with the encoded word lets reloc-apply code 624 * reuse the same pack/unpack helpers. 625 * ==================================================================== */ 626 627 #define AA64_ADR_OP_ADR 0u 628 #define AA64_ADR_OP_ADRP 1u 629 630 #define AA64_PCREL_ADR_FAMILY_MATCH 0x10000000u 631 #define AA64_PCREL_ADR_FAMILY_MASK 0x1F000000u /* bits 28:24 */ 632 633 typedef struct AA64PCRelAdr { 634 u32 op, immlo, immhi, Rd; 635 } AA64PCRelAdr; 636 637 static inline u32 aa64_pcrel_adr_pack(AA64PCRelAdr f) { 638 return ((f.op & 1u) << 31) | ((f.immlo & 3u) << 29) | 639 AA64_PCREL_ADR_FAMILY_MATCH | ((f.immhi & 0x7ffffu) << 5) | 640 (f.Rd & 0x1fu); 641 } 642 643 static inline AA64PCRelAdr aa64_pcrel_adr_unpack(u32 w) { 644 AA64PCRelAdr f; 645 f.op = (w >> 31) & 1u; 646 f.immlo = (w >> 29) & 3u; 647 f.immhi = (w >> 5) & 0x7ffffu; 648 f.Rd = w & 0x1fu; 649 return f; 650 } 651 652 static inline u32 aa64_adrp(u32 Rd, u32 immlo, u32 immhi) { 653 return aa64_pcrel_adr_pack((AA64PCRelAdr){ 654 .op = AA64_ADR_OP_ADRP, .immlo = immlo, .immhi = immhi, .Rd = Rd}); 655 } 656 static inline u32 aa64_adr(u32 Rd, u32 immlo, u32 immhi) { 657 return aa64_pcrel_adr_pack((AA64PCRelAdr){ 658 .op = AA64_ADR_OP_ADR, .immlo = immlo, .immhi = immhi, .Rd = Rd}); 659 } 660 661 /* ==================================================================== 662 * Add/Sub, immediate (ADD / SUB / ADDS / SUBS, 12-bit imm with shift) 663 * sf op(1) S(1) 100010 sh(1) imm12(12) Rn(5) Rd(5) 664 * 31 30 29 28..23 22 21..10 9..5 4..0 665 * 666 * sh selects whether imm12 is left-shifted by 12. Used by PLT entries 667 * for `add x16, x16, #lo12(slot)` where sh=0 and imm12 = slot & 0xfff. 668 * ==================================================================== */ 669 670 #define AA64_ADDSUBIMM_FAMILY_MATCH 0x11000000u 671 #define AA64_ADDSUBIMM_FAMILY_MASK 0x1F000000u /* bits 28:24 */ 672 673 typedef struct AA64AddSubImm { 674 u32 sf, op, S, sh, imm12, Rn, Rd; 675 } AA64AddSubImm; 676 677 static inline u32 aa64_addsubimm_pack(AA64AddSubImm f) { 678 return ((f.sf & 1u) << 31) | ((f.op & 1u) << 30) | ((f.S & 1u) << 29) | 679 AA64_ADDSUBIMM_FAMILY_MATCH | ((f.sh & 1u) << 22) | 680 ((f.imm12 & 0xfffu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rd & 0x1fu); 681 } 682 683 static inline AA64AddSubImm aa64_addsubimm_unpack(u32 w) { 684 AA64AddSubImm f; 685 f.sf = (w >> 31) & 1u; 686 f.op = (w >> 30) & 1u; 687 f.S = (w >> 29) & 1u; 688 f.sh = (w >> 22) & 1u; 689 f.imm12 = (w >> 10) & 0xfffu; 690 f.Rn = (w >> 5) & 0x1fu; 691 f.Rd = w & 0x1fu; 692 return f; 693 } 694 695 static inline u32 aa64_add_imm(u32 sf, u32 Rd, u32 Rn, u32 imm12, u32 sh) { 696 return aa64_addsubimm_pack((AA64AddSubImm){ 697 .sf = sf, .op = 0, .sh = sh, .imm12 = imm12, .Rn = Rn, .Rd = Rd}); 698 } 699 static inline u32 aa64_sub_imm(u32 sf, u32 Rd, u32 Rn, u32 imm12, u32 sh) { 700 return aa64_addsubimm_pack((AA64AddSubImm){ 701 .sf = sf, .op = 1, .sh = sh, .imm12 = imm12, .Rn = Rn, .Rd = Rd}); 702 } 703 /* SUBS imm — sets flags. Used for CMP imm (Rd=ZR) and for branchless 704 * compares that feed CSET. The 12-bit-shifted form covers 0..0xFFFFF000 705 * stepped by 0x1000; cg_fold collapses literal-only compares upstream, 706 * so this encoder is reached for `x cmp const` and `if (x)` patterns. */ 707 static inline u32 aa64_subs_imm12(u32 sf, u32 Rd, u32 Rn, u32 imm12, u32 sh) { 708 return aa64_addsubimm_pack((AA64AddSubImm){ 709 .sf = sf, .op = 1, .S = 1, .sh = sh, .imm12 = imm12, .Rn = Rn, .Rd = Rd}); 710 } 711 712 /* Predicate: does `imm` fit ADD/SUB/CMP's 12-bit immediate (optionally 713 * left-shifted by 12)? On success writes the encoded imm12 and sh and 714 * returns 1; on failure returns 0 and leaves outputs untouched. 715 * 716 * The encoding admits 0..4095 directly (sh=0) and multiples of 4096 up 717 * to 0xFFF000 (sh=1). Negative literals are rejected here — the caller 718 * (e.g. opt's machinize, or a smarter cg) is free to swap ADD ↔ SUB and 719 * retry with the negated literal; the bare predicate keeps the contract 720 * narrow. */ 721 static inline int aa64_addsub_imm_fits(i64 imm, u32* imm12_out, u32* sh_out) { 722 if (imm < 0) return 0; 723 u64 u = (u64)imm; 724 if (u <= 0xFFFu) { 725 *imm12_out = (u32)u; 726 *sh_out = 0; 727 return 1; 728 } 729 if ((u & 0xFFFu) == 0 && (u >> 12) <= 0xFFFu) { 730 *imm12_out = (u32)(u >> 12); 731 *sh_out = 1; 732 return 1; 733 } 734 return 0; 735 } 736 737 /* ==================================================================== 738 * Load/store, unsigned 12-bit immediate offset (LDR / STR, scaled) 739 * size(2) 111 V(1) 01 opc(2) imm12(12) Rn(5) Rt(5) 740 * 31..30 29..27 26 25..24 23..22 21..10 9..5 4..0 741 * 742 * size=11, V=0, opc=01 → LDR (64-bit, integer). imm12 is the byte 743 * offset divided by the access size (8 for LDR Xt), giving a 0..32760 744 * byte range. 745 * 746 * Only the LDR Xt form is needed by the linker today (PLT loads through 747 * x16/x17); the family encoders cover STR and the smaller widths so 748 * future callers can drop in without touching this header. 749 * ==================================================================== */ 750 751 #define AA64_LDST_SIZE_64 3u 752 #define AA64_LDST_OPC_STR 0u 753 #define AA64_LDST_OPC_LDR 1u 754 /* opc=10 -> sign-extending load into the 64-bit X register (LDRSB/LDRSH/LDRSW 755 * Xt); opc=11 is the 32-bit W form. Codegen uses the X form so a narrow signed 756 * load fills the whole register (the -O0 widening-signed-load lever). */ 757 #define AA64_LDST_OPC_LDRS_X 2u 758 759 #define AA64_LDST_UIMM_FAMILY_MATCH 0x39000000u 760 #define AA64_LDST_UIMM_FAMILY_MASK 0x3B000000u /* bits 29:27 + bits 25:24 */ 761 762 typedef struct AA64LdStUimm { 763 u32 size, V, opc, imm12, Rn, Rt; 764 } AA64LdStUimm; 765 766 static inline u32 aa64_ldst_uimm_pack(AA64LdStUimm f) { 767 return ((f.size & 3u) << 30) | AA64_LDST_UIMM_FAMILY_MATCH | 768 ((f.V & 1u) << 26) | ((f.opc & 3u) << 22) | 769 ((f.imm12 & 0xfffu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rt & 0x1fu); 770 } 771 772 static inline AA64LdStUimm aa64_ldst_uimm_unpack(u32 w) { 773 AA64LdStUimm f; 774 f.size = (w >> 30) & 3u; 775 f.V = (w >> 26) & 1u; 776 f.opc = (w >> 22) & 3u; 777 f.imm12 = (w >> 10) & 0xfffu; 778 f.Rn = (w >> 5) & 0x1fu; 779 f.Rt = w & 0x1fu; 780 return f; 781 } 782 783 /* LDR Xt, [Xn, #imm12_scaled]. imm12_scaled is the encoded field — 784 * callers pass `byte_offset >> 3` for the 64-bit form. */ 785 static inline u32 aa64_ldr64_uimm12(u32 Rt, u32 Rn, u32 imm12_scaled) { 786 return aa64_ldst_uimm_pack((AA64LdStUimm){.size = AA64_LDST_SIZE_64, 787 .V = 0, 788 .opc = AA64_LDST_OPC_LDR, 789 .imm12 = imm12_scaled, 790 .Rn = Rn, 791 .Rt = Rt}); 792 } 793 static inline u32 aa64_str64_uimm12(u32 Rt, u32 Rn, u32 imm12_scaled) { 794 return aa64_ldst_uimm_pack((AA64LdStUimm){.size = AA64_LDST_SIZE_64, 795 .V = 0, 796 .opc = AA64_LDST_OPC_STR, 797 .imm12 = imm12_scaled, 798 .Rn = Rn, 799 .Rt = Rt}); 800 } 801 802 /* ---- Scalar floating-point encoders ---- 803 * ftype: 0=single (Sn), 1=double (Dn), 3=half (Hn). The bit layouts match the 804 * FP_* decode rows in isa.c and the aa_* encoders in native.c, so encode and 805 * decode round-trip. The DP2/DP1 `op` and the FP_INT_CVT `opcode` are the 806 * named field values below. */ 807 #define AA64_FP_DP2_FMUL 0x0800u 808 #define AA64_FP_DP2_FDIV 0x1800u 809 #define AA64_FP_DP2_FADD 0x2800u 810 #define AA64_FP_DP2_FSUB 0x3800u 811 #define AA64_FP_DP2_FMAX 0x4800u 812 #define AA64_FP_DP2_FMIN 0x5800u 813 #define AA64_FP_DP2_FNMUL 0x8800u 814 #define AA64_FP_DP1_FMOV 0x4000u 815 #define AA64_FP_DP1_FABS 0xC000u 816 #define AA64_FP_DP1_FNEG 0x14000u 817 #define AA64_FP_DP1_FSQRT 0x1C000u 818 #define AA64_FP_ICVT_SCVTF 0x02u 819 #define AA64_FP_ICVT_UCVTF 0x03u 820 #define AA64_FP_ICVT_FCVTZS 0x18u 821 #define AA64_FP_ICVT_FCVTZU 0x19u 822 #define AA64_FP_ICVT_FMOV_TO_GPR 0x06u /* fmov Rd, Vn */ 823 #define AA64_FP_ICVT_FMOV_TO_FP 0x07u /* fmov Vd, Rn */ 824 825 static inline u32 aa64_fp_dp2(u32 ftype, u32 op, u32 Rd, u32 Rn, u32 Rm) { 826 return 0x1E200000u | ((ftype & 3u) << 22) | op | ((Rm & 0x1fu) << 16) | 827 ((Rn & 0x1fu) << 5) | (Rd & 0x1fu); 828 } 829 static inline u32 aa64_fp_dp1(u32 ftype, u32 op, u32 Rd, u32 Rn) { 830 return 0x1E200000u | ((ftype & 3u) << 22) | op | ((Rn & 0x1fu) << 5) | 831 (Rd & 0x1fu); 832 } 833 static inline u32 aa64_fcmp_reg(u32 ftype, u32 Rn, u32 Rm) { 834 return 0x1E202000u | ((ftype & 3u) << 22) | ((Rm & 0x1fu) << 16) | 835 ((Rn & 0x1fu) << 5); 836 } 837 static inline u32 aa64_fcvt_prec(u32 src_ftype, u32 dst_ftype, u32 Rd, u32 Rn) { 838 return 0x1E204000u | ((src_ftype & 3u) << 22) | (1u << 17) | 839 ((dst_ftype & 3u) << 15) | ((Rn & 0x1fu) << 5) | (Rd & 0x1fu); 840 } 841 static inline u32 aa64_fp_int_cvt(u32 sf, u32 ftype, u32 opcode, u32 Rd, 842 u32 Rn) { 843 return ((sf & 1u) << 31) | 0x1E200000u | ((ftype & 3u) << 22) | 844 ((opcode & 0x1fu) << 16) | ((Rn & 0x1fu) << 5) | (Rd & 0x1fu); 845 } 846 847 /* Bitfield move (opc: 0=SBFM, 1=BFM, 2=UBFM). The N bit tracks sf for the 848 * 32-/64-bit forms. Matches native.c aa_sbfm/aa_ubfm and the BITFIELD row. */ 849 static inline u32 aa64_bitfield(u32 sf, u32 opc, u32 immr, u32 imms, u32 Rd, 850 u32 Rn) { 851 return ((sf & 1u) << 31) | ((opc & 3u) << 29) | 0x13000000u | 852 ((sf & 1u) << 22) | ((immr & 0x3fu) << 16) | ((imms & 0x3fu) << 10) | 853 ((Rn & 0x1fu) << 5) | (Rd & 0x1fu); 854 } 855 856 /* Data-processing (1 source). opcode2 (bits[15:10]): RBIT=0, REV16=1, 857 * REV(32)=2, REV(64)=3, CLZ=4. Matches native.c aa_clz/aa_rbit/aa_rev. */ 858 #define AA64_DP1_RBIT 0x00u 859 #define AA64_DP1_REV16 0x01u 860 #define AA64_DP1_REV32 0x02u 861 #define AA64_DP1_REV64 0x03u 862 #define AA64_DP1_CLZ 0x04u 863 static inline u32 aa64_dp1(u32 sf, u32 opcode2, u32 Rd, u32 Rn) { 864 return ((sf & 1u) << 31) | 0x5AC00000u | ((opcode2 & 0x3fu) << 10) | 865 ((Rn & 0x1fu) << 5) | (Rd & 0x1fu); 866 } 867 868 /* ==================================================================== 869 * Load/store register pair, pre-indexed (STP / LDP, 64-bit form) 870 * opc(2) 101 V(1) 010 L(1) imm7(7) Rt2(5) Rn(5) Rt(5) 871 * 31..30 29..27 26 25..23 22 21..15 14..10 9..5 4..0 872 * 873 * 64-bit integer form fixes opc=10, V=0. L=0 → STP, L=1 → LDP. 874 * imm7 is a signed 7-bit value scaled by 8 (for the 64-bit form): the 875 * encoded field equals `byte_offset / 8`. Callers pass the scaled 876 * value already; the helper masks to 7 bits to handle negative inputs 877 * sign-extended in i32. 878 * ==================================================================== */ 879 880 #define AA64_LDSTP_PRE_FAMILY_MATCH 0x29800000u 881 #define AA64_LDSTP_PRE_FAMILY_MASK 0x7FC00000u /* bits 30:23 */ 882 883 typedef struct AA64LdStPPre { 884 u32 opc, V, L, imm7, Rt2, Rn, Rt; 885 } AA64LdStPPre; 886 887 static inline u32 aa64_ldstp_pre_pack(AA64LdStPPre f) { 888 return ((f.opc & 3u) << 30) | AA64_LDSTP_PRE_FAMILY_MATCH | 889 ((f.V & 1u) << 26) | ((f.L & 1u) << 22) | ((f.imm7 & 0x7fu) << 15) | 890 ((f.Rt2 & 0x1fu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rt & 0x1fu); 891 } 892 893 static inline AA64LdStPPre aa64_ldstp_pre_unpack(u32 w) { 894 AA64LdStPPre f; 895 f.opc = (w >> 30) & 3u; 896 f.V = (w >> 26) & 1u; 897 f.L = (w >> 22) & 1u; 898 f.imm7 = (w >> 15) & 0x7fu; 899 f.Rt2 = (w >> 10) & 0x1fu; 900 f.Rn = (w >> 5) & 0x1fu; 901 f.Rt = w & 0x1fu; 902 return f; 903 } 904 905 /* STP Xt, Xt2, [Xn, #imm7_scaled]! — opc=10 selects the 64-bit form. 906 * imm7_scaled is `byte_offset / 8`; callers pass it pre-scaled (e.g. 907 * -2 for [sp, #-16]!). */ 908 static inline u32 aa64_stp64_pre(u32 Rt, u32 Rt2, u32 Rn, i32 imm7_scaled) { 909 return aa64_ldstp_pre_pack((AA64LdStPPre){.opc = 2, 910 .V = 0, 911 .L = 0, 912 .imm7 = (u32)imm7_scaled & 0x7fu, 913 .Rt2 = Rt2, 914 .Rn = Rn, 915 .Rt = Rt}); 916 } 917 static inline u32 aa64_ldp64_pre(u32 Rt, u32 Rt2, u32 Rn, i32 imm7_scaled) { 918 return aa64_ldstp_pre_pack((AA64LdStPPre){.opc = 2, 919 .V = 0, 920 .L = 1, 921 .imm7 = (u32)imm7_scaled & 0x7fu, 922 .Rt2 = Rt2, 923 .Rn = Rn, 924 .Rt = Rt}); 925 } 926 927 /* Post-indexed STP/LDP — same field layout as the pre-indexed form, only 928 * bits[25:23] differ (001 vs 011); reuse AA64LdStPPre. Used for the slim 929 * prologue's epilogue restore: `ldp x29,x30,[sp],#16`. */ 930 #define AA64_LDSTP_POST_FAMILY_MATCH 0x28800000u 931 #define AA64_LDSTP_POST_FAMILY_MASK 0x7FC00000u /* bits 30:23 */ 932 933 static inline u32 aa64_ldstp_post_pack(AA64LdStPPre f) { 934 return ((f.opc & 3u) << 30) | AA64_LDSTP_POST_FAMILY_MATCH | 935 ((f.V & 1u) << 26) | ((f.L & 1u) << 22) | ((f.imm7 & 0x7fu) << 15) | 936 ((f.Rt2 & 0x1fu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rt & 0x1fu); 937 } 938 939 static inline u32 aa64_stp64_post(u32 Rt, u32 Rt2, u32 Rn, i32 imm7_scaled) { 940 return aa64_ldstp_post_pack((AA64LdStPPre){.opc = 2, 941 .V = 0, 942 .L = 0, 943 .imm7 = (u32)imm7_scaled & 0x7fu, 944 .Rt2 = Rt2, 945 .Rn = Rn, 946 .Rt = Rt}); 947 } 948 static inline u32 aa64_ldp64_post(u32 Rt, u32 Rt2, u32 Rn, i32 imm7_scaled) { 949 return aa64_ldstp_post_pack((AA64LdStPPre){.opc = 2, 950 .V = 0, 951 .L = 1, 952 .imm7 = (u32)imm7_scaled & 0x7fu, 953 .Rt2 = Rt2, 954 .Rn = Rn, 955 .Rt = Rt}); 956 } 957 958 /* ==================================================================== 959 * Hint instructions (NOP / YIELD / WFE / WFI / SEV / SEVL) 960 * 1101 0101 0000 0011 0010 CRm(4) op2(3) 11111 961 * 31..16 15..12 11..8 7..5 4..0 962 * 963 * NOP encodes CRm=0, op2=0 → 0xD503201F. The full hint family lives 964 * inside the system-instruction space; we only expose NOP today since 965 * that's the only slot the linker fills. 966 * ==================================================================== */ 967 968 #define AA64_HINT_FAMILY_MATCH 0xD503201Fu 969 #define AA64_HINT_FAMILY_MASK 0xFFFFF01Fu /* CRm + op2 vary */ 970 971 /* HINT #N with CRm=0: op2 selects the variant. */ 972 #define AA64_HINT_OP_NOP 0u /* CRm=0, op2=0 */ 973 #define AA64_HINT_OP_YIELD 1u /* CRm=0, op2=1 */ 974 #define AA64_HINT_OP_WFE 2u /* CRm=0, op2=2 */ 975 #define AA64_HINT_OP_WFI 3u /* CRm=0, op2=3 */ 976 #define AA64_HINT_OP_SEV 4u /* CRm=0, op2=4 */ 977 #define AA64_HINT_OP_SEVL 5u /* CRm=0, op2=5 */ 978 979 typedef struct AA64Hint { 980 u32 CRm, op2; 981 } AA64Hint; 982 983 static inline u32 aa64_hint_pack(AA64Hint f) { 984 return AA64_HINT_FAMILY_MATCH | ((f.CRm & 0xfu) << 8) | ((f.op2 & 7u) << 5); 985 } 986 987 static inline u32 aa64_hint(u32 op2) { 988 return aa64_hint_pack((AA64Hint){.CRm = 0, .op2 = op2}); 989 } 990 991 static inline AA64Hint aa64_hint_unpack(u32 w) { 992 AA64Hint f; 993 f.CRm = (w >> 8) & 0xfu; 994 f.op2 = (w >> 5) & 7u; 995 return f; 996 } 997 998 static inline u32 aa64_nop(void) { 999 return aa64_hint_pack((AA64Hint){.CRm = 0, .op2 = AA64_HINT_OP_NOP}); 1000 } 1001 1002 /* ==================================================================== 1003 * Memory barriers (DMB / DSB / ISB / CLREX) 1004 * 1101 0101 0000 0011 0011 CRm(4) op2(3) 11111 1005 * 31..16 15..12 11..8 7..5 4..0 1006 * 1007 * Shared encoding family with HINT (which uses bits[15:12]=0010); 1008 * barriers use bits[15:12]=0011. op2 selects the specific instruction: 1009 * CLREX=010 DSB=100 DMB=101 ISB=110 1010 * CRm is the option / domain (SY=15, ISH=11, NSH=7, OSH=3, ...). 1011 * ==================================================================== */ 1012 1013 #define AA64_BARRIER_FAMILY_MATCH 0xD503301Fu 1014 #define AA64_BARRIER_FAMILY_MASK 0xFFFFF01Fu /* CRm + op2 vary */ 1015 1016 #define AA64_BARRIER_OP2_CLREX 2u 1017 #define AA64_BARRIER_OP2_DSB 4u 1018 #define AA64_BARRIER_OP2_DMB 5u 1019 #define AA64_BARRIER_OP2_ISB 6u 1020 1021 /* Common CRm option encodings (ARM ARM C5.1.42). */ 1022 #define AA64_BARRIER_OPT_OSHLD 1u 1023 #define AA64_BARRIER_OPT_OSHST 2u 1024 #define AA64_BARRIER_OPT_OSH 3u 1025 #define AA64_BARRIER_OPT_NSHLD 5u 1026 #define AA64_BARRIER_OPT_NSHST 6u 1027 #define AA64_BARRIER_OPT_NSH 7u 1028 #define AA64_BARRIER_OPT_ISHLD 9u 1029 #define AA64_BARRIER_OPT_ISHST 10u 1030 #define AA64_BARRIER_OPT_ISH 11u 1031 #define AA64_BARRIER_OPT_LD 13u 1032 #define AA64_BARRIER_OPT_ST 14u 1033 #define AA64_BARRIER_OPT_SY 15u 1034 1035 typedef struct AA64Barrier { 1036 u32 CRm, op2; 1037 } AA64Barrier; 1038 1039 static inline u32 aa64_barrier_pack(AA64Barrier f) { 1040 return AA64_BARRIER_FAMILY_MATCH | ((f.CRm & 0xfu) << 8) | 1041 ((f.op2 & 7u) << 5); 1042 } 1043 1044 static inline AA64Barrier aa64_barrier_unpack(u32 w) { 1045 AA64Barrier f; 1046 f.CRm = (w >> 8) & 0xfu; 1047 f.op2 = (w >> 5) & 7u; 1048 return f; 1049 } 1050 1051 static inline u32 aa64_dmb(u32 opt) { 1052 return aa64_barrier_pack( 1053 (AA64Barrier){.CRm = opt, .op2 = AA64_BARRIER_OP2_DMB}); 1054 } 1055 static inline u32 aa64_dsb(u32 opt) { 1056 return aa64_barrier_pack( 1057 (AA64Barrier){.CRm = opt, .op2 = AA64_BARRIER_OP2_DSB}); 1058 } 1059 static inline u32 aa64_isb(u32 opt) { 1060 return aa64_barrier_pack( 1061 (AA64Barrier){.CRm = opt, .op2 = AA64_BARRIER_OP2_ISB}); 1062 } 1063 static inline u32 aa64_clrex(u32 opt) { 1064 return aa64_barrier_pack( 1065 (AA64Barrier){.CRm = opt, .op2 = AA64_BARRIER_OP2_CLREX}); 1066 } 1067 1068 /* ==================================================================== 1069 * Interrupt-mask (DAIF) system register access. Used by the IRQ-control 1070 * intrinsics; privileged at EL0. Only the encodings the backend emits live 1071 * here (they are not registered in the disassembler's mnemonic table). 1072 * MRS Xt, DAIF : 1101 0101 0011 1011 0100 0010 000 Rt -> 0xD53B4200|Rt 1073 * MSR DAIF, Xt : 1101 0101 0001 1011 0100 0010 000 Rt -> 0xD51B4200|Rt 1074 * MSR DAIFSet, #imm4 : op1=011, op2=110 -> 0xD50340DF | (imm4 << 8) 1075 * MSR DAIFClr, #imm4 : op1=011, op2=111 -> 0xD50340FF | (imm4 << 8) 1076 * imm4 = 0xF masks/unmasks D,A,I,F together. ==================== */ 1077 #define AA64_DAIF_ALL 0xfu 1078 1079 static inline u32 aa64_mrs_daif(u32 rt) { return 0xD53B4200u | (rt & 0x1fu); } 1080 static inline u32 aa64_msr_daif(u32 rt) { return 0xD51B4200u | (rt & 0x1fu); } 1081 static inline u32 aa64_msr_daifset(u32 imm4) { 1082 return 0xD50340DFu | ((imm4 & 0xfu) << 8); 1083 } 1084 static inline u32 aa64_msr_daifclr(u32 imm4) { 1085 return 0xD50340FFu | ((imm4 & 0xfu) << 8); 1086 } 1087 1088 /* ==================================================================== 1089 * Generic system-register move (MRS/MSR register form). A named system 1090 * register is the 15-bit selector op0:op1:CRn:CRm:op2 (op0's high bit is 1091 * fixed by the encoding, so only its low bit is a field). 1092 * MRS Xt, <sysreg> : 1101 0101 0 0 1 op0lo op1 CRn CRm op2 Rt (read, L=1) 1093 * MSR <sysreg>, Xt : 1101 0101 0 0 0 op0lo op1 CRn CRm op2 Rt (write, L=0) 1094 * e.g. TPIDR_EL0 = (op0=3,op1=3,CRn=13,CRm=0,op2=2): 1095 * MSR TPIDR_EL0, X0 -> 0xd51bd040 ; MRS X0, TPIDR_EL0 -> 0xd53bd040. */ 1096 static inline u32 aa64_sysreg_move(int is_read, u32 op0, u32 op1, u32 crn, 1097 u32 crm, u32 op2, u32 rt) { 1098 return 0xd5000000u | (is_read ? (1u << 21) : 0u) | ((op0 & 3u) << 19) | 1099 ((op1 & 7u) << 16) | ((crn & 0xfu) << 12) | ((crm & 0xfu) << 8) | 1100 ((op2 & 7u) << 5) | (rt & 0x1fu); 1101 } 1102 1103 /* System-register move encoding family: MRS (read, L=1) and MSR (write, 1104 * L=0). The disassembler matches these; the register selector op0:op1:CRn: 1105 * CRm:op2 and Rt are decoded from the word. op0's high bit is fixed (bit 1106 * 20), so the mask pins bits[31:20] and leaves op0lo/op1/CRn/CRm/op2/Rt. */ 1107 #define AA64_MRS_MATCH 0xd5300000u 1108 #define AA64_MSR_MATCH 0xd5100000u 1109 #define AA64_SYSREG_MOVE_MASK 0xfff00000u 1110 1111 /* Shared system-register name table (single source for the assembler's 1112 * name->selector parse and the disassembler's selector->name print). */ 1113 typedef struct AA64SysRegName { 1114 const char* name; 1115 u8 op0, op1, crn, crm, op2; 1116 } AA64SysRegName; 1117 1118 /* Resolve a system-register name (case-insensitive, length n) to its five 1119 * selector fields. Returns 1 on a hit, 0 otherwise. */ 1120 int aa64_sysreg_by_name(const char* s, size_t n, u32* op0, u32* op1, u32* crn, 1121 u32* crm, u32* op2); 1122 1123 /* Reverse lookup: canonical lowercase name for a selector, or NULL when the 1124 * selector is not in the table (the caller prints the generic Sx_x_Cx_Cx_x 1125 * spelling instead). */ 1126 const char* aa64_sysreg_name(u32 op0, u32 op1, u32 crn, u32 crm, u32 op2); 1127 1128 /* Condition-code <-> name, the single source of truth shared by the 1129 * disassembler, the codegen/assembler print path, and the assembler parse. 1130 * aa64_cond_name returns the canonical lowercase suffix for a 4-bit cond 1131 * (masked to bits[3:0]); EQ=0..AL=14, NV=15. aa64_cond_from_name parses a 1132 * name (case-insensitive, length n) back to its index, accepting the HS/LO 1133 * aliases for CS/CC; it rejects NV. Returns 1 on a hit, 0 otherwise. */ 1134 const char* aa64_cond_name(u32 cond); 1135 int aa64_cond_from_name(const char* s, size_t n, u32* out); 1136 1137 /* ==================================================================== 1138 * Load/store pair, signed-offset (STP / LDP, no pre/post-increment). 1139 * opc(2) 101 V(1) 010 L(1) imm7 Rt2 Rn Rt (bit 23 = 0) 1140 * 1141 * Mirrors the LDSTP_PRE format with bit 23 cleared; the field layout is 1142 * otherwise identical and the pack/unpack helpers above are reused for 1143 * pre/post/sign-offset via different family-match constants. Codegen 1144 * emits both X (opc=10) and FP-D (opc=01, V=1) variants for callee-save 1145 * spill/reload (`stp x29,x30,[sp,#16]`, `stp d8,d9,[sp,#32]`). */ 1146 1147 #define AA64_LDSTP_SOFF_FAMILY_MATCH 0x29000000u 1148 #define AA64_LDSTP_SOFF_FAMILY_MASK 0x7FC00000u /* bits 30:23 (bit 23 = 0) */ 1149 1150 typedef AA64LdStPPre AA64LdStPSOff; 1151 1152 static inline u32 aa64_ldstp_soff_pack(AA64LdStPSOff f) { 1153 return ((f.opc & 3u) << 30) | AA64_LDSTP_SOFF_FAMILY_MATCH | 1154 ((f.V & 1u) << 26) | ((f.L & 1u) << 22) | ((f.imm7 & 0x7fu) << 15) | 1155 ((f.Rt2 & 0x1fu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rt & 0x1fu); 1156 } 1157 1158 static inline AA64LdStPSOff aa64_ldstp_soff_unpack(u32 w) { 1159 AA64LdStPSOff f; 1160 f.opc = (w >> 30) & 3u; 1161 f.V = (w >> 26) & 1u; 1162 f.L = (w >> 22) & 1u; 1163 f.imm7 = (w >> 15) & 0x7fu; 1164 f.Rt2 = (w >> 10) & 0x1fu; 1165 f.Rn = (w >> 5) & 0x1fu; 1166 f.Rt = w & 0x1fu; 1167 return f; 1168 } 1169 1170 /* 64-bit integer STP/LDP, signed offset (no writeback). imm7_scaled is 1171 * byte_offset / 8. Used for the prologue/epilogue frame record and callee-save 1172 * pairs, which address off a fixed base (x17 / fp). */ 1173 static inline u32 aa64_stp64_soff(u32 Rt, u32 Rt2, u32 Rn, i32 imm7_scaled) { 1174 return aa64_ldstp_soff_pack((AA64LdStPSOff){.opc = 2, 1175 .V = 0, 1176 .L = 0, 1177 .imm7 = (u32)imm7_scaled & 0x7fu, 1178 .Rt2 = Rt2, 1179 .Rn = Rn, 1180 .Rt = Rt}); 1181 } 1182 static inline u32 aa64_ldp64_soff(u32 Rt, u32 Rt2, u32 Rn, i32 imm7_scaled) { 1183 return aa64_ldstp_soff_pack((AA64LdStPSOff){.opc = 2, 1184 .V = 0, 1185 .L = 1, 1186 .imm7 = (u32)imm7_scaled & 0x7fu, 1187 .Rt2 = Rt2, 1188 .Rn = Rn, 1189 .Rt = Rt}); 1190 } 1191 1192 /* ==================================================================== 1193 * Load/store, unscaled 9-bit signed offset (LDUR / STUR, V=0 and V=1). 1194 * size(2) 111 V(1) 00 opc(2) 0 imm9(9) 00 Rn(5) Rt(5) 1195 * 31..30 29..27 26 25..24 23..22 21 20..12 11..10 9..5 4..0 1196 * 1197 * size: 00=B, 01=H, 10=W, 11=X (V=0) — D when V=1 selects FP/SIMD. 1198 * opc: 00=STR, 01=LDR (sign-extension variants set opc bit 1 for the 1199 * smaller widths; not used by codegen today). */ 1200 1201 #define AA64_LDST_SIMM9_FAMILY_MATCH 0x38000000u 1202 /* bits 29:27 (=111) + bits 25:24 (=00) + bits 11:10 (=00). size, V, opc, 1203 * imm9, Rn, Rt all vary; bit 21 is fixed 0 for this variant. */ 1204 #define AA64_LDST_SIMM9_FAMILY_MASK 0x3B200C00u 1205 1206 typedef struct AA64LdStSimm9 { 1207 u32 size, V, opc, imm9, Rn, Rt; 1208 } AA64LdStSimm9; 1209 1210 static inline u32 aa64_ldst_simm9_pack(AA64LdStSimm9 f) { 1211 return ((f.size & 3u) << 30) | AA64_LDST_SIMM9_FAMILY_MATCH | 1212 ((f.V & 1u) << 26) | ((f.opc & 3u) << 22) | ((f.imm9 & 0x1ffu) << 12) | 1213 ((f.Rn & 0x1fu) << 5) | (f.Rt & 0x1fu); 1214 } 1215 1216 static inline AA64LdStSimm9 aa64_ldst_simm9_unpack(u32 w) { 1217 AA64LdStSimm9 f; 1218 f.size = (w >> 30) & 3u; 1219 f.V = (w >> 26) & 1u; 1220 f.opc = (w >> 22) & 3u; 1221 f.imm9 = (w >> 12) & 0x1ffu; 1222 f.Rn = (w >> 5) & 0x1fu; 1223 f.Rt = w & 0x1fu; 1224 return f; 1225 } 1226 1227 /* ==================================================================== 1228 * Load/store, register offset (LDR/STR Rt,[Xn,Rm{,extend{#s}}]). 1229 * size(2) 111 V(1) 00 opc(2) 1 Rm(5) option(3) S(1) 10 Rn(5) Rt(5) 1230 * 31..30 29..27 26 25..24 23..22 21 20..16 15..13 12 11..10 9..5 4..0 1231 * 1232 * option selects the index extend: 010=UXTW, 011=LSL/UXTX, 110=SXTW, 1233 * 111=SXTX. S=1 scales the index by the access size (log2 = size); S=0 1234 * leaves it unscaled. opc/size match the uimm12 form. */ 1235 1236 #define AA64_LDST_REGOFF_FAMILY_MATCH 0x38200800u 1237 /* bits 29:27 (=111), 25:24 (=00), 21 (=1), 11:10 (=10). */ 1238 #define AA64_LDST_REGOFF_FAMILY_MASK 0x3B200C00u 1239 1240 /* Index-extend option encodings. */ 1241 #define AA64_LDST_OPTION_UXTW 2u 1242 #define AA64_LDST_OPTION_LSL 3u /* a.k.a. UXTX for 64-bit index */ 1243 #define AA64_LDST_OPTION_SXTW 6u 1244 #define AA64_LDST_OPTION_SXTX 7u 1245 1246 typedef struct AA64LdStRegOff { 1247 u32 size, V, opc, Rm, option, S, Rn, Rt; 1248 } AA64LdStRegOff; 1249 1250 static inline u32 aa64_ldst_regoff_pack(AA64LdStRegOff f) { 1251 return ((f.size & 3u) << 30) | AA64_LDST_REGOFF_FAMILY_MATCH | 1252 ((f.V & 1u) << 26) | ((f.opc & 3u) << 22) | ((f.Rm & 0x1fu) << 16) | 1253 ((f.option & 7u) << 13) | ((f.S & 1u) << 12) | ((f.Rn & 0x1fu) << 5) | 1254 (f.Rt & 0x1fu); 1255 } 1256 1257 static inline AA64LdStRegOff aa64_ldst_regoff_unpack(u32 w) { 1258 AA64LdStRegOff f; 1259 f.size = (w >> 30) & 3u; 1260 f.V = (w >> 26) & 1u; 1261 f.opc = (w >> 22) & 3u; 1262 f.Rm = (w >> 16) & 0x1fu; 1263 f.option = (w >> 13) & 7u; 1264 f.S = (w >> 12) & 1u; 1265 f.Rn = (w >> 5) & 0x1fu; 1266 f.Rt = w & 0x1fu; 1267 return f; 1268 } 1269 1270 /* ==================================================================== 1271 * Load/store, immediate pre/post-index (writeback). 1272 * size(2) 111 V(1) 00 opc(2) 0 imm9(9) idx(2) Rn(5) Rt(5) 1273 * 31..30 29..27 26 25..24 23..22 21 20..12 11..10 9..5 4..0 1274 * 1275 * idx (bits[11:10]) selects: 00=unscaled (LDUR, no writeback — see the 1276 * SIMM9 helpers above), 01=post-index, 11=pre-index. imm9 is the 1277 * unscaled signed byte offset (-256..255). */ 1278 1279 #define AA64_LDST_IDX_POST 1u 1280 #define AA64_LDST_IDX_PRE 3u 1281 1282 typedef struct AA64LdStWBack { 1283 u32 size, V, opc, imm9, idx, Rn, Rt; 1284 } AA64LdStWBack; 1285 1286 static inline u32 aa64_ldst_wback_pack(AA64LdStWBack f) { 1287 return ((f.size & 3u) << 30) | AA64_LDST_SIMM9_FAMILY_MATCH | 1288 ((f.V & 1u) << 26) | ((f.opc & 3u) << 22) | ((f.imm9 & 0x1ffu) << 12) | 1289 ((f.idx & 3u) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rt & 0x1fu); 1290 } 1291 1292 static inline AA64LdStWBack aa64_ldst_wback_unpack(u32 w) { 1293 AA64LdStWBack f; 1294 f.size = (w >> 30) & 3u; 1295 f.V = (w >> 26) & 1u; 1296 f.opc = (w >> 22) & 3u; 1297 f.imm9 = (w >> 12) & 0x1ffu; 1298 f.idx = (w >> 10) & 3u; 1299 f.Rn = (w >> 5) & 0x1fu; 1300 f.Rt = w & 0x1fu; 1301 return f; 1302 } 1303 1304 /* ==================================================================== 1305 * Load/store exclusive (LDXR/STXR + acquire/release variants). 1306 * size(2) 001000 o2(1) L(1) o1(1) Rs(5) o0(1) Rt2(5) Rn(5) Rt(5) 1307 * 31..30 29..24 23 22 21 20..16 15 14..10 9..5 4..0 1308 * 1309 * size: 00=byte,01=half,10=word,11=dword. o1=0 for the LDXR/STXR 1310 * single-register family (CAS sets o1=1 via the CAS pack below). 1311 * LDXR: L=1 o0=0 o2=0 STXR: L=0 o0=0 o2=0 1312 * LDAXR: L=1 o0=1 o2=0 STLXR: L=0 o0=1 o2=0 1313 * LDAR: L=1 o0=1 o2=1 STLR: L=0 o0=1 o2=1 1314 * For LDXR/LDAXR/LDAR/STLR, Rs and Rt2 are unused (encode 11111). */ 1315 1316 #define AA64_LDSTEX_FAMILY_MATCH 0x08000000u 1317 /* bits 29:24 (=001000). */ 1318 #define AA64_LDSTEX_FAMILY_MASK 0x3F000000u 1319 1320 typedef struct AA64LdStEx { 1321 u32 size, o2, L, o1, Rs, o0, Rt2, Rn, Rt; 1322 } AA64LdStEx; 1323 1324 static inline u32 aa64_ldstex_pack(AA64LdStEx f) { 1325 return ((f.size & 3u) << 30) | AA64_LDSTEX_FAMILY_MATCH | 1326 ((f.o2 & 1u) << 23) | ((f.L & 1u) << 22) | ((f.o1 & 1u) << 21) | 1327 ((f.Rs & 0x1fu) << 16) | ((f.o0 & 1u) << 15) | 1328 ((f.Rt2 & 0x1fu) << 10) | ((f.Rn & 0x1fu) << 5) | (f.Rt & 0x1fu); 1329 } 1330 1331 static inline AA64LdStEx aa64_ldstex_unpack(u32 w) { 1332 AA64LdStEx f; 1333 f.size = (w >> 30) & 3u; 1334 f.o2 = (w >> 23) & 1u; 1335 f.L = (w >> 22) & 1u; 1336 f.o1 = (w >> 21) & 1u; 1337 f.Rs = (w >> 16) & 0x1fu; 1338 f.o0 = (w >> 15) & 1u; 1339 f.Rt2 = (w >> 10) & 0x1fu; 1340 f.Rn = (w >> 5) & 0x1fu; 1341 f.Rt = w & 0x1fu; 1342 return f; 1343 } 1344 1345 /* ==================================================================== 1346 * Compare and swap (CAS / CASA / CASL / CASAL + b/h variants, LSE). 1347 * size(2) 001000 1 L(1) 1 Rs(5) o0(1) 11111 Rn(5) Rt(5) 1348 * 31..30 29..23 . 22 . 20..16 15 14..10 9..5 4..0 1349 * 1350 * CAS: L=0 o0=0 CASA: L=1 o0=0 1351 * CASL: L=0 o0=1 CASAL: L=1 o0=1 1352 * Rt2 (bits[14:10]) is fixed at 11111. */ 1353 1354 #define AA64_CAS_FAMILY_MATCH 0x08a07c00u 1355 /* bits 29:24 (=001000), 23 (=1), 21 (=1), 14:10 (=11111). */ 1356 #define AA64_CAS_FAMILY_MASK 0x3Fa0fc00u 1357 1358 typedef struct AA64Cas { 1359 u32 size, L, Rs, o0, Rn, Rt; 1360 } AA64Cas; 1361 1362 static inline u32 aa64_cas_pack(AA64Cas f) { 1363 return ((f.size & 3u) << 30) | AA64_CAS_FAMILY_MATCH | ((f.L & 1u) << 22) | 1364 ((f.Rs & 0x1fu) << 16) | ((f.o0 & 1u) << 15) | ((f.Rn & 0x1fu) << 5) | 1365 (f.Rt & 0x1fu); 1366 } 1367 1368 static inline AA64Cas aa64_cas_unpack(u32 w) { 1369 AA64Cas f; 1370 f.size = (w >> 30) & 3u; 1371 f.L = (w >> 22) & 1u; 1372 f.Rs = (w >> 16) & 0x1fu; 1373 f.o0 = (w >> 15) & 1u; 1374 f.Rn = (w >> 5) & 0x1fu; 1375 f.Rt = w & 0x1fu; 1376 return f; 1377 } 1378 1379 /* ==================================================================== 1380 * LSE atomic memory operations (SWP / LDADD / LDCLR / LDEOR / LDSET + 1381 * acquire/release variants and b/h widths). 1382 * size(2) 111 V(1) 00 A(1) R(1) 1 Rs(5) o3(1) opc(3) 00 Rn(5) Rt(5) 1383 * 31..30 29..27 26 25..24 23 22 21 20..16 15 14..12 11..10 9..5 4..0 1384 * 1385 * A=acquire (a-suffix), R=release (l-suffix). o3=1 selects SWP (opc=000); 1386 * o3=0 with opc in {000=LDADD,001=LDCLR,010=LDEOR,011=LDSET}. */ 1387 1388 #define AA64_LSE_ATOMIC_FAMILY_MATCH 0x38200000u 1389 /* bits 29:27 (=111), 25:24 (=00), 21 (=1), 11:10 (=00). */ 1390 #define AA64_LSE_ATOMIC_FAMILY_MASK 0x3B200C00u 1391 1392 #define AA64_LSE_OPC_LDADD 0u 1393 #define AA64_LSE_OPC_LDCLR 1u 1394 #define AA64_LSE_OPC_LDEOR 2u 1395 #define AA64_LSE_OPC_LDSET 3u 1396 #define AA64_LSE_OPC_SWP 0u /* paired with o3=1 */ 1397 1398 typedef struct AA64LseAtomic { 1399 u32 size, A, R, Rs, o3, opc, Rn, Rt; 1400 } AA64LseAtomic; 1401 1402 static inline u32 aa64_lse_atomic_pack(AA64LseAtomic f) { 1403 return ((f.size & 3u) << 30) | AA64_LSE_ATOMIC_FAMILY_MATCH | 1404 ((f.A & 1u) << 23) | ((f.R & 1u) << 22) | ((f.Rs & 0x1fu) << 16) | 1405 ((f.o3 & 1u) << 15) | ((f.opc & 7u) << 12) | ((f.Rn & 0x1fu) << 5) | 1406 (f.Rt & 0x1fu); 1407 } 1408 1409 static inline AA64LseAtomic aa64_lse_atomic_unpack(u32 w) { 1410 AA64LseAtomic f; 1411 f.size = (w >> 30) & 3u; 1412 f.A = (w >> 23) & 1u; 1413 f.R = (w >> 22) & 1u; 1414 f.Rs = (w >> 16) & 0x1fu; 1415 f.o3 = (w >> 15) & 1u; 1416 f.opc = (w >> 12) & 7u; 1417 f.Rn = (w >> 5) & 0x1fu; 1418 f.Rt = w & 0x1fu; 1419 return f; 1420 } 1421 1422 /* ==================================================================== 1423 * Unconditional branch (immediate) — B / BL 1424 * op(1) 00101 imm26(26) 1425 * 31 30..26 25..0 1426 * 1427 * op=0 → B, op=1 → BL. imm26 is a signed 26-bit word displacement 1428 * (multiply by 4 to get byte offset). Codegen emits with imm26=0 paired 1429 * with a JUMP26 / CALL26 relocation. */ 1430 1431 #define AA64_BR_IMM_FAMILY_MATCH 0x14000000u 1432 #define AA64_BR_IMM_FAMILY_MASK 0x7C000000u /* bits 30:26 (=00101) */ 1433 1434 typedef struct AA64BrImm { 1435 u32 op, imm26; 1436 } AA64BrImm; 1437 1438 static inline u32 aa64_brimm_pack(AA64BrImm f) { 1439 return ((f.op & 1u) << 31) | AA64_BR_IMM_FAMILY_MATCH | 1440 (f.imm26 & 0x3ffffffu); 1441 } 1442 1443 static inline AA64BrImm aa64_brimm_unpack(u32 w) { 1444 AA64BrImm f; 1445 f.op = (w >> 31) & 1u; 1446 f.imm26 = w & 0x3ffffffu; 1447 return f; 1448 } 1449 1450 static inline u32 aa64_b(u32 imm26) { 1451 return aa64_brimm_pack((AA64BrImm){.op = 0, .imm26 = imm26}); 1452 } 1453 static inline u32 aa64_bl(u32 imm26) { 1454 return aa64_brimm_pack((AA64BrImm){.op = 1, .imm26 = imm26}); 1455 } 1456 1457 /* ==================================================================== 1458 * Conditional branch (immediate) — B.cond 1459 * 0101 0100 imm19(19) 0 cond(4) 1460 * 31..24 23..5 4 3..0 1461 * 1462 * imm19 is a signed 19-bit word displacement; cond is the 4-bit ARM 1463 * condition code (EQ=0, NE=1, ...). */ 1464 1465 #define AA64_BR_COND_FAMILY_MATCH 0x54000000u 1466 #define AA64_BR_COND_FAMILY_MASK \ 1467 0xFF000010u /* bits 31:24 fixed + bit 4 = 0 \ 1468 */ 1469 1470 typedef struct AA64BrCond { 1471 u32 imm19, cond; 1472 } AA64BrCond; 1473 1474 static inline u32 aa64_brcond_pack(AA64BrCond f) { 1475 return AA64_BR_COND_FAMILY_MATCH | ((f.imm19 & 0x7ffffu) << 5) | 1476 (f.cond & 0xfu); 1477 } 1478 1479 static inline AA64BrCond aa64_brcond_unpack(u32 w) { 1480 AA64BrCond f; 1481 f.imm19 = (w >> 5) & 0x7ffffu; 1482 f.cond = w & 0xfu; 1483 return f; 1484 } 1485 1486 /* ==================================================================== 1487 * Compare-and-branch — CBZ / CBNZ 1488 * sf 011010 op(1) imm19(19) Rt(5) 1489 * 31 30..25 24 23..5 4..0 1490 * 1491 * op=0 → CBZ (branch if zero), op=1 → CBNZ. */ 1492 1493 #define AA64_CB_FAMILY_MATCH 0x34000000u 1494 #define AA64_CB_FAMILY_MASK 0x7E000000u /* bits 30:25 (=011010) */ 1495 1496 typedef struct AA64CB { 1497 u32 sf, op, imm19, Rt; 1498 } AA64CB; 1499 1500 static inline u32 aa64_cb_pack(AA64CB f) { 1501 return ((f.sf & 1u) << 31) | AA64_CB_FAMILY_MATCH | ((f.op & 1u) << 24) | 1502 ((f.imm19 & 0x7ffffu) << 5) | (f.Rt & 0x1fu); 1503 } 1504 1505 static inline AA64CB aa64_cb_unpack(u32 w) { 1506 AA64CB f; 1507 f.sf = (w >> 31) & 1u; 1508 f.op = (w >> 24) & 1u; 1509 f.imm19 = (w >> 5) & 0x7ffffu; 1510 f.Rt = w & 0x1fu; 1511 return f; 1512 } 1513 1514 static inline u32 aa64_cbz(u32 sf, u32 Rt, u32 imm19) { 1515 return aa64_cb_pack((AA64CB){.sf = sf, .op = 0, .imm19 = imm19, .Rt = Rt}); 1516 } 1517 static inline u32 aa64_cbnz_imm(u32 sf, u32 Rt, u32 imm19) { 1518 return aa64_cb_pack((AA64CB){.sf = sf, .op = 1, .imm19 = imm19, .Rt = Rt}); 1519 } 1520 1521 /* ==================================================================== 1522 * Exception generation — BRK / SVC / HVC / SMC / HLT / UDF aliases. 1523 * 1101 0100 opc(3) imm16(16) op2(3) LL(2) 1524 * 31..24 23..21 20..5 4..2 1..0 1525 * 1526 * SVC: opc=000, LL=01. BRK: opc=001, LL=00. HVC/SMC/HLT/...: other 1527 * combos. Codegen emits BRK today. */ 1528 1529 #define AA64_EXCEPT_FAMILY_MATCH 0xD4000000u 1530 #define AA64_EXCEPT_FAMILY_MASK 0xFF000000u /* bits 31:24 */ 1531 1532 typedef struct AA64Except { 1533 u32 opc, imm16, op2, LL; 1534 } AA64Except; 1535 1536 static inline u32 aa64_except_pack(AA64Except f) { 1537 return AA64_EXCEPT_FAMILY_MATCH | ((f.opc & 7u) << 21) | 1538 ((f.imm16 & 0xffffu) << 5) | ((f.op2 & 7u) << 2) | (f.LL & 3u); 1539 } 1540 1541 static inline AA64Except aa64_except_unpack(u32 w) { 1542 AA64Except f; 1543 f.opc = (w >> 21) & 7u; 1544 f.imm16 = (w >> 5) & 0xffffu; 1545 f.op2 = (w >> 2) & 7u; 1546 f.LL = w & 3u; 1547 return f; 1548 } 1549 1550 static inline u32 aa64_brk(u32 imm16) { 1551 return aa64_except_pack( 1552 (AA64Except){.opc = 1, .imm16 = imm16, .op2 = 0, .LL = 0}); 1553 } 1554 static inline u32 aa64_svc(u32 imm16) { 1555 return aa64_except_pack( 1556 (AA64Except){.opc = 0, .imm16 = imm16, .op2 = 0, .LL = 1}); 1557 } 1558 1559 /* ==================================================================== 1560 * Disassembler descriptor table. 1561 * ==================================================================== */ 1562 1563 typedef struct AA64InsnDesc { 1564 Slice mnemonic; 1565 u32 match; 1566 u32 mask; 1567 u8 fmt; /* AA64Format */ 1568 u8 flags; /* AA64_ASMFL_* */ 1569 u8 pad[2]; 1570 } AA64InsnDesc; 1571 1572 extern const AA64InsnDesc aa64_insn_table[]; 1573 extern const u32 aa64_insn_table_n; 1574 1575 /* Linear-scan lookup. Returns the matching descriptor or NULL. First 1576 * match wins; ordering in aa64_insn_table.c puts more-specific entries 1577 * before broader ones (so aliases like MOV/MUL/NEG win over their 1578 * canonical ORR/MADD/SUB forms). */ 1579 const AA64InsnDesc* aa64_disasm_find(u32 word); 1580 1581 /* ==================================================================== 1582 * Operand print — one entry per AA64Format. 1583 * 1584 * aa64_print_operands renders the operand text (everything after the 1585 * mnemonic) for `word` into `sb`, using `desc->fmt` to dispatch. 1586 * Mnemonic itself is in `desc->mnemonic`; the caller writes it before 1587 * calling this helper. `vaddr` is the instruction's virtual address 1588 * for PC-relative formats; pass 0 if not known. */ 1589 1590 void aa64_print_operands(StrBuf* sb, const AA64InsnDesc* desc, u32 word, 1591 u64 vaddr); 1592 1593 /* If `word` is an SBFM/UBFM that has a preferred shift-alias disassembly, 1594 * return its mnemonic ("lsl"/"lsr"/"asr") and write the shift amount to 1595 * *shift; return NULL otherwise. Shared by the disassembler's mnemonic and 1596 * operand printers so the alias decision lives in one place. */ 1597 const char* aa64_bitfield_shift_alias(u32 word, u32* shift); 1598 1599 /* Preferred SBFM/UBFM extension aliases: sxtb/sxth/sxtw/uxtb/uxth. 1600 * Returns NULL when the bitfield does not match one of those forms. */ 1601 const char* aa64_bitfield_extend_alias(u32 word); 1602 1603 /* Preferred SBFM/UBFM extract aliases: sbfx/ubfx. Writes the least-significant 1604 * source bit and extracted width when an alias is available. */ 1605 const char* aa64_bitfield_extract_alias(u32 word, u32* lsb, u32* width); 1606 1607 #endif