]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile,runtime: remove ambiguously live logic
authorKeith Randall <keithr@alum.mit.edu>
Fri, 7 Sep 2018 21:55:09 +0000 (14:55 -0700)
committerKeith Randall <khr@golang.org>
Wed, 3 Oct 2018 19:54:16 +0000 (19:54 +0000)
The previous CL introduced stack objects. This CL removes the old
ambiguously live liveness analysis. After this CL we're relying
on stack objects exclusively.

Update a bunch of liveness tests to reflect the new world.

Fixes #22350

Change-Id: I739b26e015882231011ce6bc1a7f426049e59f31
Reviewed-on: https://go-review.googlesource.com/c/134156
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
16 files changed:
src/cmd/compile/internal/gc/op_string.go
src/cmd/compile/internal/gc/plive.go
src/cmd/compile/internal/gc/sinit.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/syntax.go
src/cmd/compile/internal/ssa/gen/genericOps.go
src/runtime/chan.go
src/runtime/gcinfo_test.go
src/runtime/mbitmap.go
test/fixedbugs/issue20250.go
test/live.go
test/live2.go
test/live_syscall.go
test/stackobj.go [new file with mode: 0644]
test/stackobj3.go [new file with mode: 0644]
test/uintptrescapes2.go

index 857234e45dbd53e47318de8318a3c1d7550989ab..90b95d769fd990b51c811c06e37f33b3d3cb1701 100644 (file)
@@ -4,9 +4,9 @@ package gc
 
 import "strconv"
 
-const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDARRAYBYTESTRARRAYBYTESTRTMPARRAYRUNESTRSTRARRAYBYTESTRARRAYBYTETMPSTRARRAYRUNEASAS2AS2FUNCAS2RECVAS2MAPRAS2DOTTYPEASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECMPIFACECMPSTRCOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTINDINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNOTCOMPLUSMINUSORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELPROCRANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARKILLVARLIVEINDREGSPRETJMPGETGEND"
+const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDARRAYBYTESTRARRAYBYTESTRTMPARRAYRUNESTRSTRARRAYBYTESTRARRAYBYTETMPSTRARRAYRUNEASAS2AS2FUNCAS2RECVAS2MAPRAS2DOTTYPEASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECMPIFACECMPSTRCOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTINDINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNOTCOMPLUSMINUSORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELPROCRANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVEINDREGSPRETJMPGETGEND"
 
-var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 73, 88, 100, 112, 127, 139, 141, 144, 151, 158, 165, 175, 179, 183, 191, 199, 208, 216, 219, 224, 231, 239, 245, 252, 258, 267, 275, 283, 289, 293, 302, 309, 313, 316, 323, 331, 339, 346, 352, 355, 361, 368, 376, 380, 387, 395, 397, 399, 401, 403, 405, 407, 410, 415, 423, 426, 435, 438, 442, 450, 457, 466, 469, 472, 475, 478, 481, 484, 490, 493, 496, 499, 503, 508, 512, 517, 522, 528, 533, 537, 542, 550, 558, 564, 573, 580, 584, 591, 598, 606, 610, 614, 618, 625, 632, 640, 646, 651, 656, 660, 665, 673, 678, 683, 687, 690, 698, 702, 704, 709, 713, 718, 724, 730, 736, 742, 747, 751, 758, 764, 769, 775, 778, 784, 791, 796, 800, 805, 809, 819, 824, 832, 839, 846, 854, 860, 864, 867}
+var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 73, 88, 100, 112, 127, 139, 141, 144, 151, 158, 165, 175, 179, 183, 191, 199, 208, 216, 219, 224, 231, 239, 245, 252, 258, 267, 275, 283, 289, 293, 302, 309, 313, 316, 323, 331, 339, 346, 352, 355, 361, 368, 376, 380, 387, 395, 397, 399, 401, 403, 405, 407, 410, 415, 423, 426, 435, 438, 442, 450, 457, 466, 469, 472, 475, 478, 481, 484, 490, 493, 496, 499, 503, 508, 512, 517, 522, 528, 533, 537, 542, 550, 558, 564, 573, 580, 584, 591, 598, 606, 610, 614, 618, 625, 632, 640, 646, 651, 656, 660, 665, 673, 678, 683, 687, 690, 698, 702, 704, 709, 713, 718, 724, 730, 736, 742, 747, 751, 758, 764, 769, 775, 778, 784, 791, 796, 800, 805, 809, 819, 824, 832, 838, 845, 852, 860, 866, 870, 873}
 
 func (i Op) String() string {
        if i >= Op(len(_Op_index)-1) {
index e070a5cd1af086396473d953c3436f7bba192728..563b425db7decbf287d73210cad6c3c27bddb309 100644 (file)
@@ -78,6 +78,10 @@ import (
 // that its argument is certainly dead, for use when the liveness analysis
 // would not otherwise be able to deduce that fact.
 
+// TODO: get rid of OpVarKill here. It's useful for stack frame allocation
+// so the compiler can allocate two temps to the same location. Here it's now
+// useless, since the implementation of stack objects.
+
 // BlockEffects summarizes the liveness effects on an SSA block.
 type BlockEffects struct {
        // Computed during Liveness.prologue using only the content of
@@ -85,23 +89,15 @@ type BlockEffects struct {
        //
        //      uevar: upward exposed variables (used before set in block)
        //      varkill: killed variables (set in block)
-       //      avarinit: addrtaken variables set or used (proof of initialization)
-       uevar    varRegVec
-       varkill  varRegVec
-       avarinit bvec
+       uevar   varRegVec
+       varkill varRegVec
 
        // Computed during Liveness.solve using control flow information:
        //
        //      livein: variables live at block entry
        //      liveout: variables live at block exit
-       //      avarinitany: addrtaken variables possibly initialized at block exit
-       //              (initialized in block or at exit from any predecessor block)
-       //      avarinitall: addrtaken variables certainly initialized at block exit
-       //              (initialized in block or at exit from all predecessor blocks)
-       livein      varRegVec
-       liveout     varRegVec
-       avarinitany bvec
-       avarinitall bvec
+       livein  varRegVec
+       liveout varRegVec
 }
 
 // A collection of global state used by liveness analysis.
@@ -186,10 +182,9 @@ func (idx LivenessIndex) Valid() bool {
 }
 
 type progeffectscache struct {
-       textavarinit []int32
-       retuevar     []int32
-       tailuevar    []int32
-       initialized  bool
+       retuevar    []int32
+       tailuevar   []int32
+       initialized bool
 }
 
 // varRegVec contains liveness bitmaps for variables and registers.
@@ -264,24 +259,13 @@ func (lv *Liveness) initcache() {
                        // all the parameters for correctness, and similarly it must not
                        // read the out arguments - they won't be set until the new
                        // function runs.
-
                        lv.cache.tailuevar = append(lv.cache.tailuevar, int32(i))
 
-                       if node.Addrtaken() {
-                               lv.cache.textavarinit = append(lv.cache.textavarinit, int32(i))
-                       }
-
                case PPARAMOUT:
-                       // If the result had its address taken, it is being tracked
-                       // by the avarinit code, which does not use uevar.
-                       // If we added it to uevar too, we'd not see any kill
-                       // and decide that the variable was live entry, which it is not.
-                       // So only use uevar in the non-addrtaken case.
-                       // The p.to.type == obj.TYPE_NONE limits the bvset to
-                       // non-tail-call return instructions; see note below for details.
-                       if !node.Addrtaken() {
-                               lv.cache.retuevar = append(lv.cache.retuevar, int32(i))
-                       }
+                       // All results are live at every return point.
+                       // Note that this point is after escaping return values
+                       // are copied back to the stack using their PAUTOHEAP references.
+                       lv.cache.retuevar = append(lv.cache.retuevar, int32(i))
                }
        }
 }
@@ -291,21 +275,13 @@ func (lv *Liveness) initcache() {
 //
 // The possible flags are:
 //     uevar - used by the instruction
-//     varkill - killed by the instruction
-//             for variables without address taken, means variable was set
-//             for variables with address taken, means variable was marked dead
-//     avarinit - initialized or referred to by the instruction,
-//             only for variables with address taken but not escaping to heap
-//
-// The avarinit output serves as a signal that the data has been
-// initialized, because any use of a variable must come after its
-// initialization.
+//     varkill - killed by the instruction (set)
+// A kill happens after the use (for an instruction that updates a value, for example).
 type liveEffect int
 
 const (
        uevar liveEffect = 1 << iota
        varkill
-       avarinit
 )
 
 // valueEffects returns the index of a variable in lv.vars and the
@@ -329,27 +305,15 @@ func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
        }
 
        var effect liveEffect
-       if n.Addrtaken() {
-               if v.Op != ssa.OpVarKill {
-                       effect |= avarinit
-               }
-               if v.Op == ssa.OpVarDef || v.Op == ssa.OpVarKill {
-                       effect |= varkill
-               }
-       } else {
-               // Read is a read, obviously.
-               // Addr by itself is also implicitly a read.
-               //
-               // Addr|Write means that the address is being taken
-               // but only so that the instruction can write to the value.
-               // It is not a read.
-
-               if e&ssa.SymRead != 0 || e&(ssa.SymAddr|ssa.SymWrite) == ssa.SymAddr {
-                       effect |= uevar
-               }
-               if e&ssa.SymWrite != 0 && (!isfat(n.Type) || v.Op == ssa.OpVarDef) {
-                       effect |= varkill
-               }
+       // Read is a read, obviously.
+       //
+       // Addr is a read also, as any subseqent holder of the pointer must be able
+       // to see all the values (including initialization) written so far.
+       if e&(ssa.SymRead|ssa.SymAddr) != 0 {
+               effect |= uevar
+       }
+       if e&ssa.SymWrite != 0 && (!isfat(n.Type) || v.Op == ssa.OpVarDef) {
+               effect |= varkill
        }
 
        if effect == 0 {
@@ -545,9 +509,6 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
                be.varkill = varRegVec{vars: bulk.next()}
                be.livein = varRegVec{vars: bulk.next()}
                be.liveout = varRegVec{vars: bulk.next()}
-               be.avarinit = bulk.next()
-               be.avarinitany = bulk.next()
-               be.avarinitall = bulk.next()
        }
        lv.livenessMap.reset(lv.f.NumValues())
 
@@ -869,19 +830,6 @@ func (lv *Liveness) prologue() {
                        }
                        be.uevar.regs |= regUevar
                }
-
-               // Walk the block instructions forward to update avarinit bits.
-               // avarinit describes the effect at the end of the block, not the beginning.
-               for _, val := range b.Values {
-                       pos, e := lv.valueEffects(val)
-                       // No need for regEffects because registers never appear in avarinit.
-                       if e&varkill != 0 {
-                               be.avarinit.Unset(pos)
-                       }
-                       if e&avarinit != 0 {
-                               be.avarinit.Set(pos)
-                       }
-               }
        }
 }
 
