Files
gasm-sdk/lint/registers.go
T

88 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package lint
import (
"fmt"
"strings"
"sourcedock.dev/petrbalvin/gasm-devkit/ast"
"sourcedock.dev/petrbalvin/gasm-devkit/token"
)
// amd64WordNames are the canonical 64-bit register spellings of go tool asm:
// the assembler has no RAX/EAX/RBX forms, and a file using them assembles
// under gasm but fails under the toolchain it must ship with. The rule code
// (CodeNonportableRegister) is declared in lint.go.
var amd64WordNames = map[string]string{
"AX": "AX", "BX": "BX", "CX": "CX", "DX": "DX",
"SI": "SI", "DI": "DI", "BP": "BP", "SP": "SP",
}
// nonportableRegister maps a gasm-only register alias to the canonical go
// tool asm spelling. R or E followed by a canonical word name is the only
// alias family; R8–R15 are already canonical.
func nonportableRegister(name string) (string, bool) {
up := strings.ToUpper(name)
if len(up) != 3 {
return "", false
}
var rest string
switch up[0] {
case 'R', 'E':
rest = up[1:]
default:
return "", false
}
if canon, ok := amd64WordNames[rest]; ok && canon != up {
return canon, true
}
return "", false
}
// scanNonportableRegisters returns one diagnostic per distinct non-portable
// register spelling in t. gasm neither requires nor registers these names
// (they parse as bare symbols), and go tool asm rejects them outright, so a
// kernel containing one only links through the gasm goobj path, never
// through a normal go build.
func scanNonportableRegisters(t *ast.Text) []Diagnostic {
seen := map[string]bool{}
var out []Diagnostic
report := func(name string, pos token.Position) {
canon, ok := nonportableRegister(name)
if !ok {
return
}
key := strings.ToUpper(name)
if seen[key] {
return
}
seen[key] = true
out = append(out, Diagnostic{
Pos: pos,
Severity: Warning,
Code: CodeNonportableRegister,
Message: fmt.Sprintf("register %q is a gasm extension; go tool asm spells it %s", name, canon),
})
}
for _, s := range t.Body {
in, ok := s.(*ast.Instr)
if !ok {
continue
}
for _, op := range in.Operands {
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "" {
report(op.Addr.Sym.Name, op.Pos)
}
if op.Addr.Base != "" {
report(op.Addr.Base, op.Pos)
}
if op.Addr.Index != "" {
report(op.Addr.Index, op.Pos)
}
}
}
return out
}