diff --git a/arch/arm64_ext.go b/arch/arm64_ext.go index 9d01d51..af56147 100644 --- a/arch/arm64_ext.go +++ b/arch/arm64_ext.go @@ -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 diff --git a/arch/arm64_ext_test.go b/arch/arm64_ext_test.go index bb17d62..294cc71 100644 --- a/arch/arm64_ext_test.go +++ b/arch/arm64_ext_test.go @@ -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) diff --git a/asm/arm64_ext_asm_test.go b/asm/arm64_ext_asm_test.go index 3373987..8442220 100644 --- a/asm/arm64_ext_asm_test.go +++ b/asm/arm64_ext_asm_test.go @@ -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") diff --git a/asm/extension_test.go b/asm/extension_test.go index 2c47417..d89c5f2 100644 --- a/asm/extension_test.go +++ b/asm/extension_test.go @@ -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) } }