From 0760cc91af04b0675668a3abb91f22d195fdc40b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 7 Oct 2026 19:29:39 +0200 Subject: [PATCH] feat(arch): the SVE2 three-source and bitwise combine families Assisted-by: GLM 5.3 Flash --- arch/arm64_ext.go | 86 ++++++++++++++++++++++++++++++++++++++- arch/arm64_ext_test.go | 34 ++++++++++++++++ asm/arm64_ext_asm_test.go | 12 ++++++ asm/extension_test.go | 15 ++++++- 4 files changed, 144 insertions(+), 3 deletions(-) diff --git a/arch/arm64_ext.go b/arch/arm64_ext.go index e6dfff0..7216041 100644 --- a/arch/arm64_ext.go +++ b/arch/arm64_ext.go @@ -684,6 +684,12 @@ const ( // shifted data shares one narrower arrangement, which feeds the size // field. Operands: Zm.D, Zn.T, Zd.T. ExtFormShiftWide + // ExtFormThreeSource is the SVE2 three-source bitwise form, ZBCAX + // Z23.D, Z13.D, Z21.D, Z21.D: two sources combine with the destination + // under the class's own operation, locked to quadwords. Operands: + // Za.D, Zm.D, Zdn.D, Zdn.D, the last two naming the same register, + // which the encoding carries once in the destination slot. + ExtFormThreeSource ) // Arity returns the operand count the form takes. @@ -695,7 +701,7 @@ func (f ExtForm) Arity() int { ExtFormZUnary, ExtFormZGov, ExtFormVectorsZm, ExtFormZImmediateSource: return 3 case ExtFormPredicateLogical, ExtFormPredicateSelect, ExtFormPredicateLogicalDest, - ExtFormZPredicatedSource, ExtFormZSelect: + ExtFormZPredicatedSource, ExtFormZSelect, ExtFormThreeSource: return 4 case ExtFormImmediate, ExtFormSignedImmediate, ExtFormPredicateFFRRead, ExtFormPredicateMove, ExtFormPredicateUnpack, ExtFormPredicateTest, @@ -794,6 +800,8 @@ func (f ExtForm) Kinds() []ExtOperandKind { return []ExtOperandKind{ExtZReg, ExtPReg, ExtSveMem} case ExtFormNarrow, ExtFormShiftWide: return []ExtOperandKind{ExtZReg, ExtZReg, ExtZReg} + case ExtFormThreeSource: + return []ExtOperandKind{ExtZReg, ExtZReg, ExtZReg, ExtZReg} case ExtFormQuadReduce: return []ExtOperandKind{ExtZReg, ExtPReg, ExtVReg} case ExtFormNone: @@ -924,6 +932,8 @@ func (f ExtForm) String() string { return "quadword reduction" case ExtFormShiftWide: return "wide shift" + case ExtFormThreeSource: + return "three-source bitwise" case ExtFormNone: return "no operands" default: @@ -1182,6 +1192,8 @@ func (in ExtInstr) Encode(ops []ExtOperand) ([]byte, error) { return in.encodeQuadReduce(ops) case ExtFormShiftWide: return in.encodeShiftWide(ops) + case ExtFormThreeSource: + return in.encodeThreeSource(ops) case ExtFormNone: if len(ops) != 0 { return nil, fmt.Errorf("%s: the %s form takes no operands, got %d", @@ -2674,6 +2686,35 @@ func (in ExtInstr) encodeShiftWide(ops []ExtOperand) ([]byte, error) { return extWordLE(word), nil } +// encodeThreeSource fills the SVE2 three-source bitwise form: Za.D, Zm.D, +// Zdn.D, Zdn.D, the destination read back, the whole class locked to +// quadwords. Operands 3 and 4 name the same register and the encoding +// carries it once, in the destination slot. +func (in ExtInstr) encodeThreeSource(ops []ExtOperand) ([]byte, error) { + za, zm, zdn := ops[0], ops[1], ops[2] + if err := in.zVector(za, 1); err != nil { + return nil, err + } + if err := in.zVector(zm, 2); err != nil { + return nil, err + } + if err := in.zVector(zdn, 3); err != nil { + return nil, err + } + if err := in.zVector(ops[3], 4); err != nil { + return nil, err + } + if ops[3].Reg != zdn.Reg || ops[3].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, ops[3].Reg) + } + word := in.Word + word = extSet(word, extFieldRn, uint32(za.Reg)) + word = extSet(word, extFieldRm, uint32(zm.Reg)) + word = extSet(word, extFieldRd, uint32(zdn.Reg)) + 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. @@ -3342,6 +3383,49 @@ var arm64Extensions = []ExtInstr{ Word: 0x04158000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, Ref: "ARM DDI 0487J SVE2: ZLSRR; inst_gen.go + arm64sveenc.s"}, + // --- the SVE2 three-source and bitwise combine families ------------------- + // + // The three-source bitwise operations (EOR3, BCAX, BSL and its + // complements, NBSL) locked to quadwords, and the two-vector combines + // over a free arrangement (BDEP, BEXT, BGRP, EORBT, EORTB). 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 family. The XAR rotate rides the + // tsz:imm3 immediate class, unwired with the other shift immediates. + {Name: "ZBCAX", Summary: "Broadcast-complement and exclusive-or three sources, .D locked", + Word: 0x04603800, Form: ExtFormThreeSource, Arr: ExtArrD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZBCAX; inst_gen.go + arm64sveenc.s"}, + {Name: "ZBDEP", Summary: "Deposite bit particles from adjacent pairs", + Word: 0x4500b400, Form: ExtFormVectorsZm, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZBDEP; inst_gen.go + arm64sveenc.s"}, + {Name: "ZBEXT", Summary: "Extract bit particles from adjacent pairs", + Word: 0x4500b000, Form: ExtFormVectorsZm, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZBEXT; inst_gen.go + arm64sveenc.s"}, + {Name: "ZBGRP", Summary: "Group bit particles from adjacent pairs", + Word: 0x4500b800, Form: ExtFormVectorsZm, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZBGRP; inst_gen.go + arm64sveenc.s"}, + {Name: "ZBSL", Summary: "Bitwise select three sources, .D locked", + Word: 0x04203c00, Form: ExtFormThreeSource, Arr: ExtArrD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZBSL; inst_gen.go + arm64sveenc.s"}, + {Name: "ZBSL1N", Summary: "Bitwise select with first source inverted, .D locked", + Word: 0x04603c00, Form: ExtFormThreeSource, Arr: ExtArrD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZBSL1N; inst_gen.go + arm64sveenc.s"}, + {Name: "ZBSL2N", Summary: "Bitwise select with second source inverted, .D locked", + Word: 0x04a03c00, Form: ExtFormThreeSource, Arr: ExtArrD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZBSL2N; inst_gen.go + arm64sveenc.s"}, + {Name: "ZEOR3", Summary: "Exclusive-or three sources, .D locked", + Word: 0x04203800, Form: ExtFormThreeSource, Arr: ExtArrD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZEOR3; inst_gen.go + arm64sveenc.s"}, + {Name: "ZEORBT", Summary: "Exclusive-or bottom of one source with top of the other", + Word: 0x45009000, Form: ExtFormVectorsZm, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZEORBT; inst_gen.go + arm64sveenc.s"}, + {Name: "ZEORTB", Summary: "Exclusive-or top of one source with bottom of the other", + Word: 0x45009400, Form: ExtFormVectorsZm, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZEORTB; inst_gen.go + arm64sveenc.s"}, + {Name: "ZNBSL", Summary: "Bitwise select with negated result, .D locked", + Word: 0x04e03c00, Form: ExtFormThreeSource, Arr: ExtArrD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZNBSL; 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 f3a6cef..f1a03c5 100644 --- a/arch/arm64_ext_test.go +++ b/arch/arm64_ext_test.go @@ -1471,6 +1471,40 @@ func TestArm64ExtStage4Golden(t *testing.T) { {"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}, + // The three-source and bitwise combine families. + {"ZBCAX Z23.D, Z13.D, Z21.D, Z21.D", "ZBCAX", ExtFormThreeSource, ExtQualNone, ExtArrD, + []ExtOperand{ExtVector(23, ExtArrD), ExtVector(13, ExtArrD), ExtVector(21, ExtArrD), ExtVector(21, ExtArrD)}, + 0x046d3af5}, + {"ZEOR3 Z23.D, Z13.D, Z21.D, Z21.D", "ZEOR3", ExtFormThreeSource, ExtQualNone, ExtArrD, + []ExtOperand{ExtVector(23, ExtArrD), ExtVector(13, ExtArrD), ExtVector(21, ExtArrD), ExtVector(21, ExtArrD)}, + 0x042d3af5}, + {"ZBSL Z23.D, Z13.D, Z21.D, Z21.D", "ZBSL", ExtFormThreeSource, ExtQualNone, ExtArrD, + []ExtOperand{ExtVector(23, ExtArrD), ExtVector(13, ExtArrD), ExtVector(21, ExtArrD), ExtVector(21, ExtArrD)}, + 0x042d3ef5}, + {"ZBSL1N Z23.D, Z13.D, Z21.D, Z21.D", "ZBSL1N", ExtFormThreeSource, ExtQualNone, ExtArrD, + []ExtOperand{ExtVector(23, ExtArrD), ExtVector(13, ExtArrD), ExtVector(21, ExtArrD), ExtVector(21, ExtArrD)}, + 0x046d3ef5}, + {"ZBSL2N Z23.D, Z13.D, Z21.D, Z21.D", "ZBSL2N", ExtFormThreeSource, ExtQualNone, ExtArrD, + []ExtOperand{ExtVector(23, ExtArrD), ExtVector(13, ExtArrD), ExtVector(21, ExtArrD), ExtVector(21, ExtArrD)}, + 0x04ad3ef5}, + {"ZNBSL Z23.D, Z13.D, Z21.D, Z21.D", "ZNBSL", ExtFormThreeSource, ExtQualNone, ExtArrD, + []ExtOperand{ExtVector(23, ExtArrD), ExtVector(13, ExtArrD), ExtVector(21, ExtArrD), ExtVector(21, ExtArrD)}, + 0x04ed3ef5}, + {"ZBDEP Z7.D, Z23.D, Z13.D", "ZBDEP", ExtFormVectorsZm, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(23, ExtArrD), ExtVector(13, ExtArrD)}, + 0x45c7b6ed}, + {"ZBEXT Z7.D, Z23.D, Z13.D", "ZBEXT", ExtFormVectorsZm, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(23, ExtArrD), ExtVector(13, ExtArrD)}, + 0x45c7b2ed}, + {"ZBGRP Z7.D, Z23.D, Z13.D", "ZBGRP", ExtFormVectorsZm, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(23, ExtArrD), ExtVector(13, ExtArrD)}, + 0x45c7baed}, + {"ZEORBT Z7.D, Z23.D, Z13.D", "ZEORBT", ExtFormVectorsZm, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(23, ExtArrD), ExtVector(13, ExtArrD)}, + 0x45c792ed}, + {"ZEORTB Z7.D, Z23.D, Z13.D", "ZEORTB", ExtFormVectorsZm, ExtQualNone, ExtArrNone, + []ExtOperand{ExtVector(7, ExtArrD), ExtVector(23, ExtArrD), ExtVector(13, ExtArrD)}, + 0x45c796ed}, } { 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 f928e4a..f533129 100644 --- a/asm/arm64_ext_asm_test.go +++ b/asm/arm64_ext_asm_test.go @@ -794,6 +794,18 @@ func TestArm64AssembleExtensionStage4Golden(t *testing.T) { {"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}, + // The three-source and bitwise combine families. + {"ZBCAX Z23.D, Z13.D, Z21.D, Z21.D", 0x046d3af5}, + {"ZEOR3 Z23.D, Z13.D, Z21.D, Z21.D", 0x042d3af5}, + {"ZBSL Z23.D, Z13.D, Z21.D, Z21.D", 0x042d3ef5}, + {"ZBSL1N Z23.D, Z13.D, Z21.D, Z21.D", 0x046d3ef5}, + {"ZBSL2N Z23.D, Z13.D, Z21.D, Z21.D", 0x04ad3ef5}, + {"ZNBSL Z23.D, Z13.D, Z21.D, Z21.D", 0x04ed3ef5}, + {"ZBDEP Z7.D, Z23.D, Z13.D", 0x45c7b6ed}, + {"ZBEXT Z7.D, Z23.D, Z13.D", 0x45c7b2ed}, + {"ZBGRP Z7.D, Z23.D, Z13.D", 0x45c7baed}, + {"ZEORBT Z7.D, Z23.D, Z13.D", 0x45c792ed}, + {"ZEORTB Z7.D, Z23.D, Z13.D", 0x45c796ed}, } 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 4d45991..75ac6a2 100644 --- a/asm/extension_test.go +++ b/asm/extension_test.go @@ -304,6 +304,17 @@ func TestExtensionNamesARM64(t *testing.T) { "ZLSLR", "ZLSR", "ZLSRR", + "ZBCAX", + "ZBDEP", + "ZBEXT", + "ZBGRP", + "ZBSL", + "ZBSL1N", + "ZBSL2N", + "ZEOR3", + "ZEORBT", + "ZEORTB", + "ZNBSL", // 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. @@ -416,7 +427,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 != 414 { - t.Errorf("the family registers %d instructions, want 414", n) + if n := len(arch.Extensions(arch.ARM64)); n != 425 { + t.Errorf("the family registers %d instructions, want 425", n) } }