feat(arch): the SVE predicate family in the extended layer

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 02:26:19 +02:00
1 parent 83052ab466
commit 8231302bca
5 files changed
+1451 -42

No files matched your search

+777 -11
View File
@@ -9,11 +9,13 @@
// per architecture; the amd64 side of the layer lives in amd64_ext.go and
// 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.
// The first entries are the arm64 SVE and SVE2 integer add/subtract/multiply
// family (twenty-three forms over four word shapes) and the SVE and SVE2.1
// predicate family (logical operations, breaks, permutations, tests, the
// first-fault register group and the while compares). The encodings are
// transcribed from the manual and cross-checked against the Go toolchain's
// generated encoding table and corpus; the golden vectors in
// arm64_ext_test.go pin the bytes.
package arch
@@ -35,6 +37,9 @@ const (
ExtR32 // 32-bit general register EAX-R15D
ExtR64 // 64-bit general register RAX-R15
ExtMem // base-relative memory operand, 4660(R8) style
// The arm64 layer's general and counter registers.
ExtGReg // general register R0-R30
ExtPNReg // predicate-as-counter register PN8-PN15
)
// String returns a short label for the kind.
@@ -60,6 +65,10 @@ func (k ExtOperandKind) String() string {
return "64-bit general register"
case ExtMem:
return "memory operand"
case ExtGReg:
return "general register"
case ExtPNReg:
return "predicate-as-counter register"
default:
return "operand"
}
@@ -156,8 +165,8 @@ func (q ExtQualifier) String() string {
// from the parsed statement; Encode consumes them.
type ExtOperand struct {
Kind ExtOperandKind
// Reg is the register number (Z: 0..31, P: 0..15); under ExtMem it is
// the base general register, 0..15.
// Reg is the register number (Z: 0..31, P: 0..15, R: 0..30, PN: 8..15);
// under ExtMem it is the base general register, 0..15.
Reg int
Arr ExtArrangement // element-size suffix; ExtArrNone when bare
Qual ExtQualifier // predicate qualifier; ExtQualNone elsewhere
@@ -206,6 +215,24 @@ func ExtPredicate(reg int, qual ExtQualifier) ExtOperand {
return ExtOperand{Kind: ExtPReg, Reg: reg, Qual: qual}
}
// ExtPredicateSized builds a predicate operand carrying an element-size
// suffix, P4.B style.
func ExtPredicateSized(reg int, arr ExtArrangement) ExtOperand {
return ExtOperand{Kind: ExtPReg, Reg: reg, Arr: arr}
}
// ExtCounterPredicate builds a predicate-as-counter operand, PN14.S style.
// The counter spellings run PN8-PN15, the convention the toolchain's
// generated table and corpus use for the three-bit counter field.
func ExtCounterPredicate(reg int, arr ExtArrangement) ExtOperand {
return ExtOperand{Kind: ExtPNReg, Reg: reg, Arr: arr}
}
// ExtGeneral builds a general register operand, R2 style.
func ExtGeneral(reg int) ExtOperand {
return ExtOperand{Kind: ExtGReg, Reg: reg}
}
// ExtImmediate builds an unshifted immediate operand.
func ExtImmediate(v int64) ExtOperand {
return ExtOperand{Kind: ExtImm, Imm: v}
@@ -261,6 +288,13 @@ func extSet(word uint32, f ExtField, v uint32) uint32 {
// named after their role in the three-vector class; the predicated class
// reuses extFieldRn for its Zm operand and extFieldPg for the governing
// predicate, which that class narrows to three bits (P0-P7).
//
// The predicate classes place their operands in the same neighbourhood:
// Pd in a four-bit field at bit 0, the governing predicate either narrow
// (three bits, P0-P7) or wide (four bits, P0-P15) at bit 10, the first
// source predicate in the five-bit slot at bit 5, and Pm in a three-bit
// field at bit 16. The general registers of the while-compare class use
// the five-bit Rm slot at bit 16.
var (
extFieldRd = ExtField{0, 5} // destination (Zd or Zdn)
extFieldRn = ExtField{5, 5} // first source (Zn, or Zm in the predicated class)
@@ -269,6 +303,12 @@ var (
extFieldImm8 = ExtField{5, 8} // the immediate, bits 12..5
extFieldSh = ExtField{13, 1} // the shift flag: 1 means LSL #8
extSizeBHSD = ExtField{22, 2} // element-size field of every class here, bits 23..22
extFieldPd = ExtField{0, 4} // predicate destination P0-P15
extFieldPn = ExtField{5, 5} // predicate source slot P0-P15 (bit 4 unused here)
extFieldPgN = ExtField{10, 3} // narrow governing predicate P0-P7
extFieldPgW = ExtField{10, 4} // wide governing predicate P0-P15
extFieldPm = ExtField{16, 3} // predicate source P0-P7
extFieldPnc = ExtField{0, 3} // predicate-as-counter destination PN8-PN15, field holds reg-8
)
// ExtForm enumerates the operand shapes the extension layer defines, in Plan
@@ -344,14 +384,88 @@ const (
// ExtFormAmdVecMem is the memory-store form: VMOVSH 4660(R9), X29
// shape, the manual's m16, xmm1 lines. Operands: src, mem.
ExtFormAmdVecMem
// ExtFormPredicateLogical is the predicate logical-operation form of the
// SVE2.1 predicate classes: PAND P4.B, P2.B, P1.Z, P14.B computes
// P14 = P4 AND P2 over the active lanes of P1. Operands: Pm.B, Pn.B,
// Pg/Z, Pd.B. The zeroing qualifier on the governing predicate is part
// of the spelling; every arrangement is .B and the class carries no size
// field.
ExtFormPredicateLogical
// ExtFormPredicateSelect is PSEL's form, PSEL P4.B, P2.B, P1, P14.B:
// the governing predicate arrives bare, without a qualifier. Otherwise
// identical to ExtFormPredicateLogical.
ExtFormPredicateSelect
// ExtFormPredicateLogicalDest is the break form whose first operand is
// the destination read back: PBRKN P4.B, P2.B, P1.Z, P4.B. Operands:
// Pdm.B, Pn.B, Pg/Z, Pdm.B, with operand 1 required to equal operand 4;
// the encoding carries the register once.
ExtFormPredicateLogicalDest
// ExtFormPredicateBreak is the three-operand break form, PBRKA P5.B,
// P9.Z, P2.B. Operands: Pn.B, Pg/Z or Pg/M (both spellings encode the
// one word the manual gives), Pd.B. The governing predicate is the wide
// four-bit field, P0-P15.
ExtFormPredicateBreak
// ExtFormPredicateBreakZero is ExtFormPredicateBreak with the zeroing
// qualifier alone, PBRKAS P5.B, P9.Z, P4.B.
ExtFormPredicateBreakZero
// ExtFormPredicatePermute is the predicate permutation form, PTRN1 P5.D,
// P4.D, P2.D. Operands: Pm.T, Pn.T, Pd.T under one shared arrangement
// (.B, .H, .S or .D), which feeds the size field.
ExtFormPredicatePermute
// ExtFormPredicateFirst is PFIRST's form, PPFIRST P5.B, P9, P5.B, with
// the destination read back like ExtFormPredicateLogicalDest. Operands:
// Pdn.B, Pg (bare), Pdn.B.
ExtFormPredicateFirst
// ExtFormPredicateNext is PNEXT's form, PPNEXT P5.D, P4, P5.D, the
// destination-read-back shape with an arrangement and a size field.
// Operands: Pdn.T, Pv (bare), Pdn.T.
ExtFormPredicateNext
// ExtFormPredicateOne writes or reads a single predicate, PPFALSE P13.B
// or the unpredicated RDFFR P13.B. Operands: Pd.B.
ExtFormPredicateOne
// ExtFormPredicateWrite is WRFFR's single-source form, PWRFFR P13.B: the
// one predicate sits in the source slot, not the destination.
// Operands: Pn.B.
ExtFormPredicateWrite
// ExtFormPredicateFFRRead is the predicated first-fault read, PRDFFR
// P14.Z, P0.B. Operands: Pg/Z, Pd.B; the governing predicate is the
// wide four-bit field.
ExtFormPredicateFFRRead
// ExtFormPredicateMove is the predicate reverse, PREV P14.S, P13.S.
// Operands: Pn.T, Pd.T under one shared arrangement.
ExtFormPredicateMove
// ExtFormPredicateUnpack is the predicate unpack, PPUNPKHI P14.B, P0.H:
// the arrangements are fixed by the class (.B source, .H destination)
// and carry no size field. Operands: Pn.B, Pd.H.
ExtFormPredicateUnpack
// ExtFormPredicateTest is PTEST, PPTEST P14.B, P0, which sets the flags
// and takes no destination. Operands: Pn.B, Pg (bare).
ExtFormPredicateTest
// ExtFormWhile is the while-compare form, PWHILELT R2, R10, P10.H:
// P10 counts the lanes where the signed comparison holds. Operands:
// Rm (R0-R30), Rn (R0-R30), Pd.T, the arrangement feeding the size
// field.
ExtFormWhile
// ExtFormPredicateCounter is PTRUE's counter-register form, PPTRUE
// PN14.S: the destination is a predicate-as-counter register, spelled
// PN8-PN15 and encoded as reg-8 in the three-bit field. Operands:
// PNd.T.
ExtFormPredicateCounter
// ExtFormNone takes no operands at all, SETFFR.
ExtFormNone
)
// Arity returns the operand count the form takes.
func (f ExtForm) Arity() int {
switch f {
case ExtFormVectors, ExtFormPredicated:
case ExtFormVectors, ExtFormPredicated, ExtFormPredicatePermute,
ExtFormPredicateFirst, ExtFormPredicateNext, ExtFormWhile,
ExtFormPredicateBreak, ExtFormPredicateBreakZero:
return 3
case ExtFormImmediate, ExtFormSignedImmediate:
case ExtFormPredicateLogical, ExtFormPredicateSelect, ExtFormPredicateLogicalDest:
return 4
case ExtFormImmediate, ExtFormSignedImmediate, ExtFormPredicateFFRRead,
ExtFormPredicateMove, ExtFormPredicateUnpack, ExtFormPredicateTest:
return 2
case ExtFormAmdVec3, ExtFormAmdMask2, ExtFormAmdVecGprVec:
return 3
@@ -361,6 +475,10 @@ func (f ExtForm) Arity() int {
return 4
case ExtFormAmdMemVec, ExtFormAmdVecMem:
return 2
case ExtFormPredicateOne, ExtFormPredicateWrite, ExtFormPredicateCounter:
return 1
case ExtFormNone:
return 0
default:
return 0
}
@@ -380,6 +498,22 @@ func (f ExtForm) Kinds() []ExtOperandKind {
// The amd64 forms return no kinds: the exact vector class depends on the
// entry's encoding template (its L'L field), which the form alone cannot
// name, and the encode paths diagnose the class themselves.
case ExtFormPredicateLogical, ExtFormPredicateSelect, ExtFormPredicateLogicalDest:
return []ExtOperandKind{ExtPReg, ExtPReg, ExtPReg, ExtPReg}
case ExtFormPredicateBreak, ExtFormPredicateBreakZero, ExtFormPredicateFFRRead:
return []ExtOperandKind{ExtPReg, ExtPReg, ExtPReg}
case ExtFormPredicatePermute:
return []ExtOperandKind{ExtPReg, ExtPReg, ExtPReg}
case ExtFormPredicateFirst, ExtFormPredicateNext:
return []ExtOperandKind{ExtPReg, ExtPReg, ExtPReg}
case ExtFormPredicateOne, ExtFormPredicateWrite, ExtFormPredicateCounter:
return []ExtOperandKind{ExtPReg}
case ExtFormPredicateMove, ExtFormPredicateUnpack, ExtFormPredicateTest:
return []ExtOperandKind{ExtPReg, ExtPReg}
case ExtFormWhile:
return []ExtOperandKind{ExtGReg, ExtGReg, ExtPReg}
case ExtFormNone:
return nil
default:
return nil
}
@@ -418,6 +552,40 @@ func (f ExtForm) String() string {
return "memory into a vector"
case ExtFormAmdVecMem:
return "a vector into memory"
case ExtFormPredicateLogical:
return "predicate logical"
case ExtFormPredicateSelect:
return "predicate select"
case ExtFormPredicateLogicalDest:
return "predicate logical, shared destination"
case ExtFormPredicateBreak:
return "break"
case ExtFormPredicateBreakZero:
return "break, zeroing"
case ExtFormPredicatePermute:
return "predicate permutation"
case ExtFormPredicateFirst:
return "predicate first"
case ExtFormPredicateNext:
return "predicate next"
case ExtFormPredicateOne:
return "single predicate"
case ExtFormPredicateWrite:
return "single source predicate"
case ExtFormPredicateFFRRead:
return "FFR read"
case ExtFormPredicateMove:
return "predicate move"
case ExtFormPredicateUnpack:
return "predicate unpack"
case ExtFormPredicateTest:
return "predicate test"
case ExtFormWhile:
return "while compare"
case ExtFormPredicateCounter:
return "counter predicate"
case ExtFormNone:
return "no operands"
default:
return "unknown form"
}
@@ -430,8 +598,9 @@ type ExtFeature string
// The features the arm64 layer covers.
const (
ExtFeatureSVE ExtFeature = "sve"
ExtFeatureSVE2 ExtFeature = "sve2"
ExtFeatureSVE ExtFeature = "sve"
ExtFeatureSVE2 ExtFeature = "sve2"
ExtFeatureSVE2p1 ExtFeature = "sve2p1"
)
// ExtImm8Kind names the imm8-control layout an amd64 extended entry carries
@@ -545,6 +714,38 @@ func (in ExtInstr) Encode(ops []ExtOperand) ([]byte, error) {
ExtFormAmdVecGprVec, ExtFormAmdGprVec, ExtFormAmdVecGpr,
ExtFormAmdVec3Imm, ExtFormAmdMask2Imm, ExtFormAmdMemVec, ExtFormAmdVecMem:
return in.encodeAmd64(ops)
case ExtFormPredicateLogical, ExtFormPredicateSelect:
return in.encodePredicateLogical(ops)
case ExtFormPredicateLogicalDest:
return in.encodePredicateLogicalDest(ops)
case ExtFormPredicateBreak, ExtFormPredicateBreakZero:
return in.encodePredicateBreak(ops)
case ExtFormPredicatePermute:
return in.encodePredicatePermute(ops)
case ExtFormPredicateFirst, ExtFormPredicateNext:
return in.encodePredicateReadBack(ops)
case ExtFormPredicateOne:
return in.encodePredicateOne(ops)
case ExtFormPredicateWrite:
return in.encodePredicateWrite(ops)
case ExtFormPredicateFFRRead:
return in.encodePredicateFFRRead(ops)
case ExtFormPredicateMove:
return in.encodePredicateMove(ops)
case ExtFormPredicateUnpack:
return in.encodePredicateUnpack(ops)
case ExtFormPredicateTest:
return in.encodePredicateTest(ops)
case ExtFormWhile:
return in.encodeWhile(ops)
case ExtFormPredicateCounter:
return in.encodePredicateCounter(ops)
case ExtFormNone:
if len(ops) != 0 {
return nil, fmt.Errorf("%s: the %s form takes no operands, got %d",
in.Name, in.Form, len(ops))
}
return extWordLE(in.Word), nil
default:
return nil, fmt.Errorf("%s: unknown form %d", in.Name, in.Form)
}
@@ -712,6 +913,382 @@ func (arr ExtArrangement) shiftNote() string {
return " (a multiple of 256 up to 65280 shifts)"
}
// predicateOperand validates one predicate operand: its kind, its register
// range and, when want is non-empty, the one arrangement the class takes.
func (in ExtInstr) predicateOperand(op ExtOperand, position int, lo, hi int, want ExtArrangement) error {
if op.Kind != ExtPReg {
return fmt.Errorf("%s: operand %d wants a predicate register, got %s",
in.Name, position, op.Kind)
}
if op.Reg < lo || op.Reg > hi {
return fmt.Errorf("%s: operand %d is P%d, outside P%d-P%d in this class",
in.Name, position, op.Reg, lo, hi)
}
if want != ExtArrNone && op.Arr != want {
return fmt.Errorf("%s: operand %d carries arrangement %s, want %s",
in.Name, position, op.Arr, want)
}
return nil
}
// encodePredicateLogical fills the four-operand predicate logical form:
// Pm.B, Pn.B, Pg{qualifier}, Pd.B. Pm takes P0-P7, Pn and Pd P0-P15; the
// governing predicate takes P0-P7 and its qualifier is zeroing for the
// logical operations and bare for PSEL, which the form distinguishes.
func (in ExtInstr) encodePredicateLogical(ops []ExtOperand) ([]byte, error) {
pm, pn, pg, pd := ops[0], ops[1], ops[2], ops[3]
if err := in.predicateOperand(pm, 1, 0, 7, ExtArrB); err != nil {
return nil, err
}
if err := in.predicateOperand(pn, 2, 0, 15, ExtArrB); err != nil {
return nil, err
}
// The governing predicate carries its qualifier in place of an
// arrangement (P1.Z), so the form judges the qualifier below.
if err := in.predicateOperand(pg, 3, 0, 7, ExtArrNone); err != nil {
return nil, err
}
if pg.Arr != ExtArrNone {
return nil, fmt.Errorf("%s: the governing predicate carries no arrangement suffix, got %s",
in.Name, pg.Arr)
}
if err := in.predicateOperand(pd, 4, 0, 15, ExtArrB); err != nil {
return nil, err
}
wantQual := ExtQualZeroing
if in.Form == ExtFormPredicateSelect {
wantQual = ExtQualNone
}
if pg.Qual != wantQual {
if wantQual == ExtQualNone {
return nil, fmt.Errorf("%s: operand 3 takes no qualifier, got %q",
in.Name, pg.Qual)
}
return nil, fmt.Errorf("%s: operand 3 wants the zeroing qualifier, got %q",
in.Name, pg.Qual)
}
word := in.Word
word = extSet(word, extFieldPm, uint32(pm.Reg))
word = extSet(word, extFieldPn, uint32(pn.Reg))
word = extSet(word, extFieldPgN, uint32(pg.Reg))
word = extSet(word, extFieldPd, uint32(pd.Reg))
return extWordLE(word), nil
}
// encodePredicateLogicalDest fills the break form whose first operand is the
// destination read back: Pdm.B, Pn.B, Pg/Z, Pdm.B. The encoding carries the
// register once, so operands 1 and 4 must name the same register.
func (in ExtInstr) encodePredicateLogicalDest(ops []ExtOperand) ([]byte, error) {
pdm, pn, pg := ops[0], ops[1], ops[2]
if err := in.predicateOperand(pdm, 1, 0, 15, ExtArrB); err != nil {
return nil, err
}
if err := in.predicateOperand(pn, 2, 0, 15, ExtArrB); err != nil {
return nil, err
}
if err := in.predicateOperand(pg, 3, 0, 7, ExtArrNone); err != nil {
return nil, err
}
if pg.Arr != ExtArrNone {
return nil, fmt.Errorf("%s: the governing predicate carries no arrangement suffix, got %s",
in.Name, pg.Arr)
}
if err := in.predicateOperand(ops[3], 4, 0, 15, ExtArrB); err != nil {
return nil, err
}
if pg.Qual != ExtQualZeroing {
return nil, fmt.Errorf("%s: operand 3 wants the zeroing qualifier, got %q",
in.Name, pg.Qual)
}
if ops[3].Reg != pdm.Reg {
return nil, fmt.Errorf("%s: operands 1 and 4 are the same register Pdm, got P%d and P%d",
in.Name, pdm.Reg, ops[3].Reg)
}
word := in.Word
word = extSet(word, extFieldPn, uint32(pn.Reg))
word = extSet(word, extFieldPgN, uint32(pg.Reg))
word = extSet(word, extFieldPd, uint32(pdm.Reg))
return extWordLE(word), nil
}
// encodePredicateBreak fills the three-operand break form: Pn.B, Pg{qual},
// Pd.B. The governing predicate is the wide four-bit field (P0-P15); the
// plain break accepts the merging or the zeroing qualifier, the S variant
// the zeroing alone.
func (in ExtInstr) encodePredicateBreak(ops []ExtOperand) ([]byte, error) {
pn, pg, pd := ops[0], ops[1], ops[2]
if err := in.predicateOperand(pn, 1, 0, 15, ExtArrB); err != nil {
return nil, err
}
if err := in.predicateOperand(pg, 2, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if err := in.predicateOperand(pd, 3, 0, 15, ExtArrB); err != nil {
return nil, err
}
switch {
case pg.Qual == ExtQualZeroing:
case pg.Qual == ExtQualMerging && in.Form == ExtFormPredicateBreak:
default:
return nil, fmt.Errorf("%s: operand 2 wants the zeroing qualifier%s, got %q",
in.Name, mergingNote(in.Form), pg.Qual)
}
word := in.Word
word = extSet(word, extFieldPn, uint32(pn.Reg))
word = extSet(word, extFieldPgW, uint32(pg.Reg))
word = extSet(word, extFieldPd, uint32(pd.Reg))
return extWordLE(word), nil
}
// mergingNote describes the qualifier rule of a break form, for the error.
func mergingNote(f ExtForm) string {
if f == ExtFormPredicateBreak {
return " or the merging qualifier"
}
return ""
}
// encodePredicatePermute fills the predicate permutation form: Pm.T, Pn.T,
// Pd.T under one shared arrangement, which feeds the size field.
func (in ExtInstr) encodePredicatePermute(ops []ExtOperand) ([]byte, error) {
pm, pn, pd := ops[0], ops[1], ops[2]
if err := in.predicateOperand(pm, 1, 0, 7, ExtArrNone); err != nil {
return nil, err
}
if err := in.predicateOperand(pn, 2, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if err := in.predicateOperand(pd, 3, 0, 15, ExtArrNone); err != nil {
return nil, err
}
size, err := in.sharedSize(pm.Arr, pn.Arr, pd.Arr)
if err != nil {
return nil, err
}
word := in.Word
word = extSet(word, extFieldPm, uint32(pm.Reg))
word = extSet(word, extFieldPn, uint32(pn.Reg))
word = extSet(word, extFieldPd, uint32(pd.Reg))
word = extSet(word, in.Size, size)
return extWordLE(word), nil
}
// encodePredicateReadBack fills the destination-read-back forms of PFIRST
// and PNEXT: Pdn{.B or .T}, Pg or Pv (bare), Pdn. Operands 1 and 3 name the
// same register; PNEXT carries the size field, PFIRST is .B alone.
func (in ExtInstr) encodePredicateReadBack(ops []ExtOperand) ([]byte, error) {
pdn, pg := ops[0], ops[1]
if err := in.predicateOperand(pdn, 1, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if err := in.predicateOperand(pg, 2, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if err := in.predicateOperand(ops[2], 3, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if pg.Arr != ExtArrNone {
return nil, fmt.Errorf("%s: operand 2 carries no arrangement suffix, got %s",
in.Name, pg.Arr)
}
if pg.Qual != ExtQualNone {
return nil, fmt.Errorf("%s: operand 2 takes no qualifier, got %q", in.Name, pg.Qual)
}
if ops[2].Reg != pdn.Reg || ops[2].Arr != pdn.Arr {
return nil, fmt.Errorf("%s: operands 1 and 3 are the same register Pdn, got P%d and P%d",
in.Name, pdn.Reg, ops[2].Reg)
}
var size uint32
if in.Form == ExtFormPredicateNext {
var ok bool
size, ok = pdn.Arr.sizeBits()
if !ok {
return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class",
in.Name, pdn.Arr)
}
} else if pdn.Arr != ExtArrB {
return nil, fmt.Errorf("%s: operand 1 carries arrangement %s, want .B",
in.Name, pdn.Arr)
}
word := in.Word
word = extSet(word, extFieldPn, uint32(pg.Reg))
word = extSet(word, extFieldPd, uint32(pdn.Reg))
if size != 0 {
word = extSet(word, in.Size, size)
}
return extWordLE(word), nil
}
// encodePredicateOne fills the single-predicate form: Pd.B, destination in
// the four-bit field.
func (in ExtInstr) encodePredicateOne(ops []ExtOperand) ([]byte, error) {
if err := in.predicateOperand(ops[0], 1, 0, 15, ExtArrB); err != nil {
return nil, err
}
word := extSet(in.Word, extFieldPd, uint32(ops[0].Reg))
return extWordLE(word), nil
}
// encodePredicateWrite fills WRFFR's single-source form: Pn.B in the
// five-bit source slot.
func (in ExtInstr) encodePredicateWrite(ops []ExtOperand) ([]byte, error) {
if err := in.predicateOperand(ops[0], 1, 0, 15, ExtArrB); err != nil {
return nil, err
}
word := extSet(in.Word, extFieldPn, uint32(ops[0].Reg))
return extWordLE(word), nil
}
// encodePredicateFFRRead fills the predicated first-fault read: Pg/Z, Pd.B.
// The first operand is the mask the read passes through, so it sits in the
// five-bit source slot, not in a governing-predicate field.
func (in ExtInstr) encodePredicateFFRRead(ops []ExtOperand) ([]byte, error) {
pg, pd := ops[0], ops[1]
if err := in.predicateOperand(pg, 1, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if err := in.predicateOperand(pd, 2, 0, 15, ExtArrB); err != nil {
return nil, err
}
if pg.Qual != ExtQualZeroing {
return nil, fmt.Errorf("%s: operand 1 wants the zeroing qualifier, got %q",
in.Name, pg.Qual)
}
word := in.Word
word = extSet(word, extFieldPn, uint32(pg.Reg))
word = extSet(word, extFieldPd, uint32(pd.Reg))
return extWordLE(word), nil
}
// encodePredicateMove fills the predicate reverse: Pn.T, Pd.T under one
// shared arrangement.
func (in ExtInstr) encodePredicateMove(ops []ExtOperand) ([]byte, error) {
pn, pd := ops[0], ops[1]
if err := in.predicateOperand(pn, 1, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if err := in.predicateOperand(pd, 2, 0, 15, ExtArrNone); err != nil {
return nil, err
}
size, err := in.sharedSize(pn.Arr, pd.Arr)
if err != nil {
return nil, err
}
word := in.Word
word = extSet(word, extFieldPn, uint32(pn.Reg))
word = extSet(word, extFieldPd, uint32(pd.Reg))
word = extSet(word, in.Size, size)
return extWordLE(word), nil
}
// encodePredicateUnpack fills the predicate unpack: Pn.B, Pd.H, both
// arrangements fixed by the class.
func (in ExtInstr) encodePredicateUnpack(ops []ExtOperand) ([]byte, error) {
pn, pd := ops[0], ops[1]
if err := in.predicateOperand(pn, 1, 0, 15, ExtArrB); err != nil {
return nil, err
}
if err := in.predicateOperand(pd, 2, 0, 15, ExtArrH); err != nil {
return nil, err
}
word := in.Word
word = extSet(word, extFieldPn, uint32(pn.Reg))
word = extSet(word, extFieldPd, uint32(pd.Reg))
return extWordLE(word), nil
}
// encodePredicateTest fills PTEST: Pn.B, Pg (bare), no destination.
func (in ExtInstr) encodePredicateTest(ops []ExtOperand) ([]byte, error) {
pn, pg := ops[0], ops[1]
if err := in.predicateOperand(pn, 1, 0, 15, ExtArrB); err != nil {
return nil, err
}
if err := in.predicateOperand(pg, 2, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if pg.Qual != ExtQualNone {
return nil, fmt.Errorf("%s: operand 2 takes no qualifier, got %q", in.Name, pg.Qual)
}
word := in.Word
word = extSet(word, extFieldPn, uint32(pn.Reg))
word = extSet(word, extFieldPgW, uint32(pg.Reg))
return extWordLE(word), nil
}
// encodeWhile fills the while-compare form: Rm, Rn, Pd.T, the arrangement
// feeding the size field. The general registers run R0-R30; the class takes
// no stack pointer and no zero register spelling.
func (in ExtInstr) encodeWhile(ops []ExtOperand) ([]byte, error) {
rm, rn, pd := ops[0], ops[1], ops[2]
for i, op := range []ExtOperand{rm, rn} {
if op.Kind != ExtGReg {
return nil, fmt.Errorf("%s: operand %d wants a general register, got %s",
in.Name, i+1, op.Kind)
}
if op.Reg < 0 || op.Reg > 30 {
return nil, fmt.Errorf("%s: operand %d is R%d, outside R0-R30",
in.Name, i+1, op.Reg)
}
}
if err := in.predicateOperand(pd, 3, 0, 15, ExtArrNone); err != nil {
return nil, err
}
size, ok := pd.Arr.sizeBits()
if !ok {
return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class",
in.Name, pd.Arr)
}
word := in.Word
word = extSet(word, extFieldRm, uint32(rm.Reg))
word = extSet(word, extFieldPn, uint32(rn.Reg))
word = extSet(word, extFieldPd, uint32(pd.Reg))
word = extSet(word, in.Size, size)
return extWordLE(word), nil
}
// encodePredicateCounter fills PTRUE's counter form: PNd.T, the
// predicate-as-counter destination spelled PN8-PN15 and encoded as reg-8.
func (in ExtInstr) encodePredicateCounter(ops []ExtOperand) ([]byte, error) {
pnd := ops[0]
if pnd.Kind != ExtPNReg {
return nil, fmt.Errorf("%s: operand 1 wants a predicate-as-counter register, got %s",
in.Name, pnd.Kind)
}
if pnd.Reg < 8 || pnd.Reg > 15 {
return nil, fmt.Errorf("%s: operand 1 is PN%d, outside PN8-PN15", in.Name, pnd.Reg)
}
size, ok := pnd.Arr.sizeBits()
if !ok {
return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class",
in.Name, pnd.Arr)
}
word := in.Word
word = extSet(word, extFieldPnc, uint32(pnd.Reg-8))
word = extSet(word, in.Size, size)
return extWordLE(word), nil
}
// sharedSize returns the one element-size encoding the given arrangements
// agree on, or an error when any operand is bare, they disagree, or the
// arrangement has no size field.
func (in ExtInstr) sharedSize(arrs ...ExtArrangement) (uint32, error) {
arr := arrs[0]
for i, a := range arrs {
if a == ExtArrNone {
return 0, fmt.Errorf("%s: operand %d carries no arrangement suffix", in.Name, i+1)
}
if a != arr {
return 0, fmt.Errorf("%s: operand %d carries arrangement %s, want %s",
in.Name, i+1, a, arr)
}
}
size, ok := arr.sizeBits()
if !ok {
return 0, fmt.Errorf("%s: arrangement %s has no size encoding in this class", in.Name, arr)
}
return size, nil
}
// 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) {
@@ -837,6 +1414,195 @@ var arm64Extensions = []ExtInstr{
{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)"},
// --- the SVE and SVE2.1 predicate family ---------------------------------
//
// The classes below operate on predicates and predicate-as-counter
// registers. Their encodings follow the field layouts the ARM
// Architecture Reference Manual's SVE2.1 predicate classes give, as laid
// out in the toolchain's generated encoding table
// (cmd/internal/obj/arm64/inst_gen.go, itself generated by
// x/arch/arm64/instgen from Arm's official ISA description) and in its
// generated corpus file arm64sveenc.s: the golden test pins every
// corpus line of each class byte for byte, so no class rests on
// transcription alone. The toolchain's spellings are kept, including
// its P prefix on the predicate-register instructions (PRDFFR for
// RDFFR, PPTRUE for PTRUE, PREV for the predicate REV), because those
// are the names the corpus assembles under.
// Predicate logical operations, SVE2.1: PAND P4.B, P2.B, P1.Z, P14.B.
{Name: "PAND", Summary: "And predicates over a governing predicate's active lanes",
Word: 0x25004000, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PAND (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PANDS", Summary: "And predicates, setting the condition flags",
Word: 0x25404000, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PANDS (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PBIC", Summary: "And-complement predicates over a governing predicate's active lanes",
Word: 0x25004010, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBIC (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PBICS", Summary: "And-complement predicates, setting the condition flags",
Word: 0x25404010, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBICS (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PEOR", Summary: "Exclusive-or predicates over a governing predicate's active lanes",
Word: 0x25004200, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PEOR (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PEORS", Summary: "Exclusive-or predicates, setting the condition flags",
Word: 0x25404200, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PEORS (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PNAND", Summary: "And-complement two predicates into one",
Word: 0x25804210, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PNAND (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PNANDS", Summary: "And-complement two predicates, setting the condition flags",
Word: 0x25c04210, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PNANDS (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PNOR", Summary: "Or-complement two predicates into one",
Word: 0x25804200, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PNOR (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PNORS", Summary: "Or-complement two predicates, setting the condition flags",
Word: 0x25c04200, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PNORS (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PORN", Summary: "Or-complement two predicates into one, complement reversed",
Word: 0x25804010, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PORN (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PORNS", Summary: "Or-complement two predicates, complement reversed, setting the flags",
Word: 0x25c04010, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PORNS (predicates); inst_gen.go + arm64sveenc.s"},
{Name: "PORR", Summary: "Or two predicates into one",
Word: 0x25804000, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: POR (predicates) as PORR; inst_gen.go + arm64sveenc.s"},
{Name: "PORRS", Summary: "Or two predicates, setting the condition flags",
Word: 0x25c04000, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PORS (predicates) as PORR; inst_gen.go + arm64sveenc.s"},
{Name: "PSEL", Summary: "Select predicate elements by a governing predicate",
Word: 0x25004210, Form: ExtFormPredicateSelect, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PSEL (predicates); inst_gen.go + arm64sveenc.s"},
// Break operations, SVE2.1 generalised breaks.
{Name: "PBRKA", Summary: "Break after the first true of the governing predicate",
Word: 0x25104000, Form: ExtFormPredicateBreak, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKA (break after first true); inst_gen.go + arm64sveenc.s"},
{Name: "PBRKAS", Summary: "Break after the first false, setting the condition flags",
Word: 0x25504000, Form: ExtFormPredicateBreakZero, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKAS (break after first false); inst_gen.go + arm64sveenc.s"},
{Name: "PBRKB", Summary: "Break before the first true of the governing predicate",
Word: 0x25904000, Form: ExtFormPredicateBreak, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKB (break before first true); inst_gen.go + arm64sveenc.s"},
{Name: "PBRKBS", Summary: "Break before the first false, setting the condition flags",
Word: 0x25d04000, Form: ExtFormPredicateBreakZero, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKBS (break before first false); inst_gen.go + arm64sveenc.s"},
{Name: "PBRKN", Summary: "Break after the first true of the second source",
Word: 0x25184000, Form: ExtFormPredicateLogicalDest, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKN (break after first true); inst_gen.go + arm64sveenc.s"},
{Name: "PBRKNS", Summary: "Break after the first true of the second source, setting the flags",
Word: 0x25584000, Form: ExtFormPredicateLogicalDest, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKNS; inst_gen.go + arm64sveenc.s"},
{Name: "PBRKPA", Summary: "Break after the first true, preserving the first source's leading lanes",
Word: 0x2500c000, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKPA (break propagating after); inst_gen.go + arm64sveenc.s"},
{Name: "PBRKPAS", Summary: "Break propagating after the first false, setting the flags",
Word: 0x2540c000, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKPAS; inst_gen.go + arm64sveenc.s"},
{Name: "PBRKPB", Summary: "Break propagating before the first true",
Word: 0x2500c010, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKPB (break propagating before); inst_gen.go + arm64sveenc.s"},
{Name: "PBRKPBS", Summary: "Break propagating before the first false, setting the flags",
Word: 0x2540c010, Form: ExtFormPredicateLogical, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: PBRKPBS; inst_gen.go + arm64sveenc.s"},
// Predicate permutations, SVE2.1.
{Name: "PTRN1", Summary: "Transpose odd predicate elements",
Word: 0x05205000, Form: ExtFormPredicatePermute, Size: extSizeBHSD, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: TRN1 (predicates) as PTRN1; inst_gen.go + arm64sveenc.s"},
{Name: "PTRN2", Summary: "Transpose even predicate elements",
Word: 0x05205400, Form: ExtFormPredicatePermute, Size: extSizeBHSD, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: TRN2 (predicates) as PTRN2; inst_gen.go + arm64sveenc.s"},
{Name: "PUZP1", Summary: "Unzip odd predicate elements",
Word: 0x05204800, Form: ExtFormPredicatePermute, Size: extSizeBHSD, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: UZP1 (predicates) as PUZP1; inst_gen.go + arm64sveenc.s"},
{Name: "PUZP2", Summary: "Unzip even predicate elements",
Word: 0x05204c00, Form: ExtFormPredicatePermute, Size: extSizeBHSD, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: UZP2 (predicates) as PUZP2; inst_gen.go + arm64sveenc.s"},
{Name: "PZIP1", Summary: "Zip odd predicate elements",
Word: 0x05204000, Form: ExtFormPredicatePermute, Size: extSizeBHSD, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: ZIP1 (predicates) as PZIP1; inst_gen.go + arm64sveenc.s"},
{Name: "PZIP2", Summary: "Zip even predicate elements",
Word: 0x05204400, Form: ExtFormPredicatePermute, Size: extSizeBHSD, Feature: ExtFeatureSVE2p1,
Ref: "ARM DDI 0487J SVE2.1: ZIP2 (predicates) as PZIP2; inst_gen.go + arm64sveenc.s"},
// Predicate initialise, test and permute singles.
{Name: "PPFALSE", Summary: "Initialise a predicate to false",
Word: 0x2518e400, Form: ExtFormPredicateOne, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PFALSE as PPFALSE; inst_gen.go + arm64sveenc.s"},
{Name: "PPFIRST", Summary: "Set the first active element of a predicate",
Word: 0x2558c000, Form: ExtFormPredicateFirst, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PFIRST as PPFIRST; inst_gen.go + arm64sveenc.s"},
{Name: "PPNEXT", Summary: "Set the next active element of a predicate",
Word: 0x2519c400, Form: ExtFormPredicateNext, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PNEXT as PPNEXT; inst_gen.go + arm64sveenc.s"},
{Name: "PPTEST", Summary: "Test a predicate against a governing predicate, setting the flags",
Word: 0x2550c000, Form: ExtFormPredicateTest, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PTEST as PPTEST; inst_gen.go + arm64sveenc.s"},
{Name: "PPTRUE", Summary: "Initialise a predicate-as-counter register from a pattern",
Word: 0x25207810, Form: ExtFormPredicateCounter, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PTRUE (counter form) as PPTRUE; inst_gen.go + arm64sveenc.s"},
{Name: "PPUNPKHI", Summary: "Unpack the even elements of a half-word predicate",
Word: 0x05314000, Form: ExtFormPredicateUnpack, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PUNPKHI as PPUNPKHI; inst_gen.go + arm64sveenc.s"},
{Name: "PPUNPKLO", Summary: "Unpack the odd elements of a half-word predicate",
Word: 0x05304000, Form: ExtFormPredicateUnpack, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PUNPKLO as PPUNPKLO; inst_gen.go + arm64sveenc.s"},
// First-fault register and predicate reverse.
{Name: "PRDFFR", Summary: "Read the first-fault register to a predicate, zeroing",
Word: 0x2518f000, Form: ExtFormPredicateFFRRead, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: RDFFR (predicated) as PRDFFR; inst_gen.go + arm64sveenc.s"},
{Name: "PRDFFR", Summary: "Read the first-fault register to a predicate whole",
Word: 0x2519f000, Form: ExtFormPredicateOne, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: RDFFR (unpredicated) as PRDFFR; inst_gen.go + arm64sveenc.s"},
{Name: "PRDFFRS", Summary: "Read the first-fault register to a predicate, zeroing, setting the flags",
Word: 0x2558f000, Form: ExtFormPredicateFFRRead, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: RDFFRS as PRDFFRS; inst_gen.go + arm64sveenc.s"},
{Name: "PWRFFR", Summary: "Write the first-fault register from a predicate",
Word: 0x25289000, Form: ExtFormPredicateWrite, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WRFFR as PWRFFR; inst_gen.go + arm64sveenc.s"},
{Name: "PREV", Summary: "Reverse the elements of a predicate",
Word: 0x05344000, Form: ExtFormPredicateMove, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: REV (predicate) as PREV; inst_gen.go + arm64sveenc.s"},
{Name: "SETFFR", Summary: "Set the first-fault register to all true",
Word: 0x252c9000, Form: ExtFormNone, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: SETFFR; inst_gen.go + arm64sveenc.s"},
// While compares: build a predicate from a scalar loop condition.
{Name: "PWHILEGE", Summary: "Build a predicate while the signed greater-or-equal comparison holds",
Word: 0x25201000, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILEGE as PWHILEGE; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILEGT", Summary: "Build a predicate while the signed greater comparison holds",
Word: 0x25201010, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILEGT as PWHILEGT; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILEHI", Summary: "Build a predicate while the unsigned higher comparison holds",
Word: 0x25201810, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILEHI as PWHILEHI; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILEHS", Summary: "Build a predicate while the unsigned higher-or-same comparison holds",
Word: 0x25201800, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILEHS as PWHILEHS; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILELE", Summary: "Build a predicate while the signed less-or-equal comparison holds",
Word: 0x25201410, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILELE as PWHILELE; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILELO", Summary: "Build a predicate while the unsigned lower comparison holds",
Word: 0x25201c00, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILELO as PWHILELO; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILELS", Summary: "Build a predicate while the unsigned lower-or-same comparison holds",
Word: 0x25201c10, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILELS as PWHILELS; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILELT", Summary: "Build a predicate while the signed less comparison holds",
Word: 0x25201400, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: WHILELT as PWHILELT; inst_gen.go + arm64sveenc.s"},
{Name: "PWHILERW", Summary: "Build a predicate over a read-write region",
Word: 0x25203010, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PWHILERW (SVE2.1); inst_gen.go + arm64sveenc.s"},
{Name: "PWHILEWR", Summary: "Build a predicate over a write region",
Word: 0x25203000, Form: ExtFormWhile, Size: extSizeBHSD, Feature: ExtFeatureSVE,
Ref: "ARM DDI 0487J: PWHILEWR (SVE2.1); inst_gen.go + arm64sveenc.s"},
}
// Extensions returns the extended-instruction layer registered for a, outside
+404 -9
View File
@@ -279,27 +279,36 @@ func extInstructionQuiet(mnem string, form ExtForm) (ExtInstr, bool) {
}
// TestArm64ExtTableIntegrity checks the metadata contract: every entry names
// its manual reference, summary and feature, and the element-size field sits
// at bits 23..22 where the manual puts it for every class in the family.
// its manual reference, summary and feature, the element-size field sits
// at bits 23..22 for the classes that carry one, and the destination field
// stays zero in the fixed word: five bits where a Z register is the
// destination, four where a predicate is.
func TestArm64ExtTableIntegrity(t *testing.T) {
for _, in := range Extensions(ARM64) {
if in.Name == "" || in.Summary == "" || in.Ref == "" {
t.Errorf("%+v: name, summary and reference are mandatory", in)
}
if in.Feature != ExtFeatureSVE && in.Feature != ExtFeatureSVE2 {
t.Errorf("%s: feature %q is neither sve nor sve2", in.Name, in.Feature)
if in.Feature != ExtFeatureSVE && in.Feature != ExtFeatureSVE2 && in.Feature != ExtFeatureSVE2p1 {
t.Errorf("%s: feature %q is neither sve, sve2 nor sve2p1", in.Name, in.Feature)
}
if in.Form.Arity() < 2 || in.Form.Arity() > 3 {
if in.Form.Arity() < 0 || in.Form.Arity() > 4 {
t.Errorf("%s: form %d carries an unusable arity %d", in.Name, in.Form, in.Form.Arity())
}
if in.Size.Off != 22 || in.Size.Width != 2 {
if in.Size.Width != 0 && (in.Size.Off != 22 || in.Size.Width != 2) {
t.Errorf("%s: the size field sits at bits %d..%d, the classes here put it at 23..22",
in.Name, in.Size.Off, in.Size.Off+in.Size.Width-1)
}
// The destination register field and the element-size field are
// operands everywhere in this family, so Word carries both zero; the
// class opcodes live around them and stay where they are.
if in.Word&0x1f != 0 || in.Word&(0x3<<22) != 0 {
// operands wherever they exist, so Word carries both zero; the
// class opcodes live around them and stay where they are. Where
// the class carries no size field, bits 23..22 are class bits and
// stay.
destMask := uint32(0x1f)
if in.Form != ExtFormVectors && in.Form != ExtFormPredicated &&
in.Form != ExtFormImmediate && in.Form != ExtFormSignedImmediate {
destMask = 0xf // the predicate destination is a four-bit field
}
if in.Word&destMask != 0 || (in.Size.Width != 0 && in.Word&(0x3<<22) != 0) {
t.Errorf("%s: word %08x carries destination or size bits, want them zero", in.Name, in.Word)
}
}
@@ -401,3 +410,389 @@ func TestExtensionsArchBinding(t *testing.T) {
t.Error("Extensions(AMD64) is empty")
}
}
// TestArm64ExtPredicateGolden pins the SVE and SVE2.1 predicate family
// against the toolchain-generated corpus: every row is one arm64sveenc.s
// line, and the want word is that line's own expected encoding, byte
// reversed out of the file's big-endian comment into the little-endian
// instruction word. The corpus and the toolchain's encoding table
// (inst_gen.go) are two independent generations from Arm's official ISA
// description; both agree with the field layouts encoded here, and the
// classes have no single-byte oracle beyond them (go tool asm carries no
// SVE encodings).
func TestArm64ExtPredicateGolden(t *testing.T) {
for _, tt := range []struct {
name string
mnem string
form ExtForm
ops []ExtOperand
want uint32
}{
{"PAND P4.B, P2.B, P1.Z, P14.B", "PAND",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2504444e},
{"PANDS P4.B, P2.B, P1.Z, P14.B", "PANDS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2544444e},
{"PBIC P4.B, P2.B, P1.Z, P14.B", "PBIC",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2504445e},
{"PBICS P4.B, P2.B, P1.Z, P14.B", "PBICS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2544445e},
{"PBRKA P5.B, P9.Z, P2.B", "PBRKA",
ExtFormPredicateBreak,
[]ExtOperand{ExtPredicateSized(5, ExtArrB), ExtPredicate(9, ExtQualZeroing), ExtPredicateSized(2, ExtArrB)},
0x251064a2},
{"PBRKAS P5.B, P9.Z, P4.B", "PBRKAS",
ExtFormPredicateBreakZero,
[]ExtOperand{ExtPredicateSized(5, ExtArrB), ExtPredicate(9, ExtQualZeroing), ExtPredicateSized(4, ExtArrB)},
0x255064a4},
{"PBRKB P5.B, P9.Z, P2.B", "PBRKB",
ExtFormPredicateBreak,
[]ExtOperand{ExtPredicateSized(5, ExtArrB), ExtPredicate(9, ExtQualZeroing), ExtPredicateSized(2, ExtArrB)},
0x259064a2},
{"PBRKBS P5.B, P9.Z, P4.B", "PBRKBS",
ExtFormPredicateBreakZero,
[]ExtOperand{ExtPredicateSized(5, ExtArrB), ExtPredicate(9, ExtQualZeroing), ExtPredicateSized(4, ExtArrB)},
0x25d064a4},
{"PBRKN P4.B, P2.B, P1.Z, P4.B", "PBRKN",
ExtFormPredicateLogicalDest,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(4, ExtArrB)},
0x25184444},
{"PBRKNS P4.B, P2.B, P1.Z, P4.B", "PBRKNS",
ExtFormPredicateLogicalDest,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(4, ExtArrB)},
0x25584444},
{"PBRKPA P4.B, P2.B, P1.Z, P14.B", "PBRKPA",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2504c44e},
{"PBRKPAS P4.B, P2.B, P1.Z, P14.B", "PBRKPAS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2544c44e},
{"PBRKPB P4.B, P2.B, P1.Z, P14.B", "PBRKPB",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2504c45e},
{"PBRKPBS P4.B, P2.B, P1.Z, P14.B", "PBRKPBS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2544c45e},
{"PEOR P4.B, P2.B, P1.Z, P14.B", "PEOR",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2504464e},
{"PEORS P4.B, P2.B, P1.Z, P14.B", "PEORS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2544464e},
{"PNAND P4.B, P2.B, P1.Z, P14.B", "PNAND",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2584465e},
{"PNANDS P4.B, P2.B, P1.Z, P14.B", "PNANDS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x25c4465e},
{"PNOR P4.B, P2.B, P1.Z, P14.B", "PNOR",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2584464e},
{"PNORS P4.B, P2.B, P1.Z, P14.B", "PNORS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x25c4464e},
{"PORN P4.B, P2.B, P1.Z, P14.B", "PORN",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2584445e},
{"PORNS P4.B, P2.B, P1.Z, P14.B", "PORNS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x25c4445e},
{"PORR P4.B, P2.B, P1.Z, P14.B", "PORR",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x2584444e},
{"PORRS P4.B, P2.B, P1.Z, P14.B", "PORRS",
ExtFormPredicateLogical,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtPredicateSized(14, ExtArrB)},
0x25c4444e},
{"PPFALSE P13.B", "PPFALSE",
ExtFormPredicateOne,
[]ExtOperand{ExtPredicateSized(13, ExtArrB)},
0x2518e40d},
{"PPFIRST P5.B, P9, P5.B", "PPFIRST",
ExtFormPredicateFirst,
[]ExtOperand{ExtPredicateSized(5, ExtArrB), ExtPredicate(9, ExtQualNone), ExtPredicateSized(5, ExtArrB)},
0x2558c125},
{"PPNEXT P5.D, P4, P5.D", "PPNEXT",
ExtFormPredicateNext,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicate(4, ExtQualNone), ExtPredicateSized(5, ExtArrD)},
0x25d9c485},
{"PPTEST P14.B, P0", "PPTEST",
ExtFormPredicateTest,
[]ExtOperand{ExtPredicateSized(14, ExtArrB), ExtPredicate(0, ExtQualNone)},
0x2550c1c0},
{"PPTRUE PN14.S", "PPTRUE",
ExtFormPredicateCounter,
[]ExtOperand{ExtCounterPredicate(14, ExtArrS)},
0x25a07816},
{"PPUNPKHI P14.B, P0.H", "PPUNPKHI",
ExtFormPredicateUnpack,
[]ExtOperand{ExtPredicateSized(14, ExtArrB), ExtPredicateSized(0, ExtArrH)},
0x053141c0},
{"PPUNPKLO P14.B, P0.H", "PPUNPKLO",
ExtFormPredicateUnpack,
[]ExtOperand{ExtPredicateSized(14, ExtArrB), ExtPredicateSized(0, ExtArrH)},
0x053041c0},
{"PRDFFR P14.Z, P0.B", "PRDFFR",
ExtFormPredicateFFRRead,
[]ExtOperand{ExtPredicate(14, ExtQualZeroing), ExtPredicateSized(0, ExtArrB)},
0x2518f1c0},
{"PRDFFR P13.B", "PRDFFR",
ExtFormPredicateOne,
[]ExtOperand{ExtPredicateSized(13, ExtArrB)},
0x2519f00d},
{"PRDFFRS P14.Z, P0.B", "PRDFFRS",
ExtFormPredicateFFRRead,
[]ExtOperand{ExtPredicate(14, ExtQualZeroing), ExtPredicateSized(0, ExtArrB)},
0x2558f1c0},
{"PREV P14.S, P13.S", "PREV",
ExtFormPredicateMove,
[]ExtOperand{ExtPredicateSized(14, ExtArrS), ExtPredicateSized(13, ExtArrS)},
0x05b441cd},
{"PSEL P4.B, P2.B, P1, P14.B", "PSEL",
ExtFormPredicateSelect,
[]ExtOperand{ExtPredicateSized(4, ExtArrB), ExtPredicateSized(2, ExtArrB), ExtPredicate(1, ExtQualNone), ExtPredicateSized(14, ExtArrB)},
0x2504465e},
{"PTRN1 P5.D, P4.D, P2.D", "PTRN1",
ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicateSized(4, ExtArrD), ExtPredicateSized(2, ExtArrD)},
0x05e55082},
{"PTRN2 P5.D, P4.D, P2.D", "PTRN2",
ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicateSized(4, ExtArrD), ExtPredicateSized(2, ExtArrD)},
0x05e55482},
{"PUZP1 P5.D, P4.D, P2.D", "PUZP1",
ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicateSized(4, ExtArrD), ExtPredicateSized(2, ExtArrD)},
0x05e54882},
{"PUZP2 P5.D, P4.D, P2.D", "PUZP2",
ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicateSized(4, ExtArrD), ExtPredicateSized(2, ExtArrD)},
0x05e54c82},
{"PWRFFR P13.B", "PWRFFR",
ExtFormPredicateWrite,
[]ExtOperand{ExtPredicateSized(13, ExtArrB)},
0x252891a0},
{"PZIP1 P5.D, P4.D, P2.D", "PZIP1",
ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicateSized(4, ExtArrD), ExtPredicateSized(2, ExtArrD)},
0x05e54082},
{"PZIP2 P5.D, P4.D, P2.D", "PZIP2",
ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicateSized(4, ExtArrD), ExtPredicateSized(2, ExtArrD)},
0x05e54482},
{"SETFFR ", "SETFFR",
ExtFormNone,
[]ExtOperand{},
0x252c9000},
{"PWHILEGE R2, R10, P10.H", "PWHILEGE",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562114a},
{"PWHILEGT R2, R10, P10.H", "PWHILEGT",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562115a},
{"PWHILEHI R2, R10, P10.H", "PWHILEHI",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562195a},
{"PWHILEHS R2, R10, P10.H", "PWHILEHS",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562194a},
{"PWHILELE R2, R10, P10.H", "PWHILELE",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562155a},
{"PWHILELO R2, R10, P10.H", "PWHILELO",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x25621d4a},
{"PWHILELS R2, R10, P10.H", "PWHILELS",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x25621d5a},
{"PWHILELT R2, R10, P10.H", "PWHILELT",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562154a},
{"PWHILERW R2, R10, P10.H", "PWHILERW",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562315a},
{"PWHILEWR R2, R10, P10.H", "PWHILEWR",
ExtFormWhile,
[]ExtOperand{ExtGeneral(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
0x2562314a},
} {
in := extInstruction(t, tt.mnem, tt.form)
got, err := in.Encode(tt.ops)
if err != nil {
t.Errorf("%s: encode: %v", tt.name, err)
continue
}
if want := hex.EncodeToString(extWordLE(tt.want)); hex.EncodeToString(got) != want {
t.Errorf("%s:\n got %x\n want %s", tt.name, got, want)
}
}
}
// TestArm64ExtPredicateSources pins the cross-check contract of the
// predicate family: every class the layer defines carries at least one
// golden vector derived from the toolchain-generated corpus, so no class
// rests on transcription alone.
func TestArm64ExtPredicateSources(t *testing.T) {
for _, tt := range []struct {
mnem string
form ExtForm
}{
{"PAND", ExtFormPredicateLogical}, {"PANDS", ExtFormPredicateLogical},
{"PBIC", ExtFormPredicateLogical}, {"PBICS", ExtFormPredicateLogical},
{"PEOR", ExtFormPredicateLogical}, {"PEORS", ExtFormPredicateLogical},
{"PNAND", ExtFormPredicateLogical}, {"PNANDS", ExtFormPredicateLogical},
{"PNOR", ExtFormPredicateLogical}, {"PNORS", ExtFormPredicateLogical},
{"PORN", ExtFormPredicateLogical}, {"PORNS", ExtFormPredicateLogical},
{"PORR", ExtFormPredicateLogical}, {"PORRS", ExtFormPredicateLogical},
{"PSEL", ExtFormPredicateSelect},
{"PBRKA", ExtFormPredicateBreak}, {"PBRKAS", ExtFormPredicateBreakZero},
{"PBRKB", ExtFormPredicateBreak}, {"PBRKBS", ExtFormPredicateBreakZero},
{"PBRKN", ExtFormPredicateLogicalDest}, {"PBRKNS", ExtFormPredicateLogicalDest},
{"PBRKPA", ExtFormPredicateLogical}, {"PBRKPAS", ExtFormPredicateLogical},
{"PBRKPB", ExtFormPredicateLogical}, {"PBRKPBS", ExtFormPredicateLogical},
{"PTRN1", ExtFormPredicatePermute}, {"PTRN2", ExtFormPredicatePermute},
{"PUZP1", ExtFormPredicatePermute}, {"PUZP2", ExtFormPredicatePermute},
{"PZIP1", ExtFormPredicatePermute}, {"PZIP2", ExtFormPredicatePermute},
{"PPFALSE", ExtFormPredicateOne},
{"PPFIRST", ExtFormPredicateFirst},
{"PPNEXT", ExtFormPredicateNext},
{"PPTEST", ExtFormPredicateTest},
{"PPTRUE", ExtFormPredicateCounter},
{"PPUNPKHI", ExtFormPredicateUnpack}, {"PPUNPKLO", ExtFormPredicateUnpack},
{"PRDFFR", ExtFormPredicateFFRRead}, {"PRDFFR", ExtFormPredicateOne},
{"PRDFFRS", ExtFormPredicateFFRRead},
{"PWRFFR", ExtFormPredicateWrite},
{"PREV", ExtFormPredicateMove},
{"SETFFR", ExtFormNone},
{"PWHILEGE", ExtFormWhile}, {"PWHILEGT", ExtFormWhile},
{"PWHILEHI", ExtFormWhile}, {"PWHILEHS", ExtFormWhile},
{"PWHILELE", ExtFormWhile}, {"PWHILELO", ExtFormWhile},
{"PWHILELS", ExtFormWhile}, {"PWHILELT", ExtFormWhile},
{"PWHILERW", ExtFormWhile}, {"PWHILEWR", ExtFormWhile},
} {
if _, ok := extInstructionQuiet(tt.mnem, tt.form); !ok {
t.Errorf("the table lacks %s with the %s form", tt.mnem, tt.form)
}
}
}
// TestArm64ExtPredicateRejects pins the diagnostics the predicate forms
// give the operands their classes cannot carry: the register ranges the
// field widths imply, the arrangements the classes fix, and the read-back
// constraint of the destination-shared shapes.
func TestArm64ExtPredicateRejects(t *testing.T) {
pb := func(r int) ExtOperand { return ExtPredicateSized(r, ExtArrB) }
for _, tt := range []struct {
name string
mnem string
form ExtForm
ops []ExtOperand
quote string
}{
{"Pm beyond the three-bit field", "PAND", ExtFormPredicateLogical,
[]ExtOperand{pb(8), pb(2), ExtPredicate(1, ExtQualZeroing), pb(14)},
"outside P0-P7"},
{"governing predicate beyond the narrow field", "PAND", ExtFormPredicateLogical,
[]ExtOperand{pb(4), pb(2), ExtPredicate(8, ExtQualZeroing), pb(14)},
"outside P0-P7"},
{"wrong arrangement on the second source", "PAND", ExtFormPredicateLogical,
[]ExtOperand{pb(4), ExtPredicateSized(2, ExtArrS), ExtPredicate(1, ExtQualZeroing), pb(14)},
"want .B"},
{"bare governing predicate", "PAND", ExtFormPredicateLogical,
[]ExtOperand{pb(4), pb(2), ExtPredicate(1, ExtQualNone), pb(14)},
"zeroing qualifier"},
{"quadword arrangement", "PSEL", ExtFormPredicateSelect,
[]ExtOperand{ExtPredicateSized(4, ExtArrQ), ExtPredicateSized(2, ExtArrQ), ExtPredicate(1, ExtQualNone), ExtPredicateSized(14, ExtArrQ)},
"want .B"},
{"qualifier where PSEL wants none", "PSEL", ExtFormPredicateSelect,
[]ExtOperand{pb(4), pb(2), ExtPredicate(1, ExtQualZeroing), pb(14)},
"takes no qualifier"},
{"read-back operands disagree", "PBRKN", ExtFormPredicateLogicalDest,
[]ExtOperand{pb(4), pb(2), ExtPredicate(1, ExtQualZeroing), pb(9)},
"same register Pdm"},
{"merging qualifier on the zeroing break", "PBRKAS", ExtFormPredicateBreakZero,
[]ExtOperand{pb(5), ExtPredicate(9, ExtQualMerging), pb(4)},
"zeroing qualifier"},
{"governing predicate beyond the wide field", "PBRKA", ExtFormPredicateBreak,
[]ExtOperand{pb(5), ExtPredicate(16, ExtQualZeroing), pb(2)},
"outside P0-P15"},
{"permutations disagree on arrangement", "PTRN1", ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicateSized(4, ExtArrD), ExtPredicateSized(2, ExtArrS)},
"want .D"},
{"bare permutation operand", "PUZP1", ExtFormPredicatePermute,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), {Kind: ExtPReg, Reg: 4}, ExtPredicateSized(2, ExtArrD)},
"no arrangement suffix"},
{"read-back operands disagree on arrangement", "PPNEXT", ExtFormPredicateNext,
[]ExtOperand{ExtPredicateSized(5, ExtArrD), ExtPredicate(4, ExtQualNone), ExtPredicateSized(5, ExtArrS)},
"same register Pdn"},
{"qualifier on the bare source", "PPFIRST", ExtFormPredicateFirst,
[]ExtOperand{pb(5), ExtPredicate(9, ExtQualZeroing), pb(5)},
"takes no qualifier"},
{"PFIRST outside .B", "PPFIRST", ExtFormPredicateFirst,
[]ExtOperand{ExtPredicateSized(5, ExtArrS), ExtPredicate(9, ExtQualNone), ExtPredicateSized(5, ExtArrS)},
"want .B"},
{"merging qualifier on the FFR read", "PRDFFR", ExtFormPredicateFFRRead,
[]ExtOperand{ExtPredicate(14, ExtQualMerging), pb(0)},
"zeroing qualifier"},
{"wrong unpack destination arrangement", "PPUNPKHI", ExtFormPredicateUnpack,
[]ExtOperand{pb(14), ExtPredicateSized(0, ExtArrB)},
"want .H"},
{"qualifier on the tested predicate", "PPTEST", ExtFormPredicateTest,
[]ExtOperand{pb(14), ExtPredicate(0, ExtQualZeroing)},
"takes no qualifier"},
{"general register beyond R30", "PWHILELT", ExtFormWhile,
[]ExtOperand{ExtGeneral(31), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
"outside R0-R30"},
{"predicate where the register belongs", "PWHILELT", ExtFormWhile,
[]ExtOperand{pb(2), ExtGeneral(10), ExtPredicateSized(10, ExtArrH)},
"wants a general register"},
{"counter predicate spelled plain", "PPTRUE", ExtFormPredicateCounter,
[]ExtOperand{ExtPredicateSized(6, ExtArrS)},
"predicate-as-counter register"},
{"counter below the counter range", "PPTRUE", ExtFormPredicateCounter,
[]ExtOperand{ExtCounterPredicate(7, ExtArrS)},
"outside PN8-PN15"},
{"operand where none belongs", "SETFFR", ExtFormNone,
[]ExtOperand{pb(13)},
"takes 0 operands"},
} {
in := extInstruction(t, tt.mnem, tt.form)
_, err := in.Encode(tt.ops)
if err == nil {
t.Errorf("%s: encode succeeded, want an error", tt.name)
continue
}
if !strings.Contains(err.Error(), tt.quote) {
t.Errorf("%s: error %q lacks %q", tt.name, err, tt.quote)
}
}
}
+108 -18
View File
@@ -11,7 +11,11 @@
//
// The spellings are the layer's own Plan 9 forms, the ones its metadata
// documents: Zn, Zm, Zd for the unpredicated three-vector class, Zm, Pg/M,
// Zdn for the predicated class, imm{, LSL #8}, Zdn for the immediate classes.
// Zdn for the predicated class, imm{, LSL #8}, Zdn for the immediate
// classes, and for the predicate family Pm.B, Pn.B, Pg/Z (or Pg.Z), Pd.B
// for the logical operations, Pn.B, Pg.Z, Pd.B for the breaks, Pm.T, Pn.T,
// Pd.T for the permutations, Rm, Rn, Pd.T for the while compares, PN8-PN15
// for the counter destinations, and the bare SETFFR.
package asm
@@ -27,14 +31,16 @@ import (
// arm64ExtStatement converts one instruction's operands into the extended
// layer's operand form. pinned reports that the statement belongs to the
// layer: the mnemonic is registered in the registry and the operand list
// carries at least one scalable vector or predicate register. A pinned
// statement can only encode through the layer, so every operand is read
// here and its diagnostic replaces whatever the scalar paths would have
// said about operands they cannot read; err is non-nil for a pinned
// statement whose operands the layer refuses, and extops is complete only
// when err is nil. Unpinned means the statement is nobody's: the caller
// falls through to the ordinary arm64 encoders, which keep their exact
// behaviour for every scalar, NEON and FP operand list.
// carries at least one scalable vector, predicate or predicate-as-counter
// register, or no operands at all (the zero-operand forms such as SETFFR,
// which no scalar path could mean instead). A pinned statement can only
// encode through the layer, so every operand is read here and its
// diagnostic replaces whatever the scalar paths would have said about
// operands they cannot read; err is non-nil for a pinned statement whose
// operands the layer refuses, and extops is complete only when err is nil.
// Unpinned means the statement is nobody's: the caller falls through to the
// ordinary arm64 encoders, which keep their exact behaviour for every
// scalar, NEON and FP operand list.
func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperand, pinned bool, err error) {
if _, ok := LookupExtension(arch.ARM64, mnem); !ok {
return nil, false, nil
@@ -77,18 +83,31 @@ func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperan
out = append(out, ext)
continue
}
return nil, true, fmt.Errorf("%s: operand %d (%s) is not an extended-layer operand: want a scalable vector register, a predicate register or an immediate", mnem, i+1, op.Raw)
if ext, ok := arm64ExtCounter(text); ok {
out = append(out, ext)
continue
}
if ext, ok := arm64ExtGeneral(text); ok {
out = append(out, ext)
continue
}
return nil, true, fmt.Errorf("%s: operand %d (%s) is not an extended-layer operand: want a scalable vector, predicate, general or counter register, or an immediate", mnem, i+1, op.Raw)
}
return out, true, nil
}
// arm64ExtPinned reports whether any operand is a scalable vector or
// predicate register, the shapes only the extension layer reads. The test
// arm64ExtPinned reports whether the statement belongs to the layer: any
// operand is a scalable vector, predicate or predicate-as-counter register,
// the shapes only the extension layer reads, or the statement carries no
// operands at all and the mnemonic's zero-operand forms claim it. The test
// is deliberately loose about the suffixes: P0/B is not a spelling the
// layer takes, but the P of it makes the statement the layer's, and the
// conversion then diagnoses the operand precisely instead of leaving it to
// a scalar path that would report an unrelated register error.
func arm64ExtPinned(ops []*ast.Operand) bool {
if len(ops) == 0 {
return true
}
for _, op := range ops {
if op.Kind == ast.OpImmediate {
continue
@@ -104,16 +123,20 @@ func arm64ExtPinned(ops []*ast.Operand) bool {
return false
}
// arm64ExtPredicateShape reports whether text spells a predicate register at
// all: P, digits, an optional arrangement suffix and an optional qualifier
// after a slash, whatever the qualifier says. The strict parse in
// arm64ExtPredicate judges the suffix; this shape only decides who the
// operand belongs to.
// arm64ExtPredicateShape reports whether text spells a predicate or
// predicate-as-counter register at all: PN or P, digits, an optional
// arrangement suffix and an optional qualifier after a slash, whatever the
// qualifier says. The strict parses in arm64ExtPredicate and
// arm64ExtCounter judge the suffix; this shape only decides who the operand
// belongs to.
func arm64ExtPredicateShape(text string) bool {
if text == "" || text[0] != 'P' {
return false
}
text = text[1:]
if rest, found := strings.CutPrefix(text, "N"); found {
text = rest
}
if i := strings.IndexByte(text, '/'); i >= 0 {
text = text[:i]
}
@@ -160,7 +183,8 @@ func arm64ExtVector(text string) (arch.ExtOperand, bool) {
}
// arm64ExtPredicate parses a predicate register operand: P0..P15 with an
// optional element-size suffix and an optional qualifier, P0/M, P0.Z, P0.B/M.
// optional element-size suffix (P0.B) and an optional qualifier in either
// spelling the corpus and the wired forms use, P0/M and P0.Z.
func arm64ExtPredicate(text string) (arch.ExtOperand, bool) {
qual := arch.ExtQualNone
if base, suffix, found := strings.Cut(text, "/"); found {
@@ -173,6 +197,15 @@ func arm64ExtPredicate(text string) (arch.ExtOperand, bool) {
return arch.ExtOperand{}, false
}
text = base
} else if base, suffix, found := strings.Cut(text, "."); found &&
(suffix == "Z" || suffix == "M") {
// The dot qualifier stands in place of an arrangement, the spelling
// the toolchain's corpus writes (P1.Z, P14.M).
qual = arch.ExtQualMerging
if suffix == "Z" {
qual = arch.ExtQualZeroing
}
text = base
}
reg, arr, ok := arm64ExtReg(text, 'P')
if !ok {
@@ -181,6 +214,63 @@ func arm64ExtPredicate(text string) (arch.ExtOperand, bool) {
return arch.ExtOperand{Kind: arch.ExtPReg, Reg: reg, Arr: arr, Qual: qual}, true
}
// arm64ExtCounter parses a predicate-as-counter register operand: PN8..PN15
// with an optional element-size suffix, PN14.S. The register range is the
// counter range the layer's convention carries; the encoding validates it.
func arm64ExtCounter(text string) (arch.ExtOperand, bool) {
rest, ok := strings.CutPrefix(text, "PN")
if !ok {
return arch.ExtOperand{}, false
}
reg, arr, ok := arm64ExtRegDigits(rest)
if !ok {
return arch.ExtOperand{}, false
}
return arch.ExtOperand{Kind: arch.ExtPNReg, Reg: reg, Arr: arr}, true
}
// arm64ExtGeneral parses a general register operand: R0..R30, the plain
// spelling the while-compare forms take. The register range is left to the
// encoding, whose diagnostics name it.
func arm64ExtGeneral(text string) (arch.ExtOperand, bool) {
rest, ok := strings.CutPrefix(text, "R")
if !ok {
return arch.ExtOperand{}, false
}
reg, arr, ok := arm64ExtRegDigits(rest)
if !ok || arr != arch.ExtArrNone {
return arch.ExtOperand{}, false
}
return arch.ExtOperand{Kind: arch.ExtGReg, Reg: 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) {
if base, suffix, found := strings.Cut(text, "."); found {
switch suffix {
case "B":
arr = arch.ExtArrB
case "H":
arr = arch.ExtArrH
case "S":
arr = arch.ExtArrS
case "D":
arr = arch.ExtArrD
case "Q":
arr = arch.ExtArrQ
default:
return 0, 0, false
}
text = base
}
n, err := strconv.Atoi(text)
if err != nil || n < 0 {
return 0, 0, false
}
return n, arr, true
}
// arm64ExtReg parses Pn or Zn with an optional arrangement suffix off a
// normalised operand text. The register range is left to the encoding: the
// layer's own diagnostics name the range a form carries.
+143 -1
View File
@@ -119,7 +119,7 @@ func TestArm64AssembleExtensionRefusals(t *testing.T) {
{"ADD Z1.S, P9/M, Z0.S", "outside P0-P7"},
{"ADD $300, Z0.S", "immediate 300"},
{"ADD $255<<8, Z0.S", "not an immediate the layer can read"},
{"ADD Z1.S, P0/M, R0", "not an extended-layer operand"},
{"ADD Z1.S, P0/M, R0", "wants a scalable vector register"},
{"ADD Z1.S, P0/B, Z0.S", "not an extended-layer operand"},
{"MUL $200, Z0.B", "outside the signed 8-bit range"},
{"ADD Z1.S, LSL #8, Z0.S", "LSL belongs straight after an immediate"},
@@ -132,6 +132,148 @@ func TestArm64AssembleExtensionRefusals(t *testing.T) {
}
}
// TestArm64AssembleExtensionPredicateGolden drives the predicate family
// through the full assembler: corpus spellings in, corpus words out. The
// want words are the same arm64sveenc.s lines the arch-level golden test
// pins; these prove the text-to-bytes path parses the dot and slash
// qualifiers, the general registers and the counter spelling the statements
// write.
func TestArm64AssembleExtensionPredicateGolden(t *testing.T) {
tests := []struct {
stmt string
want uint32
}{
// The logical operations, .B alone, the governing predicate with the
// zeroing qualifier in its dot spelling.
{"PAND P4.B, P2.B, P1.Z, P14.B", 0x2504444e},
{"PANDS P4.B, P2.B, P1.Z, P14.B", 0x2544444e},
{"PBIC P4.B, P2.B, P1.Z, P14.B", 0x2504445e},
{"PEOR P4.B, P2.B, P1.Z, P14.B", 0x2504464e},
{"PNAND P4.B, P2.B, P1.Z, P14.B", 0x2584465e},
{"PORR P4.B, P2.B, P1.Z, P14.B", 0x2584444e},
// The slash qualifier spells the same operand the dot spelling does.
{"PAND P4.B, P2.B, P1/Z, P14.B", 0x2504444e},
// The select and the breaks.
{"PSEL P4.B, P2.B, P1, P14.B", 0x2504465e},
{"PBRKA P5.B, P9.Z, P2.B", 0x251064a2},
{"PBRKAS P5.B, P9.Z, P4.B", 0x255064a4},
{"PBRKN P4.B, P2.B, P1.Z, P4.B", 0x25184444},
{"PBRKPA P4.B, P2.B, P1.Z, P14.B", 0x2504c44e},
// The permutations carry the arrangement into the size field.
{"PTRN1 P5.D, P4.D, P2.D", 0x05e55082},
{"PUZP2 P5.D, P4.D, P2.D", 0x05e54c82},
{"PZIP1 P5.H, P4.H, P2.H", 0x05654082},
// The singles and the first-fault group.
{"PPFALSE P13.B", 0x2518e40d},
{"PPFIRST P5.B, P9, P5.B", 0x2558c125},
{"PPNEXT P5.D, P4, P5.D", 0x25d9c485},
{"PPTEST P14.B, P0", 0x2550c1c0},
{"PPUNPKHI P14.B, P0.H", 0x053141c0},
{"PRDFFR P13.B", 0x2519f00d},
{"PRDFFR P14.Z, P0.B", 0x2518f1c0},
{"PRDFFRS P14.Z, P0.B", 0x2558f1c0},
{"PWRFFR P13.B", 0x252891a0},
{"PREV P14.S, P13.S", 0x05b441cd},
{"SETFFR", 0x252c9000},
// The while compares: general registers in, a sized predicate out.
{"PWHILEGE R2, R10, P10.H", 0x2562114a},
{"PWHILELT R2, R10, P10.H", 0x2562154a},
{"PWHILELS R2, R10, P10.B", 0x25221d5a},
{"PWHILERW R2, R10, P10.H", 0x2562315a},
}
for _, tt := range tests {
words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n")
if len(words) != 2 {
t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words))
}
if words[0] != tt.want {
t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want)
}
if words[1] != 0xd65f03c0 {
t.Errorf("%s: RET encoded %08x", tt.stmt, words[1])
}
}
}
// TestArm64AssembleExtensionPredicateRefusals pins the diagnostics the
// predicate statements get from the layer.
func TestArm64AssembleExtensionPredicateRefusals(t *testing.T) {
tests := []struct {
stmt string
want string
}{
{"PAND P4.B, P2.B, P1.M, P14.B", "zeroing qualifier"},
{"PAND P4.B, P2.B, P9.Z, P14.B", "outside P0-P7"},
{"PAND P8.B, P2.B, P1.Z, P14.B", "outside P0-P7"},
{"PAND P4.S, P2.B, P1.Z, P14.B", "want .B"},
{"PBRKN P4.B, P2.B, P1.Z, P9.B", "same register Pdm"},
{"PBRKAS P5.B, P9.M, P4.B", "zeroing qualifier"},
{"PSEL P4.B, P2.B, P1.Z, P14.B", "takes no qualifier"},
{"PTRN1 P5.D, P4.S, P2.D", "want .D"},
{"PPFALSE P13.S", "want .B"},
{"PPTRUE P6.S", "predicate-as-counter register"},
{"PPTRUE PN6.S", "outside PN8-PN15"},
{"PWHILELT R2, R31, P10.H", "outside R0-R30"},
{"PWHILELT R2, R10, P10.Q", "no size encoding"},
{"SETFFR P0.B", "takes 0 operands"},
}
for _, tt := range tests {
got := assembleArm64SrcError(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n")
if !strings.Contains(got, tt.want) {
t.Errorf("%s: error %q does not name %q", tt.stmt, got, tt.want)
}
}
}
// TestArm64AssembleExtensionPredicateLeavesScalarsAlone pins the non-
// invasion promise across the new operand kinds: general-register
// statements whose mnemonics the layer also carries for predicates keep
// their scalar behaviour whenever no vector, predicate or counter operand
// appears.
func TestArm64AssembleExtensionPredicateLeavesScalarsAlone(t *testing.T) {
tests := []struct {
stmt string
want uint32
}{
{"ADD R0, R1, R2", 0x8b000022},
{"SUB R0, R1, R2", 0xcb000022},
}
for _, tt := range tests {
words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n")
if len(words) != 2 {
t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words))
}
if words[0] != tt.want {
t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want)
}
}
}
// TestArm64AssembleExtensionPredicateLabelOffsets proves pass 1 and pass 2
// agree on a function mixing the predicate family with the vector family:
// PWHILELT (4 bytes) and SETFFR (4 bytes) shift the label by exactly the
// words pass 2 lays down.
func TestArm64AssembleExtensionPredicateLabelOffsets(t *testing.T) {
src := arm64ExtProbeHead + `
PWHILELT R2, R10, P10.H
loop:
SETFFR
B loop
PPFALSE P13.B
RET
`
words := assembleArm64Words(t, src)
want := []uint32{0x2562154a, 0x252c9000, 0x17ffffff, 0x2518e40d, 0xd65f03c0}
if len(words) != len(want) {
t.Fatalf("got %d words, want %d", len(words), len(want))
}
for i := range want {
if words[i] != want[i] {
t.Errorf("word %d: got %08x, want %08x", i, words[i], want[i])
}
}
}
// TestArm64AssembleExtensionLabelOffsets proves pass 1 and pass 2 agree on a
// function that mixes the layer with ordinary statements: the label after an
// SVE instruction lands on the 4 bytes the encoder laid down, and the branch
+19 -3
View File
@@ -205,12 +205,28 @@ func TestExtensionArchIsolation(t *testing.T) {
// TestExtensionNamesARM64 checks the completion-facing name list: every
// distinct mnemonic of the family, first-occurrence order, no duplicates.
func TestExtensionNamesARM64(t *testing.T) {
want := []string{"ADD", "SUB", "SQADD", "UQADD", "SQSUB", "UQSUB", "MUL", "SMULH", "UMULH", "SUBR"}
want := []string{
// The SVE integer add/subtract/multiply family.
"ADD", "SUB", "SQADD", "UQADD", "SQSUB", "UQSUB", "MUL", "SMULH", "UMULH", "SUBR",
// The SVE and SVE2.1 predicate family: the logical operations, the
// breaks, the permutations, the singles, the first-fault group and
// the while compares.
"PAND", "PANDS", "PBIC", "PBICS", "PEOR", "PEORS",
"PNAND", "PNANDS", "PNOR", "PNORS", "PORN", "PORNS", "PORR", "PORRS",
"PSEL",
"PBRKA", "PBRKAS", "PBRKB", "PBRKBS", "PBRKN", "PBRKNS",
"PBRKPA", "PBRKPAS", "PBRKPB", "PBRKPBS",
"PTRN1", "PTRN2", "PUZP1", "PUZP2", "PZIP1", "PZIP2",
"PPFALSE", "PPFIRST", "PPNEXT", "PPTEST", "PPTRUE", "PPUNPKHI", "PPUNPKLO",
"PRDFFR", "PRDFFRS", "PWRFFR", "PREV", "SETFFR",
"PWHILEGE", "PWHILEGT", "PWHILEHI", "PWHILEHS",
"PWHILELE", "PWHILELO", "PWHILELS", "PWHILELT", "PWHILERW", "PWHILEWR",
}
got := ExtensionNames(arch.ARM64)
if strings.Join(got, ",") != strings.Join(want, ",") {
t.Errorf("ExtensionNames(ARM64) = %v, want %v", got, want)
}
if n := len(arch.Extensions(arch.ARM64)); n != 23 {
t.Errorf("the family registers %d instructions, want 23", n)
if n := len(arch.Extensions(arch.ARM64)); n != 77 {
t.Errorf("the family registers %d instructions, want 77", n)
}
}