Files
gasm-sdk/lint/vector.go
T

71 lines
2.3 KiB
Go
Raw Normal View History

// 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
}