mirror of
https://github.com/golang/go.git
synced 2025-05-06 08:03:03 +00:00
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: Keith Randall <khr@golang.org>
This commit is contained in:
parent
18b48afec9
commit
744ebfde04
@ -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{
|
|||||||
}`,
|
}`,
|
||||||
[]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{
|
|||||||
`,
|
`,
|
||||||
[]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{
|
|||||||
// 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{
|
|||||||
`,
|
`,
|
||||||
[]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{
|
|||||||
`,
|
`,
|
||||||
[]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{
|
|||||||
`,
|
`,
|
||||||
[]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) {
|
|||||||
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) {
|
|||||||
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{
|
|||||||
{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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 := 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)
|
||||||
}
|
}
|
||||||
|
21
test/live.go
21
test/live.go
@ -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) {
|
|||||||
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 {
|
|||||||
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() {
|
|||||||
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
|
|||||||
|
|
||||||
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 {
|
|||||||
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() {
|
|||||||
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$"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user