feat(lint): eleven new rules over directives, data and addressing
Assisted-by: GLM 5.3
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package lint
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/ast"
|
||||
)
|
||||
|
||||
// checkVectorEncoding reports a kernel that mixes VEX-encoded instructions
|
||||
// with legacy SSE operating on X registers. Every switch between the two
|
||||
// encodings pays a transition penalty (the upper YMM halves must be saved
|
||||
// and restored by the processor), so a kernel is written in one encoding or
|
||||
// the other; the runtime's memmove mixes them deliberately behind feature
|
||||
// dispatch, which is why the severity is informational.
|
||||
func checkVectorEncoding(t *ast.Text, tab *arch.Table, macros map[string]bool) []Diagnostic {
|
||||
vex, legacy := false, false
|
||||
for _, s := range t.Body {
|
||||
in, ok := s.(*ast.Instr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
upper := strings.ToUpper(in.Mnemonic.Text)
|
||||
if isMacroInvocation(in.Mnemonic.Text, macros) {
|
||||
continue
|
||||
}
|
||||
// VZEROUPPER and VZEROALL are the boundary management: their purpose
|
||||
// is to clean the upper halves before legacy SSE code, so they count
|
||||
// as neither side of the mixing.
|
||||
if strings.HasPrefix(upper, "V") && upper != "VZEROUPPER" && upper != "VZEROALL" {
|
||||
vex = true
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(upper, "V") && hasXMMOperand(in, tab) {
|
||||
legacy = true
|
||||
}
|
||||
}
|
||||
if !vex || !legacy {
|
||||
return nil
|
||||
}
|
||||
return []Diagnostic{{
|
||||
Pos: t.Keyword.Pos,
|
||||
Severity: Information,
|
||||
Code: CodeVEXSSEMixing,
|
||||
Message: "mixes VEX-encoded instructions with legacy SSE on X registers; each switch " +
|
||||
"between the encodings pays a transition penalty, so keep one encoding per kernel",
|
||||
}}
|
||||
}
|
||||
|
||||
// hasXMMOperand reports whether an instruction addresses an X (128-bit SSE)
|
||||
// register. Symbol names that collide with the spelling (a bare X86, say)
|
||||
// are excluded by the register table lookup.
|
||||
func hasXMMOperand(in *ast.Instr, tab *arch.Table) bool {
|
||||
for _, op := range in.Operands {
|
||||
if op.Kind != ast.OpAddr || op.Addr.Sym == nil {
|
||||
continue
|
||||
}
|
||||
name := op.Addr.Sym.Name
|
||||
if op.Addr.Sym.Pseudo != "" || op.Addr.Base != "" || op.Addr.Index != "" || name == "" {
|
||||
continue
|
||||
}
|
||||
if r, ok := tab.Register(name); ok && r.Class == arch.Vector && strings.HasPrefix(strings.ToUpper(name), "X") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user