# Adapted from LAS Recomp tools/simd_c.py @ da35b83 (MIT, Copyright (c) 2026 Peiikai). """Portable C lowering of AArch64 FP / AdvSIMD / crypto / CRC32 instructions (lifter --simd=c). Every encoding is decoded here at lift time and turned into C over the Cpu struct, calling the static inline helpers in runtime/simd.h with constant fields (sizes, lanes, ops), so clang folds the helpers' switches. Covers ARMv8.0-A + Crypto + CRC32 (the Switch's Cortex-A57); unallocated or later-architecture encodings (FP16 arithmetic, RDM, dot product, FCMA, JSCVT, ...) raise Unsupported. """ from lifter import COND_NAMES, Unsupported, W, X, XS, bits, extend_expr, hx, set_reg, sext def lop(name, u=0): """C name of a lane op (enum in runtime/simd.h), with the unsigned flag.""" return f'LOP_{name}' + (' | LOP_U' if u else '') def vu(sz): return ('vu8', 'vu16', 'vu32', 'vu64')[sz] def vs(sz): return ('vs8', 'vs16', 'vs32', 'vs64')[sz] def ut(sz): return ('uint8_t', 'uint16_t', 'uint32_t', 'uint64_t')[sz] def VR(T, n): return f'VREG({T}, {n})' def VQ(T, Q, n): return f'VREG({T}, {n})' if Q else f'VQ({T}, 0, {n})' def VWr(T, Q, d, e): return f'VW({T}, {Q}, {d}, {e});' def cond(c): return f'COND_{COND_NAMES[c].upper()}(c)' if c < 14 else '1' def gen(op, sz, ne, d, szd, offd, keep, n, szn, offn, m=-1, szm=0, offm=0, bidx=-1, bimm=0): return (f'sl_gen(c, {op}, {sz}, {ne}, {d}, {szd}, {offd}, {keep}, {n}, {szn}, {offn}, {m}, {szm}, {offm}, ' f'{bidx}, {hx(bimm)});') def same(op, sz, ne, d, n, m=-1, bimm=0, bidx=-1): """Lane-wise op, all operands the same size.""" return gen(op, sz, ne, d, sz, 0, 0, n, sz, 0, m, sz, 0, bidx, bimm) def lanes(Q, sz): return (8 << Q) >> sz def shuf(T, a, b, idx): return f'SHUF({a}, {b}, {", ".join(str(i) for i in idx)})' def u128(v): lo, hi = v & ((1 << 64) - 1), v >> 64 return f'((u128){hx(hi)} << 64 | {hx(lo)})' if hi else hx(lo) FT = {0: ('s', 'vf32', 'vu32', 'float', 'VSF32', 'VS32', 'FP_SETS'), 1: ('d', 'vf64', 'vu64', 'double', 'VSF64', 'VS64', 'FP_SETD')} FOP3 = { # vector three-same FP: (U, a, opcode) -> FOP (0, 0, 0b11000): 'FOP_MAXNM', (0, 0, 0b11010): 'FOP_ADD', (0, 0, 0b11011): 'FOP_MULX', (0, 0, 0b11110): 'FOP_MAX', (0, 0, 0b11111): 'FOP_RECPS', (0, 1, 0b11000): 'FOP_MINNM', (0, 1, 0b11010): 'FOP_SUB', (0, 1, 0b11110): 'FOP_MIN', (0, 1, 0b11111): 'FOP_RSQRTS', (1, 0, 0b11011): 'FOP_MUL', (1, 0, 0b11111): 'FOP_DIV', (1, 1, 0b11010): 'FOP_ABD', } FPAIRV = {(1, 0, 0b11000): 'FOP_MAXNM', (1, 1, 0b11000): 'FOP_MINNM', (1, 0, 0b11010): 'FOP_ADD', (1, 0, 0b11110): 'FOP_MAX', (1, 1, 0b11110): 'FOP_MIN'} FCMP3 = {(0, 0, 0b11100): 0, (1, 0, 0b11100): 1, (1, 1, 0b11100): 2, (1, 0, 0b11101): 3, (1, 1, 0b11101): 4} def vfp_expand_imm(imm8, N): E = 8 if N == 32 else 11 F = N - E - 1 sign = imm8 >> 7 b6 = (imm8 >> 6) & 1 exp = ((b6 ^ 1) << (E - 1)) | ((((1 << (E - 3)) - 1) if b6 else 0) << 2) | ((imm8 >> 4) & 3) frac = (imm8 & 15) << (F - 4) return (sign << (N - 1)) | (exp << F) | frac def simd_expand_imm(op, cmode, imm8): c = cmode >> 1 if c == 0: imm = (imm8 << 32) | imm8 elif c == 1: imm = ((imm8 << 8) << 32) | (imm8 << 8) elif c == 2: imm = ((imm8 << 16) << 32) | (imm8 << 16) elif c == 3: imm = ((imm8 << 24) << 32) | (imm8 << 24) elif c == 4: imm = sum(imm8 << s for s in range(0, 64, 16)) elif c == 5: imm = sum((imm8 << 8) << s for s in range(0, 64, 16)) elif c == 6: v = ((imm8 << 8) | 0xFF) if not (cmode & 1) else ((imm8 << 16) | 0xFFFF) imm = (v << 32) | v else: if not (cmode & 1) and not op: imm = sum(imm8 << s for s in range(0, 64, 8)) elif not (cmode & 1) and op: imm = 0 for i in range(8): if (imm8 >> i) & 1: imm |= 0xFF << (8 * i) elif (cmode & 1) and not op: v = vfp_expand_imm(imm8, 32) imm = (v << 32) | v else: imm = vfp_expand_imm(imm8, 64) return imm class SimdC: """FP/SIMD decoder emitting portable C. Methods return C statements or raise Unsupported.""" # ------------------------------------------------------------ top level def dp(self, w, pc): op0, op1, op2, op3 = bits(w, 28, 4), bits(w, 23, 2), bits(w, 19, 4), bits(w, 10, 9) if op0 == 0b0100 and not (op1 & 2) and (op2 & 7) == 5 and (op3 & 0b110000011) == 0b10: return self.crypto_aes(w) if op0 == 0b0101 and not (op1 & 2) and not (op2 & 4) and (op3 & 0b000100011) == 0: return self.crypto_sha3(w) if op0 == 0b0101 and not (op1 & 2) and (op2 & 7) == 5 and (op3 & 0b110000011) == 0b10: return self.crypto_sha2(w) if (op0 & 0b1101) == 0b0101: # AdvSIMD scalar if op1 == 0 and not (op2 & 0b1100) and (op3 & 0b000100001) == 1: return self.scalar_copy(w) if not (op1 & 2) and (op2 & 7) == 4 and (op3 & 0b110000011) == 0b10: return self.misc2(w, scalar=True) if not (op1 & 2) and (op2 & 7) == 6 and (op3 & 0b110000011) == 0b10: return self.scalar_pairwise(w) if not (op1 & 2) and (op2 & 4) and (op3 & 3) == 0: return self.three_diff(w, scalar=True) if not (op1 & 2) and (op2 & 4) and (op3 & 1): return self.three_same(w, scalar=True) if op1 == 2 and (op3 & 1): return self.shift_imm(w, scalar=True) if (op1 & 2) and not (op3 & 1): return self.indexed(w, scalar=True) raise Unsupported(f'simd scalar {w:08x}') if (op0 & 0b1011) == 0 and not (op1 & 2) and not (op2 & 4) and (op3 & 0b000100011) == 0: return self.tbl(w) if (op0 & 0b1011) == 0 and not (op1 & 2) and not (op2 & 4) and (op3 & 0b000100011) == 2: return self.permute(w) if (op0 & 0b1011) == 0b0010 and not (op1 & 2) and not (op2 & 4) and (op3 & 0b000100001) == 0: return self.ext(w) if (op0 & 0b1001) == 0: # AdvSIMD vector if op1 == 0 and not (op2 & 0b1100) and (op3 & 0b000100001) == 1: return self.copy(w) if not (op1 & 2) and (op2 & 4) and (op3 & 1): return self.three_same(w) if not (op1 & 2) and (op2 & 4) and (op3 & 3) == 0: return self.three_diff(w) if not (op1 & 2) and (op2 & 7) == 4 and (op3 & 0b110000011) == 0b10: return self.misc2(w) if not (op1 & 2) and (op2 & 7) == 6 and (op3 & 0b110000011) == 0b10: return self.across(w) if op1 == 2 and op2 == 0 and (op3 & 1): return self.modimm(w) if op1 == 2 and op2 and (op3 & 1): return self.shift_imm(w) if (op1 & 2) and not (op3 & 1): return self.indexed(w) raise Unsupported(f'simd vector {w:08x}') if (op0 & 0b0101) == 0b0001: # scalar FP if bits(w, 29, 1) or (bits(w, 31, 1) and (op1 & 2 or (op2 & 4 and op3 & 0b111111))): raise Unsupported('fp M/S') if op1 & 2: return self.fp_dp3(w) if not (op2 & 4): return self.fp_fixed(w) if (op3 & 0b111111) == 0: return self.fp_int(w) if (op3 & 0b11111) == 0b10000: return self.fp_dp1(w) if (op3 & 0b1111) == 0b1000: return self.fp_cmp(w) if (op3 & 0b111) == 0b100: return self.fp_imm(w) if (op3 & 3) == 1: return self.fp_ccmp(w) if (op3 & 3) == 2: return self.fp_dp2(w) if (op3 & 3) == 3: return self.fp_csel(w) raise Unsupported('fp op3') raise Unsupported(f'fp/simd {w:08x}') # ------------------------------------------------------------ crypto def crypto_aes(self, w): if bits(w, 22, 2): raise Unsupported('aes size') opc = bits(w, 12, 5) if opc not in (4, 5, 6, 7): raise Unsupported('aes opcode') return f'sv_aes(c, {opc - 4}, {bits(w, 0, 5)}, {bits(w, 5, 5)});' def crypto_sha3(self, w): if bits(w, 22, 2): raise Unsupported('sha size') opc, d, n, m = bits(w, 12, 3), bits(w, 0, 5), bits(w, 5, 5), bits(w, 16, 5) if opc < 3: return f'sv_sha1hash(c, {opc}, {d}, {n}, {m});' if opc == 3: return f'sv_sha_misc(c, 0, {d}, {n}, {m});' if opc in (4, 5): return f'sv_sha256hash(c, {int(opc == 4)}, {d}, {n}, {m});' if opc == 6: return f'sv_sha_misc(c, 3, {d}, {n}, {m});' raise Unsupported('sha3 opcode') def crypto_sha2(self, w): if bits(w, 22, 2): raise Unsupported('sha size') opc, d, n = bits(w, 12, 5), bits(w, 0, 5), bits(w, 5, 5) op = {0: 4, 1: 1, 2: 2}.get(opc) if op is None: raise Unsupported('sha2 opcode') return f'sv_sha_misc(c, {op}, {d}, {n}, 0);' # ------------------------------------------------------------ copy / permute / table @staticmethod def _imm5(imm5): if not imm5 & 15: raise Unsupported('copy imm5') sz = (imm5 & -imm5).bit_length() - 1 return sz, imm5 >> (sz + 1) def scalar_copy(self, w): if bits(w, 29, 1) or bits(w, 11, 4): raise Unsupported('scalar copy') sz, idx = self._imm5(bits(w, 16, 5)) if sz > 3: raise Unsupported('scalar dup size') d, n = bits(w, 0, 5), bits(w, 5, 5) return f'c->v[{d}] = (({("uint8_t", "sa16", "sa32", "sa64")[sz]} *)&c->v[{n}])[{idx}];' def copy(self, w): Q, op, imm5, imm4 = bits(w, 30, 1), bits(w, 29, 1), bits(w, 16, 5), bits(w, 11, 4) d, n = bits(w, 0, 5), bits(w, 5, 5) sz, idx = self._imm5(imm5) if sz > 3: raise Unsupported('copy size') P = ('uint8_t', 'sa16', 'sa32', 'sa64')[sz] if op: if not Q: raise Unsupported('ins elem Q') idx2 = imm4 >> sz return f'(({P} *)&c->v[{d}])[{idx}] = (({P} *)&c->v[{n}])[{idx2}];' if imm4 == 0: # DUP (element) if sz == 3 and not Q: raise Unsupported('dup 1d') nl = 16 >> sz return VWr(vu(sz), Q, d, shuf(vu(sz), VR(vu(sz), n), VR(vu(sz), n), [idx] * nl)) if imm4 == 1: # DUP (general) if sz == 3 and not Q: raise Unsupported('dup 1d') return VWr(vu(sz), Q, d, f'({vu(sz)}){{}} + ({ut(sz)}){X(n)}') if imm4 == 3: # INS (general) if not Q: raise Unsupported('ins Q') return f'(({P} *)&c->v[{d}])[{idx}] = ({ut(sz)}){X(n)};' if imm4 == 5: # SMOV if sz >= 2 + Q: raise Unsupported('smov size') st = ('int8_t', 'int16_t', 'int32_t')[sz] return set_reg(Q, d, f'(uint64_t)(int64_t)({st})(({P} *)&c->v[{n}])[{idx}]') if imm4 == 7: # UMOV if (Q and sz != 3) or (not Q and sz == 3): raise Unsupported('umov size') return set_reg(Q, d, f'(({P} *)&c->v[{n}])[{idx}]') raise Unsupported('copy imm4') def permute(self, w): Q, sz, m, opc, n, d = bits(w, 30, 1), bits(w, 22, 2), bits(w, 16, 5), bits(w, 12, 3), bits(w, 5, 5), bits(w, 0, 5) if opc in (0, 4) or (sz == 3 and not Q): raise Unsupported('permute') ne, NL = lanes(Q, sz), 16 >> sz part = opc >> 2 kind = opc & 3 idx = [] for i in range(ne): if kind == 1: # UZP j = 2 * i + part idx.append(j if j < ne else NL + j - ne) elif kind == 2: # TRN p = i & ~1 idx.append(p + part if not (i & 1) else NL + p + part) else: # ZIP base = part * ne // 2 idx.append(base + i // 2 if not (i & 1) else NL + base + i // 2) idx += [0] * (NL - ne) return VWr(vu(sz), Q, d, shuf(vu(sz), VR(vu(sz), n), VR(vu(sz), m), idx)) def ext(self, w): Q, m, imm4, n, d = bits(w, 30, 1), bits(w, 16, 5), bits(w, 11, 4), bits(w, 5, 5), bits(w, 0, 5) if bits(w, 22, 2) or (not Q and imm4 >= 8): raise Unsupported('ext') if Q: idx = [imm4 + i for i in range(16)] else: idx = [(imm4 + i) if imm4 + i < 8 else 16 + imm4 + i - 8 for i in range(8)] + [0] * 8 return VWr('vu8', Q, d, shuf('vu8', VR('vu8', n), VR('vu8', m), idx)) def tbl(self, w): Q, m, ln, op, n, d = bits(w, 30, 1), bits(w, 16, 5), bits(w, 13, 2), bits(w, 12, 1), bits(w, 5, 5), bits(w, 0, 5) if bits(w, 22, 2): raise Unsupported('tbl op2') return f'sv_tbl(c, {Q}, {ln}, {op}, {d}, {n}, {m});' # ------------------------------------------------------------ three same def three_same(self, w, scalar=False): Q, U, sz, m, opc, n, d = (bits(w, 30, 1), bits(w, 29, 1), bits(w, 22, 2), bits(w, 16, 5), bits(w, 11, 5), bits(w, 5, 5), bits(w, 0, 5)) if opc >= 0b11000: return self.three_same_fp(w, scalar) if scalar: return self.three_same_scalar(U, sz, opc, d, n, m) if sz == 3 and not Q and opc != 3: raise Unsupported('3same 1d') ne = lanes(Q, sz) T, S = vu(sz), vs(sz) a, b, o = VR(T, n), VR(T, m), VR(T, d) sa, sb = VR(S, n), VR(S, m) x, y = (a, b) if U else (sa, sb) if opc in (0, 2, 4, 14, 15, 12, 13) and sz == 3: raise Unsupported('3same size') if opc == 0: return same(lop('HADD', U), sz, ne, d, n, m) if opc == 1: return VWr(T, Q, d, f'({T})__builtin_elementwise_{"add"}_sat({x}, {y})') if opc == 2: return same(lop('RHADD', U), sz, ne, d, n, m) if opc == 3: a64, b64, o64 = VR('vu64', n), VR('vu64', m), VR('vu64', d) e = [[f'{a64} & {b64}', f'{a64} & ~{b64}', f'{a64} | {b64}', f'{a64} | ~{b64}'], [f'{a64} ^ {b64}', f'({o64} & {a64}) | (~{o64} & {b64})', f'({o64} & ~{b64}) | ({a64} & {b64})', f'({o64} & {b64}) | ({a64} & ~{b64})']][U][sz] return VWr('vu64', Q, d, e) if opc == 4: return same(lop('HSUB', U), sz, ne, d, n, m) if opc == 5: return VWr(T, Q, d, f'({T})__builtin_elementwise_sub_sat({x}, {y})') if opc == 6: return VWr(T, Q, d, f'({T})({x} > {y})') if opc == 7: return VWr(T, Q, d, f'({T})({x} >= {y})') if 8 <= opc <= 11: return same(lop(('SHL', 'QSHL', 'RSHL', 'QRSHL')[opc - 8], U), sz, ne, d, n, m) if opc == 12: return VWr(T, Q, d, f'({T})__builtin_elementwise_max({x}, {y})') if opc == 13: return VWr(T, Q, d, f'({T})__builtin_elementwise_min({x}, {y})') if opc in (14, 15): e = f'({T})__builtin_elementwise_max({x}, {y}) - ({T})__builtin_elementwise_min({x}, {y})' return VWr(T, Q, d, f'{o} + ({e})' if opc == 15 else e) if opc == 16: return VWr(T, Q, d, f'{a} {"-" if U else "+"} {b}') if opc == 17: return VWr(T, Q, d, f'({T})({a} == {b})' if U else f'({T})(({a} & {b}) != 0)') if sz == 3 and opc != 23: raise Unsupported('3same size') if opc == 18: return VWr(T, Q, d, f'{o} {"-" if U else "+"} {a} * {b}') if opc == 19: if U: if sz: raise Unsupported('pmul size') return same(lop('PMUL'), 0, ne, d, n, m) return VWr(T, Q, d, f'{a} * {b}') if opc in (20, 21, 23): if opc == 23 and U: raise Unsupported('addp U') ev, od = self._pair_idx(Q, sz) A, B = (a, b) if (U or opc == 23) else (sa, sb) L, R = shuf(T, A, B, ev), shuf(T, A, B, od) if opc == 23: return VWr(T, Q, d, f'({T})({L} + {R})') return VWr(T, Q, d, f'({T})__builtin_elementwise_{"max" if opc == 20 else "min"}({L}, {R})') if opc == 22: if sz not in (1, 2): raise Unsupported('sqdmulh size') return same(lop('SQRDMULH' if U else 'SQDMULH'), sz, ne, d, n, m) raise Unsupported('3same opcode') @staticmethod def _pair_idx(Q, sz): """Lane indices (into SHUF(a, b)) of the left / right operands of a pairwise op; for Q=0 the padding lanes point at a's upper half.""" ne, NL = lanes(Q, sz), 16 >> sz src = list(range(ne)) + [NL + i for i in range(ne)] ev = [src[2 * k] for k in range(ne)] + [ne % NL] * (NL - ne) od = [src[2 * k + 1] for k in range(ne)] + [ne % NL] * (NL - ne) return ev, od def three_same_scalar(self, U, sz, opc, d, n, m): if opc in (1, 5): return same(lop('UQADD' if U else 'SQADD') if opc == 1 else lop('UQSUB' if U else 'SQSUB'), sz, 1, d, n, m) if opc in (9, 11): return same(lop('QSHL' if opc == 9 else 'QRSHL', U), sz, 1, d, n, m) if opc == 22: if sz not in (1, 2): raise Unsupported('sqdmulh size') return same(lop('SQRDMULH' if U else 'SQDMULH'), sz, 1, d, n, m) if sz != 3: raise Unsupported('scalar 3same size') a, b = f'VS64({n})', f'VS64({m})' sa, sb = f'(int64_t)VS64({n})', f'(int64_t)VS64({m})' x, y = (a, b) if U else (sa, sb) if opc == 6: return f'c->v[{d}] = ({x} > {y}) ? ~0ull : 0;' if opc == 7: return f'c->v[{d}] = ({x} >= {y}) ? ~0ull : 0;' if opc in (8, 10): return same(lop('SHL' if opc == 8 else 'RSHL', U), 3, 1, d, n, m) if opc == 16: return f'c->v[{d}] = (uint64_t)({a} {"-" if U else "+"} {b});' if opc == 17: return f'c->v[{d}] = ({a} == {b}) ? ~0ull : 0;' if U else f'c->v[{d}] = ({a} & {b}) ? ~0ull : 0;' raise Unsupported('scalar 3same opcode') def three_same_fp(self, w, scalar): Q, U, a_, sz, m, opc, n, d = (bits(w, 30, 1), bits(w, 29, 1), bits(w, 23, 1), bits(w, 22, 1), bits(w, 16, 5), bits(w, 11, 5), bits(w, 5, 5), bits(w, 0, 5)) if not scalar and sz and not Q: raise Unsupported('fp 2d Q') F, VT, VU, T, SF, SU, SET = FT[sz] key = (U, a_, opc) if key in FCMP3: cop = FCMP3[key] if scalar: return (f'c->v[{d}] = ({ut(2 + sz)})vcmp_{F}(c, {cop}, ({VT}){{{SF}({n})}}, ' f'({VT}){{{SF}({m})}})[0];') return VWr(VU, Q, d, f'vcmp_{F}(c, {cop}, {VQ(VT, Q, n)}, {VQ(VT, Q, m)})') if scalar: if key not in ((0, 0, 0b11011), (0, 0, 0b11111), (0, 1, 0b11111), (1, 1, 0b11010)): raise Unsupported('scalar fp 3same') fop = FOP3[key] x = f'neg_{F}({SF}({n}))' if fop in ('FOP_RECPS', 'FOP_RSQRTS') else f'{SF}({n})' return f'{SET}({d}, op_{F}(c, {fop}, {x}, {SF}({m}), 0));' A, B = VQ(VT, Q, n), VQ(VT, Q, m) if key in FPAIRV: ev, od = self._pair_idx(Q, 2 + sz) return f'SV_FOP_{F.upper()}({FPAIRV[key]}, {Q}, {d}, {shuf(VT, A, B, ev)}, {shuf(VT, A, B, od)}, ({VT}){{}});' if U == 0 and opc == 0b11001: # FMLA / FMLS: the multiplicand is negated for FMLS return f'SV_FOP_{F.upper()}(FOP_MADD, {Q}, {d}, {VQ(VT, Q, d)}, {f"vneg_{F}({A})" if a_ else A}, {B});' fop = FOP3.get(key) if fop is None: raise Unsupported('fp 3same opcode') if fop in ('FOP_RECPS', 'FOP_RSQRTS'): A = f'vneg_{F}({A})' return f'SV_FOP_{F.upper()}({fop}, {Q}, {d}, {A}, {B}, ({VT}){{}});' # ------------------------------------------------------------ three different def three_diff(self, w, scalar=False): Q, U, sz, m, opc, n, d = (bits(w, 30, 1), bits(w, 29, 1), bits(w, 22, 2), bits(w, 16, 5), bits(w, 12, 4), bits(w, 5, 5), bits(w, 0, 5)) if scalar: if U or opc not in (9, 11, 13) or sz not in (1, 2): raise Unsupported('scalar 3diff') op = {9: 'SQDMLAL', 11: 'SQDMLSL', 13: 'SQDMULL'}[opc] return gen(lop(op), sz, 1, d, sz + 1, 0, 0, n, sz, 0, m, sz, 0) if opc == 14: # PMULL if U: raise Unsupported('pmull U') if sz == 3: return f'sl_pmull64(c, {Q}, {d}, {n}, {m});' if sz: raise Unsupported('pmull size') if sz == 3 or opc == 15: raise Unsupported('3diff size') ne = 8 >> sz off = Q * ne if opc in (4, 6): # ADDHN/RADDHN/SUBHN/RSUBHN: narrow into the low or high half op = ('ADDHN', 'RADDHN', 'SUBHN', 'RSUBHN')[(opc == 6) * 2 + U] return gen(lop(op), sz, ne, d, sz, off, Q, n, sz + 1, 0, m, sz + 1, 0) if opc in (1, 3): # wide op = 'ADDW' if opc == 1 else 'SUBW' return gen(lop(op, U), sz, ne, d, sz + 1, 0, 0, n, sz + 1, 0, m, sz, off) op = {0: 'ADDL', 2: 'SUBL', 5: 'ABAL', 7: 'ABDL', 8: 'MLAL', 10: 'MLSL', 12: 'MULL', 9: 'SQDMLAL', 11: 'SQDMLSL', 13: 'SQDMULL', 14: 'PMULL'}[opc] if op.startswith('SQD'): if U or sz not in (1, 2): raise Unsupported('sqdmull') return gen(lop(op, U if not op.startswith(('SQD', 'PM')) else 0), sz, ne, d, sz + 1, 0, 0, n, sz, off, m, sz, off) # ------------------------------------------------------------ two-register misc def misc2(self, w, scalar=False): Q, U, sz, opc, n, d = bits(w, 30, 1), bits(w, 29, 1), bits(w, 22, 2), bits(w, 12, 5), bits(w, 5, 5), bits(w, 0, 5) if opc >= 0b11000 or opc in (0b10110, 0b10111) or 0b01100 <= opc <= 0b01111: return self.misc2_fp(w, scalar) if scalar: return self.misc2_scalar(U, sz, opc, d, n) ne = lanes(Q, sz) T, S = vu(sz), vs(sz) if opc == 0 or (opc == 1 and not U): # REV64 / REV32 / REV16 gsz = {(0, 0): 3, (1, 0): 2, (0, 1): 1}.get((U, opc)) if gsz is None or sz >= gsz: raise Unsupported('rev') es = 1 << sz gb = 1 << gsz idx = [] for i in range(16): g, o = i // gb * gb, i % gb e, b = o // es, o % es idx.append(g + (gb // es - 1 - e) * es + b) return VWr('vu8', Q, d, shuf('vu8', VR('vu8', n), VR('vu8', n), idx)) if sz == 3 and not Q and opc not in (0b10010, 0b10100, 0b10011): raise Unsupported('misc 1d') if opc in (2, 6): if sz == 3: raise Unsupported('addlp size') return f'sl_addlp(c, {U}, {int(opc == 6)}, {Q}, {sz}, {d}, {n});' if opc == 3: return same(lop('USQADD' if U else 'SUQADD'), sz, ne, d, n) if opc == 4: if sz == 3: raise Unsupported('clz size') return same(lop('CLZ' if U else 'CLS'), sz, ne, d, n) if opc == 5: if not U: if sz: raise Unsupported('cnt size') return same(lop('CNT'), 0, ne, d, n) if sz == 0: return VWr('vu64', Q, d, f'~{VR("vu64", n)}') if sz == 1: return same(lop('RBIT'), 0, lanes(Q, 0), d, n) raise Unsupported('not/rbit size') if opc == 7: return same(lop('SQNEG' if U else 'SQABS'), sz, ne, d, n) a, sa = VR(T, n), VR(S, n) if opc == 8: return VWr(T, Q, d, f'({T})({sa} {">=" if U else ">"} 0)') if opc == 9: return VWr(T, Q, d, f'({T})({sa} {"<=" if U else "=="} 0)') if opc == 10 and not U: return VWr(T, Q, d, f'({T})({sa} < 0)') if opc == 11: if U: return VWr(T, Q, d, f'-{a}') return VWr(T, Q, d, f'(({T}){sa} ^ ({T})({sa} >> {(8 << sz) - 1})) - ({T})({sa} >> {(8 << sz) - 1})') if opc in (0b10010, 0b10100): # XTN / SQXTUN / SQXTN / UQXTN if sz == 3: raise Unsupported('xtn size') op = {(0b10010, 0): 'XTN', (0b10010, 1): 'SQXTUN', (0b10100, 0): 'SQXTN', (0b10100, 1): 'UQXTN'}[(opc, U)] ne = 8 >> sz return gen(lop(op), sz, ne, d, sz, Q * ne, Q, n, sz + 1, 0) if opc == 0b10011 and U: # SHLL if sz == 3: raise Unsupported('shll size') ne = 8 >> sz return gen(lop('SHLL'), sz, ne, d, sz + 1, 0, 0, n, sz, Q * ne) raise Unsupported('misc2 opcode') def misc2_scalar(self, U, sz, opc, d, n): if opc == 3: return same(lop('USQADD' if U else 'SUQADD'), sz, 1, d, n) if opc == 7: return same(lop('SQNEG' if U else 'SQABS'), sz, 1, d, n) if opc in (0b10010, 0b10100): if sz == 3 or (opc == 0b10010 and not U): raise Unsupported('scalar xtn') op = {(0b10010, 1): 'SQXTUN', (0b10100, 0): 'SQXTN', (0b10100, 1): 'UQXTN'}[(opc, U)] return gen(lop(op), sz, 1, d, sz, 0, 0, n, sz + 1, 0) if sz != 3: raise Unsupported('scalar misc size') a, sa = f'VS64({n})', f'(int64_t)VS64({n})' if opc == 8: return f'c->v[{d}] = ({sa} {">=" if U else ">"} 0) ? ~0ull : 0;' if opc == 9: return f'c->v[{d}] = ({sa} {"<=" if U else "=="} 0) ? ~0ull : 0;' if opc == 10 and not U: return f'c->v[{d}] = ({sa} < 0) ? ~0ull : 0;' if opc == 11: return f'c->v[{d}] = (uint64_t)-{a};' if U else f'c->v[{d}] = ({sa} < 0) ? (uint64_t)-{a} : {a};' raise Unsupported('scalar misc opcode') def misc2_fp(self, w, scalar): """FP two-register misc, vector and scalar.""" Q, U, a_, sz, opc, n, d = (bits(w, 30, 1), bits(w, 29, 1), bits(w, 23, 1), bits(w, 22, 1), bits(w, 12, 5), bits(w, 5, 5), bits(w, 0, 5)) F, VT, VU, T, SF, SU, SET = FT[sz] if opc in (0b10110, 0b10111): # FCVTN / FCVTL / FCVTXN if a_: raise Unsupported('fcvtn a') if opc == 0b10110 and U: if not sz: raise Unsupported('fcvtxn size') if scalar: return f'FP_SETS({d}, simd_d2s_odd(c, VSF64({n})));' return f'sv_fcvtn(c, 1, {Q}, 1, {d}, {n});' if scalar or U: raise Unsupported('fcvtn/l') if opc == 0b10110: return f'sv_fcvtn(c, {sz}, {Q}, 0, {d}, {n});' return f'sv_fcvtl(c, {sz}, {Q}, {d}, {n});' if not scalar and sz and not Q: raise Unsupported('fp misc 2d Q') x = VQ(VT, Q, n) if 0b01100 <= opc <= 0b01111: if not a_: raise Unsupported('fp16 misc') if opc == 0b01111: # FABS / FNEG if scalar: raise Unsupported('scalar fabs') mask = '0x80000000u' if not sz else '0x8000000000000000ull' return VWr(VU, Q, d, f'{VR(VU, n)} ^ {mask}' if U else f'{VR(VU, n)} & ~{mask}') cop = {(0b01100, 0): (2, 0), (0b01100, 1): (1, 0), (0b01101, 0): (0, 0), (0b01101, 1): (1, 1), (0b01110, 0): (2, 1)}.get((opc, U)) if cop is None: raise Unsupported('fcm zero') op, swap = cop if scalar: a, b = f'({VT}){{{SF}({n})}}', f'({VT}){{}}' if swap: a, b = b, a return f'c->v[{d}] = ({ut(2 + sz)})vcmp_{F}(c, {op}, {a}, {b})[0];' a, b = x, f'({VT}){{}}' if swap: a, b = b, a return VWr(VU, Q, d, f'vcmp_{F}(c, {op}, {a}, {b})') if opc == 0b11111: if not a_: raise Unsupported('fp misc 11111') if U: if scalar: raise Unsupported('scalar fsqrt') return f'SV_FOP_{F.upper()}(FOP_SQRT, {Q}, {d}, {x}, {x}, ({VT}){{}});' if not scalar: raise Unsupported('vector frecpx') return f'{SET}({d}, recpx_{F}(c, {SF}({n})));' if opc in (0b11000, 0b11001): # FRINT* if scalar: raise Unsupported('scalar frint') rm = {(0, 0, 0): 0, (0, 0, 1): 2, (0, 1, 0): 1, (0, 1, 1): 3, (1, 0, 0): 4, (1, 0, 1): 5, (1, 1, 1): 6}.get( (U, a_, opc & 1)) if rm is None: raise Unsupported('frint') return VWr(VT, Q, d, f'vfrint_{F}(c, {x}, {rm})') if opc in (0b11010, 0b11011) or (opc == 0b11100 and not a_): # FCVT[NPMZA][SU] rm = {(0b11010, 0): 0, (0b11010, 1): 1, (0b11011, 0): 2, (0b11011, 1): 3, (0b11100, 0): 4}[(opc, a_)] if scalar: return f'c->v[{d}] = ({ut(2 + sz)})toint_{F}(c, {SF}({n}), {rm}, 0, {32 << sz}, {U});' return VWr(VU, Q, d, f'vtoint_{F}(c, {x}, {rm}, 0, {U})') if opc == 0b11100: # URECPE / URSQRTE if scalar or sz: raise Unsupported('urecpe') return same(lop('URSQRTE' if U else 'URECPE'), 2, lanes(Q, 2), d, n) if opc == 0b11101: if not a_: # SCVTF / UCVTF if scalar: return f'{SET}({d}, fromint_{F}(c, {SU}({n}), {32 << sz}, {U}, 0));' return VWr(VT, Q, d, f'vfromint_{F}(c, {VR(VU, n)}, 0, {U})') if scalar: # FRECPE / FRSQRTE return f'{SET}({d}, {"rsqrte" if U else "recpe"}_{F}(c, {SF}({n})));' return VWr(VT, Q, d, f'v{"rsqrte" if U else "recpe"}_{F}(c, {x})') raise Unsupported('fp misc opcode') # ------------------------------------------------------------ across lanes / pairwise def across(self, w): Q, U, sz, opc, n, d = bits(w, 30, 1), bits(w, 29, 1), bits(w, 22, 2), bits(w, 12, 5), bits(w, 5, 5), bits(w, 0, 5) if opc in (0b01100, 0b01111): if not U: raise Unsupported('fp16 across') if not Q or (sz & 1): raise Unsupported('fp across size') fop = {(0b01100, 0): 'FOP_MAXNM', (0b01100, 1): 'FOP_MINNM', (0b01111, 0): 'FOP_MAX', (0b01111, 1): 'FOP_MIN'}[(opc, sz >> 1)] return f'sv_fred_s(c, {fop}, {d}, {n});' op = {0b00011: 1, 0b01010: 2, 0b11010: 3, 0b11011: 0}.get(opc) if op is None or (op == 0 and U) or sz == 3 or (sz == 2 and not Q): raise Unsupported('across') return f'sl_across(c, {op}, {U}, {Q}, {sz}, {d}, {n});' def scalar_pairwise(self, w): U, sz, opc, n, d = bits(w, 29, 1), bits(w, 22, 2), bits(w, 12, 5), bits(w, 5, 5), bits(w, 0, 5) if not U: if opc == 0b11011 and sz == 3: return f'c->v[{d}] = (uint64_t)(VS64({n}) + VHI64({n}));' raise Unsupported('scalar pairwise') fop = {(0b01100, 0): 'FOP_MAXNM', (0b01100, 1): 'FOP_MINNM', (0b01101, 0): 'FOP_ADD', (0b01111, 0): 'FOP_MAX', (0b01111, 1): 'FOP_MIN'}.get((opc, sz >> 1)) if fop is None: raise Unsupported('scalar fp pairwise') F, VT, VU, T, SF, SU, SET = FT[sz & 1] return f'{SET}({d}, pair_{F}(c, {fop}, {VR(VT, n)}[0], {VR(VT, n)}[1]));' # ------------------------------------------------------------ modified immediate def modimm(self, w): Q, op, cmode, o2, d = bits(w, 30, 1), bits(w, 29, 1), bits(w, 12, 4), bits(w, 11, 1), bits(w, 0, 5) imm8 = (bits(w, 16, 3) << 5) | bits(w, 5, 5) if o2: raise Unsupported('fmov half imm') if cmode == 15 and op and not Q: raise Unsupported('fmov 1d') imm = simd_expand_imm(op, cmode, imm8) c4 = cmode if (c4 < 8 and c4 & 1) or (8 <= c4 < 12 and c4 & 1): # ORR / BIC if op: imm = ~imm & ((1 << 64) - 1) return VWr('vu64', Q, d, f'{VR("vu64", d)} & {hx(imm)}') return VWr('vu64', Q, d, f'{VR("vu64", d)} | {hx(imm)}') if op and not (c4 == 14 or c4 == 15): # MVNI imm = ~imm & ((1 << 64) - 1) v = imm | (imm << 64) if Q else imm return f'c->v[{d}] = {u128(v)};' # ------------------------------------------------------------ shift by immediate def shift_imm(self, w, scalar=False): Q, U, immh, immb, opc, n, d = (bits(w, 30, 1), bits(w, 29, 1), bits(w, 19, 4), bits(w, 16, 3), bits(w, 11, 5), bits(w, 5, 5), bits(w, 0, 5)) if not immh: raise Unsupported('shift immh') sz = immh.bit_length() - 1 es = 8 << sz ih = (immh << 3) | immb rsh, lsh = 2 * es - ih, ih - es if scalar: Q = 1 ne = 1 if scalar else lanes(Q, sz) narrow = 0b10000 <= opc <= 0b10011 if opc in (0b11100, 0b11111): # fixed-point conversions if sz < 2: raise Unsupported('fp16 fixed') if sz == 3 and not Q: raise Unsupported('fixed 1d') F, VT, VU, T, SF, SU, SET = FT[sz - 2] fb = rsh if opc == 0b11100: if scalar: return f'{SET}({d}, fromint_{F}(c, {SU}({n}), {es}, {U}, {fb}));' return VWr(VT, Q, d, f'vfromint_{F}(c, {VR(VU, n)}, {fb}, {U})') if scalar: return f'c->v[{d}] = ({ut(sz)})toint_{F}(c, {SF}({n}), 3, {fb}, {es}, {U});' return VWr(VU, Q, d, f'vtoint_{F}(c, {VQ(VT, Q, n)}, 3, {fb}, {U})') if narrow: if sz == 3: raise Unsupported('narrow shift size') ne = 1 if scalar else 8 >> sz op = {(0b10000, 0): ('SHRN', 0), (0b10001, 0): ('RSHRN', 0), (0b10000, 1): ('QSHRUN', 0), (0b10001, 1): ('QRSHRUN', 0), (0b10010, 0): ('QSHRN', 0), (0b10010, 1): ('QSHRN', 1), (0b10011, 0): ('QRSHRN', 0), (0b10011, 1): ('QRSHRN', 1)}[(opc, U)] if scalar and op[0] in ('SHRN', 'RSHRN'): raise Unsupported('scalar shrn') part = 0 if scalar else Q return gen(lop(*op), sz, ne, d, sz, part * ne, part, n, sz + 1, 0, -1, 0, 0, -1, rsh) if opc == 0b10100: # SSHLL / USHLL if scalar or sz == 3: raise Unsupported('sshll') ne = 8 >> sz return gen(lop('SHLLI', U), sz, ne, d, sz + 1, 0, 0, n, sz, Q * ne, -1, 0, 0, -1, lsh) if sz == 3 and not Q: raise Unsupported('shift 1d') if scalar and sz != 3 and opc not in (0b01100, 0b01110): raise Unsupported('scalar shift size') T, S = vu(sz), vs(sz) if opc in (0, 2): # SSHR/USHR, SSRA/USRA if U: e = f'({T}){{}}' if rsh >= es else f'({VR(T, n)} >> {rsh})' else: e = f'({T})({VR(S, n)} >> {min(rsh, es - 1)})' if opc == 2: e = f'{VR(T, d)} + {e}' return VWr(T, Q, d, e) if not scalar else f'c->v[{d}] = (uint64_t)(({e})[0]);' if opc in (4, 6): return same(lop('RSHRI' if opc == 4 else 'RSRAI', U), sz, ne, d, n, -1, rsh) if opc == 8: if not U: raise Unsupported('sri U') return same(lop('SRI'), sz, ne, d, n, -1, rsh) if opc == 10: if U: return same(lop('SLI'), sz, ne, d, n, -1, lsh) e = f'{VR(T, n)} << {lsh}' return VWr(T, Q, d, e) if not scalar else f'c->v[{d}] = (uint64_t)(({e})[0]);' if opc == 12: if not U: raise Unsupported('sqshlu U') return same(lop('QSHLUI'), sz, ne, d, n, -1, lsh) if opc == 14: return same(lop('QSHLI', U), sz, ne, d, n, -1, lsh) raise Unsupported('shift imm opcode') # ------------------------------------------------------------ indexed element def indexed(self, w, scalar=False): Q, U, sz, L, M, rm, opc, H, n, d = (bits(w, 30, 1), bits(w, 29, 1), bits(w, 22, 2), bits(w, 21, 1), bits(w, 20, 1), bits(w, 16, 4), bits(w, 12, 4), bits(w, 11, 1), bits(w, 5, 5), bits(w, 0, 5)) fp = opc in (1, 5, 9) and not (U and opc != 9) if fp: if sz < 2: raise Unsupported('fp16 indexed') fsz = sz & 1 if fsz: if L: raise Unsupported('indexed L') idx = H else: idx = (H << 1) | L m = (M << 4) | rm if not scalar and fsz and not Q: raise Unsupported('indexed 2d Q') F, VT, VU, T, SF, SU, SET = FT[fsz] if scalar: el = f'{VR(VT, m)}[{idx}]' if opc == 9: return f'{SET}({d}, op_{F}(c, {"FOP_MULX" if U else "FOP_MUL"}, {SF}({n}), {el}, 0));' x = f'{SF}({n})' if opc == 1 else f'neg_{F}({SF}({n}))' return f'{SET}({d}, op_{F}(c, FOP_MADD, {SF}({d}), {x}, {el}));' el = f'vsplat_{F}({VR(VT, m)}, {idx}, {Q})' if opc == 9: return f'SV_FOP_{F.upper()}({"FOP_MULX" if U else "FOP_MUL"}, {Q}, {d}, {VQ(VT, Q, n)}, {el}, ({VT}){{}});' x = VQ(VT, Q, n) if opc == 1 else f'vneg_{F}({VQ(VT, Q, n)})' return f'SV_FOP_{F.upper()}(FOP_MADD, {Q}, {d}, {VQ(VT, Q, d)}, {x}, {el});' if sz == 1: idx, m = (H << 2) | (L << 1) | M, rm elif sz == 2: idx, m = (H << 1) | L, (M << 4) | rm else: raise Unsupported('indexed size') T = vu(sz) if scalar: op = {(0, 3): 'SQDMLAL', (0, 7): 'SQDMLSL', (0, 11): 'SQDMULL', (0, 12): 'SQDMULH', (0, 13): 'SQRDMULH'}.get((U, opc)) if op is None: raise Unsupported('scalar indexed') if op in ('SQDMULH', 'SQRDMULH'): return gen(lop(op), sz, 1, d, sz, 0, 0, n, sz, 0, m, sz, 0, idx) return gen(lop(op), sz, 1, d, sz + 1, 0, 0, n, sz, 0, m, sz, 0, idx) ne = lanes(Q, sz) el = shuf(T, VR(T, m), VR(T, m), [idx] * (16 >> sz)) if (U, opc) == (0, 8): return VWr(T, Q, d, f'{VR(T, n)} * {el}') if (U, opc) in ((1, 0), (1, 4)): return VWr(T, Q, d, f'{VR(T, d)} {"+" if opc == 0 else "-"} {VR(T, n)} * {el}') if (U, opc) in ((0, 12), (0, 13)): return same(lop('SQDMULH' if opc == 12 else 'SQRDMULH'), sz, ne, d, n, m, 0, idx) lng = {2: 'MLAL', 6: 'MLSL', 10: 'MULL', 3: 'SQDMLAL', 7: 'SQDMLSL', 11: 'SQDMULL'}.get(opc) if lng is None or (U and lng.startswith('SQ')): raise Unsupported('indexed opcode') ne = 8 >> sz return gen(lop(lng, U if not lng.startswith('SQ') else 0), sz, ne, d, sz + 1, 0, 0, n, sz, Q * ne, m, sz, 0, idx) # ------------------------------------------------------------ scalar FP @staticmethod def _ftype(w): t = bits(w, 22, 2) if t > 1: raise Unsupported('fp type') return t def fp_dp3(self, w): t = self._ftype(w) o1, o0 = bits(w, 21, 1), bits(w, 15, 1) d, n, m, a = bits(w, 0, 5), bits(w, 5, 5), bits(w, 16, 5), bits(w, 10, 5) F = FT[t][0] na, nn = o1, o1 ^ o0 # FMADD 00, FMSUB 01 (-n), FNMADD 10 (-a, -n), FNMSUB 11 (-a) return f'fp_fma_{F}(c, {na}, {nn}, {d}, {n}, {m}, {a});' def fp_dp2(self, w): t = self._ftype(w) opc, d, n, m = bits(w, 12, 4), bits(w, 0, 5), bits(w, 5, 5), bits(w, 16, 5) fop = ('FOP_MUL', 'FOP_DIV', 'FOP_ADD', 'FOP_SUB', 'FOP_MAX', 'FOP_MIN', 'FOP_MAXNM', 'FOP_MINNM', 'FOP_NMUL')[opc] if opc <= 8 else None if fop is None: raise Unsupported('fp dp2') return f'fp_op_{FT[t][0]}(c, {fop}, {d}, {n}, {m});' def fp_dp1(self, w): t = bits(w, 22, 2) opc, d, n = bits(w, 15, 6), bits(w, 0, 5), bits(w, 5, 5) if t == 3: # half source: only FCVT if opc == 4: return f'FP_SETS({d}, simd_h2s(c, VS16({n})));' if opc == 5: return f'FP_SETD({d}, simd_h2d(c, VS16({n})));' raise Unsupported('fp16 dp1') if t == 2: raise Unsupported('fp type') F, VT, VU, T, SF, SU, SET = FT[t] sign = '0x80000000u' if not t else '0x8000000000000000ull' if opc == 0: return f'c->v[{d}] = {SU}({n});' if opc == 1: return f'c->v[{d}] = {SU}({n}) & ~{sign};' if opc == 2: return f'c->v[{d}] = {SU}({n}) ^ {sign};' if opc == 3: return f'fp_op_{F}(c, FOP_SQRT, {d}, {n}, {n});' if opc in (4, 5, 7): to = {4: 0, 5: 1, 7: 3}[opc] if to == t: raise Unsupported('fcvt same') if to == 3: return f'c->v[{d}] = {"simd_s2h" if not t else "simd_dd2h"}(c, {SF}({n}));' return f'FP_SETD({d}, simd_s2d(c, VSF32({n})));' if to else f'FP_SETS({d}, simd_d2s(c, VSF64({n})));' rm = {8: 0, 9: 1, 10: 2, 11: 3, 12: 4, 14: 5, 15: 6}.get(opc) if rm is None: raise Unsupported('fp dp1 opcode') return f'{SET}({d}, frint_{F}(c, {SF}({n}), {rm}));' def fp_cmp(self, w): t = self._ftype(w) if bits(w, 14, 2) or bits(w, 0, 3): raise Unsupported('fcmp op') F, VT, VU, T, SF, SU, SET = FT[t] n, m, zero = bits(w, 5, 5), bits(w, 16, 5), bits(w, 3, 1) b = '0' if zero else f'{SF}({m})' return f'c->nzcv = cmp_{F}(c, {SF}({n}), {b});' def fp_ccmp(self, w): t = self._ftype(w) F, VT, VU, T, SF, SU, SET = FT[t] n, m, cnd, nzcv = bits(w, 5, 5), bits(w, 16, 5), bits(w, 12, 4), bits(w, 0, 4) return f'c->nzcv = {cond(cnd)} ? cmp_{F}(c, {SF}({n}), {SF}({m})) : {hx(nzcv << 28)};' def fp_csel(self, w): t = self._ftype(w) SU = FT[t][5] d, n, m, cnd = bits(w, 0, 5), bits(w, 5, 5), bits(w, 16, 5), bits(w, 12, 4) return f'c->v[{d}] = {cond(cnd)} ? {SU}({n}) : {SU}({m});' def fp_imm(self, w): t = self._ftype(w) if bits(w, 5, 5): raise Unsupported('fmov imm5') imm8, d = bits(w, 13, 8), bits(w, 0, 5) return f'c->v[{d}] = {hx(vfp_expand_imm(imm8, 32 << t))};' def fp_fixed(self, w): sf, t, rmode, opc, scale, n, d = (bits(w, 31, 1), bits(w, 22, 2), bits(w, 19, 2), bits(w, 16, 3), bits(w, 10, 6), bits(w, 5, 5), bits(w, 0, 5)) if t > 1: raise Unsupported('fixed type') if not sf and scale < 32: raise Unsupported('fixed scale') F, VT, VU, T, SF, SU, SET = FT[t] fb, ib = 64 - scale, 64 if sf else 32 if rmode == 0 and opc in (2, 3): src = X(n) if sf else W(n) return f'{SET}({d}, fromint_{F}(c, {src}, {ib}, {opc & 1}, {fb}));' if rmode == 3 and opc in (0, 1): return set_reg(sf, d, f'toint_{F}(c, {SF}({n}), 3, {fb}, {ib}, {opc})') raise Unsupported('fixed opcode') def fp_int(self, w): sf, t, rmode, opc, n, d = bits(w, 31, 1), bits(w, 22, 2), bits(w, 19, 2), bits(w, 16, 3), bits(w, 5, 5), bits(w, 0, 5) if opc in (6, 7): # FMOV if t == 2 and sf and rmode == 1: return set_reg(1, d, f'VHI64({n})') if opc == 6 else f'VHI64({d}) = {X(n)};' if rmode or t != sf or t > 1: raise Unsupported('fmov general') if opc == 6: return set_reg(sf, d, f'VS64({n})' if sf else f'VS32({n})') return f'c->v[{d}] = {X(n) if sf else W(n)};' if t > 1: raise Unsupported('fp int type') F, VT, VU, T, SF, SU, SET = FT[t] ib = 64 if sf else 32 if opc in (2, 3): if rmode: raise Unsupported('cvtf rmode') return f'{SET}({d}, fromint_{F}(c, {X(n) if sf else W(n)}, {ib}, {opc & 1}, 0));' if opc in (4, 5): if rmode: raise Unsupported('fcvta rmode') rm = 4 else: rm = (0, 1, 2, 3)[rmode] return set_reg(sf, d, f'toint_{F}(c, {SF}({n}), {rm}, 0, {ib}, {opc & 1})') # ------------------------------------------------------------ loads / stores def ldst(self, w, pc): rt = bits(w, 0, 5) if bits(w, 27, 3) == 0b011 and bits(w, 24, 2) == 0: # literal opc = bits(w, 30, 2) if opc == 3: raise Unsupported('ldr literal opc') addr = (pc + sext(bits(w, 5, 19), 19) * 4) & ((1 << 64) - 1) return f'c->v[{rt}] = {("LD32", "LD64", "LD128")[opc]}({hx(addr)});' if bits(w, 31, 1) == 0 and bits(w, 24, 6) in (0b001100, 0b001101): return self.ldst_struct(w) if bits(w, 27, 3) == 0b101: return self.ldst_pair(w) if bits(w, 27, 3) == 0b111: return self.ldst_reg(w) raise Unsupported(f'ldst simd {w:08x}') def ldst_pair(self, w): opc, mode, L = bits(w, 30, 2), bits(w, 23, 2), bits(w, 22, 1) imm7, rt2, rn, rt = sext(bits(w, 15, 7), 7), bits(w, 10, 5), bits(w, 5, 5), bits(w, 0, 5) if opc == 3: raise Unsupported('pair opc 3') scale = 2 + opc off = imm7 << scale base = XS(rn) post, wb = mode == 1, mode in (1, 3) ea = base if post else (f'{base} + {off}' if off >= 0 else f'{base} - {-off}') ld, st = {2: ('LD32', 'ST32'), 3: ('LD64', 'ST64'), 4: ('LD128', 'ST128')}[scale] sz = 1 << scale lines = [f'uint64_t ea = {ea};'] if L: lines.append(f'u128 a = {ld}(ea), b = {ld}(ea + {sz});') lines.append(f'c->v[{rt}] = a; c->v[{rt2}] = b;') else: cast = {2: 'VS32', 3: 'VS64', 4: 'c->v'}[scale] v1, v2 = (f'c->v[{rt}]', f'c->v[{rt2}]') if scale == 4 else (f'{cast}({rt})', f'{cast}({rt2})') lines.append(f'{st}(ea, {v1}); {st}(ea + {sz}, {v2});') if wb: dst = 'c->sp' if rn == 31 else f'c->x[{rn}]' if not post: lines.append(f'{dst} = ea;') else: lines.append(f'{dst} = ea + {off};' if off >= 0 else f'{dst} = ea - {-off};') return '{ ' + ' '.join(lines) + ' }' def ldst_reg(self, w): size, opc, rn, rt = bits(w, 30, 2), bits(w, 22, 2), bits(w, 5, 5), bits(w, 0, 5) if opc & 2: if size: raise Unsupported('ldst simd size') scale = 4 else: scale = size L = opc & 1 base = XS(rn) wb = None if bits(w, 24, 2) == 1: ea = f'{base} + {hx(bits(w, 10, 12) << scale)}' elif bits(w, 24, 2) == 0: kind = bits(w, 10, 2) if bits(w, 21, 1): if kind != 2: raise Unsupported('ldst simd reg kind') rm, option, S = bits(w, 16, 5), bits(w, 13, 3), bits(w, 12, 1) if option not in (2, 3, 6, 7): raise Unsupported('ldst simd reg option') ea = f'{base} + {extend_expr(rm, option, scale if S else 0)}' else: imm9 = sext(bits(w, 12, 9), 9) si = f'+ {imm9}' if imm9 >= 0 else f'- {-imm9}' if kind == 0: ea = f'{base} {si}' elif kind == 1: ea, wb = base, f'{base} {si}' elif kind == 3: ea, wb = f'{base} {si}', 'ea' else: raise Unsupported('ldst simd unprivileged') else: raise Unsupported('ldst simd') parts = [f'uint64_t ea = {ea};'] if L: parts.append(f'c->v[{rt}] = {("LD8", "LD16", "LD32", "LD64", "LD128")[scale]}(ea);') else: src = (f'VS8({rt})', f'VS16({rt})', f'VS32({rt})', f'VS64({rt})', f'c->v[{rt}]')[scale] parts.append(f'{("ST8", "ST16", "ST32", "ST64", "ST128")[scale]}(ea, {src});') if wb: parts.append(f'{"c->sp" if rn == 31 else f"c->x[{rn}]"} = {wb};') return '{ ' + ' '.join(parts) + ' }' def ldst_struct(self, w): Q, L, post, rm, rn, rt, sz = (bits(w, 30, 1), bits(w, 22, 1), bits(w, 23, 1), bits(w, 16, 5), bits(w, 5, 5), bits(w, 0, 5), bits(w, 10, 2)) if not post and rm: raise Unsupported('ldst struct rm') base = XS(rn) if bits(w, 24, 1) == 0: # multiple structures if bits(w, 21, 1): raise Unsupported('ldst multiple bit21') opc = bits(w, 12, 4) sel = {0: (4, 1), 2: (1, 4), 4: (3, 1), 6: (1, 3), 7: (1, 1), 8: (2, 1), 10: (1, 2)}.get(opc) if sel is None: raise Unsupported('ldst multiple opcode') selem, rpt = sel if sz == 3 and not Q and selem > 1: raise Unsupported('ldst multiple 1d') total = (8 << Q) * selem * rpt if selem == 1: # LD1/ST1: whole registers regs = [(rt + i) % 32 for i in range(rpt)] step = 16 if Q else 8 if L: acc = ' '.join(f'c->v[{r}] = {"LD128" if Q else "LD64"}(ea + {i * step});' for i, r in enumerate(regs)) else: acc = ' '.join(f'{"ST128" if Q else "ST64"}(ea + {i * step}, {"c->v[%d]" % r if Q else "VS64(%d)" % r});' for i, r in enumerate(regs)) else: acc = f'sl_{"ldm" if L else "stm"}(c, {Q}, {sz}, {selem}, {rpt}, {rt}, ea);' else: # single structure opc, S, R = bits(w, 13, 3), bits(w, 12, 1), bits(w, 21, 1) scale = opc >> 1 selem = ((opc & 1) << 1 | R) + 1 if scale == 3: if not L or S: raise Unsupported('ldnr') total = selem << sz acc = f'sl_ldr(c, {Q}, {sz}, {selem}, {rt}, ea);' else: if scale == 0: idx = (Q << 3) | (S << 2) | sz elif scale == 1: if sz & 1: raise Unsupported('ld1 h size') idx = (Q << 2) | (S << 1) | (sz >> 1) else: if sz & 2: raise Unsupported('ld1 s size') if sz & 1: if S: raise Unsupported('ld1 d S') idx, scale = Q, 3 else: idx = (Q << 1) | S total = selem << scale acc = f'sl_{"lds" if L else "sts"}(c, {scale}, {selem}, {idx}, {rt}, ea);' parts = [f'uint64_t ea = {base};', acc] if post: dst = 'c->sp' if rn == 31 else f'c->x[{rn}]' parts.append(f'{dst} = ea + {total};' if rm == 31 else f'{dst} = ea + {X(rm)};') return '{ ' + ' '.join(parts) + ' }' # ------------------------------------------------------------ CRC32 @staticmethod def crc(w): sf, rm, C, sz, rn, rd = bits(w, 31, 1), bits(w, 16, 5), bits(w, 12, 1), bits(w, 10, 2), bits(w, 5, 5), bits(w, 0, 5) if (sz == 3) != bool(sf): raise Unsupported('crc size') val = X(rm) if sz == 3 else W(rm) return set_reg(0, rd, f'{"CRC32C" if C else "CRC32"}({W(rn)}, {val}, {1 << sz})')