feat: gasm-devkit 0.1.0 — GAsm lexer, parser, linter, formatter, LSP and amd64 assembler
Assisted-by: Qwen 3.8 Max Preview
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package lint
|
||||
|
||||
import "testing"
|
||||
|
||||
// TestRegisterClobber detects writes to callee-saved registers that are not
|
||||
// saved and restored.
|
||||
func TestRegisterClobber(t *testing.T) {
|
||||
// BX (callee-saved on amd64) is written but never saved → clobbered.
|
||||
clob := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tMOVQ CX, BX\n"+
|
||||
"\tRET\n")
|
||||
if codes(clob)[CodeRegisterClobber] != 1 {
|
||||
t.Fatalf("unsaved callee-saved write should be flagged: %+v", clob)
|
||||
}
|
||||
|
||||
// Saved and restored → preserved.
|
||||
saved := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $8\n"+
|
||||
"\tPUSHQ BX\n"+
|
||||
"\tMOVQ CX, BX\n"+
|
||||
"\tPOPQ BX\n"+
|
||||
"\tRET\n")
|
||||
if codes(saved)[CodeRegisterClobber] != 0 {
|
||||
t.Fatalf("saved/restored register must not be flagged: %+v", saved)
|
||||
}
|
||||
|
||||
// A caller-saved register (CX) is fine to write.
|
||||
caller := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tMOVQ $1, CX\n"+
|
||||
"\tRET\n")
|
||||
if codes(caller)[CodeRegisterClobber] != 0 {
|
||||
t.Fatalf("caller-saved register must not be flagged: %+v", caller)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFuncdata validates the FUNCDATA/PCDATA structural checks.
|
||||
func TestFuncdata(t *testing.T) {
|
||||
// Well formed: no findings.
|
||||
good := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tFUNCDATA $0, gclocals·abc(SB)\n"+
|
||||
"\tPCDATA $1, $0\n"+
|
||||
"\tRET\n")
|
||||
if codes(good)[CodeFuncdata] != 0 {
|
||||
t.Fatalf("well-formed FUNCDATA/PCDATA must not be flagged: %+v", good)
|
||||
}
|
||||
|
||||
// FUNCDATA with one operand.
|
||||
bad1 := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tFUNCDATA $0\n"+
|
||||
"\tRET\n")
|
||||
if codes(bad1)[CodeFuncdata] == 0 {
|
||||
t.Fatal("FUNCDATA with one operand should be flagged")
|
||||
}
|
||||
|
||||
// PCDATA with a non-immediate value.
|
||||
bad2 := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tPCDATA $0, AX\n"+
|
||||
"\tRET\n")
|
||||
if codes(bad2)[CodeFuncdata] == 0 {
|
||||
t.Fatal("PCDATA with a register value should be flagged")
|
||||
}
|
||||
|
||||
// FUNCDATA index out of range.
|
||||
bad3 := lintSrc(t, "#include \"textflag.h\"\n"+
|
||||
"TEXT ·f(SB), NOSPLIT, $0\n"+
|
||||
"\tFUNCDATA $99, gclocals·abc(SB)\n"+
|
||||
"\tRET\n")
|
||||
if codes(bad3)[CodeFuncdata] == 0 {
|
||||
t.Fatal("out-of-range FUNCDATA index should be flagged")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user