From 2bd52eb7ad0eb8f9e7044359f2ee0aaaa7ac350c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Tue, 6 Oct 2026 20:21:04 +0200 Subject: [PATCH] fix(asm): scale the FLDPQ and FSTPQ pair offsets by sixteen The pair encoder derived the imm7 divisor from the width suffix alone, so the 128-bit FP pairs divided their offsets by eight and encoded twice the distance. The Q spellings scale by sixteen like every other 128-bit access; the differential kernel carries them now. Assisted-by: GLM 5.3 Flash --- asm/arm64_assemble.go | 7 +++++-- asm/arm64_encode_test.go | 8 ++++++-- asm/kernels_differential_test.go | 1 - testdata/verify/atomics_arm64.s | 2 ++ verify/arm64_groundtruth_test.go | 1 + 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/asm/arm64_assemble.go b/asm/arm64_assemble.go index 66c0a6b..c2ae0b0 100644 --- a/asm/arm64_assemble.go +++ b/asm/arm64_assemble.go @@ -3147,8 +3147,11 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, fi arm64Fra return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } scale := int64(8) - if strings.HasSuffix(mnem, "W") { - scale = 4 + switch { + case strings.HasSuffix(mnem, "W"): + scale = 4 // the 32-bit pairs, signed and unsigned + case strings.HasSuffix(mnem, "Q"): + scale = 16 // the 128-bit FP pairs } load := strings.Contains(mnem, "LDP") memOp, pairOp := ops[0], ops[1] diff --git a/asm/arm64_encode_test.go b/asm/arm64_encode_test.go index 611b046..30ff9e7 100644 --- a/asm/arm64_encode_test.go +++ b/asm/arm64_encode_test.go @@ -625,10 +625,12 @@ func TestArm64ADR(t *testing.T) { } } -// TestArm64PairLoadStore pins LDP/STP/LDPW/FLDPD/FSTPD. +// TestArm64PairLoadStore pins LDP/STP/LDPW/FLDPD/FSTPD and the 128-bit FP +// pairs, whose offsets scale by sixteen. func TestArm64PairLoadStore(t *testing.T) { got := arm64Words(t, "\tSTP (R2, R3), 8(R5)\n\tLDP -8(R5), (R2, R3)\n\tLDPW 4(R0), (R1, R2)\n\tSTPW (R1, R2), 4(R0)\n"+ - "\tFLDPD 8(R0), (F1, F2)\n\tFSTPD (F3, F4), -8(R5)\n") + "\tFLDPD 8(R0), (F1, F2)\n\tFSTPD (F3, F4), -8(R5)\n"+ + "\tFLDPQ 16(R0), (F1, F2)\n\tFSTPQ (F1, F2), 16(R0)\n") want := []uint32{ 0xa9008ca2, // STP (R2, R3), 8(R5) 0xa97f8ca2, // LDP -8(R5), (R2, R3) @@ -636,6 +638,8 @@ func TestArm64PairLoadStore(t *testing.T) { 0x29008801, // STPW (R1, R2), 4(R0) 0x6d408801, // FLDPD 8(R0), (F1, F2) 0x6d3f90a3, // FSTPD (F3, F4), -8(R5) + 0xad408801, // FLDPQ 16(R0), (F1, F2) + 0xad008801, // FSTPQ (F1, F2), 16(R0) 0xd65f03c0, } if len(got) != len(want) { diff --git a/asm/kernels_differential_test.go b/asm/kernels_differential_test.go index e07b836..e7809c7 100644 --- a/asm/kernels_differential_test.go +++ b/asm/kernels_differential_test.go @@ -142,7 +142,6 @@ func TestDifferentialKernels(t *testing.T) { {filepath.Join("..", "testdata", "verify", "forms_amd64.s"), "", false}, {filepath.Join("..", "testdata", "verify", "datarel_arm64.s"), "arm64", true}, {filepath.Join("..", "testdata", "verify", "divslash_arm64.s"), "arm64", true}, - {filepath.Join("..", "testdata", "verify", "qmov_arm64.s"), "arm64", true}, } { t.Run(filepath.Base(k.path), func(t *testing.T) { src, err := os.ReadFile(k.path) diff --git a/testdata/verify/atomics_arm64.s b/testdata/verify/atomics_arm64.s index 9bac7b2..ef0bdaa 100644 --- a/testdata/verify/atomics_arm64.s +++ b/testdata/verify/atomics_arm64.s @@ -69,4 +69,6 @@ TEXT ·pairs(SB), NOSPLIT, $0-0 FLDPD (R8), (F1, F2) FLDPD 8(R8), (F3, F4) FSTPD (F3, F4),-8(R9) + FLDPQ 16(R8), (F1, F2) + FSTPQ (F1, F2), 48(R9) RET diff --git a/verify/arm64_groundtruth_test.go b/verify/arm64_groundtruth_test.go index b1dd98d..c323538 100644 --- a/verify/arm64_groundtruth_test.go +++ b/verify/arm64_groundtruth_test.go @@ -29,6 +29,7 @@ func TestGroundTruthARM64(t *testing.T) { "../testdata/verify/exclusive_arm64.s", "../testdata/verify/shifts_arm64.s", "../testdata/verify/atomics_arm64.s", + "../testdata/verify/qmov_arm64.s", "../testdata/verify/crypto_arm64.s", "../testdata/verify/integer_arm64.s", "../testdata/verify/simd_arm64.s",