diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index ac7f3eb22b..0706d95937 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -2767,10 +2767,14 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask) s.addNamedValue(left, right) return } - // Left is not ssa-able. Compute its address. - if left.Op == ONAME && left.Class() != PEXTERN && skip == 0 { - s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, left, s.mem(), !left.IsAutoTmp()) + + // If this assignment clobbers an entire local variable, then emit + // OpVarDef so liveness analysis knows the variable is redefined. + if base := clobberBase(left); base.Op == ONAME && base.Class() != PEXTERN && skip == 0 { + s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, base, s.mem(), !base.IsAutoTmp()) } + + // Left is not ssa-able. Compute its address. addr := s.addr(left, false) if isReflectHeaderDataField(left) { // Package unsafe's documentation says storing pointers into @@ -6210,3 +6214,13 @@ func (n *Node) StorageClass() ssa.StorageClass { return 0 } } + +func clobberBase(n *Node) *Node { + if n.Op == ODOT && n.Left.Type.NumFields() == 1 { + return clobberBase(n.Left) + } + if n.Op == OINDEX && n.Left.Type.IsArray() && n.Left.Type.NumElem() == 1 { + return clobberBase(n.Left) + } + return n +} diff --git a/test/live.go b/test/live.go index ec51193725..b6e6d93f5f 100644 --- a/test/live.go +++ b/test/live.go @@ -659,7 +659,7 @@ func bad40() { func good40() { ret := T40{} // ERROR "stack object ret T40$" - ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+ ret$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" + ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" t := &ret printnl() // ERROR "live at call to printnl: ret$" // Note: ret is live at the printnl because the compiler moves &ret @@ -704,3 +704,14 @@ func f42() { //go:noescape func f43(a []*int) + +// Assigning to a sub-element that makes up an entire local variable +// should clobber that variable. +func f44(f func() [2]*int) interface{} { // ERROR "live at entry to f44: f" + type T struct { + s [1][2]*int + } + ret := T{} + ret.s[0] = f() + return ret // ERROR "stack object .autotmp_5 T" +} diff --git a/test/live2.go b/test/live2.go index cea312f075..83a6cb7db6 100644 --- a/test/live2.go +++ b/test/live2.go @@ -27,14 +27,14 @@ func newT40() *T40 { } func bad40() { - t := newT40() // ERROR "live at call to makemap: ret$" "stack object ret T40$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" + t := newT40() // ERROR "stack object ret T40$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" printnl() // ERROR "live at call to printnl: ret$" useT40(t) } func good40() { ret := T40{} // ERROR "stack object ret T40$" - ret.m = make(map[int]int, 42) // ERROR "live at call to makemap: ret$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" + ret.m = make(map[int]int, 42) // ERROR "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" t := &ret printnl() // ERROR "live at call to printnl: ret$" useT40(t)