// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause // This file carries the extended-instruction layer: instructions the Go // toolchain does not know at all, described as data and validated against // golden vectors from the Arm Architecture Reference Manual rather than // against the toolchain. It sits beside the generated tables, never inside // them: arch/arm64_gen.go stays untouched, and Extensions returns the layer // per architecture so a later amd64 table attaches through the same door. // // The first entry is the arm64 SVE and SVE2 integer add/subtract/multiply // family (twenty-three forms over four word shapes). The encodings are // transcribed from the manual and cross-checked against the GNU assembler's // and LLVM's published encodings; the golden vectors in arm64_ext_test.go pin // the bytes. package arch import "fmt" // ExtOperandKind classifies one operand of an extended instruction. type ExtOperandKind uint8 // Operand kinds. const ( ExtZReg ExtOperandKind = iota // scalable vector register Z0-Z31 ExtPReg // predicate register P0-P15 ExtImm // immediate ) // String returns a short label for the kind. func (k ExtOperandKind) String() string { switch k { case ExtZReg: return "scalable vector register" case ExtPReg: return "predicate register" case ExtImm: return "immediate" default: return "operand" } } // ExtArrangement is the element-size suffix a scalable vector operand // carries: .B, .H, .S, .D or .Q. ExtArrNone means the operand is written // bare, which the SVE forms in this layer reject. type ExtArrangement uint8 // Arrangements, widest last. const ( ExtArrNone ExtArrangement = iota ExtArrB // 8-bit elements ExtArrH // 16-bit elements ExtArrS // 32-bit elements ExtArrD // 64-bit elements ExtArrQ // 128-bit elements ) // String returns the assembler suffix, with the leading dot. func (a ExtArrangement) String() string { switch a { case ExtArrB: return ".B" case ExtArrH: return ".H" case ExtArrS: return ".S" case ExtArrD: return ".D" case ExtArrQ: return ".Q" default: return "" } } // Width returns the byte width of one element under the arrangement. func (a ExtArrangement) Width() int { switch a { case ExtArrB: return 1 case ExtArrH: return 2 case ExtArrS: return 4 case ExtArrD: return 8 case ExtArrQ: return 16 default: return 0 } } // sizeBits maps the arrangement onto the two-bit size field the integer SVE // classes carry at bits 23..22: 00=B, 01=H, 10=S, 11=D. ok is false for the // arrangements no such class accepts (.Q and the bare spelling). func (a ExtArrangement) sizeBits() (uint32, bool) { switch a { case ExtArrB, ExtArrH, ExtArrS, ExtArrD: return uint32(a) - 1, true default: return 0, false } } // ExtQualifier is the predicate qualifier spelled after the slash. type ExtQualifier uint8 // Predicate qualifiers. const ( ExtQualNone ExtQualifier = iota // bare Pn (non-predicating position) ExtQualMerging // /M, inactive lanes keep the destination ExtQualZeroing // /Z, inactive lanes become zero ) // String returns the assembler spelling, with the leading slash. func (q ExtQualifier) String() string { switch q { case ExtQualMerging: return "/M" case ExtQualZeroing: return "/Z" default: return "" } } // ExtOperand is one operand of an extended instruction, already resolved to // its pieces: a register with its arrangement and qualifier, or an immediate // with its optional left shift. The assembler's future hook constructs these // from the parsed statement; Encode consumes them. type ExtOperand struct { Kind ExtOperandKind Reg int // register number (Z: 0..31, P: 0..15) Arr ExtArrangement // element-size suffix; ExtArrNone when bare Qual ExtQualifier // predicate qualifier; ExtQualNone elsewhere Imm int64 // immediate value (ExtImm only) // Shift carries the LSL amount an immediate form shifts the constant by // before use (0 or 8 in the SVE add/subtract immediate class). HasShift // separates a spelled shift (validated as written) from an unshifted // operand (the encoder may derive the sh bit from the value). Shift int HasShift bool } // ExtVector builds a scalable vector operand, ADD Z1.S style. func ExtVector(reg int, arr ExtArrangement) ExtOperand { return ExtOperand{Kind: ExtZReg, Reg: reg, Arr: arr} } // ExtPredicate builds a predicate operand with its qualifier, P0/M style. func ExtPredicate(reg int, qual ExtQualifier) ExtOperand { return ExtOperand{Kind: ExtPReg, Reg: reg, Qual: qual} } // ExtImmediate builds an unshifted immediate operand. func ExtImmediate(v int64) ExtOperand { return ExtOperand{Kind: ExtImm, Imm: v} } // ExtShiftedImmediate builds an immediate operand with a spelled LSL amount. func ExtShiftedImmediate(v int64, shift int) ExtOperand { return ExtOperand{Kind: ExtImm, Imm: v, Shift: shift, HasShift: true} } // ExtField is one named field of the 32-bit encoding word: a bit offset from // the least significant end and the field's width. type ExtField struct { Off uint8 Width uint8 } // extMask returns the field's bits as a mask. func extMask(f ExtField) uint32 { return ^uint32(0) >> (32 - f.Width) } // extSet ORs v into the field of word. func extSet(word uint32, f ExtField, v uint32) uint32 { return word | (v&extMask(f))< 31 { return nil, fmt.Errorf("%s: operand %d is Z%d, outside Z0-Z31", in.Name, i+1, op.Reg) } } arr, err := in.sharedArrangement(ops) if err != nil { return nil, err } size, ok := arr.sizeBits() if !ok { return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class", in.Name, arr) } word := in.Word word = extSet(word, extFieldRn, uint32(ops[0].Reg)) word = extSet(word, extFieldRm, uint32(ops[1].Reg)) word = extSet(word, extFieldRd, uint32(ops[2].Reg)) word = extSet(word, in.Size, size) return extWordLE(word), nil } // encodePredicated fills the governed destructive form: Zm, Pg/M, Zdn. The // predicate is a 3-bit field, the merging qualifier alone, and carries no // arrangement suffix in this class. func (in ExtInstr) encodePredicated(ops []ExtOperand) ([]byte, error) { zm, pg, zdn := ops[0], ops[1], ops[2] if zm.Kind != ExtZReg { return nil, fmt.Errorf("%s: operand 1 wants a scalable vector register, got %s", in.Name, zm.Kind) } if zm.Reg < 0 || zm.Reg > 31 { return nil, fmt.Errorf("%s: operand 1 is Z%d, outside Z0-Z31", in.Name, zm.Reg) } if pg.Kind != ExtPReg { return nil, fmt.Errorf("%s: operand 2 wants a predicate register, got %s", in.Name, pg.Kind) } if pg.Reg < 0 || pg.Reg > 7 { return nil, fmt.Errorf("%s: operand 2 is P%d, outside P0-P7 in this class", in.Name, pg.Reg) } if pg.Qual != ExtQualMerging { return nil, fmt.Errorf("%s: operand 2 wants the merging qualifier /M, got %q", in.Name, pg.Qual) } if pg.Arr != ExtArrNone { return nil, fmt.Errorf("%s: the governing predicate carries no arrangement suffix, got %s", in.Name, pg.Arr) } if zdn.Kind != ExtZReg { return nil, fmt.Errorf("%s: operand 3 wants a scalable vector register, got %s", in.Name, zdn.Kind) } if zdn.Reg < 0 || zdn.Reg > 31 { return nil, fmt.Errorf("%s: operand 3 is Z%d, outside Z0-Z31", in.Name, zdn.Reg) } if zm.Arr != zdn.Arr { return nil, fmt.Errorf("%s: operands 1 and 3 carry arrangements %s and %s, they must match", in.Name, zm.Arr, zdn.Arr) } size, ok := zdn.Arr.sizeBits() if !ok { return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class", in.Name, zdn.Arr) } word := in.Word word = extSet(word, extFieldRn, uint32(zm.Reg)) word = extSet(word, extFieldPg, uint32(pg.Reg)) word = extSet(word, extFieldRd, uint32(zdn.Reg)) word = extSet(word, in.Size, size) return extWordLE(word), nil } // encodeImmediate fills the add/subtract immediate form: imm{, LSL #8}, Zdn. // The class encodes an unsigned imm8 with one shift bit, so a bare multiple // of 256 derives the shift the way the GNU assembler canonicalises it. func (in ExtInstr) encodeImmediate(ops []ExtOperand) ([]byte, error) { imm, zdn := ops[0], ops[1] imm8, sh, err := in.addSubImmediate(imm, zdn.Arr) if err != nil { return nil, err } word := in.Word word = extSet(word, extFieldImm8, uint32(imm8)) if sh != 0 { word = extSet(word, extFieldSh, 1) } word, err = in.setDestAndSize(word, zdn) if err != nil { return nil, err } return extWordLE(word), nil } // encodeSignedImmediate fills the multiply immediate form: simm8, Zdn, with // no shift bit in the class. func (in ExtInstr) encodeSignedImmediate(ops []ExtOperand) ([]byte, error) { imm, zdn := ops[0], ops[1] if imm.Kind != ExtImm { return nil, fmt.Errorf("%s: operand 1 wants an immediate, got %s", in.Name, imm.Kind) } if imm.HasShift { return nil, fmt.Errorf("%s: the signed immediate class takes no shift", in.Name) } if imm.Imm < -128 || imm.Imm > 127 { return nil, fmt.Errorf("%s: immediate %d is outside the signed 8-bit range -128..127", in.Name, imm.Imm) } word := in.Word word = extSet(word, extFieldImm8, uint32(imm.Imm)) word, err := in.setDestAndSize(word, zdn) if err != nil { return nil, err } return extWordLE(word), nil } // addSubImmediate resolves the immediate operand of the add/subtract // immediate class into its imm8 and shift bit: a spelled shift is validated // as written, a bare multiple of 256 (on .H, .S or .D) derives one. func (in ExtInstr) addSubImmediate(op ExtOperand, arr ExtArrangement) (imm8, sh int, err error) { if op.Kind != ExtImm { return 0, 0, fmt.Errorf("%s: operand 1 wants an immediate, got %s", in.Name, op.Kind) } switch { case op.HasShift: if op.Shift != 0 && op.Shift != 8 { return 0, 0, fmt.Errorf("%s: the shift amount must be 0 or 8, got %d", in.Name, op.Shift) } if arr == ExtArrB && op.Shift != 0 { return 0, 0, fmt.Errorf("%s: arrangement .B takes no shift", in.Name) } if op.Imm < 0 || op.Imm > 255 { return 0, 0, fmt.Errorf("%s: immediate %d is outside the unsigned 8-bit range 0..255", in.Name, op.Imm) } return int(op.Imm), op.Shift, nil case op.Imm >= 0 && op.Imm <= 255: return int(op.Imm), 0, nil case arr != ExtArrB && op.Imm >= 256 && op.Imm <= 255<<8 && op.Imm%256 == 0: // A bare multiple of 256 rides the shift bit, 65280 = 255<<8 included. return int(op.Imm / 256), 8, nil default: return 0, 0, fmt.Errorf("%s: immediate %d is not an unsigned imm8%s, nor a multiple of 256 the shift bit can carry", in.Name, op.Imm, arr.shiftNote()) } } // shiftNote describes where a shifted constant is expressible, for the // immediate range error. func (arr ExtArrangement) shiftNote() string { if arr == ExtArrB { return " (and .B takes no shifted constant)" } return " (a multiple of 256 up to 65280 shifts)" } // setDestAndSize fills the destructive destination register and the size // field from the arrangement the vector carries. func (in ExtInstr) setDestAndSize(word uint32, zdn ExtOperand) (uint32, error) { if zdn.Kind != ExtZReg { return 0, fmt.Errorf("%s: operand 2 wants a scalable vector register, got %s", in.Name, zdn.Kind) } if zdn.Reg < 0 || zdn.Reg > 31 { return 0, fmt.Errorf("%s: operand 2 is Z%d, outside Z0-Z31", in.Name, zdn.Reg) } size, ok := zdn.Arr.sizeBits() if !ok { return 0, fmt.Errorf("%s: arrangement %s has no size encoding in this class", in.Name, zdn.Arr) } word = extSet(word, extFieldRd, uint32(zdn.Reg)) word = extSet(word, in.Size, size) return word, nil } // sharedArrangement returns the one arrangement all vector operands carry, or // an error when any operand is bare or they disagree. func (in ExtInstr) sharedArrangement(ops []ExtOperand) (ExtArrangement, error) { arr := ops[0].Arr for i, op := range ops { if op.Arr == ExtArrNone { return 0, fmt.Errorf("%s: operand %d carries no arrangement suffix", in.Name, i+1) } if op.Arr != arr { return 0, fmt.Errorf("%s: operand %d carries arrangement %s, want %s", in.Name, i+1, op.Arr, arr) } } return arr, nil } // extWordLE returns a 32-bit encoding word as 4 little-endian bytes. func extWordLE(w uint32) []byte { return []byte{byte(w), byte(w >> 8), byte(w >> 16), byte(w >> 24)} } // --- the arm64 SVE/SVE2 table ------------------------------------------------ // arm64Extensions is the extended instruction layer of arm64: the SVE and // SVE2 integer add/subtract/multiply family. The Go toolchain knows none of // these; the encodings are transcribed from the ARM Architecture Reference // Manual (DDI 0487J, Part C, Chapter C8, the alphabetical list of SVE // instructions) and cross-checked against the GNU assembler's and LLVM's // published encodings. var arm64Extensions = []ExtInstr{ // Unpredicated three-vector forms: ADD Z0.S, Z1.S, Z2.S. {Name: "ADD", Summary: "Add scalable vector elements, unpredicated", Word: 0x04200000, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: ADD (vectors, unpredicated)"}, {Name: "SUB", Summary: "Subtract scalable vector elements, unpredicated", Word: 0x04200400, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SUB (vectors, unpredicated)"}, {Name: "SQADD", Summary: "Add signed saturating scalable vector elements, unpredicated", Word: 0x04201000, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SQADD (vectors, unpredicated)"}, {Name: "UQADD", Summary: "Add unsigned saturating scalable vector elements, unpredicated", Word: 0x04201400, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: UQADD (vectors, unpredicated)"}, {Name: "SQSUB", Summary: "Subtract signed saturating scalable vector elements, unpredicated", Word: 0x04201800, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SQSUB (vectors, unpredicated)"}, {Name: "UQSUB", Summary: "Subtract unsigned saturating scalable vector elements, unpredicated", Word: 0x04201c00, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: UQSUB (vectors, unpredicated)"}, {Name: "MUL", Summary: "Multiply scalable vector elements, unpredicated", Word: 0x04206000, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE2, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: MUL (vectors, unpredicated)"}, {Name: "SMULH", Summary: "Multiply signed scalable vector elements, keeping the high half, unpredicated", Word: 0x04206800, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE2, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SMULH (vectors, unpredicated)"}, {Name: "UMULH", Summary: "Multiply unsigned scalable vector elements, keeping the high half, unpredicated", Word: 0x04206c00, Form: ExtFormVectors, Size: extSizeBHSD, Feature: ExtFeatureSVE2, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: UMULH (vectors, unpredicated)"}, // Governed destructive forms, merging: ADD Z1.S, P0/M, Z0.S. {Name: "ADD", Summary: "Add scalable vector elements under a governing predicate, merging", Word: 0x04000000, Form: ExtFormPredicated, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: ADD (vectors, predicated)"}, {Name: "SUB", Summary: "Subtract scalable vector elements under a governing predicate, merging", Word: 0x04010000, Form: ExtFormPredicated, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SUB (vectors, predicated)"}, {Name: "SUBR", Summary: "Reverse-subtract scalable vector elements under a governing predicate, merging", Word: 0x04030000, Form: ExtFormPredicated, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SUBR (vectors, predicated)"}, {Name: "MUL", Summary: "Multiply scalable vector elements under a governing predicate, merging", Word: 0x04100000, Form: ExtFormPredicated, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: MUL (vectors, predicated)"}, {Name: "SMULH", Summary: "Multiply signed scalable vector elements, keeping the high half, under a governing predicate, merging", Word: 0x04120000, Form: ExtFormPredicated, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SMULH (vectors, predicated)"}, {Name: "UMULH", Summary: "Multiply unsigned scalable vector elements, keeping the high half, under a governing predicate, merging", Word: 0x04130000, Form: ExtFormPredicated, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: UMULH (vectors, predicated)"}, // Immediate forms: ADD $255, Z0.S. {Name: "ADD", Summary: "Add an unsigned immediate to scalable vector elements", Word: 0x2520c000, Form: ExtFormImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: ADD (vectors, immediate)"}, {Name: "SUB", Summary: "Subtract an unsigned immediate from scalable vector elements", Word: 0x2521c000, Form: ExtFormImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SUB (vectors, immediate)"}, {Name: "SUBR", Summary: "Subtract scalable vector elements from an unsigned immediate", Word: 0x2523c000, Form: ExtFormImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SUBR (vectors, immediate)"}, {Name: "SQADD", Summary: "Add a signed saturating unsigned immediate to scalable vector elements", Word: 0x2524c000, Form: ExtFormImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SQADD (vectors, immediate)"}, {Name: "UQADD", Summary: "Add an unsigned saturating immediate to scalable vector elements", Word: 0x2525c000, Form: ExtFormImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: UQADD (vectors, immediate)"}, {Name: "SQSUB", Summary: "Subtract an unsigned immediate from scalable vector elements with signed saturation", Word: 0x2526c000, Form: ExtFormImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: SQSUB (vectors, immediate)"}, {Name: "UQSUB", Summary: "Subtract an unsigned immediate from scalable vector elements with unsigned saturation", Word: 0x2527c000, Form: ExtFormImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: UQSUB (vectors, immediate)"}, // The signed immediate of the multiply class: MUL $-128, Z0.B. {Name: "MUL", Summary: "Multiply scalable vector elements by a signed immediate", Word: 0x2530c000, Form: ExtFormSignedImmediate, Size: extSizeBHSD, Feature: ExtFeatureSVE, Ref: "ARM DDI 0487J, C8.2 SVE instruction descriptions: MUL (vectors, immediate)"}, } // Extensions returns the extended-instruction layer registered for a, outside // the generated tables. An architecture whose extended layer is not built // yet returns nothing: the mechanism is ordinary code, not a build tag, and // it simply offers no instruction where none is registered. func Extensions(a Arch) []ExtInstr { switch a { case ARM64: return arm64Extensions default: return nil } }