From ee2c6d51b326716c4ab7cf13aeccb8ba6611001c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 7 Oct 2026 18:57:50 +0200 Subject: [PATCH] fix(arch): keep Zdn out of the class bits of the predicated Z-alias source Assisted-by: GLM 5.3 Flash --- arch/arm64_ext.go | 8 +++++--- arch/arm64_ext_test.go | 9 +++++++++ asm/arm64_ext_asm_test.go | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/arch/arm64_ext.go b/arch/arm64_ext.go index 2578c00..3a4b0d9 100644 --- a/arch/arm64_ext.go +++ b/arch/arm64_ext.go @@ -573,8 +573,9 @@ const ( // ExtFormZPredicatedSource is the Z-alias binary predicated form, // ZADD Z15.B, Z0.B, P3.M, Z0.B: the destination's source copy is // spelled and must equal the destination. Operands: Zm.T, Zdn.T, - // Pg/M, Zdn.T. Fields: Zm in the second-source slot, Zdn in the - // first-source slot, Pg narrow, Zdn in the destination. + // Pg/M, Zdn.T. Fields: Zm in the first-source slot at bits 9..5, Pg + // narrow, Zdn in the destination slot; bits 16..20 are class bits no + // operand touches. ExtFormZPredicatedSource // ExtFormZImmediateSource is the Z-alias immediate form, ZADD $6, // Z7.D, Z7.D: the destination's source copy is spelled and must equal @@ -1787,6 +1788,8 @@ func (in ExtInstr) encodeZUnary(ops []ExtOperand) ([]byte, error) { // encodeZPredicatedSource fills the Z-alias binary predicated form: Zm.T, // Zdn.T, Pg/M, Zdn.T, the source copy spelled and equal to the destination. +// Zm rides the first-source slot at bits 9..5 and Zdn the destination slot; +// bits 16..20 carry the class opcode and stay clear of operands. func (in ExtInstr) encodeZPredicatedSource(ops []ExtOperand) ([]byte, error) { zm, zdn, pg := ops[0], ops[1], ops[2] if err := in.zVector(zm, 1); err != nil { @@ -1819,7 +1822,6 @@ func (in ExtInstr) encodeZPredicatedSource(ops []ExtOperand) ([]byte, error) { } word := in.Word word = extSet(word, extFieldRn, uint32(zm.Reg)) - word = extSet(word, extFieldRm, uint32(zdn.Reg)) word = extSet(word, extFieldPgN, uint32(pg.Reg)) word = extSet(word, extFieldRd, uint32(zdn.Reg)) if size != 0 { diff --git a/arch/arm64_ext_test.go b/arch/arm64_ext_test.go index 17fb7bc..f603916 100644 --- a/arch/arm64_ext_test.go +++ b/arch/arm64_ext_test.go @@ -880,6 +880,15 @@ func TestArm64ExtZGolden(t *testing.T) { {"ZMOVPRFX Z7.D, P4.Z, Z21.D", "ZMOVPRFX", ExtFormZUnary, ExtQualAny, ExtArrNone, []ExtOperand{ExtVector(7, ExtArrD), ExtPredicate(4, ExtQualZeroing), ExtVector(21, ExtArrD)}, 0x04d030f5}, + // The destination read back spells twice and encodes once, in the + // destination slot alone: bits 16..20 are class bits, so a non-zero + // Zdn must not reach them. + {"ZADD Z15.B, Z5.B, P3.M, Z5.B", "ZADD", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(15, ExtArrB), ExtVector(5, ExtArrB), ExtPredicate(3, ExtQualMerging), ExtVector(5, ExtArrB)}, + 0x04000de5}, + {"ZSUB Z20.D, Z13.D, P6.M, Z13.D", "ZSUB", ExtFormZPredicatedSource, ExtQualMerging, ExtArrNone, + []ExtOperand{ExtVector(20, ExtArrD), ExtVector(13, ExtArrD), ExtPredicate(6, ExtQualMerging), ExtVector(13, ExtArrD)}, + 0x04c11a8d}, {"ZMOVPRFX Z11, Z6", "ZMOVPRFX", ExtFormZMoveBare, 0, ExtArrNone, []ExtOperand{{Kind: ExtZReg, Reg: 11}, {Kind: ExtZReg, Reg: 6}}, 0x0420bd66}, diff --git a/asm/arm64_ext_asm_test.go b/asm/arm64_ext_asm_test.go index 4f4ddf2..4f53f36 100644 --- a/asm/arm64_ext_asm_test.go +++ b/asm/arm64_ext_asm_test.go @@ -334,6 +334,10 @@ func TestArm64AssembleExtensionStage2Golden(t *testing.T) { }{ {"ZABS Z7.D, P4.M, Z13.D", 0x04d6b0ed}, {"ZABS Z7.D, P4.Z, Z13.D", 0x04c6b0ed}, + // The destination read back encodes once, in the destination slot + // alone: a non-zero Zdn must stay clear of the class bits. + {"ZADD Z15.B, Z5.B, P3.M, Z5.B", 0x04000de5}, + {"ZSUB Z20.D, Z13.D, P6.M, Z13.D", 0x04c11a8d}, {"ZADD Z15.B, Z0.B, P3.M, Z0.B", 0x04000de0}, {"ZADD Z7.D, Z23.D, Z13.D", 0x04e702ed}, {"ZAND Z15.B, Z0.B, P3.M, Z0.B", 0x041a0de0},