feat(lint): eleven new rules over directives, data and addressing

Assisted-by: GLM 5.3
This commit is contained in:
2026-10-02 00:40:54 +02:00
parent f405cea5bc
commit 69dcbec8ef
12 changed files with 1431 additions and 19 deletions
+95
View File
@@ -0,0 +1,95 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package lint
import (
"testing"
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
"sourcedock.dev/petrbalvin/gasm-sdk/parser"
)
// TestVEXSSEMixing checks the encoding-mixing advisory: VEX and legacy SSE
// in one kernel pay a transition penalty on every switch, so the finding is
// informational, not a defect.
func TestVEXSSEMixing(t *testing.T) {
// GOROOT's memmove shape: VMOVDQU on Y registers beside MOVOU on X.
diags := lintSrc(t, `
#include "textflag.h"
TEXT ·copy(SB), NOSPLIT, $0
VMOVDQU (SI), Y4
MOVOU (SI), X0
RET
`)
if codes(diags)[CodeVEXSSEMixing] != 1 {
t.Fatalf("want one vex-sse-mixing, got %+v", diags)
}
// One encoding throughout is the recommendation.
diags = lintSrc(t, `
#include "textflag.h"
TEXT ·copy(SB), NOSPLIT, $0
VMOVDQU (SI), Y4
VPSUBD Y1, Y2, Y3
RET
`)
if codes(diags)[CodeVEXSSEMixing] != 0 {
t.Fatalf("pure VEX kernel must not be flagged: %+v", diags)
}
diags = lintSrc(t, `
#include "textflag.h"
TEXT ·copy(SB), NOSPLIT, $0
MOVOU (SI), X0
PADDB X1, X2
RET
`)
if codes(diags)[CodeVEXSSEMixing] != 0 {
t.Fatalf("pure legacy kernel must not be flagged: %+v", diags)
}
// VZEROUPPER is the boundary management, not a side of the mixing: the
// idiomatic epilogue after legacy SSE stays clean.
diags = lintSrc(t, `
#include "textflag.h"
TEXT ·copy(SB), NOSPLIT, $0
MOVOU (SI), X0
PADDB X1, X2
VZEROUPPER
RET
`)
if codes(diags)[CodeVEXSSEMixing] != 0 {
t.Fatalf("VZEROUPPER epilogue must not be flagged: %+v", diags)
}
// A symbol whose name collides with the register spelling (the runtime's
// internal∕cpu·X86 feature flags) is not a register.
diags = lintSrc(t, `
#include "textflag.h"
TEXT ·probe(SB), NOSPLIT, $0
CMPB internal∕cpu·X86+const_offsetX86HasERMS(SB), $1
VMOVDQU (SI), Y4
RET
`)
if codes(diags)[CodeVEXSSEMixing] != 0 {
t.Fatalf("a symbol named X86 must not count as legacy SSE: %+v", diags)
}
// The rule is amd64-only: arm64 V registers are not SSE at all.
diags = lintSrcArch(t, "f_arm64.s", `
#include "textflag.h"
TEXT ·f(SB), NOSPLIT, $0
VLD1 (R0), [V1.B16]
RET
`)
if codes(diags)[CodeVEXSSEMixing] != 0 {
t.Fatalf("arm64 must not be flagged: %+v", diags)
}
// -disable silences the rule.
f, _ := parser.Parse("t_amd64.s", "#include \"textflag.h\"\nTEXT ·copy(SB), NOSPLIT, $0\n\tVMOVDQU (SI), Y4\n\tMOVOU (SI), X0\n\tRET\n")
diags = File(f, Config{Arch: arch.AMD64, Disable: map[string]bool{CodeVEXSSEMixing: true}})
if codes(diags)[CodeVEXSSEMixing] != 0 {
t.Fatalf("disabled rule must stay silent: %+v", diags)
}
}