feat(lint): eleven new rules over directives, data and addressing
Assisted-by: GLM 5.3
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user