feat(arch): the same-size shift-immediate family

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 21:39:45 +02:00
1 parent eda8b16c5f
commit 1031cd9ae7
4 files changed
+234 -13

No files matched your search

+164 -11
View File
@@ -785,6 +785,28 @@ const (
// source's element size, split like the narrowing class; the amount
// runs 0..esize-1.
ExtFormShiftImmWide
// ExtFormShiftImmSame is the same-size shift by immediate, ZSRI $6,
// Z7.D, Z23.D and its accumulating siblings ZSSRA, ZUSRA, ZSRSRA and
// ZURSRA: a right shift within one shared arrangement, the
// destination (or the read-modify-write accumulator) in the
// destination slot. Operands: imm, Zn.T, Zd.T. C = 2 x esize -
// amount over the shared element size, split imm3 into bits 18..16,
// the low tsz pair into bits 20..19 and the top tsz pair into bits
// 23..22; the amount runs 1..esize-1.
ExtFormShiftImmSame
// ExtFormShiftImmPredRight is the predicated right shift by
// immediate, ZASR $6, Z23.B, P1.M, Z23.B and ZASRD beside it: the
// right-shift counterpart of the predicated left shifts. Operands:
// imm, Zdn.T, Pg/M, Zdn.T. C = 2 x esize - amount over the
// destination's element size, split like the predicated left shifts;
// the amount runs 1..esize-1.
ExtFormShiftImmPredRight
// ExtFormShiftImmXar is the three-source extract and narrow, ZXAR
// $6, Z23.B, Z21.B, Z21.B: the destination is spelled twice and
// encoded once. Operands: imm, Zm.T, Zdn.T, Zdn.T. C = 2 x esize -
// amount over the shared element size, split like the same-size
// class; the amount runs 1..esize-1.
ExtFormShiftImmXar
)
// Arity returns the operand count the form takes.
@@ -823,9 +845,9 @@ func (f ExtForm) Arity() int {
return 2
case ExtFormCountPnVec:
return 2
case ExtFormShiftImmPred:
case ExtFormShiftImmPred, ExtFormShiftImmPredRight, ExtFormShiftImmXar:
return 4
case ExtFormShiftImmNarrow, ExtFormShiftImmWide:
case ExtFormShiftImmNarrow, ExtFormShiftImmWide, ExtFormShiftImmSame:
return 3
case ExtFormReduceReadBack:
return 4
@@ -895,9 +917,9 @@ func (f ExtForm) Kinds() []ExtOperandKind {
return []ExtOperandKind{ExtPReg, ExtGReg}
case ExtFormCountPnVec:
return []ExtOperandKind{ExtPReg, ExtZReg}
case ExtFormShiftImmPred:
case ExtFormShiftImmPred, ExtFormShiftImmPredRight, ExtFormShiftImmXar:
return []ExtOperandKind{ExtImm, ExtZReg, ExtPReg, ExtZReg}
case ExtFormShiftImmNarrow, ExtFormShiftImmWide:
case ExtFormShiftImmNarrow, ExtFormShiftImmWide, ExtFormShiftImmSame:
return []ExtOperandKind{ExtImm, ExtZReg, ExtZReg}
case ExtFormCountPnW:
return []ExtOperandKind{ExtGReg, ExtPReg, ExtGReg}
@@ -1041,6 +1063,12 @@ func (f ExtForm) String() string {
return "narrowing shift by immediate"
case ExtFormShiftImmWide:
return "widening shift by immediate"
case ExtFormShiftImmSame:
return "same-size shift by immediate"
case ExtFormShiftImmPredRight:
return "predicated right shift by immediate"
case ExtFormShiftImmXar:
return "three-source extract and narrow"
case ExtFormCountPnW:
return "counter step to a register"
case ExtFormReduce:
@@ -1319,10 +1347,16 @@ func (in ExtInstr) Encode(ops []ExtOperand) ([]byte, error) {
return in.encodeCountPnVec(ops)
case ExtFormShiftImmPred:
return in.encodeShiftImmPred(ops)
case ExtFormShiftImmPredRight:
return in.encodeShiftImmPredRight(ops)
case ExtFormShiftImmNarrow:
return in.encodeShiftImmNarrow(ops)
case ExtFormShiftImmWide:
return in.encodeShiftImmWide(ops)
case ExtFormShiftImmSame:
return in.encodeShiftImmSame(ops)
case ExtFormShiftImmXar:
return in.encodeShiftImmXar(ops)
case ExtFormCountPnW:
return in.encodeCountPnW(ops)
case ExtFormReduce:
@@ -2474,13 +2508,25 @@ func (in ExtInstr) shiftImmAmount(op ExtOperand, position, lo, hi int) (int, err
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.
// encodeShiftImmPred fills the predicated left shift by immediate: imm,
// Zdn.T, Pg/M, Zdn.T, the destination spelled twice and encoded once.
func (in ExtInstr) encodeShiftImmPred(ops []ExtOperand) ([]byte, error) {
return in.encodeShiftImmPredC(ops, func(esize, v int) int { return esize + v })
}
// encodeShiftImmPredRight fills the predicated right shift by immediate,
// the right-shift counterpart of the predicated left shifts.
func (in ExtInstr) encodeShiftImmPredRight(ops []ExtOperand) ([]byte, error) {
return in.encodeShiftImmPredC(ops, func(esize, v int) int { return 2*esize - v })
}
// encodeShiftImmPredC carries the predicated shift-by-immediate layout the
// left and right shifts share: the amount rides the class's tsz:imm3
// fields, C = dir(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) encodeShiftImmPredC(ops []ExtOperand, dir func(esize, v int) int) ([]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
@@ -2508,7 +2554,7 @@ func (in ExtInstr) encodeShiftImmPred(ops []ExtOperand) ([]byte, error) {
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
c := dir(esize, v)
word := in.Word
word = extSet(word, extFieldImm3T5, uint32(c))
word = extSet(word, extFieldTszM8, uint32(c>>3))
@@ -2518,6 +2564,86 @@ func (in ExtInstr) encodeShiftImmPred(ops []ExtOperand) ([]byte, error) {
return extWordLE(word), nil
}
// setShiftImmTsz16 ORs the three-vector tsz:imm3 chunks of c into the
// word: imm3 into bits 18..16, the low tsz pair into bits 20..19 and the
// top tsz pair into bits 23..22.
func setShiftImmTsz16(word uint32, c int) uint32 {
word = extSet(word, extFieldImm3T16, uint32(c))
word = extSet(word, extFieldTszM19, uint32(c>>3))
word = extSet(word, extFieldTszH22W, uint32(c>>5))
return word
}
// encodeShiftImmSame fills the same-size shift by immediate: imm, Zn.T,
// Zd.T, a right shift within one shared arrangement, the destination (or
// the read-modify-write accumulator) in the destination slot. C = 2 x
// esize - amount over the shared element size.
func (in ExtInstr) encodeShiftImmSame(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 _, ok := zn.Arr.sizeBits(); !ok {
return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class",
in.Name, zn.Arr)
}
if err := in.zVector(zd, 3); err != nil {
return nil, err
}
if zd.Arr != zn.Arr {
return nil, fmt.Errorf("%s: operands 2 and 3 carry arrangements %s and %s, they must match",
in.Name, zn.Arr, zd.Arr)
}
esize := zn.Arr.elemBits()
v, err := in.shiftImmAmount(imm, 1, 1, esize-1)
if err != nil {
return nil, err
}
c := 2*esize - v
word := setShiftImmTsz16(in.Word, c)
word = extSet(word, extFieldRn, uint32(zn.Reg))
word = extSet(word, extFieldRd, uint32(zd.Reg))
return extWordLE(word), nil
}
// encodeShiftImmXar fills the three-source extract and narrow: imm, Zm.T,
// Zdn.T, Zdn.T, the destination spelled twice and encoded once. C = 2 x
// esize - amount over the shared element size.
func (in ExtInstr) encodeShiftImmXar(ops []ExtOperand) ([]byte, error) {
imm, zm, zdn, zdn2 := ops[0], ops[1], ops[2], ops[3]
if err := in.zVector(zm, 2); err != nil {
return nil, err
}
if _, ok := zm.Arr.sizeBits(); !ok {
return nil, fmt.Errorf("%s: arrangement %s has no size encoding in this class",
in.Name, zm.Arr)
}
if err := in.zVector(zdn, 3); err != nil {
return nil, err
}
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 3 and 4 are the same register Zdn, got Z%d and Z%d",
in.Name, zdn.Reg, zdn2.Reg)
}
if zdn.Arr != zm.Arr {
return nil, fmt.Errorf("%s: operands 2 and 3 carry arrangements %s and %s, they must match",
in.Name, zm.Arr, zdn.Arr)
}
esize := zm.Arr.elemBits()
v, err := in.shiftImmAmount(imm, 1, 1, esize-1)
if err != nil {
return nil, err
}
c := 2*esize - v
word := setShiftImmTsz16(in.Word, c)
word = extSet(word, extFieldRn, uint32(zm.Reg))
word = extSet(word, extFieldRd, uint32(zdn.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
@@ -5123,6 +5249,33 @@ var arm64Extensions = []ExtInstr{
{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"},
{Name: "ZSRI", Summary: "Shift right and insert by immediate",
Word: 0x4500f000, Form: ExtFormShiftImmSame, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SRI (immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZASR", Summary: "Arithmetic shift right by immediate",
Word: 0x04209000, Form: ExtFormShiftImmSame, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: ASR (immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSSRA", Summary: "Signed shift right and accumulate by immediate",
Word: 0x4500e000, Form: ExtFormShiftImmSame, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SSRA (immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZUSRA", Summary: "Unsigned shift right and accumulate by immediate",
Word: 0x4500e400, Form: ExtFormShiftImmSame, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: USRA (immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZSRSRA", Summary: "Signed rounding shift right and accumulate by immediate",
Word: 0x4500e800, Form: ExtFormShiftImmSame, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: SRSRA (immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZURSRA", Summary: "Unsigned rounding shift right and accumulate by immediate",
Word: 0x4500ec00, Form: ExtFormShiftImmSame, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: URSRA (immediate); inst_gen.go + arm64sveenc.s"},
{Name: "ZASR", Summary: "Arithmetic shift right by immediate, over a governing predicate",
Word: 0x04008000, Form: ExtFormShiftImmPredRight, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: ASR (immediate, predicated); inst_gen.go + arm64sveenc.s"},
{Name: "ZASRD", Summary: "Arithmetic shift right by immediate, rounding, over a governing predicate",
Word: 0x04048000, Form: ExtFormShiftImmPredRight, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: ASRD (immediate, predicated); inst_gen.go + arm64sveenc.s"},
{Name: "ZXAR", Summary: "Extract and narrow by immediate from three sources",
Word: 0x04203400, Form: ExtFormShiftImmXar, Feature: ExtFeatureSVE2,
Ref: "ARM DDI 0487J SVE2: XAR; inst_gen.go + arm64sveenc.s"},
}
// Extensions returns the extended-instruction layer registered for a, outside
+46
View File
@@ -1779,6 +1779,34 @@ func TestArm64ExtShiftImmGolden(t *testing.T) {
{"ZSQSHL $20, Z8.S, P4.M, Z8.S", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(20), ExtVector(8, ExtArrS), ExtPredicate(4, ExtQualMerging), ExtVector(8, ExtArrS)},
0x04469288},
// The same-size right shifts, the accumulator and the extract.
{"ZSRI $6, Z7.D, Z23.D", "ZSRI", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(6), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
0x45daf0f7},
{"ZSSRA $6, Z7.D, Z23.D", "ZSSRA", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(6), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
0x45dae0f7},
{"ZUSRA $6, Z7.D, Z23.D", "ZUSRA", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(6), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
0x45dae4f7},
{"ZSRSRA $6, Z7.D, Z23.D", "ZSRSRA", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(6), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
0x45dae8f7},
{"ZURSRA $6, Z7.D, Z23.D", "ZURSRA", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(6), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
0x45daecf7},
{"ZASR $6, Z7.D, Z23.D", "ZASR", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(6), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
0x04fa90f7},
{"ZASR $6, Z23.B, P1.M, Z23.B", "ZASR", ExtFormShiftImmPredRight,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
0x04008557},
{"ZASRD $6, Z23.B, P1.M, Z23.B", "ZASRD", ExtFormShiftImmPredRight,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
0x04048557},
{"ZXAR $6, Z23.B, Z21.B, Z21.B", "ZXAR", ExtFormShiftImmXar,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtVector(21, ExtArrB), ExtVector(21, ExtArrB)},
0x042a36f5},
} {
in := extInstructionMeta(t, tt.mnem, tt.form, 0, ExtArrNone)
got, err := in.Encode(tt.ops)
@@ -1848,6 +1876,24 @@ func TestArm64ExtShiftImmRejects(t *testing.T) {
{"amount zero", "ZSQSHL", ExtFormShiftImmPred,
[]ExtOperand{ExtImmediate(0), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
"outside the range 1..7"},
{"same-size arrangements differ", "ZSRI", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(6), ExtVector(7, ExtArrB), ExtVector(23, ExtArrH)},
"must match"},
{"same-size amount zero", "ZSRI", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(0), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
"outside the range 1..63"},
{"same-size amount at the element size", "ZSRI", ExtFormShiftImmSame,
[]ExtOperand{ExtImmediate(64), ExtVector(7, ExtArrD), ExtVector(23, ExtArrD)},
"outside the range 1..63"},
{"predicated right amount at the element size", "ZASR", ExtFormShiftImmPredRight,
[]ExtOperand{ExtImmediate(8), ExtVector(23, ExtArrB), ExtPredicate(1, ExtQualMerging), ExtVector(23, ExtArrB)},
"outside the range 1..7"},
{"extract read-back mismatch", "ZXAR", ExtFormShiftImmXar,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtVector(21, ExtArrB), ExtVector(20, ExtArrB)},
"the same register Zdn"},
{"extract arrangements differ", "ZXAR", ExtFormShiftImmXar,
[]ExtOperand{ExtImmediate(6), ExtVector(23, ExtArrB), ExtVector(21, ExtArrH), ExtVector(21, ExtArrH)},
"must match"},
} {
in := extInstruction(t, tt.mnem, tt.form)
_, err := in.Encode(tt.ops)
+15
View File
@@ -994,6 +994,16 @@ func TestArm64AssembleExtensionShiftImmGolden(t *testing.T) {
{"ZSQSHRNT $16, Z5.D, Z4.S", 0x457024a4},
{"ZSSHLLB $0, Z3.B, Z2.H", 0x4508a062},
{"ZSQSHL $20, Z8.S, P4.M, Z8.S", 0x04469288},
// The same-size right shifts, the accumulator and the extract.
{"ZSRI $6, Z7.D, Z23.D", 0x45daf0f7},
{"ZSSRA $6, Z7.D, Z23.D", 0x45dae0f7},
{"ZUSRA $6, Z7.D, Z23.D", 0x45dae4f7},
{"ZSRSRA $6, Z7.D, Z23.D", 0x45dae8f7},
{"ZURSRA $6, Z7.D, Z23.D", 0x45daecf7},
{"ZASR $6, Z7.D, Z23.D", 0x04fa90f7},
{"ZASR $6, Z23.B, P1.M, Z23.B", 0x04008557},
{"ZASRD $6, Z23.B, P1.M, Z23.B", 0x04048557},
{"ZXAR $6, Z23.B, Z21.B, Z21.B", 0x042a36f5},
}
for _, tt := range tests {
words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n")
@@ -1029,6 +1039,11 @@ func TestArm64AssembleExtensionShiftImmRefusals(t *testing.T) {
{"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"},
{"ZSRI $6, Z7.B, Z23.H", "must match"},
{"ZSRI $0, Z7.D, Z23.D", "outside the range 1..63"},
{"ZASR $8, Z23.B, P1.M, Z23.B", "outside the range 1..7"},
{"ZXAR $6, Z23.B, Z21.B, Z20.B", "the same register Zdn"},
{"ZXAR $6, Z23.B, Z21.H, Z21.H", "must match"},
}
for _, tt := range tests {
got := assembleArm64SrcError(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n")
+9 -2
View File
@@ -516,12 +516,19 @@ func TestExtensionNamesARM64(t *testing.T) {
"ZSQSHL",
"ZSQSHLU",
"ZUQSHL",
"ZSRI",
"ZSSRA",
"ZUSRA",
"ZSRSRA",
"ZURSRA",
"ZASRD",
"ZXAR",
}
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 != 528 {
t.Errorf("the family registers %d instructions, want 528", n)
if n := len(arch.Extensions(arch.ARM64)); n != 537 {
t.Errorf("the family registers %d instructions, want 537", n)
}
}