From bd80499c745f4182276e9bf937120cd0d1eb5dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 7 Oct 2026 19:26:23 +0200 Subject: [PATCH] feat(arch): the SVE2 shift-by-vector family Assisted-by: GLM 5.3 Flash --- arch/arm64_ext.go | 87 ++++++++++++++++++++++++++++++++++++++- arch/arm64_ext_test.go | 31 ++++++++++++++ asm/arm64_ext_asm_test.go | 11 +++++ asm/extension_test.go | 10 ++++- 4 files changed, 135 insertions(+), 4 deletions(-) diff --git a/arch/arm64_ext.go b/arch/arm64_ext.go index 292ae71..e6dfff0 100644 --- a/arch/arm64_ext.go +++ b/arch/arm64_ext.go @@ -679,6 +679,11 @@ const ( // the predicate in the narrow governing field and the vector in the // destination slot, the reduction class's own layout. ExtFormQuadReduce + // ExtFormShiftWide is the wide-vector shift, ZASR Z7.D, Z6.H, Z13.H: + // the shift amounts ride a double-width vector locked to .D while the + // shifted data shares one narrower arrangement, which feeds the size + // field. Operands: Zm.D, Zn.T, Zd.T. + ExtFormShiftWide ) // Arity returns the operand count the form takes. @@ -716,7 +721,7 @@ func (f ExtForm) Arity() int { return 2 case ExtFormReduceReadBack: return 4 - case ExtFormSveLoad, ExtFormSveStore, ExtFormNarrow, ExtFormQuadReduce: + case ExtFormSveLoad, ExtFormSveStore, ExtFormNarrow, ExtFormQuadReduce, ExtFormShiftWide: return 3 case ExtFormNone: return 0 @@ -787,7 +792,7 @@ func (f ExtForm) Kinds() []ExtOperandKind { return []ExtOperandKind{ExtSveMem, ExtPReg, ExtZReg} case ExtFormSveStore: return []ExtOperandKind{ExtZReg, ExtPReg, ExtSveMem} - case ExtFormNarrow: + case ExtFormNarrow, ExtFormShiftWide: return []ExtOperandKind{ExtZReg, ExtZReg, ExtZReg} case ExtFormQuadReduce: return []ExtOperandKind{ExtZReg, ExtPReg, ExtVReg} @@ -917,6 +922,8 @@ func (f ExtForm) String() string { return "narrowing two-to-one" case ExtFormQuadReduce: return "quadword reduction" + case ExtFormShiftWide: + return "wide shift" case ExtFormNone: return "no operands" default: @@ -1173,6 +1180,8 @@ func (in ExtInstr) Encode(ops []ExtOperand) ([]byte, error) { return in.encodeNarrow(ops) case ExtFormQuadReduce: return in.encodeQuadReduce(ops) + case ExtFormShiftWide: + return in.encodeShiftWide(ops) case ExtFormNone: if len(ops) != 0 { return nil, fmt.Errorf("%s: the %s form takes no operands, got %d", @@ -2629,6 +2638,42 @@ func (in ExtInstr) encodeQuadReduce(ops []ExtOperand) ([]byte, error) { return extWordLE(word), nil } +// encodeShiftWide fills the wide-vector shift: Zm.D, Zn.T, Zd.T. The shift +// amounts ride a double-width vector locked to .D while the shifted data +// carries one shared narrower arrangement, which feeds the size field. +func (in ExtInstr) encodeShiftWide(ops []ExtOperand) ([]byte, error) { + zm, zn, zd := ops[0], ops[1], ops[2] + if err := in.zVector(zm, 1); err != nil { + return nil, err + } + if zm.Arr != ExtArrD { + return nil, fmt.Errorf("%s: operand 1 carries arrangement %s, want .D", + in.Name, zm.Arr) + } + if err := in.zVector(zn, 2); err != nil { + return nil, err + } + if err := in.zVector(zd, 3); err != nil { + return nil, err + } + if zn.Arr != zd.Arr { + return nil, fmt.Errorf("%s: operands 2 and 3 carry arrangements %s and %s, they must match", + in.Name, zn.Arr, zd.Arr) + } + size, err := in.zSize(zn.Arr) + if err != nil { + return nil, err + } + word := in.Word + word = extSet(word, extFieldRm, uint32(zm.Reg)) + word = extSet(word, extFieldRn, uint32(zn.Reg)) + word = extSet(word, extFieldRd, uint32(zd.Reg)) + if size != 0 { + 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. @@ -3259,6 +3304,44 @@ var arm64Extensions = []ExtInstr{ Word: 0x040f2000, Form: ExtFormQuadReduce, Size: extSizeBHSD, Feature: ExtFeatureSVE2p1, Ref: "ARM DDI 0487J SVE2.1: ZUMINQV; inst_gen.go + arm64sveenc.s"}, + // --- the SVE2 shift-by-vector family -------------------------------------- + // + // The arithmetic, logical and rounding shift steps over a vector of + // shift amounts: the predicated-source shapes share one arrangement, + // and the wide three-vector shape takes its amounts from a double-width + // vector locked to .D. The shift-by-immediate shapes of the same + // mnemonics (the tsz:imm3 class) are not wired yet. 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 shapes. + {Name: "ZASR", Summary: "Shift right arithmetic by a vector, merging", + Word: 0x04108000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZASR; inst_gen.go + arm64sveenc.s"}, + {Name: "ZASR", Summary: "Shift right arithmetic by a wide vector, unpredicated", + Word: 0x04208000, Form: ExtFormShiftWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZASR; inst_gen.go + arm64sveenc.s"}, + {Name: "ZASRR", Summary: "Shift right arithmetic by a vector with rounding, merging", + Word: 0x04148000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZASRR; inst_gen.go + arm64sveenc.s"}, + {Name: "ZLSL", Summary: "Shift left by a vector, merging", + Word: 0x04138000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZLSL; inst_gen.go + arm64sveenc.s"}, + {Name: "ZLSL", Summary: "Shift left by a wide vector, unpredicated", + Word: 0x04208c00, Form: ExtFormShiftWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZLSL; inst_gen.go + arm64sveenc.s"}, + {Name: "ZLSLR", Summary: "Shift left by a vector with rounding, merging", + Word: 0x04178000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZLSLR; inst_gen.go + arm64sveenc.s"}, + {Name: "ZLSR", Summary: "Shift right logical by a vector, merging", + Word: 0x04118000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZLSR; inst_gen.go + arm64sveenc.s"}, + {Name: "ZLSR", Summary: "Shift right logical by a wide vector, unpredicated", + Word: 0x04208400, Form: ExtFormShiftWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZLSR; inst_gen.go + arm64sveenc.s"}, + {Name: "ZLSRR", Summary: "Shift right logical by a vector with rounding, merging", + Word: 0x04158000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZLSRR; inst_gen.go + arm64sveenc.s"}, + // --- the SVE2 crypto family ---------------------------------------------- // // The multi-precision carry steps (ADCLB, ADCLT, SBCLB, SBCLT), the SHA3 diff --git a/arch/arm64_ext_test.go b/arch/arm64_ext_test.go index 3513be5..f3a6cef 100644 --- a/arch/arm64_ext_test.go +++ b/arch/arm64_ext_test.go @@ -1440,6 +1440,37 @@ func TestArm64ExtStage4Golden(t *testing.T) { {"ZFMINQV Z25.S, P3, V5.S4", "ZFMINQV", ExtFormQuadReduce, ExtQualNone, ExtArrNone, []ExtOperand{ExtVector(25, ExtArrS), ExtPredicateSized(3, ExtArrNone), ExtOperand{Kind: ExtVReg, Reg: 5, Arr: ExtArrS}}, 0x6497af25}, + // The shift-by-vector family. + {"ZASR Z15.B, Z0.B, P3.M, Z0.B", "ZASR", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(15, ExtArrB), ExtVector(0, ExtArrB), ExtPredicate(3, ExtQualMerging), ExtVector(0, ExtArrB)}, + 0x04108de0}, + {"ZASR Z2.D, Z10.D, P3.M, Z10.D", "ZASR", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(2, ExtArrD), ExtVector(10, ExtArrD), ExtPredicate(3, ExtQualMerging), ExtVector(10, ExtArrD)}, + 0x04d08c4a}, + {"ZASR Z7.D, Z6.H, Z13.H", "ZASR", ExtFormShiftWide, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(6, ExtArrH), ExtVector(13, ExtArrH)}, + 0x046780cd}, + {"ZASRR Z15.B, Z0.B, P3.M, Z0.B", "ZASRR", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(15, ExtArrB), ExtVector(0, ExtArrB), ExtPredicate(3, ExtQualMerging), ExtVector(0, ExtArrB)}, + 0x04148de0}, + {"ZLSL Z2.D, Z10.D, P3.M, Z10.D", "ZLSL", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(2, ExtArrD), ExtVector(10, ExtArrD), ExtPredicate(3, ExtQualMerging), ExtVector(10, ExtArrD)}, + 0x04d38c4a}, + {"ZLSL Z7.D, Z6.H, Z13.H", "ZLSL", ExtFormShiftWide, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(6, ExtArrH), ExtVector(13, ExtArrH)}, + 0x04678ccd}, + {"ZLSLR Z15.B, Z0.B, P3.M, Z0.B", "ZLSLR", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(15, ExtArrB), ExtVector(0, ExtArrB), ExtPredicate(3, ExtQualMerging), ExtVector(0, ExtArrB)}, + 0x04178de0}, + {"ZLSR Z15.B, Z0.B, P3.M, Z0.B", "ZLSR", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(15, ExtArrB), ExtVector(0, ExtArrB), ExtPredicate(3, ExtQualMerging), ExtVector(0, ExtArrB)}, + 0x04118de0}, + {"ZLSR Z7.D, Z6.H, Z13.H", "ZLSR", ExtFormShiftWide, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(6, ExtArrH), ExtVector(13, ExtArrH)}, + 0x046784cd}, + {"ZLSRR Z15.B, Z0.B, P3.M, Z0.B", "ZLSRR", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(15, ExtArrB), ExtVector(0, ExtArrB), ExtPredicate(3, ExtQualMerging), ExtVector(0, ExtArrB)}, + 0x04158de0}, } { in := extInstructionMeta(t, tt.mnem, tt.form, tt.qual, tt.arr) got, err := in.Encode(tt.ops) diff --git a/asm/arm64_ext_asm_test.go b/asm/arm64_ext_asm_test.go index b133ef2..f928e4a 100644 --- a/asm/arm64_ext_asm_test.go +++ b/asm/arm64_ext_asm_test.go @@ -783,6 +783,17 @@ func TestArm64AssembleExtensionStage4Golden(t *testing.T) { {"ZFMAXQV Z25.S, P3, V5.S4", 0x6496af25}, {"ZFMINNMQV Z25.S, P3, V5.S4", 0x6495af25}, {"ZFMINQV Z25.S, P3, V5.S4", 0x6497af25}, + // The shift-by-vector family. + {"ZASR Z15.B, Z0.B, P3.M, Z0.B", 0x04108de0}, + {"ZASR Z2.D, Z10.D, P3.M, Z10.D", 0x04d08c4a}, + {"ZASR Z7.D, Z6.H, Z13.H", 0x046780cd}, + {"ZASRR Z15.B, Z0.B, P3.M, Z0.B", 0x04148de0}, + {"ZLSL Z2.D, Z10.D, P3.M, Z10.D", 0x04d38c4a}, + {"ZLSL Z7.D, Z6.H, Z13.H", 0x04678ccd}, + {"ZLSLR Z15.B, Z0.B, P3.M, Z0.B", 0x04178de0}, + {"ZLSR Z15.B, Z0.B, P3.M, Z0.B", 0x04118de0}, + {"ZLSR Z7.D, Z6.H, Z13.H", 0x046784cd}, + {"ZLSRR Z15.B, Z0.B, P3.M, Z0.B", 0x04158de0}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") diff --git a/asm/extension_test.go b/asm/extension_test.go index 6b713db..4d45991 100644 --- a/asm/extension_test.go +++ b/asm/extension_test.go @@ -298,6 +298,12 @@ func TestExtensionNamesARM64(t *testing.T) { "ZSMINQV", "ZUMAXQV", "ZUMINQV", + "ZASR", + "ZASRR", + "ZLSL", + "ZLSLR", + "ZLSR", + "ZLSRR", // The stage-three families: the SVE2 crypto group, the predicate // counters and loop terminators with the 32-bit while compares, and // the reductions into a SIMD register. @@ -410,7 +416,7 @@ func TestExtensionNamesARM64(t *testing.T) { if strings.Join(got, ",") != strings.Join(want, ",") { t.Errorf("ExtensionNames(ARM64) = %v, want %v", got, want) } - if n := len(arch.Extensions(arch.ARM64)); n != 405 { - t.Errorf("the family registers %d instructions, want 405", n) + if n := len(arch.Extensions(arch.ARM64)); n != 414 { + t.Errorf("the family registers %d instructions, want 414", n) } }