feat(arch): the SVE predicate family in the extended layer
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
83052ab466
commit
8231302bca
5 files changed
+1451
-42
No files matched your search
+777
-11
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user