@@ -892,51 +840,10 @@ func (lv *Liveness) solve() {
        nvars := int32(len(lv.vars))
        newlivein := varRegVec{vars: bvalloc(nvars)}
        newliveout := varRegVec{vars: bvalloc(nvars)}
-       any := bvalloc(nvars)
-       all := bvalloc(nvars)
 
-       // Push avarinitall, avarinitany forward.
-       // avarinitall says the addressed var is initialized along all paths reaching the block exit.
-       // avarinitany says the addressed var is initialized along some path reaching the block exit.
-       for _, b := range lv.f.Blocks {
-               be := lv.blockEffects(b)
-               if b == lv.f.Entry {
-                       be.avarinitall.Copy(be.avarinit)
-               } else {
-                       be.avarinitall.Clear()
-                       be.avarinitall.Not()
-               }
-               be.avarinitany.Copy(be.avarinit)
-       }
-
-       // Walk blocks in the general direction of propagation (RPO
-       // for avarinit{any,all}, and PO for live{in,out}). This
-       // improves convergence.
+       // Walk blocks in postorder ordering. This improves convergence.
        po := lv.f.Postorder()
 
-       for change := true; change; {
-               change = false
-               for i := len(po) - 1; i >= 0; i-- {
-                       b := po[i]
-                       be := lv.blockEffects(b)
-                       lv.avarinitanyall(b, any, all)
-
-                       any.AndNot(any, be.varkill.vars)
-                       all.AndNot(all, be.varkill.vars)
-                       any.Or(any, be.avarinit)
-                       all.Or(all, be.avarinit)
-                       if !any.Eq(be.avarinitany) {
-                               change = true
-                               be.avarinitany.Copy(any)
-                       }
-
-                       if !all.Eq(be.avarinitall) {
-                               change = true
-                               be.avarinitall.Copy(all)
-                       }
-               }
-       }
-
        // Iterate through the blocks in reverse round-robin fashion. A work
        // queue might be slightly faster. As is, the number of iterations is
        // so low that it hardly seems to be worth the complexity.
@@ -957,7 +864,7 @@ func (lv *Liveness) solve() {
                                        newliveout.vars.Set(pos)
                                }
                        case ssa.BlockExit:
-                               // nothing to do
+                               // panic exit - nothing to do
                        default:
                                // A variable is live on output from this block
                                // if it is live on input to some successor.
@@ -975,7 +882,7 @@ func (lv *Liveness) solve() {
                        }
 
                        // A variable is live on input to this block
-                       // if it is live on output from this block and
+                       // if it is used by this block, or live on output from this block and
                        // not set by the code in this block.
                        //
                        // in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
@@ -990,8 +897,6 @@ func (lv *Liveness) solve() {
 func (lv *Liveness) epilogue() {
        nvars := int32(len(lv.vars))
        liveout := varRegVec{vars: bvalloc(nvars)}
-       any := bvalloc(nvars)
-       all := bvalloc(nvars)
        livedefer := bvalloc(nvars) // always-live variables
 
        // If there is a defer (that could recover), then all output
@@ -1017,6 +922,9 @@ func (lv *Liveness) epilogue() {
                                livedefer.Set(int32(i))
                        }
                        if n.IsOutputParamHeapAddr() {
+                               // This variable will be overwritten early in the function
+                               // prologue (from the result of a mallocgc) but we need to
+                               // zero it in case that malloc causes a stack scan.
                                n.Name.SetNeedzero(true)
                                livedefer.Set(int32(i))
                        }
@@ -1033,9 +941,6 @@ func (lv *Liveness) epilogue() {
        {
                // Reserve an entry for function entry.
                live := bvalloc(nvars)
-               for _, pos := range lv.cache.textavarinit {
-                       live.Set(pos)
-               }
                lv.livevars = append(lv.livevars, varRegVec{vars: live})
        }
 
@@ -1043,53 +948,14 @@ func (lv *Liveness) epilogue() {
                be := lv.blockEffects(b)
                firstBitmapIndex := len(lv.livevars)
 
-               // Compute avarinitany and avarinitall for entry to block.
-               // This duplicates information known during Liveness.solve
-               // but avoids storing two more vectors for each block.
-               lv.avarinitanyall(b, any, all)
-
                // Walk forward through the basic block instructions and
                // allocate liveness maps for those instructions that need them.
-               // Seed the maps with information about the addrtaken variables.
                for _, v := range b.Values {
-                       pos, e := lv.valueEffects(v)
-                       // No need for regEffects because registers never appear in avarinit.
-                       if e&varkill != 0 {
-                               any.Unset(pos)
-                               all.Unset(pos)
-                       }
-                       if e&avarinit != 0 {
-                               any.Set(pos)
-                               all.Set(pos)
-                       }
-
                        if !lv.issafepoint(v) {
                                continue
                        }
 
-                       // Annotate ambiguously live variables so that they can
-                       // be zeroed at function entry and at VARKILL points.
-                       // liveout is dead here and used as a temporary.
-                       liveout.vars.AndNot(any, all)
-                       if !liveout.vars.IsEmpty() {
-                               for pos := int32(0); pos < liveout.vars.n; pos++ {
-                                       if !liveout.vars.Get(pos) {
-                                               continue
-                                       }
-                                       all.Set(pos) // silence future warnings in this block
-                                       n := lv.vars[pos]
-                                       if !n.Name.Needzero() {
-                                               n.Name.SetNeedzero(true)
-                                               if debuglive >= 1 {
-                                                       Warnl(v.Pos, "%v: %L is ambiguously live", lv.fn.Func.Nname, n)
-                                               }
-                                       }
-                               }
-                       }
-
-                       // Live stuff first.
                        live := bvalloc(nvars)
-                       live.Copy(any)
                        lv.livevars = append(lv.livevars, varRegVec{vars: live})
                }
 
@@ -1128,6 +994,17 @@ func (lv *Liveness) epilogue() {
                                Fatalf("bad index for entry point: %v", index)
                        }
 
+                       // Check to make sure only input variables are live.
+                       for i, n := range lv.vars {
+                               if !liveout.vars.Get(int32(i)) {
+                                       continue
+                               }
+                               if n.Class() == PPARAM {
+                                       continue // ok
+                               }
+                               Fatalf("bad live variable at entry of %v: %L", lv.fn.Func.Nname, n)
+                       }
+
                        // Record live variables.
                        live := &lv.livevars[index]
                        live.Or(*live, liveout)
@@ -1330,28 +1207,6 @@ func clobberPtr(b *ssa.Block, v *Node, offset int64) {
        b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, v)
 }
 
-func (lv *Liveness) avarinitanyall(b *ssa.Block, any, all bvec) {
-       if len(b.Preds) == 0 {
-               any.Clear()
-               all.Clear()
-               for _, pos := range lv.cache.textavarinit {
-                       any.Set(pos)
-                       all.Set(pos)
-               }
-               return
-       }
-
-       be := lv.blockEffects(b.Preds[0].Block())
-       any.Copy(be.avarinitany)
-       all.Copy(be.avarinitall)
-
-       for _, pred := range b.Preds[1:] {
-               be := lv.blockEffects(pred.Block())
-               any.Or(any, be.avarinitany)
-               all.And(all, be.avarinitall)
-       }
-}
-
 // Compact coalesces identical bitmaps from lv.livevars into the sets
 // lv.stackMapSet and lv.regMaps.
 //
@@ -1559,7 +1414,6 @@ func (lv *Liveness) printDebug() {
                        printed = false
                        printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0, regUevar)
                        printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0, regKill)
-                       printed = lv.printeffect(printed, "avarinit", pos, effect&avarinit != 0, 0)
                        if printed {
                                fmt.Printf("\n")
                        }
@@ -1596,9 +1450,6 @@ func (lv *Liveness) printDebug() {
                printed = false
                printed = lv.printbvec(printed, "varkill", be.varkill)
                printed = lv.printbvec(printed, "liveout", be.liveout)
-               printed = lv.printbvec(printed, "avarinit", varRegVec{vars: be.avarinit})
-               printed = lv.printbvec(printed, "avarinitany", varRegVec{vars: be.avarinitany})
-               printed = lv.printbvec(printed, "avarinitall", varRegVec{vars: be.avarinitall})
                if printed {
                        fmt.Printf("\n")
                }
index f76b02828ff633bd2f342b75a314f98a8274d9a1..9d1114fa4337480f0ce8221dea5479546f3df590 100644 (file)
@@ -839,6 +839,10 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
                        a = nod(OAS, x, nil)
                        a = typecheck(a, Etop)
                        init.Append(a) // zero new temp
+               } else {
+                       // Declare that we're about to initialize all of x.
+                       // (Which happens at the *vauto = vstat below.)
+                       init.Append(nod(OVARDEF, x, nil))
                }
 
                a = nod(OADDR, x, nil)
@@ -849,6 +853,8 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
                        a = typecheck(a, Etop)
                        init.Append(a) // zero new temp
                        a = a.Left
+               } else {
+                       init.Append(nod(OVARDEF, a, nil))
                }
 
                a = nod(OADDR, a, nil)
index 663042754ebd372a71753a6c91d7872c5b7ab7e1..138ce08feca8f460a5586cf15a24e59bb1fa1776 100644 (file)
@@ -755,8 +755,8 @@ func (s *state) stmtList(l Nodes) {
 
 // stmt converts the statement n to SSA and adds it to s.
 func (s *state) stmt(n *Node) {
-       if !(n.Op == OVARKILL || n.Op == OVARLIVE) {
-               // OVARKILL and OVARLIVE are invisible to the programmer, so we don't use their line numbers to avoid confusion in debugging.
+       if !(n.Op == OVARKILL || n.Op == OVARLIVE || n.Op == OVARDEF) {
+               // OVARKILL, OVARLIVE, and OVARDEF are invisible to the programmer, so we don't use their line numbers to avoid confusion in debugging.
                s.pushLine(n.Pos)
                defer s.popLine()
        }
@@ -1170,6 +1170,10 @@ func (s *state) stmt(n *Node) {
                }
                s.startBlock(bEnd)
 
+       case OVARDEF:
+               if !s.canSSA(n.Left) {
+                       s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, n.Left, s.mem(), false)
+               }
        case OVARKILL:
                // Insert a varkill op to record that a variable is no longer live.
                // We only care about liveness info at call sites, so putting the
@@ -4977,6 +4981,12 @@ func emitStackObjects(e *ssafn, pp *Progs) {
        p.To.Type = obj.TYPE_MEM
        p.To.Name = obj.NAME_EXTERN
        p.To.Sym = x
+
+       if debuglive != 0 {
+               for _, v := range vars {
+                       Warnl(v.Pos, "stack object %v %s", v, v.Type.String())
+               }
+       }
 }
 
 // genssa appends entries to pp for each instruction in f.
@@ -5056,24 +5066,8 @@ func genssa(f *ssa.Func, pp *Progs) {
                        case ssa.OpGetG:
                                // nothing to do when there's a g register,
                                // and checkLower complains if there's not
-                       case ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive:
+                       case ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive, ssa.OpVarKill:
                                // nothing to do; already used by liveness
-                       case ssa.OpVarKill:
-                               // Zero variable if it is ambiguously live.
-                               // After the VARKILL anything this variable references
-                               // might be collected. If it were to become live again later,
-                               // the GC will see references to already-collected objects.
-                               // See issue 20029.
-                               n := v.Aux.(*Node)
-                               if n.Name.Needzero() {
-                                       if n.Class() != PAUTO {
-                                               v.Fatalf("zero of variable which isn't PAUTO %v", n)
-                                       }
-                                       if n.Type.Size()%int64(Widthptr) != 0 {
-                                               v.Fatalf("zero of variable not a multiple of ptr size %v", n)
-                                       }
-                                       thearch.ZeroAuto(s.pp, n)
-                               }
                        case ssa.OpPhi:
                                CheckLoweredPhi(v)
                        case ssa.OpConvert:
index df23b83f299b96b4be1e1681b348f0cba0878d2a..eb2ab6b916ab060875f4bd5e9acb50ed3666a00a 100644 (file)
@@ -739,6 +739,7 @@ const (
        OCLOSUREVAR // variable reference at beginning of closure function
        OCFUNC      // reference to c function pointer (not go func value)
        OCHECKNIL   // emit code to ensure pointer/interface not nil
+       OVARDEF     // variable is about to be fully initialized
        OVARKILL    // variable is dead
        OVARLIVE    // variable is alive
        OINDREGSP   // offset plus indirect of REGSP, such as 8(SP).
index 7292012d26751d161739896d2e0bec58ccbc4e08..ee9c6fa0f6601700c6934f251a097f9288bfa311 100644 (file)
@@ -470,8 +470,9 @@ var genericOps = []opData{
 
        {name: "VarDef", argLength: 1, aux: "Sym", typ: "Mem", symEffect: "None", zeroWidth: true}, // aux is a *gc.Node of a variable that is about to be initialized.  arg0=mem, returns mem
        {name: "VarKill", argLength: 1, aux: "Sym", symEffect: "None"},                             // aux is a *gc.Node of a variable that is known to be dead.  arg0=mem, returns mem
-       {name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read", zeroWidth: true},            // aux is a *gc.Node of a variable that must be kept live.  arg0=mem, returns mem
-       {name: "KeepAlive", argLength: 2, typ: "Mem", zeroWidth: true},                             // arg[0] is a value that must be kept alive until this mark.  arg[1]=mem, returns mem
+       // TODO: what's the difference betweeen VarLive and KeepAlive?
+       {name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read", zeroWidth: true}, // aux is a *gc.Node of a variable that must be kept live.  arg0=mem, returns mem
+       {name: "KeepAlive", argLength: 2, typ: "Mem", zeroWidth: true},                  // arg[0] is a value that must be kept alive until this mark.  arg[1]=mem, returns mem
 
        // Ops for breaking 64-bit operations on 32-bit architectures
        {name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
index a4ee51ca39d26c8525708e4e072b77eac0fa9c00..5cf0b86f586140ab53e9ad592190b422db8c309e 100644 (file)
@@ -232,6 +232,11 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
        gp.param = nil
        c.sendq.enqueue(mysg)
        goparkunlock(&c.lock, waitReasonChanSend, traceEvGoBlockSend, 3)
+       // Ensure the value being sent is kept alive until the
+       // receiver copies it out. The sudog has a pointer to the
+       // stack object, but sudogs aren't considered as roots of the
+       // stack tracer.
+       KeepAlive(ep)
 
        // someone woke us up.
        if mysg != gp.waiting {
index 7dd1a5607cfd53f4a542cf3c9af91b74911a9a57..0741f6361cfdfcac78921ed915cac5f8195a9a35 100644 (file)
@@ -35,14 +35,46 @@ func TestGCInfo(t *testing.T) {
        verifyGCInfo(t, "data eface", &dataEface, infoEface)
        verifyGCInfo(t, "data iface", &dataIface, infoIface)
 
-       verifyGCInfo(t, "stack Ptr", new(Ptr), infoPtr)
-       verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
-       verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
-       verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
-       verifyGCInfo(t, "stack string", new(string), infoString)
-       verifyGCInfo(t, "stack slice", new([]string), infoSlice)
-       verifyGCInfo(t, "stack eface", new(interface{}), infoEface)
-       verifyGCInfo(t, "stack iface", new(Iface), infoIface)
+       {
+               var x Ptr
+               verifyGCInfo(t, "stack Ptr", &x, infoPtr)
+               runtime.KeepAlive(x)
+       }
+       {
+               var x ScalarPtr
+               verifyGCInfo(t, "stack ScalarPtr", &x, infoScalarPtr)
+               runtime.KeepAlive(x)
+       }
+       {
+               var x PtrScalar
+               verifyGCInfo(t, "stack PtrScalar", &x, infoPtrScalar)
+               runtime.KeepAlive(x)
+       }
+       {
+               var x BigStruct
+               verifyGCInfo(t, "stack BigStruct", &x, infoBigStruct())
+               runtime.KeepAlive(x)
+       }
+       {
+               var x string
+               verifyGCInfo(t, "stack string", &x, infoString)
+               runtime.KeepAlive(x)
+       }
+       {
+               var x []string
+               verifyGCInfo(t, "stack slice", &x, infoSlice)
+               runtime.KeepAlive(x)
+       }
+       {
+               var x interface{}
+               verifyGCInfo(t, "stack eface", &x, infoEface)
+               runtime.KeepAlive(x)
+       }
+       {
+               var x Iface
+               verifyGCInfo(t, "stack iface", &x, infoIface)
+               runtime.KeepAlive(x)
+       }
 
        for i := 0; i < 10; i++ {
                verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(padDead(infoPtr)))
index 5301e692e043bc164d826de6717488e1f4b63800..87fa027b4ebec94df91569379909c5cc89979e32 100644 (file)
@@ -1994,7 +1994,9 @@ func reflect_gcbits(x interface{}) []byte {
        return ret
 }
 
-// Returns GC type info for object p for testing.
+// Returns GC type info for the pointer stored in ep for testing.
+// If ep points to the stack, only static live information will be returned
+// (i.e. not for objects which are only dynamically live stack objects).
 func getgcmask(ep interface{}) (mask []byte) {
        e := *efaceOf(&ep)
        p := e.data
@@ -2051,11 +2053,6 @@ func getgcmask(ep interface{}) (mask []byte) {
                _g_ := getg()
                gentraceback(_g_.m.curg.sched.pc, _g_.m.curg.sched.sp, 0, _g_.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0)
                if frame.fn.valid() {
-                       // TODO: once stack objects are enabled (and their pointers
-                       // are no longer described by the stack pointermap directly),
-                       // tests using this will probably need fixing. We might need
-                       // to loop through the stackobjects and if we're inside one,
-                       // use the pointermap from that object.
                        locals, _, _ := getStackMap(&frame, nil, false)
                        if locals.n == 0 {
                                return
index 6fc861a8dc42040ba4ba2056f83461de03b10a16..47879385d22fb66cf2281037c0f76d880ab6e688 100644 (file)
@@ -15,10 +15,10 @@ type T struct {
 }
 
 func f(a T) { // ERROR "live at entry to f: a"
-       var e interface{}
-       func() { // ERROR "live at entry to f.func1: a &e"
-               e = a.s // ERROR "live at call to convT2Estring: a &e"
-       }() // ERROR "live at call to f.func1: e$"
+       var e interface{} // ERROR "stack object e interface \{\}$"
+       func() {          // ERROR "live at entry to f.func1: a &e"
+               e = a.s // ERROR "live at call to convT2Estring: &e" "stack object a T$"
+       }()
        // Before the fix, both a and e were live at the previous line.
        _ = e
 }
index 13bdc4aae1d248a1dbdc845739e5c971d69350fe..679562d9bf4956827433130632796cbb1b2f6832 100644 (file)
@@ -32,9 +32,9 @@ func printbytepointer(*byte)
 func printint(int)
 
 func f1() {
-       var x *int
-       printpointer(&x) // ERROR "live at call to printpointer: x$"
+       var x *int       // ERROR "stack object x \*int$"
        printpointer(&x) // ERROR "live at call to printpointer: x$"
+       printpointer(&x)
 }
 
 func f2(b bool) {
@@ -42,9 +42,9 @@ func f2(b bool) {
                printint(0) // nothing live here
                return
        }
-       var x *int
-       printpointer(&x) // ERROR "live at call to printpointer: x$"
+       var x *int       // ERROR "stack object x \*int$"
        printpointer(&x) // ERROR "live at call to printpointer: x$"
+       printpointer(&x)
 }
 
 func f3(b1, b2 bool) {
@@ -60,15 +60,15 @@ func f3(b1, b2 bool) {
        }
 
        if b2 {
-               var x *int
-               printpointer(&x) // ERROR "live at call to printpointer: x$"
+               var x *int       // ERROR "stack object x \*int$"
                printpointer(&x) // ERROR "live at call to printpointer: x$"
+               printpointer(&x)
        } else {
-               var y *int
-               printpointer(&y) // ERROR "live at call to printpointer: y$"
+               var y *int       // ERROR "stack object y \*int$"
                printpointer(&y) // ERROR "live at call to printpointer: y$"
+               printpointer(&y)
        }
-       printint(0) // ERROR "f3: x \(type \*int\) is ambiguously live$" "f3: y \(type \*int\) is ambiguously live$" "live at call to printint: x y$"
+       printint(0) // nothing is live here
 }
 
 // The old algorithm treated x as live on all code that
@@ -83,7 +83,7 @@ func f4(b1, b2 bool) { // x not live here
                return
        }
        var z **int
-       x := new(int)
+       x := new(int) // ERROR "stack object x \*int$"
        *x = 42
        z = &x
        printint(**z) // ERROR "live at call to printint: x$"
@@ -99,15 +99,15 @@ func f4(b1, b2 bool) { // x not live here
 func f5(b1 bool) {
        var z **int
        if b1 {
-               x := new(int)
+               x := new(int) // ERROR "stack object x \*int$"
                *x = 42
                z = &x
        } else {
-               y := new(int)
+               y := new(int) // ERROR "stack object y \*int$"
                *y = 54
                z = &y
        }
-       printint(**z) // ERROR "f5: x \(type \*int\) is ambiguously live$" "f5: y \(type \*int\) is ambiguously live$" "live at call to printint: x y$"
+       printint(**z) // nothing live here
 }
 
 // confusion about the _ result used to cause spurious "live at entry to f6: _".
@@ -119,7 +119,7 @@ func f6() (_, y string) {
 
 // confusion about addressed results used to cause "live at entry to f7: x".
 
-func f7() (x string) {
+func f7() (x string) { // ERROR "stack object x string"
        _ = &x
        x = "hello"
        return
@@ -141,7 +141,7 @@ var i9 interface{}
 func f9() bool {
        g8()
        x := i9
-       y := interface{}(str()) // ERROR "live at call to convT2Estring: .autotmp_[0-9]+ x.data$" "live at call to str: x.data$"
+       y := interface{}(str()) // ERROR "live at call to convT2Estring: x.data$" "live at call to str: x.data$" "stack object .autotmp_[0-9]+ string$"
        i9 = y                  // make y escape so the line above has to call convT2E
        return x != y
 }
@@ -163,7 +163,7 @@ var b bool
 
 // this used to have a spurious "live at entry to f11a: ~r0"
 func f11a() *int {
-       select { // ERROR "live at call to selectgo: .autotmp_[0-9]+$"
+       select { // ERROR "stack object .autotmp_[0-9]+ \[2\]struct"
        case <-c:
                return nil
        case <-c:
@@ -178,7 +178,7 @@ func f11b() *int {
                // get to the bottom of the function.
                // This used to have a spurious "live at call to printint: p".
                printint(1) // nothing live here!
-               select {    // ERROR "live at call to selectgo: .autotmp_[0-9]+$"
+               select {    // ERROR "stack object .autotmp_[0-9]+ \[2\]struct"
                case <-c:
                        return nil
                case <-c:
@@ -198,7 +198,7 @@ func f11c() *int {
                // Unlike previous, the cases in this select fall through,
                // so we can get to the println, so p is not dead.
                printint(1) // ERROR "live at call to printint: p$"
-               select {    // ERROR "live at call to selectgo: .autotmp_[0-9]+ p$"
+               select {    // ERROR "live at call to selectgo: p$" "stack object .autotmp_[0-9]+ \[2\]struct"
                case <-c:
                case <-c:
                }
@@ -233,8 +233,8 @@ func h13(string, string) string
 // more incorrectly placed VARDEF.
 
 func f14() {
-       x := g14()
-       printstringpointer(&x) // ERROR "live at call to printstringpointer: x$"
+       x := g14() // ERROR "stack object x string$"
+       printstringpointer(&x)
 }
 
 func g14() string
@@ -254,10 +254,10 @@ func iface() interface{}
 
 func f16() {
        if b {
-               delete(mi, iface()) // ERROR "live at call to mapdelete: .autotmp_[0-9]+$"
+               delete(mi, iface()) // ERROR "stack object .autotmp_[0-9]+ interface \{\}$"
        }
-       delete(mi, iface()) // ERROR "live at call to mapdelete: .autotmp_[0-9]+$"
-       delete(mi, iface()) // ERROR "live at call to mapdelete: .autotmp_[0-9]+$"
+       delete(mi, iface()) // ERROR "stack object .autotmp_[0-9]+ interface \{\}$"
+       delete(mi, iface()) // ERROR "stack object .autotmp_[0-9]+ interface \{\}$"
 }
 
 var m2s map[string]*byte
@@ -300,10 +300,10 @@ func f18() {
        // temporary introduced by orderexpr.
        var z *byte
        if b {
-               z = m2[g18()] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+               z = m2[g18()] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
        }
-       z = m2[g18()] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
-       z = m2[g18()] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+       z = m2[g18()] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
+       z = m2[g18()] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
        printbytepointer(z)
 }
 
@@ -317,30 +317,30 @@ func f19() {
        var z *byte
 
        if b {
-               z = <-ch // ERROR "live at call to chanrecv1: .autotmp_[0-9]+$"
+               z = <-ch // ERROR "stack object .autotmp_[0-9]+ \*byte$"
        }
-       z = <-ch // ERROR "live at call to chanrecv1: .autotmp_[0-9]+$"
-       z = <-ch // ERROR "live at call to chanrecv1: .autotmp_[0-9]+$"
+       z = <-ch // ERROR "stack object .autotmp_[0-9]+ \*byte$"
+       z = <-ch // ERROR "stack object .autotmp_[0-9]+ \*byte$" "live at call to chanrecv1: .autotmp_[0-9]+$"
        printbytepointer(z)
 }
 
 func f20() {
        // src temporary for channel send
        if b {
-               ch <- byteptr() // ERROR "live at call to chansend1: .autotmp_[0-9]+$"
+               ch <- byteptr() // ERROR "stack object .autotmp_[0-9]+ \*byte$"
        }
-       ch <- byteptr() // ERROR "live at call to chansend1: .autotmp_[0-9]+$"
-       ch <- byteptr() // ERROR "live at call to chansend1: .autotmp_[0-9]+$"
+       ch <- byteptr() // ERROR "stack object .autotmp_[0-9]+ \*byte$"
+       ch <- byteptr() // ERROR "stack object .autotmp_[0-9]+ \*byte$"
 }
 
 func f21() {
        // key temporary for mapaccess using array literal key.
        var z *byte
        if b {
-               z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+               z = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
        }
-       z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
-       z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+       z = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
+       z = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
        printbytepointer(z)
 }
 
@@ -349,10 +349,10 @@ func f23() {
        var z *byte
        var ok bool
        if b {
-               z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: .autotmp_[0-9]+$"
+               z, ok = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
        }
-       z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: .autotmp_[0-9]+$"
-       z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: .autotmp_[0-9]+$"
+       z, ok = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
+       z, ok = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
        printbytepointer(z)
        print(ok)
 }
@@ -361,10 +361,10 @@ func f24() {
        // key temporary for map access using array literal key.
        // value temporary too.
        if b {
-               m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: .autotmp_[0-9]+$"
+               m2[[2]string{"x", "y"}] = nil // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
        }
-       m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: .autotmp_[0-9]+$"
-       m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: .autotmp_[0-9]+$"
+       m2[[2]string{"x", "y"}] = nil // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
+       m2[[2]string{"x", "y"}] = nil // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
 }
 
 // defer should not cause spurious ambiguously live variables
@@ -387,10 +387,10 @@ func g25()
 
 func f26(b bool) {
        if b {
-               print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: .autotmp_[0-9]+$"
+               print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "stack object .autotmp_[0-9]+ \[3\]interface \{\}$"
        }
-       print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: .autotmp_[0-9]+$"
-       print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: .autotmp_[0-9]+$"
+       print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "stack object .autotmp_[0-9]+ \[3\]interface \{\}$"
+       print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "stack object .autotmp_[0-9]+ \[3\]interface \{\}$"
        printnl()
 }
 
@@ -402,10 +402,10 @@ func print26(...interface{})
 func f27(b bool) {
        x := 0
        if b {
-               call27(func() { x++ }) // ERROR "live at call to call27: .autotmp_[0-9]+$"
+               call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
        }
-       call27(func() { x++ }) // ERROR "live at call to call27: .autotmp_[0-9]+$"
-       call27(func() { x++ }) // ERROR "live at call to call27: .autotmp_[0-9]+$"
+       call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
+       call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
        printnl()
 }
 
@@ -414,11 +414,11 @@ func f27(b bool) {
 func f27defer(b bool) {
        x := 0
        if b {
-               defer call27(func() { x++ }) // ERROR "live at call to deferproc: .autotmp_[0-9]+$" "live at call to deferreturn: .autotmp_[0-9]+$"
+               defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
        }
-       defer call27(func() { x++ }) // ERROR "f27defer: .autotmp_[0-9]+ \(type struct { F uintptr; x \*int }\) is ambiguously live$" "live at call to deferproc: .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to deferreturn: .autotmp_[0-9]+ .autotmp_[0-9]+$"
-       printnl()                    // ERROR "live at call to printnl: .autotmp_[0-9]+ .autotmp_[0-9]+$"
-} // ERROR "live at call to deferreturn: .autotmp_[0-9]+ .autotmp_[0-9]+$"
+       defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
+       printnl()
+}
 
 // and newproc (go) escapes to the heap
 
@@ -440,24 +440,24 @@ var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string
 
 func f28(b bool) {
        if b {
-               printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: .autotmp_[0-9]+$" "live at call to printstring: .autotmp_[0-9]+$"
+               printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "stack object .autotmp_[0-9]+ \[10\]string$"
        }
-       printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: .autotmp_[0-9]+$" "live at call to printstring: .autotmp_[0-9]+$"
-       printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: .autotmp_[0-9]+$" "live at call to printstring: .autotmp_[0-9]+$"
+       printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "stack object .autotmp_[0-9]+ \[10\]string$"
+       printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "stack object .autotmp_[0-9]+ \[10\]string$"
 }
 
 // map iterator should die on end of range loop
 
 func f29(b bool) {
        if b {
-               for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
+               for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.iter\[string\]int$"
                        printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
                }
        }
-       for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
+       for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.iter\[string\]int$"
                printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
        }
-       for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
+       for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.iter\[string\]int$"
                printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
        }
 }
@@ -473,20 +473,19 @@ type pstruct struct {
 }
 
 func f30(b bool) {
-       // two live temps during printintpointer(p):
-       // in the copy of p.intp and
+       // live temp during printintpointer(p):
        // the internal iterator pointer if a pointer to pstruct in pstructarr
        // can not be easily computed by strength reduction.
        if b {
-               for _, p := range pstructarr {
-                       printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
+               for _, p := range pstructarr { // ERROR "stack object .autotmp_[0-9]+ \[10\]pstruct$"
+                       printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+$"
                }
        }
-       for _, p := range pstructarr {
-               printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
+       for _, p := range pstructarr { // ERROR "stack object .autotmp_[0-9]+ \[10\]pstruct$"
+               printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+$"
        }
-       for _, p := range pstructarr {
-               printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
+       for _, p := range pstructarr { // ERROR "stack object .autotmp_[0-9]+ \[10\]pstruct$"
+               printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+$"
        }
 }
 
@@ -494,13 +493,13 @@ func f30(b bool) {
 
 func f31(b1, b2, b3 bool) {
        if b1 {
-               g31(str()) // ERROR "live at call to convT2Estring: .autotmp_[0-9]+$" "live at call to g31: .autotmp_[0-9]+$"
+               g31(str()) // ERROR "stack object .autotmp_[0-9]+ string$"
        }
        if b2 {
-               h31(str()) // ERROR "live at call to convT2Estring: .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to h31: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$"
+               h31(str()) // ERROR "live at call to convT2Estring: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ string$"
        }
        if b3 {
-               panic(str()) // ERROR "live at call to convT2Estring: .autotmp_[0-9]+$" "live at call to gopanic: .autotmp_[0-9]+$"
+               panic(str()) // ERROR "stack object .autotmp_[0-9]+ string$"
        }
        print(b3)
 }
@@ -520,10 +519,10 @@ var t32 T32
 
 func f32(b bool) {
        if b {
-               call32(t32.Inc) // ERROR "live at call to call32: .autotmp_[0-9]+$"
+               call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{"
        }
-       call32(t32.Inc) // ERROR "live at call to call32: .autotmp_[0-9]+$"
-       call32(t32.Inc) // ERROR "live at call to call32: .autotmp_[0-9]+$"
+       call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{"
+       call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{"
 }
 
 //go:noescape
@@ -535,7 +534,7 @@ func call32(func())
 var m33 map[interface{}]int
 
 func f33() {
-       if m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+       if m33[byteptr()] == 0 { // ERROR "stack object .autotmp_[0-9]+ interface \{\}$"
                printnl()
                return
        } else {
@@ -545,7 +544,7 @@ func f33() {
 }
 
 func f34() {
-       if m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+       if m33[byteptr()] == 0 { // ERROR "stack object .autotmp_[0-9]+ interface \{\}$"
                printnl()
                return
        }
@@ -553,7 +552,8 @@ func f34() {
 }
 
 func f35() {
-       if m33[byteptr()] == 0 && m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" "f35: .autotmp_[0-9]+ \(type interface \{\}\) is ambiguously live$"
+       if m33[byteptr()] == 0 && // ERROR "stack object .autotmp_[0-9]+ interface \{\}"
+               m33[byteptr()] == 0 { // ERROR "stack object .autotmp_[0-9]+ interface \{\}"
                printnl()
                return
        }
@@ -561,7 +561,8 @@ func f35() {
 }
 
 func f36() {
-       if m33[byteptr()] == 0 || m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" "f36: .autotmp_[0-9]+ \(type interface \{\}\) is ambiguously live$"
+       if m33[byteptr()] == 0 || // ERROR "stack object .autotmp_[0-9]+ interface \{\}"
+               m33[byteptr()] == 0 { // ERROR "stack object .autotmp_[0-9]+ interface \{\}"
                printnl()
                return
        }
@@ -569,7 +570,9 @@ func f36() {
 }
 
 func f37() {
-       if (m33[byteptr()] == 0 || m33[byteptr()] == 0) && m33[byteptr()] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$" "f37: .autotmp_[0-9]+ \(type interface \{\}\) is ambiguously live$"
+       if (m33[byteptr()] == 0 || // ERROR "stack object .autotmp_[0-9]+ interface \{\}"
+               m33[byteptr()] == 0) && // ERROR "stack object .autotmp_[0-9]+ interface \{\}"
+               m33[byteptr()] == 0 { // ERROR "stack object .autotmp_[0-9]+ interface \{\}"
                printnl()
                return
        }
@@ -589,14 +592,14 @@ func f38(b bool) {
        // we care that the println lines have no live variables
        // and therefore no output.
        if b {
-               select { // ERROR "live at call to selectgo:( .autotmp_[0-9]+)+$"
+               select { // ERROR "live at call to selectgo:( .autotmp_[0-9]+)+$" "stack object .autotmp_[0-9]+ \[4\]struct \{"
                case <-fc38():
                        printnl()
-               case fc38() <- *fi38(1): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$"
+               case fc38() <- *fi38(1): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "stack object .autotmp_[0-9]+ string$"
                        printnl()
-               case *fi38(2) = <-fc38(): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$"
+               case *fi38(2) = <-fc38(): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "stack object .autotmp_[0-9]+ string$"
                        printnl()
-               case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call to fb38:( .autotmp_[0-9]+)+$" "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$"
+               case *fi38(3), *fb38() = <-fc38(): // ERROR "stack object .autotmp_[0-9]+ string$" "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$"
                        printnl()
                }
                printnl()
@@ -655,15 +658,17 @@ func bad40() {
 }
 
 func good40() {
-       ret := T40{}
-       ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+ ret$"
+       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$"
        t := &ret
-       printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
-       useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
+       printnl() // ERROR "live at call to printnl: ret$"
+       // Note: ret is live at the printnl because the compiler moves &ret
+       // from before the printnl to after.
+       useT40(t)
 }
 
 func ddd1(x, y *int) { // ERROR "live at entry to ddd1: x y$"
-       ddd2(x, y) // ERROR "live at call to ddd2: .autotmp_[0-9]+$"
+       ddd2(x, y) // ERROR "stack object .autotmp_[0-9]+ \[2\]\*int$"
        printnl()
        // Note: no .?autotmp live at printnl.  See issue 16996.
 }
index cc1b0b7acf580cb1902c71ee143b658b747bd255..cea312f075187f2457bf5f36e4f0d9fb54aa8367 100644 (file)
@@ -10,7 +10,6 @@
 package main
 
 // issue 8142: lost 'addrtaken' bit on inlined variables.
-// no inlining in this test, so just checking that non-inlined works.
 
 func printnl()
 
@@ -28,15 +27,15 @@ 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$"
-       useT40(t)     // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
+       t := newT40() // ERROR "live at call to makemap: ret$" "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{}
-       ret.m = make(map[int]int, 42) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
+       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$"
        t := &ret
-       printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
-       useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
+       printnl() // ERROR "live at call to printnl: ret$"
+       useT40(t)
 }
index 65a161c028eb64f1f3ba7bae3392c05beab38964..b7b85bcabf13b4013606eb75a24e908da02c4698 100644 (file)
@@ -19,22 +19,22 @@ func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr"
 
 func g() {
        var t int
-       f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: .?autotmp" "g &t does not escape"
+       f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: .?autotmp" "g &t does not escape" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
 }
 
 func h() {
        var v int
-       syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: .?autotmp" "h &v does not escape"
+       syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: .?autotmp" "h &v does not escape" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
 }
 
 func i() {
        var t int
        p := unsafe.Pointer(&t) // ERROR "i &t does not escape"
-       f(uintptr(p))           // ERROR "live at call to f: .?autotmp"
+       f(uintptr(p))           // ERROR "live at call to f: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
 }
 
 func j() {
        var v int
        p := unsafe.Pointer(&v)              // ERROR "j &v does not escape"
-       syscall.Syscall(0, 1, uintptr(p), 2) // ERROR "live at call to Syscall: .?autotmp"
+       syscall.Syscall(0, 1, uintptr(p), 2) // ERROR "live at call to Syscall: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
 }
diff --git a/test/stackobj.go b/test/stackobj.go
new file mode 100644 (file)
index 0000000..b6af4bd
--- /dev/null
@@ -0,0 +1,57 @@
+// run
+
+// Copyright 2018 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 main
+
+import (
+       "fmt"
+       "runtime"
+)
+
+type HeapObj [8]int64
+
+type StkObj struct {
+       h *HeapObj
+}
+
+var n int
+var c int = -1
+
+func gc() {
+       // encourage heap object to be collected, and have its finalizer run.
+       runtime.GC()
+       runtime.GC()
+       runtime.GC()
+       n++
+}
+
+func main() {
+       f()
+       gc() // prior to stack objects, heap object is not collected until here
+       if c < 0 {
+               panic("heap object never collected")
+       }
+       if c != 1 {
+               panic(fmt.Sprintf("expected collection at phase 1, got phase %d", c))
+       }
+}
+
+func f() {
+       var s StkObj
+       s.h = new(HeapObj)
+       runtime.SetFinalizer(s.h, func(h *HeapObj) {
+               // Remember at what phase the heap object was collected.
+               c = n
+       })
+       g(&s)
+       gc()
+}
+
+func g(s *StkObj) {
+       gc() // heap object is still live here
+       runtime.KeepAlive(s)
+       gc() // heap object should be collected here
+}
diff --git a/test/stackobj3.go b/test/stackobj3.go
new file mode 100644 (file)
index 0000000..8bb8fb3
--- /dev/null
@@ -0,0 +1,93 @@
+// run
+
+// Copyright 2018 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.
+
+// This test makes sure that ambiguously live arguments work correctly.
+
+package main
+
+import (
+       "runtime"
+)
+
+type HeapObj [8]int64
+
+type StkObj struct {
+       h *HeapObj
+}
+
+var n int
+var c int = -1
+
+func gc() {
+       // encourage heap object to be collected, and have its finalizer run.
+       runtime.GC()
+       runtime.GC()
+       runtime.GC()
+       n++
+}
+
+var null StkObj
+
+var sink *HeapObj
+
+//go:noinline
+func use(p *StkObj) {
+}
+
+//go:noinline
+func f(s StkObj, b bool) {
+       var p *StkObj
+       if b {
+               p = &s
+       } else {
+               p = &null
+       }
+       // use is required here to prevent the conditional
+       // code above from being executed after the first gc() call.
+       use(p)
+       // If b==false, h should be collected here.
+       gc() // 0
+       sink = p.h
+       gc() // 1
+       sink = nil
+       // If b==true, h should be collected here.
+       gc() // 2
+}
+
+func fTrue() {
+       var s StkObj
+       s.h = new(HeapObj)
+       c = -1
+       n = 0
+       runtime.SetFinalizer(s.h, func(h *HeapObj) {
+               // Remember at what phase the heap object was collected.
+               c = n
+       })
+       f(s, true)
+       if c != 2 {
+               panic("bad liveness")
+       }
+}
+
+func fFalse() {
+       var s StkObj
+       s.h = new(HeapObj)
+       c = -1
+       n = 0
+       runtime.SetFinalizer(s.h, func(h *HeapObj) {
+               // Remember at what phase the heap object was collected.
+               c = n
+       })
+       f(s, false)
+       if c != 0 {
+               panic("bad liveness")
+       }
+}
+
+func main() {
+       fTrue()
+       fFalse()
+}
index c94bc148c81f5dc0c0e9058cd44c4569eeeae47b..e7b5d721f5fe7d71db2f5d47641bb2ac5d7248d1 100644 (file)
@@ -30,14 +30,14 @@ func F4(...uintptr) {} // ERROR "escaping ...uintptr"
 
 func G() {
        var t int                        // ERROR "moved to heap"
-       F1(uintptr(unsafe.Pointer(&t)))  // ERROR "live at call to F1: .?autotmp" "&t escapes to heap"
+       F1(uintptr(unsafe.Pointer(&t)))  // ERROR "live at call to F1: .?autotmp" "&t escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
        var t2 int                       // ERROR "moved to heap"
-       F3(uintptr(unsafe.Pointer(&t2))) // ERROR "live at call to F3: .?autotmp" "&t2 escapes to heap"
+       F3(uintptr(unsafe.Pointer(&t2))) // ERROR "live at call to F3: .?autotmp" "&t2 escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
 }
 
 func H() {
        var v int                                 // ERROR "moved to heap"
-       F2(0, 1, uintptr(unsafe.Pointer(&v)), 2)  // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap"
+       F2(0, 1, uintptr(unsafe.Pointer(&v)), 2)  // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
        var v2 int                                // ERROR "moved to heap"
-       F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap"
+       F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
 }