feat(lint): flag writes to the platform-reserved register
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// 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/arch"
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/ast"
|
||||
)
|
||||
|
||||
// reservedRegister returns the platform-reserved general-purpose register of
|
||||
// the architecture, or "" when it has none. arm64 reserves R18 for platform
|
||||
// use: the OS may own it (the Windows TEB, the Darwin el0 TLS), the Go
|
||||
// toolchain never allocates it, and the Go assembler offers no spelling that
|
||||
// even addresses it, so a kernel writing R18 cannot be assembled by the tool
|
||||
// it must ship with. riscv64, loong64 and amd64 reserve no register beyond
|
||||
// the ones the Go ABI fixes, which the register-clobber audit covers.
|
||||
func reservedRegister(a arch.Arch) string {
|
||||
if a == arch.ARM64 {
|
||||
return "R18"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// scanReservedRegisterWrites flags instructions whose destination is the
|
||||
// platform-reserved register. Only the Plan 9 destination (the last
|
||||
// operand) is checked: reads such as MOVD (R18), R0 are legitimate address
|
||||
// uses, and multi-register stores (STP) keep the rule silent rather than
|
||||
// guess. Like the label heuristics, the check is suppressed in macro-using
|
||||
// files, where an opaque macro may save and restore the register.
|
||||
func scanReservedRegisterWrites(t *ast.Text, a arch.Arch) []Diagnostic {
|
||||
res := reservedRegister(a)
|
||||
if res == "" {
|
||||
return nil
|
||||
}
|
||||
var out []Diagnostic
|
||||
for _, s := range t.Body {
|
||||
in, ok := s.(*ast.Instr)
|
||||
if !ok || len(in.Operands) == 0 {
|
||||
continue
|
||||
}
|
||||
dst := in.Operands[dstIndex(in)]
|
||||
if r := gprName(dst, a); r != "" && strings.EqualFold(r, res) {
|
||||
out = append(out, Diagnostic{
|
||||
Pos: dst.Pos,
|
||||
Severity: Warning,
|
||||
Code: CodeReservedRegister,
|
||||
Message: fmt.Sprintf("write to %s, the platform-reserved register: the OS may own it and the Go assembler cannot spell it", res),
|
||||
})
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user