// Copyright (c) 2026 Petr BalvĂ­n (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 }