mirror of
https://github.com/golang/go
synced 2025-06-08 16:43:15 +00:00
cmd/compile: add clobberdeadreg mode
When -clobberdeadreg flag is set, the compiler inserts code that clobbers integer registers at call sites. This may be helpful for debugging register ABI. Only implemented on AMD64 for now. Change-Id: Ia203d3f891c30fd95d0103489056fe01d63a2899 Reviewed-on: https://go-review.googlesource.com/c/go/+/302809 Trust: Cherry Zhang <cherryyz@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
src
cmd
compile
internal
amd64
arm
arm64
base
mips
mips64
ppc64
riscv64
s390x
ssa
test
wasm
x86
runtime
test/codegen
@ -1242,6 +1242,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
p.To.Reg = x86.REG_SP
|
p.To.Reg = x86.REG_SP
|
||||||
ssagen.AddAux(&p.To, v)
|
ssagen.AddAux(&p.To, v)
|
||||||
p.To.Offset += 4
|
p.To.Offset += 4
|
||||||
|
case ssa.OpClobberReg:
|
||||||
|
x := uint64(0xdeaddeaddeaddead)
|
||||||
|
p := s.Prog(x86.AMOVQ)
|
||||||
|
p.From.Type = obj.TYPE_CONST
|
||||||
|
p.From.Offset = int64(x)
|
||||||
|
p.To.Type = obj.TYPE_REG
|
||||||
|
p.To.Reg = v.Reg()
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
}
|
}
|
||||||
|
@ -861,7 +861,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
|
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpARMInvertFlags:
|
case ssa.OpARMInvertFlags:
|
||||||
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
|
@ -1100,7 +1100,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
|
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpARM64InvertFlags:
|
case ssa.OpARM64InvertFlags:
|
||||||
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
|
@ -90,6 +90,7 @@ type CmdFlags struct {
|
|||||||
CPUProfile string "help:\"write cpu profile to `file`\""
|
CPUProfile string "help:\"write cpu profile to `file`\""
|
||||||
Complete bool "help:\"compiling complete package (no C or assembly)\""
|
Complete bool "help:\"compiling complete package (no C or assembly)\""
|
||||||
ClobberDead bool "help:\"clobber dead stack slots (for debugging)\""
|
ClobberDead bool "help:\"clobber dead stack slots (for debugging)\""
|
||||||
|
ClobberDeadReg bool "help:\"clobber dead registers (for debugging)\""
|
||||||
Dwarf bool "help:\"generate DWARF symbols\""
|
Dwarf bool "help:\"generate DWARF symbols\""
|
||||||
DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\"" // &Ctxt.UseBASEntries, set below
|
DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\"" // &Ctxt.UseBASEntries, set below
|
||||||
DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\"" // &Ctxt.Flag_locationlists, set below
|
DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\"" // &Ctxt.Flag_locationlists, set below
|
||||||
|
@ -798,7 +798,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
p := s.Prog(obj.AGETCALLERPC)
|
p := s.Prog(obj.AGETCALLERPC)
|
||||||
p.To.Type = obj.TYPE_REG
|
p.To.Type = obj.TYPE_REG
|
||||||
p.To.Reg = v.Reg()
|
p.To.Reg = v.Reg()
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
|
@ -765,7 +765,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
p := s.Prog(obj.AGETCALLERPC)
|
p := s.Prog(obj.AGETCALLERPC)
|
||||||
p.To.Type = obj.TYPE_REG
|
p.To.Type = obj.TYPE_REG
|
||||||
p.To.Reg = v.Reg()
|
p.To.Reg = v.Reg()
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
|
@ -1927,7 +1927,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
|
case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
|
||||||
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
|
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
|
@ -629,6 +629,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
p.To.Sym = ir.Syms.Duffcopy
|
p.To.Sym = ir.Syms.Duffcopy
|
||||||
p.To.Offset = v.AuxInt
|
p.To.Offset = v.AuxInt
|
||||||
|
|
||||||
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
|
|
||||||
default:
|
default:
|
||||||
v.Fatalf("Unhandled op %v", v.Op)
|
v.Fatalf("Unhandled op %v", v.Op)
|
||||||
}
|
}
|
||||||
|
@ -844,7 +844,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
bne.To.SetTarget(cs)
|
bne.To.SetTarget(cs)
|
||||||
case ssa.OpS390XSYNC:
|
case ssa.OpS390XSYNC:
|
||||||
s.Prog(s390x.ASYNC)
|
s.Prog(s390x.ASYNC)
|
||||||
case ssa.OpClobber:
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
|
@ -613,6 +613,7 @@ var genericOps = []opData{
|
|||||||
|
|
||||||
// Clobber experiment op
|
// Clobber experiment op
|
||||||
{name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // write an invalid pointer value to the given pointer slot of a stack variable
|
{name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // write an invalid pointer value to the given pointer slot of a stack variable
|
||||||
|
{name: "ClobberReg", argLength: 0, typ: "Void"}, // clobber a register
|
||||||
}
|
}
|
||||||
|
|
||||||
// kind controls successors implicit exit
|
// kind controls successors implicit exit
|
||||||
|
@ -2921,6 +2921,7 @@ const (
|
|||||||
OpAtomicOr8Variant
|
OpAtomicOr8Variant
|
||||||
OpAtomicOr32Variant
|
OpAtomicOr32Variant
|
||||||
OpClobber
|
OpClobber
|
||||||
|
OpClobberReg
|
||||||
)
|
)
|
||||||
|
|
||||||
var opcodeTable = [...]opInfo{
|
var opcodeTable = [...]opInfo{
|
||||||
@ -36373,6 +36374,11 @@ var opcodeTable = [...]opInfo{
|
|||||||
symEffect: SymNone,
|
symEffect: SymNone,
|
||||||
generic: true,
|
generic: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ClobberReg",
|
||||||
|
argLen: 0,
|
||||||
|
generic: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Op) Asm() obj.As { return opcodeTable[o].asm }
|
func (o Op) Asm() obj.As { return opcodeTable[o].asm }
|
||||||
|
@ -114,6 +114,7 @@
|
|||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmd/compile/internal/base"
|
||||||
"cmd/compile/internal/ir"
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
"cmd/internal/objabi"
|
"cmd/internal/objabi"
|
||||||
@ -301,6 +302,9 @@ type regAllocState struct {
|
|||||||
|
|
||||||
// blockOrder[b.ID] corresponds to the index of block b in visitOrder.
|
// blockOrder[b.ID] corresponds to the index of block b in visitOrder.
|
||||||
blockOrder []int32
|
blockOrder []int32
|
||||||
|
|
||||||
|
// whether to insert instructions that clobber dead registers at call sites
|
||||||
|
doClobber bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type endReg struct {
|
type endReg struct {
|
||||||
@ -339,6 +343,17 @@ func (s *regAllocState) freeRegs(m regMask) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clobberRegs inserts instructions that clobber registers listed in m.
|
||||||
|
func (s *regAllocState) clobberRegs(m regMask) {
|
||||||
|
m &= s.allocatable & s.f.Config.gpRegMask // only integer register can contain pointers, only clobber them
|
||||||
|
for m != 0 {
|
||||||
|
r := pickReg(m)
|
||||||
|
m &^= 1 << r
|
||||||
|
x := s.curBlock.NewValue0(src.NoXPos, OpClobberReg, types.TypeVoid)
|
||||||
|
s.f.setHome(x, &s.registers[r])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setOrig records that c's original value is the same as
|
// setOrig records that c's original value is the same as
|
||||||
// v's original value.
|
// v's original value.
|
||||||
func (s *regAllocState) setOrig(c *Value, v *Value) {
|
func (s *regAllocState) setOrig(c *Value, v *Value) {
|
||||||
@ -700,6 +715,14 @@ func (s *regAllocState) init(f *Func) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The clobberdeadreg experiment inserts code to clobber dead registers
|
||||||
|
// at call sites.
|
||||||
|
// Ignore huge functions to avoid doing too much work.
|
||||||
|
if base.Flag.ClobberDeadReg && len(s.f.Blocks) <= 10000 {
|
||||||
|
// TODO: honor GOCLOBBERDEADHASH, or maybe GOSSAHASH.
|
||||||
|
s.doClobber = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a use record for id at distance dist from the start of the block.
|
// Adds a use record for id at distance dist from the start of the block.
|
||||||
@ -1314,6 +1337,9 @@ func (s *regAllocState) regalloc(f *Func) {
|
|||||||
}
|
}
|
||||||
if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 {
|
if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 {
|
||||||
// No register allocation required (or none specified yet)
|
// No register allocation required (or none specified yet)
|
||||||
|
if s.doClobber && v.Op.IsCall() {
|
||||||
|
s.clobberRegs(regspec.clobbers)
|
||||||
|
}
|
||||||
s.freeRegs(regspec.clobbers)
|
s.freeRegs(regspec.clobbers)
|
||||||
b.Values = append(b.Values, v)
|
b.Values = append(b.Values, v)
|
||||||
s.advanceUses(v)
|
s.advanceUses(v)
|
||||||
@ -1475,6 +1501,11 @@ func (s *regAllocState) regalloc(f *Func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dump any registers which will be clobbered
|
// Dump any registers which will be clobbered
|
||||||
|
if s.doClobber && v.Op.IsCall() {
|
||||||
|
// clobber registers that are marked as clobber in regmask, but
|
||||||
|
// don't clobber inputs.
|
||||||
|
s.clobberRegs(regspec.clobbers &^ s.tmpused &^ s.nospill)
|
||||||
|
}
|
||||||
s.freeRegs(regspec.clobbers)
|
s.freeRegs(regspec.clobbers)
|
||||||
s.tmpused |= regspec.clobbers
|
s.tmpused |= regspec.clobbers
|
||||||
|
|
||||||
|
@ -20,7 +20,15 @@ func main() { fmt.Println("hello") }
|
|||||||
|
|
||||||
func TestClobberDead(t *testing.T) {
|
func TestClobberDead(t *testing.T) {
|
||||||
// Test that clobberdead mode generates correct program.
|
// Test that clobberdead mode generates correct program.
|
||||||
|
runHello(t, "-clobberdead")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClobberDeadReg(t *testing.T) {
|
||||||
|
// Test that clobberdeadreg mode generates correct program.
|
||||||
|
runHello(t, "-clobberdeadreg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHello(t *testing.T, flag string) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
// This test rebuilds the runtime with a special flag, which
|
// This test rebuilds the runtime with a special flag, which
|
||||||
// takes a while.
|
// takes a while.
|
||||||
@ -36,7 +44,7 @@ func TestClobberDead(t *testing.T) {
|
|||||||
t.Fatalf("write file failed: %v", err)
|
t.Fatalf("write file failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all=-clobberdead", src)
|
cmd := exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all="+flag, src)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("go run failed: %v\n%s", err, out)
|
t.Fatalf("go run failed: %v\n%s", err, out)
|
||||||
|
@ -190,6 +190,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
p := s.Prog(storeOp(v.Type))
|
p := s.Prog(storeOp(v.Type))
|
||||||
ssagen.AddrAuto(&p.To, v)
|
ssagen.AddrAuto(&p.To, v)
|
||||||
|
|
||||||
|
case ssa.OpClobber, ssa.OpClobberReg:
|
||||||
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if v.Type.IsMemory() {
|
if v.Type.IsMemory() {
|
||||||
return
|
return
|
||||||
|
@ -832,6 +832,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||||||
p.To.Type = obj.TYPE_MEM
|
p.To.Type = obj.TYPE_MEM
|
||||||
p.To.Reg = x86.REG_SP
|
p.To.Reg = x86.REG_SP
|
||||||
ssagen.AddAux(&p.To, v)
|
ssagen.AddAux(&p.To, v)
|
||||||
|
case ssa.OpClobberReg:
|
||||||
|
// TODO: implement for clobberdead experiment. Nop is ok for now.
|
||||||
default:
|
default:
|
||||||
v.Fatalf("genValue not implemented: %s", v.LongString())
|
v.Fatalf("genValue not implemented: %s", v.LongString())
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ TEXT runtime·fcntl_trampoline<ABIInternal>(SB),NOSPLIT,$0
|
|||||||
// mstart_stub is the first function executed on a new thread started by pthread_create.
|
// mstart_stub is the first function executed on a new thread started by pthread_create.
|
||||||
// It just does some low-level setup and then calls mstart.
|
// It just does some low-level setup and then calls mstart.
|
||||||
// Note: called with the C calling convention.
|
// Note: called with the C calling convention.
|
||||||
TEXT runtime·mstart_stub(SB),NOSPLIT,$0
|
TEXT runtime·mstart_stub<ABIInternal>(SB),NOSPLIT,$0
|
||||||
// DI points to the m.
|
// DI points to the m.
|
||||||
// We are already on m's g0 stack.
|
// We are already on m's g0 stack.
|
||||||
|
|
||||||
|
33
test/codegen/clobberdeadreg.go
Normal file
33
test/codegen/clobberdeadreg.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// asmcheck -gcflags=-clobberdeadreg
|
||||||
|
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package codegen
|
||||||
|
|
||||||
|
type S struct {
|
||||||
|
a, b, c, d, e, f int
|
||||||
|
}
|
||||||
|
|
||||||
|
func F(a, b, c int, d S) {
|
||||||
|
// -2401018187971961171 is 0xdeaddeaddeaddead
|
||||||
|
// amd64:`MOVQ\t\$-2401018187971961171, AX`, `MOVQ\t\$-2401018187971961171, BX`, `MOVQ\t\$-2401018187971961171, CX`
|
||||||
|
// amd64:`MOVQ\t\$-2401018187971961171, DX`, `MOVQ\t\$-2401018187971961171, SI`, `MOVQ\t\$-2401018187971961171, DI`
|
||||||
|
// amd64:`MOVQ\t\$-2401018187971961171, R8`, `MOVQ\t\$-2401018187971961171, R9`, `MOVQ\t\$-2401018187971961171, R10`
|
||||||
|
// amd64:`MOVQ\t\$-2401018187971961171, R11`, `MOVQ\t\$-2401018187971961171, R12`, `MOVQ\t\$-2401018187971961171, R13`
|
||||||
|
// amd64:-`MOVQ\t\$-2401018187971961171, BP` // frame pointer is not clobbered
|
||||||
|
StackArgsCall(a, b, c, d)
|
||||||
|
// amd64:`MOVQ\t\$-2401018187971961171, R12`, `MOVQ\t\$-2401018187971961171, R13`, `MOVQ\t\$-2401018187971961171, DX`
|
||||||
|
// amd64:-`MOVQ\t\$-2401018187971961171, AX`, -`MOVQ\t\$-2401018187971961171, R11` // register args are not clobbered
|
||||||
|
RegArgsCall(a, b, c, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func StackArgsCall(int, int, int, S) {}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
//go:registerparams
|
||||||
|
func RegArgsCall(int, int, int, S) {}
|
Reference in New Issue
Block a user