feat(arch): the shift-immediate tsz:imm3 scheme

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 21:39:45 +02:00
1 parent 718181e7a7
commit eda8b16c5f
4 files changed
+555 -2

No files matched your search

+270
View File
@@ -134,6 +134,12 @@ func (a ExtArrangement) Width() int {
}
}
// elemBits returns the arrangement's element size in bits, the scale the
// shift-immediate classes encode their amounts against.
func (a ExtArrangement) elemBits() int {
return a.Width() * 8
}
// sizeBits maps the arrangement onto the two-bit size field the integer SVE
// classes carry at bits 23..22: 00=B, 01=H, 10=S, 11=D. ok is false for the
// arrangements no such class accepts (.Q and the bare spelling).
@@ -407,6 +413,18 @@ var (
extFieldPnc = ExtField{0, 3} // predicate-as-counter destination PN8-PN15, field holds reg-8
extFieldPn4 = ExtField{5, 4} // predicate source P0-P15 at bits 8..5 (counter family)
extFieldImm6 = ExtField{5, 6} // the signed imm6 of the vector-length arithmetic
// The tsz:imm3 shift-immediate fields: the amount rides a 5..7-bit
// integer C, split imm3 (the low three bits), a two-bit tsz chunk and
// a one- or two-bit tsz top. The three-vector classes split into
// bits 18..16, 20..19 and 22; the predicated class into bits 7..5,
// 9..8 and 23..22. The tsz chunks are the arrangement's own spelling
// and stand in for the size field these classes do not carry.
extFieldImm3T16 = ExtField{16, 3} // the low three bits of C, three-vector classes
extFieldTszM19 = ExtField{19, 2} // the middle tsz chunk of the three-vector classes
extFieldTszH22 = ExtField{22, 1} // the top tsz bit of the three-vector classes
extFieldImm3T5 = ExtField{5, 3} // the low three bits of C, predicated class
extFieldTszM8 = ExtField{8, 2} // the middle tsz chunk of the predicated class
extFieldTszH22W = ExtField{22, 2} // the top tsz chunk of the predicated class
)
// ExtForm enumerates the operand shapes the extension layer defines, in Plan
@@ -742,6 +760,31 @@ const (
// destination slot, and the predicate's arrangement feeds the size
// field.
ExtFormCountPnVec
// ExtFormShiftImmPred is the predicated shift by immediate, ZSQSHL
// $6, Z23.B, P1.M, Z23.B. Operands: imm, Zdn.T, Pg/M, Zdn.T, the
// destination spelled twice and encoded once. The amount rides the
// class's tsz:imm3 fields: C = esize + amount, split imm3 into bits
// 7..5, the low tsz pair into bits 9..8 and the top tsz pair into
// bits 23..22, where the tsz bits are the arrangement's own spelling
// and double as its size encoding; no separate size field exists.
// The amount runs 1..esize-1 and the merging qualifier is the only
// one the class spells.
ExtFormShiftImmPred
// ExtFormShiftImmNarrow is the three-vector narrowing shift by
// immediate, ZSHRNB $9, Z7.S, Z23.H: a right shift of the wide source
// whose result the narrow destination keeps, one element step down
// (.H+.B, .S+.H, .D+.S). Operands: imm, Zn.Tb, Zd.T. C = 2 x esize
// - amount over the destination's element size, split imm3 into bits
// 18..16, the low tsz pair into bits 20..19 and the top tsz bit into
// bit 22; the amount runs 1..esize.
ExtFormShiftImmNarrow
// ExtFormShiftImmWide is the three-vector widening shift by
// immediate, ZSSHLLB $5, Z22.S, Z10.D: a left shift of the narrow
// source into a destination one element step up (.B+.H, .H+.S,
// .S+.D). Operands: imm, Zn.Tb, Zd.T. C = esize + amount over the
// source's element size, split like the narrowing class; the amount
// runs 0..esize-1.
ExtFormShiftImmWide
)
// Arity returns the operand count the form takes.
@@ -780,6 +823,10 @@ func (f ExtForm) Arity() int {
return 2
case ExtFormCountPnVec:
return 2
case ExtFormShiftImmPred:
return 4
case ExtFormShiftImmNarrow, ExtFormShiftImmWide:
return 3
case ExtFormReduceReadBack:
return 4
case ExtFormSveLoad, ExtFormSveStore, ExtFormNarrow, ExtFormQuadReduce, ExtFormShiftWide,
@@ -848,6 +895,10 @@ func (f ExtForm) Kinds() []ExtOperandKind {
return []ExtOperandKind{ExtPReg, ExtGReg}
case ExtFormCountPnVec:
return []ExtOperandKind{ExtPReg, ExtZReg}
case ExtFormShiftImmPred:
return []ExtOperandKind{ExtImm, ExtZReg, ExtPReg, ExtZReg}
case ExtFormShiftImmNarrow, ExtFormShiftImmWide:
return []ExtOperandKind{ExtImm, ExtZReg, ExtZReg}
case ExtFormCountPnW:
return []ExtOperandKind{ExtGReg, ExtPReg, ExtGReg}
case ExtFormReduce:
@@ -984,6 +1035,12 @@ func (f ExtForm) String() string {
return "counter step, result dropped"
case ExtFormCountPnVec:
return "counter step on a vector"
case ExtFormShiftImmPred:
return "predicated shift by immediate"
case ExtFormShiftImmNarrow:
return "narrowing shift by immediate"
case ExtFormShiftImmWide:
return "widening shift by immediate"
case ExtFormCountPnW:
return "counter step to a register"
case ExtFormReduce:
@@ -1260,6 +1317,12 @@ func (in ExtInstr) Encode(ops []ExtOperand) ([]byte, error) {
return in.encodeCountPn(ops)
case ExtFormCountPnVec:
return in.encodeCountPnVec(ops)
case ExtFormShiftImmPred:
return in.encodeShiftImmPred(ops)
case ExtFormShiftImmNarrow:
return in.encodeShiftImmNarrow(ops)
case ExtFormShiftImmWide:
return in.encodeShiftImmWide(ops)
case ExtFormCountPnW:
return in.encodeCountPnW(ops)
case ExtFormReduce:
@@ -2393,6 +2456,139 @@ func (in ExtInstr) encodeCountPnVec(ops []ExtOperand) ([]byte, error) {
return extWordLE(word), nil
}
// shiftImmAmount validates the immediate shift operand against the closed
// range lo..hi and refuses the LSL spelling, which belongs to the
// immediate-constant classes and not to the shift-immediate ones.
func (in ExtInstr) shiftImmAmount(op ExtOperand, position, lo, hi int) (int, error) {
if op.Kind != ExtImm {
return 0, fmt.Errorf("%s: operand %d wants an immediate, got %s", in.Name, position, op.Kind)
}
if op.HasShift {
return 0, fmt.Errorf("%s: the shift-immediate class takes no LSL shift", in.Name)
}
v := int(op.Imm)
if v < lo || v > hi {
return 0, fmt.Errorf("%s: shift amount %d is outside the range %d..%d in this class",
in.Name, v, lo, hi)
}
return v, nil
}
// encodeShiftImmPred fills the predicated shift by immediate: imm, Zdn.T,
// Pg/M, Zdn.T, the destination spelled twice and encoded once. The amount
// rides the class's tsz:imm3 fields: C = esize + amount, split imm3 into
// bits 7..5, the low tsz pair into bits 9..8 and the top tsz pair into
// bits 23..22, where the tsz bits spell the arrangement and stand in for
// the size field the class does not carry.
func (in ExtInstr) encodeShiftImmPred(ops []ExtOperand) ([]byte, error) {
imm, zdn, pg, zdn2 := ops[0], ops[1], ops[2], ops[3]
if err := in.zVector(zdn, 2); err != nil {
return nil, err
}
if _, ok := zdn.Arr.sizeBits(); !ok {
return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class",
in.Name, zdn.Arr)
}
esize := zdn.Arr.elemBits()
v, err := in.shiftImmAmount(imm, 1, 1, esize-1)
if err != nil {
return nil, err
}
if err := in.predicateOperand(pg, 3, 0, 15, ExtArrNone); err != nil {
return nil, err
}
if pg.Qual != ExtQualMerging {
return nil, fmt.Errorf("%s: operand 3 wants the merging qualifier /M, got %q",
in.Name, pg.Qual)
}
if err := in.zVector(zdn2, 4); err != nil {
return nil, err
}
if zdn2.Reg != zdn.Reg || zdn2.Arr != zdn.Arr {
return nil, fmt.Errorf("%s: operands 2 and 4 are the same register Zdn, got Z%d and Z%d",
in.Name, zdn.Reg, zdn2.Reg)
}
c := esize + v
word := in.Word
word = extSet(word, extFieldImm3T5, uint32(c))
word = extSet(word, extFieldTszM8, uint32(c>>3))
word = extSet(word, extFieldTszH22W, uint32(c>>5))
word = extSet(word, extFieldRd, uint32(zdn.Reg))
word = extSet(word, extFieldPgW, uint32(pg.Reg))
return extWordLE(word), nil
}
// encodeShiftImmNarrow fills the three-vector narrowing shift by immediate:
// imm, Zn.Tb, Zd.T, a right shift of the wide source whose result the
// destination one element step down keeps (.H+.B, .S+.H, .D+.S). The
// amount runs 1..esize over the destination's element size and C = 2 x
// esize - amount, split imm3 into bits 18..16, the low tsz pair into bits
// 20..19 and the top tsz bit into bit 22.
func (in ExtInstr) encodeShiftImmNarrow(ops []ExtOperand) ([]byte, error) {
imm, zn, zd := ops[0], ops[1], ops[2]
if err := in.zVector(zn, 2); err != nil {
return nil, err
}
if zn.Arr < ExtArrH || zn.Arr > ExtArrD {
return nil, fmt.Errorf("%s: operand 2 carries arrangement %s, want .H, .S or .D", in.Name, zn.Arr)
}
if err := in.zVector(zd, 3); err != nil {
return nil, err
}
if zd.Arr != zn.Arr-1 {
return nil, fmt.Errorf("%s: operand 3 carries arrangement %s, want one element step below %s",
in.Name, zd.Arr, zn.Arr)
}
esize := zd.Arr.elemBits()
v, err := in.shiftImmAmount(imm, 1, 1, esize)
if err != nil {
return nil, err
}
c := 2*esize - v
word := in.Word
word = extSet(word, extFieldImm3T16, uint32(c))
word = extSet(word, extFieldTszM19, uint32(c>>3))
word = extSet(word, extFieldTszH22, uint32(c>>5))
word = extSet(word, extFieldRn, uint32(zn.Reg))
word = extSet(word, extFieldRd, uint32(zd.Reg))
return extWordLE(word), nil
}
// encodeShiftImmWide fills the three-vector widening shift by immediate:
// imm, Zn.Tb, Zd.T, a left shift of the narrow source into a destination
// one element step up (.B+.H, .H+.S, .S+.D). The amount runs
// 0..esize-1 over the source's element size and C = esize + amount, split
// like the narrowing class.
func (in ExtInstr) encodeShiftImmWide(ops []ExtOperand) ([]byte, error) {
imm, zn, zd := ops[0], ops[1], ops[2]
if err := in.zVector(zn, 2); err != nil {
return nil, err
}
if zn.Arr < ExtArrB || zn.Arr > ExtArrS {
return nil, fmt.Errorf("%s: operand 2 carries arrangement %s, want .B, .H or .S", in.Name, zn.Arr)
}
if err := in.zVector(zd, 3); err != nil {
return nil, err
}
if zd.Arr != zn.Arr+1 {
return nil, fmt.Errorf("%s: operand 3 carries arrangement %s, want one element step above %s",
in.Name, zd.Arr, zn.Arr)
}
esize := zn.Arr.elemBits()
v, err := in.shiftImmAmount(imm, 1, 0, esize-1)
if err != nil {
return nil, err
}
c := esize + v
word := in.Word
word = extSet(word, extFieldImm3T16, uint32(c))
word = extSet(word, extFieldTszM19, uint32(c>>3))
word = extSet(word, extFieldTszH22, uint32(c>>5))
word = extSet(word, extFieldRn, uint32(zn.Reg))
word = extSet(word, extFieldRd, uint32(zd.Reg))
return extWordLE(word), nil
}
// encodeCountPnW fills the predicate-counter step with a scalar result:
// Rd, Pn.T, Rd, the destination spelled twice. The predicate occupies the
// four-bit field at bits 8..5 and its arrangement feeds the size field.
@@ -4853,6 +5049,80 @@ var arm64Extensions = []ExtInstr{
Word: 0xe540a000, Form: ExtFormSveStore, Arr: ExtArrD, Feature: ExtFeatureSVE,
SveMode: extMemVecBase, SveShift: 0, SveBase: ExtArrD,
Ref: "ARM DDI 0487J: gather/scatter (vector base alone); inst_gen.go + arm64sveenc.s"},
// The shift-immediate classes: the SVE2 narrowing and widening
// three-vector shifts and the predicated saturating left shifts. The
// amount rides the class's tsz:imm3 fields, whose tsz bits double as
// the arrangement spelling: right shifts take C = 2 x esize - amount
// over the destination's element size, left shifts C = esize + amount
// over the source's (or the destination's own, in the predicated
// class), and C splits imm3, a two-bit tsz chunk and a one- or
// two-bit tsz top. Provenance as above: the toolchain encoding table
// and its generated corpus, both produced from Arm's official ISA
// description, and the golden test pins every corpus line of the
// covered classes.
{Name: "ZSQSHRUNB", Summary: "Shift right narrowing, unsigned results from signed, bottom",
Word: 0x45200000, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQSHRUN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSQSHRUNT", Summary: "Shift right narrowing, unsigned results from signed, top",
Word: 0x45200400, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQSHRUN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSHRNB", Summary: "Shift right narrowing by immediate, bottom",
Word: 0x45201000, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSHRNT", Summary: "Shift right narrowing by immediate, top",
Word: 0x45201400, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZRSHRNB", Summary: "Rounding shift right narrowing by immediate, bottom",
Word: 0x45201800, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: RSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZRSHRNT", Summary: "Rounding shift right narrowing by immediate, top",
Word: 0x45201c00, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: RSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSQSHRNB", Summary: "Saturating signed shift right narrowing by immediate, bottom",
Word: 0x45202000, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSQSHRNT", Summary: "Saturating signed shift right narrowing by immediate, top",
Word: 0x45202400, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSQRSHRNB", Summary: "Saturating rounding signed shift right narrowing by immediate, bottom",
Word: 0x45202800, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQRSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSQRSHRNT", Summary: "Saturating rounding signed shift right narrowing by immediate, top",
Word: 0x45202c00, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQRSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZUQSHRNB", Summary: "Saturating unsigned shift right narrowing by immediate, bottom",
Word: 0x45203000, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: UQSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZUQSHRNT", Summary: "Saturating unsigned shift right narrowing by immediate, top",
Word: 0x45203400, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: UQSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZUQRSHRNB", Summary: "Saturating rounding unsigned shift right narrowing by immediate, bottom",
Word: 0x45203800, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: UQRSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZUQRSHRNT", Summary: "Saturating rounding unsigned shift right narrowing by immediate, top",
Word: 0x45203c00, Form: ExtFormShiftImmNarrow, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: UQRSHRN (narrowing, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSSHLLB", Summary: "Signed shift left widening by immediate, bottom",
Word: 0x4500a000, Form: ExtFormShiftImmWide, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SSHLL (widening, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSSHLLT", Summary: "Signed shift left widening by immediate, top",
Word: 0x4500a400, Form: ExtFormShiftImmWide, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SSHLL (widening, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZUSHLLB", Summary: "Unsigned shift left widening by immediate, bottom",
Word: 0x4500a800, Form: ExtFormShiftImmWide, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: USHLL (widening, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZUSHLLT", Summary: "Unsigned shift left widening by immediate, top",
Word: 0x4500ac00, Form: ExtFormShiftImmWide, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: USHLL (widening, immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSQSHL", Summary: "Saturating signed shift left by immediate, over a governing predicate",
Word: 0x04068000, Form: ExtFormShiftImmPred, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQSHL (immediate, predicated); inst_gen.go + arm64sveenc.s"},
{Name: "ZSQSHLU", Summary: "Saturating signed shift left by immediate, unsigned bases, over a governing predicate",
Word: 0x040f8000, Form: ExtFormShiftImmPred, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SQSHLU (immediate, predicated); inst_gen.go + arm64sveenc.s"},
{Name: "ZUQSHL", Summary: "Saturating unsigned shift left by immediate, over a governing predicate",
Word: 0x04078000, Form: ExtFormShiftImmPred, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: UQSHL (immediate, predicated); inst_gen.go + arm64sveenc.s"},
}
// Extensions returns the extended-instruction layer registered for a, outside
+178
View File
@@ -1683,6 +1683,184 @@ func extSveRow(t *testing.T, mnem string, mode uint8, shift int, base, arr ExtAr
return ExtInstr{}
}
// TestArm64ExtShiftImmGolden pins the shift-immediate classes against the
// toolchain-generated corpus at the metadata layer: the narrowing and
// widening three-vector shifts and the predicated saturating left shifts.
// Every row is one arm64sveenc.s line and the want word is that line's own
// encoding; the amount's tsz:imm3 split is derived in the encoders, right
// shifts taking C = 2 x esize - amount over the destination's element size
// and left shifts C = esize + amount over the source's (or the
// destination's own, in the predicated class).
func TestArm64ExtShiftImmGolden(t *testing.T) {
for _, tt := range []struct {
name string
mnem string
form ExtForm
ops []ExtOperand
want uint32
}{
// The narrowing family, one corpus line each over .S+.H.
{"ZSHRNB $9, Z7.S, Z23.H", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453710f7},
{"ZSHRNT $9, Z7.S, Z23.H", "ZSHRNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453714f7},
{"ZRSHRNB $9, Z7.S, Z23.H", "ZRSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453718f7},
{"ZRSHRNT $9, Z7.S, Z23.H", "ZRSHRNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x45371cf7},
{"ZSQSHRNB $9, Z7.S, Z23.H", "ZSQSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453720f7},
{"ZSQSHRNT $9, Z7.S, Z23.H", "ZSQSHRNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453724f7},
{"ZSQRSHRNB $9, Z7.S, Z23.H", "ZSQRSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453728f7},
{"ZSQRSHRNT $9, Z7.S, Z23.H", "ZSQRSHRNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x45372cf7},
{"ZSQSHRUNB $9, Z7.S, Z23.H", "ZSQSHRUNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453700f7},
{"ZSQSHRUNT $9, Z7.S, Z23.H", "ZSQSHRUNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453704f7},
{"ZUQSHRNB $9, Z7.S, Z23.H", "ZUQSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453730f7},
{"ZUQSHRNT $9, Z7.S, Z23.H", "ZUQSHRNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453734f7},
{"ZUQRSHRNB $9, Z7.S, Z23.H", "ZUQRSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x453738f7},
{"ZUQRSHRNT $9, Z7.S, Z23.H", "ZUQRSHRNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
0x45373cf7},
// The width extensions the corpus does not carry: .H+.B and .D+.S,
// derived the same way and pinned to the words the fields give.
{"ZSHRNB $8, Z6.H, Z22.B", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(8), ExtVector(6, ExtArrH), ExtVector(22, ExtArrB)},
0x452810d6},
{"ZSQSHRNT $16, Z5.D, Z4.S", "ZSQSHRNT", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(16), ExtVector(5, ExtArrD), ExtVector(4, ExtArrS)},
0x457024a4},
// The widening family, one corpus line each over .S+.D.
{"ZSSHLLB $5, Z22.S, Z10.D", "ZSSHLLB", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(5), ExtVector(22, ExtArrS), ExtVector(10, ExtArrD)},
0x4545a2ca},
{"ZSSHLLT $5, Z22.S, Z10.D", "ZSSHLLT", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(5), ExtVector(22, ExtArrS), ExtVector(10, ExtArrD)},
0x4545a6ca},
{"ZUSHLLB $5, Z22.S, Z10.D", "ZUSHLLB", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(5), ExtVector(22, ExtArrS), ExtVector(10, ExtArrD)},
0x4545aaca},
{"ZUSHLLT $5, Z22.S, Z10.D", "ZUSHLLT", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(5), ExtVector(22, ExtArrS), ExtVector(10, ExtArrD)},
0x4545aeca},
{"ZSSHLLB $0, Z3.B, Z2.H", "ZSSHLLB", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(0), ExtVector(3, ExtArrB), ExtVector(2, ExtArrH)},
0x4508a062},
// The predicated saturating left shifts, one corpus line each.
{"ZSQSHL $6, Z23.B, P1.M, Z23.B", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
0x040685d7},
{"ZSQSHLU $6, Z23.B, P1.M, Z23.B", "ZSQSHLU", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
0x040f85d7},
{"ZUQSHL $6, Z23.B, P1.M, Z23.B", "ZUQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
0x040785d7},
{"ZSQSHL $20, Z8.S, P4.M, Z8.S", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(20), ExtVector(8, ExtArrS), ExtPredicate(4, ExtQualMerging), ExtVector(8, ExtArrS)},
0x04469288},
} {
in := extInstructionMeta(t, tt.mnem, tt.form, 0, ExtArrNone)
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)
}
}
}
// TestArm64ExtShiftImmRejects pins the diagnostics the shift-immediate
// forms give: the element-step pairs the classes take, the amount ranges
// and the read-back and qualifier rules of the predicated class.
func TestArm64ExtShiftImmRejects(t *testing.T) {
for _, tt := range []struct {
name string
mnem string
form ExtForm
ops []ExtOperand
quote string
}{
{"source not one step wide", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrD), ExtVector(23, ExtArrH)},
"one element step below .D"},
{"destination not one step narrow", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrS), ExtVector(23, ExtArrS)},
"one element step below"},
{"byte source", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(9), ExtVector(7, ExtArrB), ExtVector(23, ExtArrB)},
"want .H, .S or .D"},
{"amount over the destination element", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(17), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
"outside the range 1..16"},
{"amount zero on a right shift", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtImmediate(0), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
"outside the range 1..16"},
{"LSL spelling refused", "ZSHRNB", ExtFormShiftImmNarrow,
[]ExtOperand{ExtShiftedImmediate(9, 8), ExtVector(7, ExtArrS), ExtVector(23, ExtArrH)},
"takes no LSL shift"},
{"source not one step narrow", "ZSSHLLB", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(5), ExtVector(22, ExtArrS), ExtVector(10, ExtArrS)},
"one element step above .S"},
{"destination not one step wide", "ZSSHLLB", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(5), ExtVector(22, ExtArrS), ExtVector(10, ExtArrQ)},
"one element step above"},
{"amount over the source element", "ZSSHLLB", ExtFormShiftImmWide,
[]ExtOperand{ExtImmediate(32), ExtVector(22, ExtArrS), ExtVector(10, ExtArrD)},
"outside the range 0..31"},
{"zeroing qualifier", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualZeroing), ExtVector(23, ExtArrB)},
"/M"},
{"destination read-back mismatch", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(22, ExtArrB)},
"the same register Zdn"},
{"destination arrangement mismatch", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrH)},
"the same register Zdn"},
{"quadword has no size encoding", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrQ), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrQ)},
"no size encoding"},
{"amount over the element size", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(8), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
"outside the range 1..7"},
{"amount zero", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(0), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
"outside the range 1..7"},
} {
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)
}
}
}
// TestArm64ExtGatherGolden pins the gather/scatter family against the
// toolchain-generated corpus at the metadata layer: one row per addressing
// mode, the want word that corpus line's own encoding. The asm-level
+81
View File
@@ -956,3 +956,84 @@ func TestArm64AssembleExtensionGatherRefusals(t *testing.T) {
}
}
}
// TestArm64AssembleExtensionShiftImmGolden drives the shift-immediate
// classes through the full assembler: every corpus line of the three
// families, corpus text in, corpus word out. The narrowing and widening
// three-vector shifts and the predicated saturating left shifts carry the
// amount in the class's tsz:imm3 fields, whose tsz bits double as the
// arrangement spelling.
func TestArm64AssembleExtensionShiftImmGolden(t *testing.T) {
tests := []struct {
stmt string
want uint32
}{
{"ZSHRNB $9, Z7.S, Z23.H", 0x453710f7},
{"ZSHRNT $9, Z7.S, Z23.H", 0x453714f7},
{"ZRSHRNB $9, Z7.S, Z23.H", 0x453718f7},
{"ZRSHRNT $9, Z7.S, Z23.H", 0x45371cf7},
{"ZSQSHRNB $9, Z7.S, Z23.H", 0x453720f7},
{"ZSQSHRNT $9, Z7.S, Z23.H", 0x453724f7},
{"ZSQRSHRNB $9, Z7.S, Z23.H", 0x453728f7},
{"ZSQRSHRNT $9, Z7.S, Z23.H", 0x45372cf7},
{"ZSQSHRUNB $9, Z7.S, Z23.H", 0x453700f7},
{"ZSQSHRUNT $9, Z7.S, Z23.H", 0x453704f7},
{"ZUQSHRNB $9, Z7.S, Z23.H", 0x453730f7},
{"ZUQSHRNT $9, Z7.S, Z23.H", 0x453734f7},
{"ZUQRSHRNB $9, Z7.S, Z23.H", 0x453738f7},
{"ZUQRSHRNT $9, Z7.S, Z23.H", 0x45373cf7},
{"ZSSHLLB $5, Z22.S, Z10.D", 0x4545a2ca},
{"ZSSHLLT $5, Z22.S, Z10.D", 0x4545a6ca},
{"ZUSHLLB $5, Z22.S, Z10.D", 0x4545aaca},
{"ZUSHLLT $5, Z22.S, Z10.D", 0x4545aeca},
{"ZSQSHL $6, Z23.B, P1.M, Z23.B", 0x040685d7},
{"ZSQSHLU $6, Z23.B, P1.M, Z23.B", 0x040f85d7},
{"ZUQSHL $6, Z23.B, P1.M, Z23.B", 0x040785d7},
// Width extensions beyond the corpus lines.
{"ZSHRNB $8, Z6.H, Z22.B", 0x452810d6},
{"ZSQSHRNT $16, Z5.D, Z4.S", 0x457024a4},
{"ZSSHLLB $0, Z3.B, Z2.H", 0x4508a062},
{"ZSQSHL $20, Z8.S, P4.M, Z8.S", 0x04469288},
}
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])
}
}
}
// TestArm64AssembleExtensionShiftImmRefusals pins the diagnostics the
// shift-immediate statements get from the layer: the element-step pairs
// the classes take, the amount ranges and the read-back and qualifier
// rules of the predicated class.
func TestArm64AssembleExtensionShiftImmRefusals(t *testing.T) {
tests := []struct {
stmt string
want string
}{
{"ZSHRNB $9, Z7.S, Z23.S", "one element step below"},
{"ZSHRNB $9, Z7.S, Z23.B", "one element step below"},
{"ZSHRNB $17, Z7.S, Z23.H", "outside the range 1..16"},
{"ZSHRNB $0, Z7.S, Z23.H", "outside the range 1..16"},
{"ZSHRNB $9, LSL #8, Z7.S, Z23.H", "takes no LSL shift"},
{"ZSSHLLB $32, Z22.S, Z10.D", "outside the range 0..31"},
{"ZSSHLLB $5, Z22.S, Z10.Q", "one element step above .S"},
{"ZSQSHL $6, Z23.B, P1.Z, Z23.B", "/M"},
{"ZSQSHL $6, Z23.B, P1.M, Z22.B", "the same register Zdn"},
{"ZSQSHL $6, Z23.Q, P1.M, Z23.Q", "no size encoding"},
{"ZSQSHL $8, Z23.B, P1.M, Z23.B", "outside the range 1..7"},
}
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)
}
}
}
+26 -2
View File
@@ -492,12 +492,36 @@ func TestExtensionNamesARM64(t *testing.T) {
"ZST1D",
"ZST1H",
"ZST1W",
// The shift-immediate classes: the narrowing and widening
// three-vector shifts and the predicated saturating left shifts,
// in first-occurrence order.
"ZSQSHRUNB",
"ZSQSHRUNT",
"ZSHRNB",
"ZSHRNT",
"ZRSHRNB",
"ZRSHRNT",
"ZSQSHRNB",
"ZSQSHRNT",
"ZSQRSHRNB",
"ZSQRSHRNT",
"ZUQSHRNB",
"ZUQSHRNT",
"ZUQRSHRNB",
"ZUQRSHRNT",
"ZSSHLLB",
"ZSSHLLT",
"ZUSHLLB",
"ZUSHLLT",
"ZSQSHL",
"ZSQSHLU",
"ZUQSHL",
}
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 != 507 {
t.Errorf("the family registers %d instructions, want 507", n)
if n := len(arch.Extensions(arch.ARM64)); n != 528 {
t.Errorf("the family registers %d instructions, want 528", n)
}
}