fix(asm): enforce the arm64 VLD/VST post-index contract

Assisted-by: GLM 5.3
This commit is contained in:
petrbalvin committed 2026-10-07 13:49:49 +02:00
1 parent 7bc80ccb54
commit 2385bb7069
3 files changed
+83 -12

No files matched your search

+50
View File
@@ -2267,6 +2267,56 @@ func TestArm64NoopVsNop(t *testing.T) {
}
}
// TestArm64VLDSTPostIndexContract pins the structure-load post-index rules
// against `go tool asm` (Go 1.27, arm64): the implicit by-size increment of
// an unspelled or zero offset (both load and replicate forms, the words
// taken from the toolchain's own listing), the register post-index that
// takes a bare register only, and the spelled immediate that must match the
// transferred bytes.
func TestArm64VLDSTPostIndexContract(t *testing.T) {
got := arm64Words(t,
"\tVLD1.P (R3), [V31.H8, V0.H8]\n"+ // implicit 2*16
"\tVLD1R.P (R1), [V9.B8]\n"+ // implicit 1*1
"\tVLD3R.P 6(R15), [V15.H4,V16.H4,V17.H4]\n"+ // 3*2 spelled
"\tVLD1.P (R8)(R20), [V21.B16, V22.B16]\n") // register post-index
want := []uint32{
0x4cdfa47f, // VLD1.P (R3), [V31.H8, V0.H8]
0x0ddfc029, // VLD1R.P (R1), [V9.B8]
0x0ddfe5ef, // VLD3R.P 6(R15), [V15.H4,V16.H4,V17.H4]
0x4cd4a115, // VLD1.P (R8)(R20), [V21.B16, V22.B16]
0xd65f03c0, // RET
}
if len(got) != len(want) {
t.Fatalf("word count = %d, want %d (got %08x)", len(got), len(want), got)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("word %d = %08x, want %08x", i, got[i], want[i])
}
}
reject := []string{
"\tVLD1\t(R8)(R13), [V2.B16]\n", // register index without .P
"\tVLD1\t8(R9), [V2.B16]\n", // offset without .P
"\tVLD1.P\t8(R8)(R13), [V2.B16]\n", // offset beside the register index
"\tVLD1.P\t(R8)(R9.UXTW), [V2.B16]\n", // extended post-index register
"\tVLD1.P\t(R8)(R9<<2), [V2.B16]\n", // shifted post-index register
"\tVST1.P\t[V1.B16], (R8)(R9.UXTW)\n", // extended, store side
"\tVLD1.P\t17(R1), [V2.B16]\n", // 16 bytes transferred
"\tVLD1.P\t-16(R1), [V2.B16]\n", // negative increment
"\tVST1.P\t[V4.S4,V5.S4], 48(R1)\n", // 2*16, not 48
"\tVLD3R.P\t24(R15), [V15.H4,V16.H4,V17.H4]\n", // 3*2, not 24
}
for _, src := range reject {
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n"+src+"\tRET\n")
if len(errs) > 0 {
continue // a parse rejection is a rejection
}
if _, err := AssembleFileARM64(f); err == nil {
t.Errorf("expected rejection for %q, got nil", strings.TrimSpace(src))
}
}
}
// TestArm64FPImmediate pins the FP immediate moves against `go tool asm`
// words: the FMOV (immediate) instruction for the 8-bit encodable values and
// the FMOV-from-ZR move for zero, plus the rejections the toolchain raises