]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: add clobberdeadreg mode
authorCherry Zhang <cherryyz@google.com>
Wed, 17 Mar 2021 23:15:38 +0000 (19:15 -0400)
committerCherry Zhang <cherryyz@google.com>
Fri, 19 Mar 2021 23:21:21 +0000 (23:21 +0000)
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>
17 files changed:
src/cmd/compile/internal/amd64/ssa.go
src/cmd/compile/internal/arm/ssa.go
src/cmd/compile/internal/arm64/ssa.go
src/cmd/compile/internal/base/flag.go
src/cmd/compile/internal/mips/ssa.go
src/cmd/compile/internal/mips64/ssa.go
src/cmd/compile/internal/ppc64/ssa.go
src/cmd/compile/internal/riscv64/ssa.go
src/cmd/compile/internal/s390x/ssa.go
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/regalloc.go
src/cmd/compile/internal/test/clobberdead_test.go
src/cmd/compile/internal/wasm/ssa.go
src/cmd/compile/internal/x86/ssa.go
src/runtime/sys_darwin_amd64.s
test/codegen/clobberdeadreg.go [new file with mode: 0644]

index bdd9da77b0dfa7708e4171c0a994ed657fd72ae0..2c767d36d7fff8e34bf11bbe0a0867a8bcfedf90 100644 (file)
@@ -1242,6 +1242,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                p.To.Reg = x86.REG_SP
                ssagen.AddAux(&p.To, v)
                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:
                v.Fatalf("genValue not implemented: %s", v.LongString())
        }
index 7b2fec3765da812adbb39be9033f6dc977ffdd01..832f940c261ee438228cd554003c3d5e2aa5e22b 100644 (file)
@@ -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())
        case ssa.OpARMInvertFlags:
                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.
        default:
                v.Fatalf("genValue not implemented: %s", v.LongString())
index 3250b49c92f2b6fd75eecd167eaa3212d61f5a69..afd0d66d7254e03419c45e68ac47af1529dffed2 100644 (file)
@@ -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())
        case ssa.OpARM64InvertFlags:
                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.
        default:
                v.Fatalf("genValue not implemented: %s", v.LongString())
index 751ab1b5c7d5bbec5a63bf3378ed9a7d505cd1eb..eb46ed99afe5b69124c6f738e9482a0b41bc0dc3 100644 (file)
@@ -90,6 +90,7 @@ type CmdFlags struct {
        CPUProfile         string       "help:\"write cpu profile to `file`\""
        Complete           bool         "help:\"compiling complete package (no C or assembly)\""
        ClobberDead        bool         "help:\"clobber dead stack slots (for debugging)\""
+       ClobberDeadReg     bool         "help:\"clobber dead registers (for debugging)\""
        Dwarf              bool         "help:\"generate DWARF symbols\""
        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
index 13736d12b4d087b916a36cd03448f628533b7121..e0447f38cbf23347c05a831d304581259eb9b092 100644 (file)
@@ -798,7 +798,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                p := s.Prog(obj.AGETCALLERPC)
                p.To.Type = obj.TYPE_REG
                p.To.Reg = v.Reg()
-       case ssa.OpClobber:
+       case ssa.OpClobber, ssa.OpClobberReg:
                // TODO: implement for clobberdead experiment. Nop is ok for now.
        default:
                v.Fatalf("genValue not implemented: %s", v.LongString())
index c5a3ca305afaff97280bea743a5fc50a6a176d91..e821a00876fa799bc6b0a4413ed48acb445e336e 100644 (file)
@@ -765,7 +765,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                p := s.Prog(obj.AGETCALLERPC)
                p.To.Type = obj.TYPE_REG
                p.To.Reg = v.Reg()
-       case ssa.OpClobber:
+       case ssa.OpClobber, ssa.OpClobberReg:
                // TODO: implement for clobberdead experiment. Nop is ok for now.
        default:
                v.Fatalf("genValue not implemented: %s", v.LongString())
index 899f5ee6afed641ca1344bb57fa43259f4937a07..a0ad69a68d250ea2b4a9f489bdf7f9a17e97bf9e 100644 (file)
@@ -1927,7 +1927,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
        case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
                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.
        default:
                v.Fatalf("genValue not implemented: %s", v.LongString())
index 4a858de191226ec1cd189b12b8be34d25c32702f..64a9b3b33b9aca34cfd15284ff14eb765a67d52a 100644 (file)
@@ -629,6 +629,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                p.To.Sym = ir.Syms.Duffcopy
                p.To.Offset = v.AuxInt
 
+       case ssa.OpClobber, ssa.OpClobberReg:
+               // TODO: implement for clobberdead experiment. Nop is ok for now.
+
        default:
                v.Fatalf("Unhandled op %v", v.Op)
        }
