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