feat(asm): encode the arm64 SVE gather loads and scatter stores

Assisted-by: GLM 5.3
This commit is contained in:
petrbalvin committed 2026-10-07 13:51:10 +02:00
1 parent 6faf850793
commit 104bea036b
5 files changed
+1273 -2

No files matched your search

+130
View File
@@ -79,6 +79,20 @@ func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperan
out = append(out, ext)
continue
}
// The gather/scatter destination list, [Z13.B]: one scalable vector
// in brackets, its arrangement part of the instruction's identity.
if strings.HasPrefix(text, "[") && strings.HasSuffix(text, "]") {
if ext, ok := arm64ExtVector(strings.Trim(text, "[]")); ok {
out = append(out, ext)
continue
}
}
// The gather/scatter memory operand: a parenthesised register pair,
// an immediate-offset base or a lone vector base.
if ext, ok := arm64ExtSveMem(text); ok {
out = append(out, ext)
continue
}
if ext, ok := arm64ExtVector(text); ok {
out = append(out, ext)
continue
@@ -285,6 +299,122 @@ func arm64ExtSIMD(text string) (arch.ExtOperand, bool) {
return arch.ExtOperand{Kind: arch.ExtVReg, Reg: reg}, true
}
// arm64ExtSveMem parses the gather/scatter memory operand off a normalised
// operand text: the parenthesised pair (R6)(R14), (Z23.D<<1)(R24) and
// (Z4.S.UXTW)(R3), the immediate-offset base 6(Z7.S), and the lone vector
// base (Z5.D) of the stores. The second parenthesis accepts the
// stack-pointer spelling RSP; the ranges and the mode's own rules are left
// to the encoding, whose diagnostics name them.
func arm64ExtSveMem(text string) (arch.ExtOperand, bool) {
// The immediate-offset spelling: digits straight before the parenthesis.
if i := strings.IndexByte(text, '('); i > 0 && i == strings.LastIndexByte(text, '(') {
disp, err := strconv.ParseUint(text[:i], 10, 32)
if err == nil && strings.HasSuffix(text, ")") {
op, ok := arm64ExtSveMemGroup(text[i+1 : len(text)-1])
if !ok {
return arch.ExtOperand{}, false
}
if !op.BaseVec || op.Extend != 0 || op.Shift != 0 {
return arch.ExtOperand{}, false
}
op.Imm = int64(disp)
return op, true
}
}
// The parenthesised forms: one group or two.
rest, ok := strings.CutPrefix(text, "(")
if !ok || !strings.HasSuffix(text, ")") {
return arch.ExtOperand{}, false
}
rest = rest[:len(rest)-1]
first := rest
op := arch.ExtOperand{Off: -1}
if base, second, found := strings.Cut(rest, ")("); found {
first = base
off, ok := arm64ExtSveMemOffset(second)
if !ok {
return arch.ExtOperand{}, false
}
op = off
}
group, ok := arm64ExtSveMemGroup(first)
if !ok {
return arch.ExtOperand{}, false
}
group.Off = op.Off
group.Reg31 = op.Reg31
return group, true
}
// arm64ExtSveMemGroup parses one parenthesised memory register: R6, R6<<3,
// Z23.D, Z23.D<<1, Z4.S.UXTW or Z7.D.SXTW. The general registers run
// R0-R30 and the scalable vectors Z0-Z31 with an .S or .D element size and
// an optional UXTW or SXTW extension; the ranges are left to the encoding.
func arm64ExtSveMemGroup(text string) (arch.ExtOperand, bool) {
op := arch.ExtOperand{Kind: arch.ExtSveMem, Off: -1}
if base, shift, found := strings.Cut(text, "<<"); found {
n, err := strconv.Atoi(shift)
if err != nil || n < 0 {
return arch.ExtOperand{}, false
}
op.Shift = n
text = base
}
parts := strings.Split(text, ".")
switch parts[0][0] {
case 'R':
reg, err := strconv.Atoi(parts[0][1:])
if err != nil || len(parts) != 1 {
return arch.ExtOperand{}, false
}
op.Reg = reg
case 'Z':
reg, err := strconv.Atoi(parts[0][1:])
if err != nil || len(parts) < 2 || len(parts) > 3 {
return arch.ExtOperand{}, false
}
switch parts[1] {
case "S":
op.Arr = arch.ExtArrS
case "D":
op.Arr = arch.ExtArrD
default:
return arch.ExtOperand{}, false
}
op.Reg = reg
op.BaseVec = true
if len(parts) == 3 {
switch parts[2] {
case "UXTW":
op.Extend = 1
case "SXTW":
op.Extend = 2
default:
return arch.ExtOperand{}, false
}
}
default:
return arch.ExtOperand{}, false
}
return op, true
}
// arm64ExtSveMemOffset parses the second parenthesis of a gather/scatter
// memory operand: a plain R0-R30 or the stack-pointer spelling RSP.
func arm64ExtSveMemOffset(text string) (arch.ExtOperand, bool) {
if text == "RSP" {
return arch.ExtOperand{Off: 31, Reg31: 2}, true
}
if len(text) < 2 || text[0] != 'R' {
return arch.ExtOperand{}, false
}
reg, err := strconv.Atoi(text[1:])
if err != nil {
return arch.ExtOperand{}, false
}
return arch.ExtOperand{Off: reg}, true
}
// arm64ExtRegDigits parses the digits and optional arrangement suffix of a
// register spelling once the letter prefix is gone.
func arm64ExtRegDigits(text string) (reg int, arr arch.ExtArrangement, ok bool) {