Files
NX2navi/recomp/simd_c.py
chrg6 c4268779fc Write a V register with one 16-byte store.
A scalar result (an FP operation, a scalar load, FMOV, a compare, a conversion, a 64-bit vector operation) was assigned
to the register as a zero-extended 128-bit integer, which the compiler writes as two 8-byte stores, and INS and a
single-lane load wrote their lane alone. The next 16-byte load of the register then cannot take its value from the
store buffer and waits until the stores have left it.

Every such write now builds the whole value in a vector register and stores it once (runtime/simd.h): VSET puts a
scalar in the low bits with zero above it, VSET128 writes a 128-bit value built in an integer, and VINS loads the
register, replaces one lane and stores all of it. VINS passes the value through an empty asm statement, without which
the compiler turns the load, the insert and the store back into a store of the lane.

In the 360 guest functions a play profile sampled, stores of a part of a V register fall from 64322 to 4 and whole
stores rise from 10932 to 43665; the four left copy a 16-byte guest load that the compiler itself splits because the
memory was just written in halves. Host instructions fall from 1448075 to 1427163 and stores to the Cpu from 177160 to
145566; its loads rise from 141090 to 143521, as an insert loads the register it changes.

The lifted text of FP and SIMD code changes with this, so those files are compiled again.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 21:33:21 -05:00

1165 lines
52 KiB
Python

# Copyright (c) 2026 Peiikai (MIT; see THIRD_PARTY_NOTICES.md).
"""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 vset(d, e, full=False):
"""C that writes V register d whole (runtime/simd.h VSET, VSET128): e is a scalar of at most 64 bits, which leaves
the bits above it zero, or with full a 128-bit value."""
return f'VSET128({d}, {e});' if full else f'VSET({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'VSET({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'VINS({vu(sz)}, {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'VINS({vu(sz)}, {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'VSET({d}, ({x} > {y}) ? ~0ull : 0);'
if opc == 7:
return f'VSET({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'VSET({d}, (uint64_t)({a} {"-" if U else "+"} {b}));'
if opc == 17:
return f'VSET({d}, ({a} == {b}) ? ~0ull : 0);' if U else f'VSET({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'VSET({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'VSET({d}, ({sa} {">=" if U else ">"} 0) ? ~0ull : 0);'
if opc == 9:
return f'VSET({d}, ({sa} {"<=" if U else "=="} 0) ? ~0ull : 0);'
if opc == 10 and not U:
return f'VSET({d}, ({sa} < 0) ? ~0ull : 0);'
if opc == 11:
return f'VSET({d}, (uint64_t)-{a});' if U else f'VSET({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'VSET({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'VSET({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'VSET({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 vset(d, u128(v), v >> 64 != 0)
# ------------------------------------------------------------ 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'VSET({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'VSET({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'VSET({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'VSET({d}, {SU}({n}));'
if opc == 1:
return f'VSET({d}, {SU}({n}) & ~{sign});'
if opc == 2:
return f'VSET({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'VSET({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'VSET({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'VSET({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'VINS(vu64, {d}, 1, {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'VSET({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 vset(rt, f'{("LD32", "LD64", "LD128")[opc]}({hx(addr)})', opc == 2)
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" if scale == 4 else "uint64_t"} a = {ld}(ea), b = {ld}(ea + {sz});')
lines.append(f'{vset(rt, "a", scale == 4)} {vset(rt2, "b", scale == 4)}')
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(vset(rt, f'{("LD8", "LD16", "LD32", "LD64", "LD128")[scale]}(ea)', scale == 4))
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(vset(r, f'{"LD128" if Q else "LD64"}(ea + {i * step})', Q)
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})')