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: default avatarKeith Randall <khr@golang.org>
parent 18b48afe
...@@ -973,13 +973,23 @@ var linuxAMD64Tests = []*asmTest{ ...@@ -973,13 +973,23 @@ var linuxAMD64Tests = []*asmTest{
// make sure assembly output has matching offset and base register. // make sure assembly output has matching offset and base register.
` `
func f72(a, b int) int { func f72(a, b int) int {
var x [16]byte // use some frame //go:noinline
_ = x func() {_, _ = a, b} () // use some frame
return b return b
} }
`, `,
[]string{"b\\+40\\(SP\\)"}, []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{ var linux386Tests = []*asmTest{
...@@ -1015,6 +1025,16 @@ var linux386Tests = []*asmTest{ ...@@ -1015,6 +1025,16 @@ var linux386Tests = []*asmTest{
}`, }`,
[]string{"\tADDL\t[$]19", "\tIMULL"}, // (n+19)*a []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{ var linuxS390XTests = []*asmTest{
...@@ -1293,6 +1313,16 @@ var linuxS390XTests = []*asmTest{ ...@@ -1293,6 +1313,16 @@ var linuxS390XTests = []*asmTest{
`, `,
[]string{"\tFLOGR\t"}, []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{ var linuxARMTests = []*asmTest{
...@@ -1404,13 +1434,23 @@ var linuxARMTests = []*asmTest{ ...@@ -1404,13 +1434,23 @@ var linuxARMTests = []*asmTest{
// make sure assembly output has matching offset and base register. // make sure assembly output has matching offset and base register.
` `
func f13(a, b int) int { func f13(a, b int) int {
var x [16]byte // use some frame //go:noinline
_ = x func() {_, _ = a, b} () // use some frame
return b return b
} }
`, `,
[]string{"b\\+4\\(FP\\)"}, []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{ var linuxARM64Tests = []*asmTest{
...@@ -1584,6 +1624,16 @@ 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\\)"}, []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{ var linuxMIPSTests = []*asmTest{
...@@ -1667,6 +1717,16 @@ var linuxMIPSTests = []*asmTest{ ...@@ -1667,6 +1717,16 @@ var linuxMIPSTests = []*asmTest{
`, `,
[]string{"\tCLZ\t"}, []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{ var linuxPPC64LETests = []*asmTest{
...@@ -1751,6 +1811,16 @@ var linuxPPC64LETests = []*asmTest{ ...@@ -1751,6 +1811,16 @@ var linuxPPC64LETests = []*asmTest{
`, `,
[]string{"\tROTL\t"}, []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 // TestLineNumber checks to make sure the generated assembly has line numbers
......
...@@ -291,7 +291,14 @@ func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) { ...@@ -291,7 +291,14 @@ func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
return n, ssa.SymWrite return n, ssa.SymWrite
case ssa.OpVarLive: 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: case ssa.OpVarDef, ssa.OpVarKill:
return v.Aux.(*Node), ssa.SymWrite return v.Aux.(*Node), ssa.SymWrite
case ssa.OpKeepAlive: case ssa.OpKeepAlive:
......
...@@ -936,7 +936,16 @@ func (s *state) stmt(n *Node) { ...@@ -936,7 +936,16 @@ func (s *state) stmt(n *Node) {
if !n.Left.Addrtaken() { if !n.Left.Addrtaken() {
s.Fatalf("VARLIVE variable %v must have Addrtaken set", n.Left) 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: case OCHECKNIL:
p := s.expr(n.Left) p := s.expr(n.Left)
......
...@@ -356,6 +356,7 @@ var passes = [...]pass{ ...@@ -356,6 +356,7 @@ var passes = [...]pass{
{name: "tighten", fn: tighten}, // move values closer to their uses {name: "tighten", fn: tighten}, // move values closer to their uses
{name: "lower", fn: lower, required: true}, {name: "lower", fn: lower, required: true},
{name: "lowered cse", fn: cse}, {name: "lowered cse", fn: cse},
{name: "elim unread autos", fn: elimUnreadAutos},
{name: "lowered deadcode", fn: deadcode, required: true}, {name: "lowered deadcode", fn: deadcode, required: true},
{name: "checkLower", fn: checkLower, required: true}, {name: "checkLower", fn: checkLower, required: true},
{name: "late phielim", fn: phielim}, {name: "late phielim", fn: phielim},
......
...@@ -131,3 +131,55 @@ func dse(f *Func) { ...@@ -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() { ...@@ -84,7 +84,7 @@ func main() {
slicevar := make([]string, 0, 16) slicevar := make([]string, 0, 16)
slicevar = append(slicevar, mapvar["abc"]) slicevar = append(slicevar, mapvar["abc"])
fmt.Println("hi") // line 13 fmt.Println("hi") // line 13
_ = ptrvar runtime.KeepAlive(ptrvar)
gslice = slicevar gslice = slicevar
runtime.KeepAlive(mapvar) runtime.KeepAlive(mapvar)
} }
......
...@@ -239,15 +239,6 @@ func f14() { ...@@ -239,15 +239,6 @@ func f14() {
func g14() string 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 // Checking that various temporaries do not persist or cause
// ambiguously live values that must be zeroed. // ambiguously live values that must be zeroed.
// The exact temporary names are inconsequential but we are // The exact temporary names are inconsequential but we are
...@@ -384,10 +375,9 @@ func f25(b bool) { ...@@ -384,10 +375,9 @@ func f25(b bool) {
return return
} }
var x string var x string
_ = &x x = g14()
x = g15() // ERROR "live at call to g15: x$" printstring(x)
printstring(x) // ERROR "live at call to printstring: x$" }
} // ERROR "live at call to deferreturn: x$"
func g25() func g25()
...@@ -641,6 +631,9 @@ type T40 struct { ...@@ -641,6 +631,9 @@ type T40 struct {
m map[int]int m map[int]int
} }
//go:noescape
func useT40(*T40)
func newT40() *T40 { func newT40() *T40 {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$" ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$"
...@@ -658,7 +651,7 @@ func good40() { ...@@ -658,7 +651,7 @@ func good40() {
ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$" ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
t := &ret t := &ret
printnl() // ERROR "live at call to printnl: .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 ddd1(x, y *int) { // ERROR "live at entry to ddd1: x y$" func ddd1(x, y *int) { // ERROR "live at entry to ddd1: x y$"
......
...@@ -14,6 +14,9 @@ package main ...@@ -14,6 +14,9 @@ package main
func printnl() func printnl()
//go:noescape
func useT40(*T40)
type T40 struct { type T40 struct {
m map[int]int m map[int]int
} }
...@@ -27,7 +30,7 @@ func newT40() *T40 { ...@@ -27,7 +30,7 @@ func newT40() *T40 {
func bad40() { func bad40() {
t := newT40() // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$" t := newT40() // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
printnl() // ERROR "live at call to printnl: .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() { func good40() {
...@@ -35,5 +38,5 @@ func good40() { ...@@ -35,5 +38,5 @@ func good40() {
ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$" ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
t := &ret t := &ret
printnl() // ERROR "live at call to printnl: .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$"
} }
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