From aeb109a64c231d01c1ebddbe84744bae3ae33aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 7 Oct 2026 20:08:45 +0200 Subject: [PATCH] feat(arch): the vector-length arithmetic pseudo group Assisted-by: GLM 5.3 Flash --- arch/arm64_ext.go | 82 ++++++++++++++++++++++++++++++++++++++++++- asm/extension_test.go | 7 ++-- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/arch/arm64_ext.go b/arch/arm64_ext.go index 5311ee0..bf88ed1 100644 --- a/arch/arm64_ext.go +++ b/arch/arm64_ext.go @@ -406,6 +406,7 @@ var ( extFieldPm = ExtField{16, 3} // predicate source P0-P7 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 ) // ExtForm enumerates the operand shapes the extension layer defines, in Plan @@ -701,6 +702,14 @@ const ( // Zm.H, Zn.H, Pg/M, Zda.H. Fields: Zm at bits 16..20, Zn in the // first-source slot, Pg narrow, Zda in the destination slot. ExtFormMulPred + // ExtFormVLArith is the vector-length arithmetic, ADDVL $9, R11, RSP: + // a signed imm6 multiple of the vector length added to a general + // register, either slot spelled RSP. Operands: imm, Rn|SP, Rd|SP. + // Fields: the imm6 at bits 10..5, Rn at bits 20..16, Rd at bits 4..0. + ExtFormVLArith + // ExtFormVLRead is the vector-length read, RDVL $1, R27. Operands: + // imm, Rd (R0-R30). Fields: the imm6 at bits 10..5, Rd at bits 4..0. + ExtFormVLRead // ExtFormCompare is the SVE2 compare to a predicate destination, // ZCMPEQ Z0.H, Z2.H, P0.Z, P14.H: the comparison writes the zeroing // predicate P0-P7 spelled with its zeroing qualifier. Operands: Zm.T, @@ -760,7 +769,7 @@ func (f ExtForm) Arity() int { return 1 case ExtFormZReadBack, ExtFormCountP, ExtFormCountPnW, ExtFormReduce, ExtFormZLastReg: return 3 - case ExtFormZSameReg, ExtFormCountPn, ExtFormCTerm: + case ExtFormZSameReg, ExtFormCountPn, ExtFormCTerm, ExtFormVLRead: return 2 case ExtFormReduceReadBack: return 4 @@ -838,6 +847,10 @@ func (f ExtForm) Kinds() []ExtOperandKind { return []ExtOperandKind{ExtZReg, ExtPReg, ExtSveMem} case ExtFormNarrow, ExtFormShiftWide, ExtFormLongMul: return []ExtOperandKind{ExtZReg, ExtZReg, ExtZReg} + case ExtFormVLArith: + return []ExtOperandKind{ExtImm, ExtGReg, ExtGReg} + case ExtFormVLRead: + return []ExtOperandKind{ExtImm, ExtGReg} case ExtFormConvert: return []ExtOperandKind{ExtZReg, ExtPReg, ExtZReg} case ExtFormThreeSource, ExtFormMulPred, ExtFormCompare, ExtFormCompareWide: @@ -984,6 +997,10 @@ func (f ExtForm) String() string { return "compare to a predicate" case ExtFormCompareWide: return "wide compare to a predicate" + case ExtFormVLArith: + return "vector-length arithmetic" + case ExtFormVLRead: + return "vector-length read" case ExtFormNone: return "no operands" default: @@ -1254,6 +1271,10 @@ func (in ExtInstr) Encode(ops []ExtOperand) ([]byte, error) { return in.encodeCompare(ops) case ExtFormCompareWide: return in.encodeCompare(ops) + case ExtFormVLArith: + return in.encodeVLArith(ops) + case ExtFormVLRead: + return in.encodeVLRead(ops) case ExtFormNone: if len(ops) != 0 { return nil, fmt.Errorf("%s: the %s form takes no operands, got %d", @@ -2853,6 +2874,48 @@ func (in ExtInstr) encodeMulPred(ops []ExtOperand) ([]byte, error) { // encodeLongMul fills the SVE2 BFloat16 lengthened multiply: Za.H, Zn.H, // Zm.S, the two halfword vectors under the row's Arr and the wide source // one step above it. +// encodeVLArith fills the vector-length arithmetic: imm, Rn|SP, Rd|SP. +// The signed imm6 counts multiples of the vector length; the register +// slots spell RSP, never a plain R31. +func (in ExtInstr) encodeVLArith(ops []ExtOperand) ([]byte, error) { + imm, rn, rd := ops[0], ops[1], ops[2] + if imm.Kind != ExtImm { + return nil, fmt.Errorf("%s: operand 1 wants an immediate, got %s", in.Name, imm.Kind) + } + if imm.Imm < -32 || imm.Imm > 31 { + return nil, fmt.Errorf("%s: immediate %d is outside the signed 6-bit range -32..31", in.Name, imm.Imm) + } + if err := in.generalOperand(rn, 2, 2); err != nil { + return nil, err + } + if err := in.generalOperand(rd, 3, 2); err != nil { + return nil, err + } + word := in.Word + word = extSet(word, extFieldImm6, uint32(imm.Imm)) + word = extSet(word, extFieldRm, uint32(rn.Reg)) + word = extSet(word, extFieldRd, uint32(rd.Reg)) + return extWordLE(word), nil +} + +// encodeVLRead fills the vector-length read: imm, Rd (R0-R30). +func (in ExtInstr) encodeVLRead(ops []ExtOperand) ([]byte, error) { + imm, rd := ops[0], ops[1] + if imm.Kind != ExtImm { + return nil, fmt.Errorf("%s: operand 1 wants an immediate, got %s", in.Name, imm.Kind) + } + if imm.Imm < -32 || imm.Imm > 31 { + return nil, fmt.Errorf("%s: immediate %d is outside the signed 6-bit range -32..31", in.Name, imm.Imm) + } + if err := in.generalOperand(rd, 2, 0); err != nil { + return nil, err + } + word := in.Word + word = extSet(word, extFieldImm6, uint32(imm.Imm)) + word = extSet(word, extFieldRd, uint32(rd.Reg)) + return extWordLE(word), nil +} + // encodeCompare fills the SVE2 compare forms, the shared-arrangement and // the wide shape alike: Zm, Zn, Pg/Z, Pd. The comparison writes the // zeroing predicate P0-P7; under the wide shape the magnitudes ride the @@ -3797,6 +3860,23 @@ var arm64Extensions = []ExtInstr{ {Name: "ZCMPNE", Summary: "Compare not equal against wide magnitudes, writing a zeroing predicate", Word: 0x24002010, Form: ExtFormCompareWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, Ref: "ARM DDI 0487J SVE2: ZCMPNE; inst_gen.go + arm64sveenc.s"}, + + // --- the vector-length arithmetic pseudo group ---------------------------- + // + // ADDVL, ADDPL and RDVL over the signed imm6 multiple of the vector + // length or the parameter register. 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. + {Name: "ADDPL", Summary: "Add a signed multiple of the parameter register count to a register", + Word: 0x04605000, Form: ExtFormVLArith, Feature: ExtFeatureSVE, + Ref: "ARM DDI 0487J: ADDPL; inst_gen.go + arm64sveenc.s"}, + {Name: "ADDVL", Summary: "Add a signed multiple of the vector length to a register", + Word: 0x04205000, Form: ExtFormVLArith, Feature: ExtFeatureSVE, + Ref: "ARM DDI 0487J: ADDVL; inst_gen.go + arm64sveenc.s"}, + {Name: "RDVL", Summary: "Read the vector length multiplied by a signed immediate", + Word: 0x04bf5000, Form: ExtFormVLRead, Feature: ExtFeatureSVE, + Ref: "ARM DDI 0487J: RDVL; inst_gen.go + arm64sveenc.s"}, // --- the SVE multiple-structure loads and stores -------------------------- // // The two-, three- and four-register list loads and stores over the diff --git a/asm/extension_test.go b/asm/extension_test.go index 2008cbe..aa9a3cf 100644 --- a/asm/extension_test.go +++ b/asm/extension_test.go @@ -346,6 +346,9 @@ func TestExtensionNamesARM64(t *testing.T) { "ZCMPHI", "ZCMPHS", "ZCMPNE", + "ADDPL", + "ADDVL", + "RDVL", "ZLD2B", "ZLD2D", "ZLD2H", @@ -488,7 +491,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 != 497 { - t.Errorf("the family registers %d instructions, want 497", n) + if n := len(arch.Extensions(arch.ARM64)); n != 500 { + t.Errorf("the family registers %d instructions, want 500", n) } }