index 7646be61479cfb486f5e75f7b30ef9d648f929f2..ddc05b36add0fa1b9fd22c3697c12be3b124e6eb 100644 (file)
@@ -844,7 +844,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                bne.To.SetTarget(cs)
        case ssa.OpS390XSYNC:
                s.Prog(s390x.ASYNC)
-       case ssa.OpClobber:
+       case ssa.OpClobber, ssa.OpClobberReg:
                // TODO: implement for clobberdead experiment. Nop is ok for now.
        default:
                v.Fatalf("genValue not implemented: %s", v.LongString())
index 85c58ef74cbd9dcb78cd42087368ffccdd9fd6e5..c38d22e07f5ac24563e0f0dc1e89d045c252d0ea 100644 (file)
@@ -613,6 +613,7 @@ var genericOps = []opData{
 
        // 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: "ClobberReg", argLength: 0, typ: "Void"},                                // clobber a register
 }
 
 //     kind          controls        successors   implicit exit
index d572466b9849cc8961c9e35c31ac04ccc8816c22..db51ed95c5c283d2ec654a923329fa994d45422b 100644 (file)
@@ -2921,6 +2921,7 @@ const (
        OpAtomicOr8Variant
        OpAtomicOr32Variant
        OpClobber
+       OpClobberReg
 )
 
 var opcodeTable = [...]opInfo{
@@ -36373,6 +36374,11 @@ var opcodeTable = [...]opInfo{
                symEffect: SymNone,
                generic:   true,
        },
+       {
+               name:    "ClobberReg",
+               argLen:  0,
+               generic: true,
+       },
 }
 
 func (o Op) Asm() obj.As          { return opcodeTable[o].asm }
index 42df8387e49f08e0a682d5e11d79ba80cc403008..1baff184b0b4fcff81238dee737f1b2c43dac556 100644 (file)
 package ssa
 
 import (
+       "cmd/compile/internal/base"
        "cmd/compile/internal/ir"
        "cmd/compile/internal/types"
        "cmd/internal/objabi"
@@ -301,6 +302,9 @@ type regAllocState struct {
 
        // blockOrder[b.ID] corresponds to the index of block b in visitOrder.
        blockOrder []int32
+
+       // whether to insert instructions that clobber dead registers at call sites
+       doClobber bool
 }
 
 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
 // v's original 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.
@@ -1314,6 +1337,9 @@ func (s *regAllocState) regalloc(f *Func) {
                        }
                        if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 {
                                // No register allocation required (or none specified yet)
+                               if s.doClobber && v.Op.IsCall() {
+                                       s.clobberRegs(regspec.clobbers)
+                               }
                                s.freeRegs(regspec.clobbers)
                                b.Values = append(b.Values, v)
                                s.advanceUses(v)
@@ -1475,6 +1501,11 @@ func (s *regAllocState) regalloc(f *Func) {
                        }
 
                        // 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.tmpused |= regspec.clobbers
 
index 3e2aadcbf55341ed89dc238de04ac5babf36ab02..88b7d34623a77c82cf0a1e5647c7b2afe7891a54 100644 (file)
@@ -20,7 +20,15 @@ func main() { fmt.Println("hello") }
 
 func TestClobberDead(t *testing.T) {
        // 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() {
                // This test rebuilds the runtime with a special flag, which
                // takes a while.
@@ -36,7 +44,7 @@ func TestClobberDead(t *testing.T) {
                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()
        if err != nil {
                t.Fatalf("go run failed: %v\n%s", err, out)
index e4ef9d7c6a8762e53fd92ff665d6f91a45f4655c..904871b15f9665cbc9007989e38e9cf2fb267cc4 100644 (file)
@@ -190,6 +190,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                p := s.Prog(storeOp(v.Type))
                ssagen.AddrAuto(&p.To, v)
 
+       case ssa.OpClobber, ssa.OpClobberReg:
+               // TODO: implement for clobberdead experiment. Nop is ok for now.
+
        default:
                if v.Type.IsMemory() {
                        return
index e8c92c0f00d26303051436830e0f63409cd8f146..a06fdbcb71770ebce782dcbba69fdc673b236ac7 100644 (file)
@@ -832,6 +832,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                p.To.Type = obj.TYPE_MEM
                p.To.Reg = x86.REG_SP
                ssagen.AddAux(&p.To, v)
+       case ssa.OpClobberReg:
+               // TODO: implement for clobberdead experiment. Nop is ok for now.
        default:
                v.Fatalf("genValue not implemented: %s", v.LongString())
        }
index 0fe8c7e172f5b59df7aefa62379186b6ee00d489..3e9eccf19e1c2843cd6cad332375ed6b2992064f 100644 (file)
@@ -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.
 // It just does some low-level setup and then calls mstart.
 // 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.
        // We are already on m's g0 stack.
 
diff --git a/test/codegen/clobberdeadreg.go b/test/codegen/clobberdeadreg.go
new file mode 100644 (file)
index 0000000..026850a
--- /dev/null
@@ -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) {}