feat(asm): encode the arm64 SIMD arrangement bits

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 02:36:24 +02:00
1 parent 458cdd2066
commit 9ef14bdb71
5 files changed
+407 -133

No files matched your search

+84
View File
@@ -1894,3 +1894,87 @@ func TestArm64RegOffsetRejections(t *testing.T) {
}
}
}
// TestArm64SimdArrangementBits pins the arrangement bits the first SIMD pass
// got wrong, word-verified against `go tool asm` (Go 1.27, arm64): the FP
// one-bit size field (S=0, D=1 at bit 22), the SSHL/USHL size bits, the long
// extends' immh field, the narrow family's fixed bit 16, the UADDW size bits
// off the narrow side with Q from the spelling, the scalar D forms of the
// bare VADD/VSUB spellings and the INS lane packing.
func TestArm64SimdArrangementBits(t *testing.T) {
got := arm64Words(t,
"\tVFADD V0.S4, V0.S4, V1.S4\n"+
"\tVFADD V0.D2, V0.D2, V1.D2\n"+
"\tVFABS V0.D2, V1.D2\n"+
"\tVSCVTF V1.D2, V2.D2\n"+
"\tVSSHL V1.B8, V2.B8, V3.B8\n"+
"\tVSSHL V1.S4, V2.S4, V3.S4\n"+
"\tVUSHL V1.H4, V2.H4, V3.H4\n"+
"\tVRBIT V24.B8, V24.B8\n"+
"\tVUXTL V30.B8, V30.H8\n"+
"\tVUXTL V29.S2, V2.D2\n"+
"\tVUXTL2 V30.H8, V30.S4\n"+
"\tVXTN V1.H8, V2.B8\n"+
"\tVFCVTN V1.D2, V2.S2\n"+
"\tVFCVTL V1.S2, V2.D2\n"+
"\tVUADDW V13.H4, V10.S4, V11.S4\n"+
"\tVUADDW2 V13.H8, V20.S4, V30.S4\n"+
"\tVADD V1, V2, V3\n"+
"\tVSUB V12, V20, V30\n"+
"\tVMOV V12.S[2], V12.S[3]\n"+
"\tVMOV V12.H[3], V12.H[5]\n")
want := []uint32{
0x4e20d401, // VFADD V0.4S: FP size field clear for S
0x4e60d401, // VFADD V0.2D: FP size bit 22, not bit 23
0x4ee0f801, // VFABS V1.2D: one-bit FP size
0x4e61d822, // VSCVTF V2.2D: bit 22, the Q-only mask must not strip it
0x0e214443, // VSSHL V3.8B: base without the pre-set size and Q bits
0x4ea14443, // VSSHL V3.4S: integer size bits from the arrangement
0x2e614443, // VUSHL V3.4H: U bit plus the H size
0x2e605b18, // VRBIT V24.8B: no Q bit in the base
0x2f08a7de, // VUXTL: immh = 1 at bit 19 for the byte extend
0x2f20a7a2, // VUXTL: immh = 4 at bit 21 for the word extend
0x6f10a7de, // VUXTL2: immh = 2 plus the 128-bit flag
0x0e212822, // VXTN: fixed bit 16 in the base
0x0e616822, // VFCVTN: bits 16 and 22, Q rides the spelling
0x0e617822, // VFCVTL: bit 22, Q rides the spelling
0x2e6d114b, // VUADDW: size bits off the narrow side, Q clear
0x6e6d129e, // VUADDW2: size off the narrow side, Q from the spelling
0x5ee18443, // VADD scalar D form for the bare spelling
0x7eec869e, // VSUB scalar D form for the bare spelling
0x6e1c458c, // INS: imm4 = 2<<2 for the word source lane 2
0x6e16358c, // INS: imm4 = 3<<1 for the halfword source lane 3
0xd65f03c0, // RET
}
if len(got) != len(want) {
t.Fatalf("word count = %d, want %d", len(got), len(want))
}
for i := range want {
if got[i] != want[i] {
t.Errorf("word %d = %08x, want %08x", i, got[i], want[i])
}
}
}
// TestArm64SimdArrangementRejections pins the arrangements the toolchain
// refuses on the FP SIMD rows and the element-to-element moves: the
// half-width FP spellings, the Q1 spelling on the integer shifts, and the
// mixed element letters of INS.
func TestArm64SimdArrangementRejections(t *testing.T) {
for _, src := range []string{
"\tVFADD\tV1.H4, V2.H4, V3.H4\n",
"\tVFADD\tV1.H8, V2.H8, V3.H8\n",
"\tVFABS\tV1.H4, V2.H4\n",
"\tVSCVTF\tV1.H4, V2.H4\n",
"\tVSSHL\tV1.Q1, V2.Q1, V3.Q1\n",
"\tVMOV\tV12.S[0], V12.D[1]\n",
} {
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))
}
}
}