diff --git a/arch/arm64_ext.go b/arch/arm64_ext.go index 147d99b..5311ee0 100644 --- a/arch/arm64_ext.go +++ b/arch/arm64_ext.go @@ -701,6 +701,18 @@ 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 + // 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, + // Zn.T, Pg/Z, Pd.T under one shared arrangement, which feeds the size + // field. Fields: Zm at bits 16..20, Zn in the first-source slot, Pg + // narrow, Pd in the four-bit destination slot. + ExtFormCompare + // ExtFormCompareWide is the wide compare, ZCMPEQ Z0.D, Z12.S, P0.Z, + // P14.S: the compared magnitudes ride a double-width vector locked to + // .D while the compared data shares one narrower arrangement. Operands: + // Zm.D, Zn.T, Pg/Z, Pd.T. + ExtFormCompareWide // ExtFormLongMul is the SVE2 BFloat16 lengthened multiply, // ZBFMLALB Z7.H, Z6.H, Z23.S: two halfword vectors multiply into a // word accumulator, the wide source locked to .S. Operands: Za.H, @@ -725,7 +737,8 @@ func (f ExtForm) Arity() int { ExtFormZUnary, ExtFormZGov, ExtFormVectorsZm, ExtFormZImmediateSource: return 3 case ExtFormPredicateLogical, ExtFormPredicateSelect, ExtFormPredicateLogicalDest, - ExtFormZPredicatedSource, ExtFormZSelect, ExtFormThreeSource, ExtFormMulPred: + ExtFormZPredicatedSource, ExtFormZSelect, ExtFormThreeSource, ExtFormMulPred, + ExtFormCompare, ExtFormCompareWide: return 4 case ExtFormImmediate, ExtFormSignedImmediate, ExtFormPredicateFFRRead, ExtFormPredicateMove, ExtFormPredicateUnpack, ExtFormPredicateTest, @@ -827,7 +840,7 @@ func (f ExtForm) Kinds() []ExtOperandKind { return []ExtOperandKind{ExtZReg, ExtZReg, ExtZReg} case ExtFormConvert: return []ExtOperandKind{ExtZReg, ExtPReg, ExtZReg} - case ExtFormThreeSource, ExtFormMulPred: + case ExtFormThreeSource, ExtFormMulPred, ExtFormCompare, ExtFormCompareWide: return []ExtOperandKind{ExtZReg, ExtZReg, ExtPReg, ExtZReg} case ExtFormQuadReduce: return []ExtOperandKind{ExtZReg, ExtPReg, ExtVReg} @@ -967,6 +980,10 @@ func (f ExtForm) String() string { return "multiply-accumulate" case ExtFormLongMul: return "lengthened multiply" + case ExtFormCompare: + return "compare to a predicate" + case ExtFormCompareWide: + return "wide compare to a predicate" case ExtFormNone: return "no operands" default: @@ -1233,6 +1250,10 @@ func (in ExtInstr) Encode(ops []ExtOperand) ([]byte, error) { return in.encodeMulPred(ops) case ExtFormLongMul: return in.encodeLongMul(ops) + case ExtFormCompare: + return in.encodeCompare(ops) + case ExtFormCompareWide: + return in.encodeCompare(ops) case ExtFormNone: if len(ops) != 0 { return nil, fmt.Errorf("%s: the %s form takes no operands, got %d", @@ -1893,9 +1914,9 @@ func (in ExtInstr) encodeZPredicatedSource(ops []ExtOperand) ([]byte, error) { if err := in.zVector(ops[3], 4); err != nil { return nil, err } - if pg.Qual != ExtQualMerging { - return nil, fmt.Errorf("%s: operand 3 wants the merging qualifier, got %q", - in.Name, pg.Qual) + if want := in.PgQual; pg.Qual != want { + return nil, fmt.Errorf("%s: operand 3 wants the %s qualifier, got %q", + in.Name, want.String(), pg.Qual) } if ops[3].Reg != zdn.Reg || ops[3].Arr != zdn.Arr { return nil, fmt.Errorf("%s: operands 2 and 4 are the same register Zdn, got Z%d and Z%d", @@ -2832,6 +2853,62 @@ 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. +// 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 +// double-width vector locked to .D. Fields: Zm at bits 16..20, Zn in the +// first-source slot, Pg narrow, Pd in the four-bit destination slot. +func (in ExtInstr) encodeCompare(ops []ExtOperand) ([]byte, error) { + zm, zn, pg, pd := ops[0], ops[1], ops[2], ops[3] + if err := in.zVector(zm, 1); err != nil { + return nil, err + } + if in.Form == ExtFormCompareWide { + 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 in.Form == ExtFormCompare && zn.Arr != zm.Arr { + return nil, fmt.Errorf("%s: operands 1 and 2 carry arrangements %s and %s, they must match", in.Name, zm.Arr, zn.Arr) + } + if in.Form == ExtFormCompareWide { + if zn.Arr == ExtArrD { + return nil, fmt.Errorf("%s: operand 2 carries arrangement .D, want .B, .H or .S", in.Name) + } + } + if err := in.predicateOperand(pg, 3, 0, 7, ExtArrNone); err != nil { + return nil, err + } + if pg.Qual != ExtQualZeroing { + return nil, fmt.Errorf("%s: operand 3 wants the zeroing qualifier, got %q", in.Name, pg.Qual) + } + if pd.Kind != ExtPReg { + return nil, fmt.Errorf("%s: operand 4 wants a predicate register, got %s", in.Name, pd.Kind) + } + if pd.Reg < 0 || pd.Reg > 15 { + return nil, fmt.Errorf("%s: operand 4 is P%d, outside P0-P15", in.Name, pd.Reg) + } + if in.Form == ExtFormCompare && pd.Arr != zn.Arr { + return nil, fmt.Errorf("%s: operand 4 carries arrangement %s, want %s", in.Name, pd.Arr, zn.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, extFieldPgN, uint32(pg.Reg)) + word = extSet(word, extFieldPd, uint32(pd.Reg)) + if size != 0 { + word = extSet(word, in.Size, size) + } + return extWordLE(word), nil +} + func (in ExtInstr) encodeLongMul(ops []ExtOperand) ([]byte, error) { za, zn, zm := ops[0], ops[1], ops[2] if err := in.zVector(za, 1); err != nil { @@ -3668,6 +3745,58 @@ var arm64Extensions = []ExtInstr{ {Name: "ZBFSCALE", Summary: "Scale BFloat16 halfwords by a power of two, merging", Word: 0x65098000, Form: ExtFormZPredicatedSource, Arr: ExtArrH, PgQual: ExtQualMerging, Feature: ExtFeatureSVE2, Ref: "ARM DDI 0487J SVE2: ZBFSCALE; inst_gen.go + arm64sveenc.s"}, + + // --- the SVE2.1 last-active and compare families -------------------------- + // + // The conditionally-extract-last-active-element vector shape and the + // compares to a predicate destination, in their shared-arrangement and + // wide spellings. The register-destination shapes of CLASTA and + // CLASTB, and the immediate compares, stay unwired. 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 classes. + {Name: "ZCLASTA", Summary: "Conditionally extract the last active element, vector to vector", + Word: 0x05288000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualNone, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCLASTA; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCLASTB", Summary: "Conditionally extract the last active element from the bottom, vector to vector", + Word: 0x05298000, Form: ExtFormZPredicatedSource, Size: extSizeBHSD, PgQual: ExtQualNone, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCLASTB; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPEQ", Summary: "Compare equal, writing a zeroing predicate", + Word: 0x2400a000, Form: ExtFormCompare, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPEQ; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPEQ", Summary: "Compare equal against wide magnitudes, writing a zeroing predicate", + Word: 0x24002000, Form: ExtFormCompareWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPEQ; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPGE", Summary: "Compare greater or equal signed, writing a zeroing predicate", + Word: 0x24008000, Form: ExtFormCompare, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPGE; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPGE", Summary: "Compare greater or equal signed against wide magnitudes, writing a zeroing predicate", + Word: 0x24004000, Form: ExtFormCompareWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPGE; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPGT", Summary: "Compare greater signed, writing a zeroing predicate", + Word: 0x24008010, Form: ExtFormCompare, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPGT; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPGT", Summary: "Compare greater signed against wide magnitudes, writing a zeroing predicate", + Word: 0x24004010, Form: ExtFormCompareWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPGT; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPHI", Summary: "Compare higher unsigned, writing a zeroing predicate", + Word: 0x24000010, Form: ExtFormCompare, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPHI; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPHI", Summary: "Compare higher unsigned against wide magnitudes, writing a zeroing predicate", + Word: 0x2400c010, Form: ExtFormCompareWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPHI; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPHS", Summary: "Compare higher or same unsigned, writing a zeroing predicate", + Word: 0x24000000, Form: ExtFormCompare, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPHS; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPHS", Summary: "Compare higher or same unsigned against wide magnitudes, writing a zeroing predicate", + Word: 0x2400c000, Form: ExtFormCompareWide, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPHS; inst_gen.go + arm64sveenc.s"}, + {Name: "ZCMPNE", Summary: "Compare not equal, writing a zeroing predicate", + Word: 0x2400a010, Form: ExtFormCompare, Size: extSizeBHSD, Feature: ExtFeatureSVE2, + Ref: "ARM DDI 0487J SVE2: ZCMPNE; inst_gen.go + arm64sveenc.s"}, + {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 SVE multiple-structure loads and stores -------------------------- // // The two-, three- and four-register list loads and stores over the diff --git a/arch/arm64_ext_test.go b/arch/arm64_ext_test.go index ebcf62d..48b2b9c 100644 --- a/arch/arm64_ext_test.go +++ b/arch/arm64_ext_test.go @@ -311,7 +311,8 @@ func TestArm64ExtTableIntegrity(t *testing.T) { ExtFormPredicateBreak, ExtFormPredicateBreakZero, ExtFormPredicatePermute, ExtFormPredicateFirst, ExtFormPredicateNext, ExtFormPredicateOne, ExtFormPredicateFFRRead, ExtFormPredicateMove, ExtFormPredicateUnpack, - ExtFormPredicateTest, ExtFormWhile, ExtFormPredicateCounter, ExtFormZUnary: + ExtFormPredicateTest, ExtFormWhile, ExtFormPredicateCounter, ExtFormZUnary, + ExtFormCompare, ExtFormCompareWide: destMask = 0xf } roundOne := in.Form == ExtFormVectors || in.Form == ExtFormPredicated || diff --git a/asm/arm64_ext_asm_test.go b/asm/arm64_ext_asm_test.go index 2dd18f3..bf93922 100644 --- a/asm/arm64_ext_asm_test.go +++ b/asm/arm64_ext_asm_test.go @@ -876,7 +876,7 @@ func TestArm64AssembleExtensionStage4Refusals(t *testing.T) { {"ZADDHNB Z22.D, Z10.D, Z8.H", "want .S"}, {"ZADDHNB Z22.Q, Z10.Q, Z8.D", "want .H, .S or .D"}, {"ZADDHNB Z22.S, Z10.S, Z8", "carries no arrangement suffix"}, - {"ZADDP Z15.B, Z0.B, P3.Z, Z0.B", "merging qualifier"}, + {"ZADDP Z15.B, Z0.B, P3.Z, Z0.B", "/M qualifier"}, {"ZADDPT Z7.S, Z6.S, Z23.S", "want .D"}, {"ZADDQV Z25.S, P9, V5.S4", "outside P0-P7"}, {"ZADDQV Z25.S, P3/M, V5.S4", "takes no qualifier"}, diff --git a/asm/extension_test.go b/asm/extension_test.go index bda6c39..2008cbe 100644 --- a/asm/extension_test.go +++ b/asm/extension_test.go @@ -338,6 +338,14 @@ func TestExtensionNamesARM64(t *testing.T) { "ZBFMLSLT", "ZBFMMLA", "ZBFSCALE", + "ZCLASTA", + "ZCLASTB", + "ZCMPEQ", + "ZCMPGE", + "ZCMPGT", + "ZCMPHI", + "ZCMPHS", + "ZCMPNE", "ZLD2B", "ZLD2D", "ZLD2H", @@ -480,7 +488,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 != 483 { - t.Errorf("the family registers %d instructions, want 483", n) + if n := len(arch.Extensions(arch.ARM64)); n != 497 { + t.Errorf("the family registers %d instructions, want 497", n) } }