Commit 744ebfde authored by Michael Munday's avatar Michael Munday

cmd/compile: eliminate stores to unread auto variables

This is a crude compiler pass to eliminate stores to auto variables
that are only ever written to.

Eliminates an unnecessary store to x from the following code:

func f() int {
	var x := 1
	return *(&x)
}

Fixes #19765.

Change-Id: If2c63a8ae67b8c590b6e0cc98a9610939a3eeffa
Reviewed-on: https://go-review.googlesource.com/38746
Run-TryBot: Michael Munday <mike.munday@ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent 18b48afe
......@@ -973,13 +973,23 @@ var linuxAMD64Tests = []*asmTest{
// make sure assembly output has matching offset and base register.
`
func f72(a, b int) int {
var x [16]byte // use some frame
_ = x
//go:noinline
func() {_, _ = a, b} () // use some frame
return b
}
`,
[]string{"b\\+40\\(SP\\)"},
},
{
// check that stack store is optimized away
`
func $() int {
var x int
return *(&x)
}
`,
[]string{"TEXT\t.*, [$]0-8"},
},
}
var linux386Tests = []*asmTest{
......@@ -1015,6 +1025,16 @@ var linux386Tests = []*asmTest{
}`,
[]string{"\tADDL\t[$]19", "\tIMULL"}, // (n+19)*a
},
{
// check that stack store is optimized away
`
func $() int {
var x int
return *(&x)
}
`,
[]string{"TEXT\t.*, [$]0-4"},
},
}
var linuxS390XTests = []*asmTest{
......@@ -1293,6 +1313,16 @@ var linuxS390XTests = []*asmTest{
`,
[]string{"\tFLOGR\t"},
},
{
// check that stack store is optimized away
`
func $() int {
var x int
return *(&x)
}
`,
[]string{"TEXT\t.*, [$]0-8"},
},
}
var linuxARMTests = []*asmTest{
......@@ -1404,13 +1434,23 @@ var linuxARMTests = []*asmTest{
// make sure assembly output has matching offset and base register.
`
func f13(a, b int) int {
var x [16]byte // use some frame
_ = x
//go:noinline
func() {_, _ = a, b} () // use some frame
return b
}
`,
[]string{"b\\+4\\(FP\\)"},
},
{
// check that stack store is optimized away
`
func $() int {
var x int
return *(&x)
}
`,
[]string{"TEXT\t.*, [$]-4-4"},
},
}
var linuxARM64Tests = []*asmTest{
......@@ -1584,6 +1624,16 @@ var linuxARM64Tests = []*asmTest{
`,
[]string{"\tMOVD\t\"\"\\.a\\+[0-9]+\\(FP\\), R[0-9]+", "\tMOVD\tR[0-9]+, \"\"\\.b\\+[0-9]+\\(FP\\)"},
},
{
// check that stack store is optimized away
`
func $() int {
var x int
return *(&x)
}
`,
[]string{"TEXT\t.*, [$]-8-8"},
},
}
var linuxMIPSTests = []*asmTest{
......@@ -1667,6 +1717,16 @@ var linuxMIPSTests = []*asmTest{
`,
[]string{"\tCLZ\t"},
},
{
// check that stack store is optimized away
`
func $() int {
var x int
return *(&x)
}
`,
[]string{"TEXT\t.*, [$]-4-4"},
},
}
var linuxPPC64LETests = []*asmTest{
......@@ -1751,6 +1811,16 @@ var linuxPPC64LETests = []*asmTest{
`,
[]string{"\tROTL\t"},
},
{
// check that stack store is optimized away
`
func $() int {
var x int
return *(&x)
}
`,
[]string{"TEXT\t.*, [$]0-8"},
},
}
// TestLineNumber checks to make sure the generated assembly has line numbers
......
......@@ -291,7 +291,14 @@ func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
return n, ssa.SymWrite
case ssa.OpVarLive:
return v.Aux.(*Node), ssa.SymRead
switch a := v.Aux.(type) {
case *ssa.ArgSymbol:
return a.Node.(*Node), ssa.SymRead
case *ssa.AutoSymbol:
return a.Node.(*Node), ssa.SymRead
default:
Fatalf("unknown VarLive aux type: %s", v.LongString())
}
case ssa.OpVarDef, ssa.OpVarKill:
return v.Aux.(*Node), ssa.SymWrite
case ssa.OpKeepAlive:
......
......@@ -936,7 +936,16 @@ func (s *state) stmt(n *Node) {
if !n.Left.Addrtaken() {
s.Fatalf("VARLIVE variable %v must have Addrtaken set", n.Left)
}
s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, n.Left, s.mem())
var aux interface{}
switch n.Left.Class() {
case PAUTO:
aux = s.lookupSymbol(n.Left, &ssa.AutoSymbol{Node: n.Left})
case PPARAM, PPARAMOUT:
aux = s.lookupSymbol(n.Left, &ssa.ArgSymbol{Node: n.Left})
default:
s.Fatalf("VARLIVE variable %v must be Auto or Arg", n.Left)
}
s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, aux, s.mem())
case OCHECKNIL:
p := s.expr(n.Left)
......
......@@ -356,6 +356,7 @@ var passes = [...]pass{
{name: "tighten", fn: tighten}, // move values closer to their uses
{name: "lower", fn: lower, required: true},
{name: "lowered cse", fn: cse},
{name: "elim unread autos", fn: elimUnreadAutos},
{name: "lowered deadcode", fn: deadcode, required: true},
{name: "checkLower", fn: checkLower, required: true},
{name: "late phielim", fn: phielim},
......
......@@ -131,3 +131,55 @@ func dse(f *Func) {
}
}
}
// elimUnreadAutos deletes stores to autos that are never read from.
func elimUnreadAutos(f *Func) {
// Loop over all ops that affect autos taking note of which
// autos we need and also stores that we might be able to
// eliminate.
seen := make(map[GCNode]bool)
var stores []*Value
for _, b := range f.Blocks {
for _, v := range b.Values {
var sym *AutoSymbol
sym, ok := v.Aux.(*AutoSymbol)
if !ok {
continue
}
effect := v.Op.SymEffect()
switch effect {
case SymWrite:
// If we haven't seen the auto yet
// then this might be a store we can
// eliminate.
if !seen[sym.Node] {
stores = append(stores, v)
}
default:
// Assume the auto is needed (loaded,
// has its address taken, etc.).
// Note we have to check the uses
// because dead loads haven't been
// eliminated yet.
if v.Uses > 0 {
seen[sym.Node] = true
}
}
}
}
// Eliminate stores to unread autos.
for _, store := range stores {
sym, _ := store.Aux.(*AutoSymbol)
if seen[sym.Node] {
continue
}
// replace store with OpCopy
store.SetArgs1(store.MemoryArg())
store.Aux = nil
store.AuxInt = 0
store.Op = OpCopy
}
}
......@@ -84,7 +84,7 @@ func main() {
slicevar := make([]string, 0, 16)
slicevar = append(slicevar, mapvar["abc"])
fmt.Println("hi") // line 13
_ = ptrvar
runtime.KeepAlive(ptrvar)
gslice = slicevar
runtime.KeepAlive(mapvar)
}
......
......@@ -239,15 +239,6 @@ func f14() {
func g14() string
func f15() {
var x string
_ = &x
x = g15() // ERROR "live at call to g15: x$"
printstring(x) // ERROR "live at call to printstring: x$"
}
func g15() string
// Checking that various temporaries do not persist or cause
// ambiguously live values that must be zeroed.
// The exact temporary names are inconsequential but we are
......@@ -384,10 +375,9 @@ func f25(b bool) {
return
}
var x string
_ = &x
x = g15() // ERROR "live at call to g15: x$"
printstring(x) // ERROR "live at call to printstring: x$"
} // ERROR "live at call to deferreturn: x$"
x = g14()
printstring(x)
}
func g25()
......@@ -641,6 +631,9 @@ type T40 struct {
m map[int]int
}
//go:noescape
func useT40(*T40)
func newT40() *T40 {
ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$"
......@@ -658,7 +651,7 @@ func good40() {
ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
t := &ret
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
_ = t
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
}
func ddd1(x, y *int) { // ERROR "live at entry to ddd1: x y$"
......
......@@ -14,6 +14,9 @@ package main
func printnl()
//go:noescape
func useT40(*T40)
type T40 struct {
m map[int]int
}
......@@ -27,7 +30,7 @@ func newT40() *T40 {
func bad40() {
t := newT40() // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
_ = t
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
}
func good40() {
......@@ -35,5 +38,5 @@ func good40() {
ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
t := &ret
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
_ = t
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment