mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
cmd/compile,runtime: remove ambiguously live logic
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>
This commit is contained in:
parent
cbafcc55e8
commit
9a8372f8bd
@ -4,9 +4,9 @@ package gc
|
|||||||
|
|
||||||
import "strconv"
|
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 {
|
func (i Op) String() string {
|
||||||
if i >= Op(len(_Op_index)-1) {
|
if i >= Op(len(_Op_index)-1) {
|
||||||
|
@ -78,6 +78,10 @@ import (
|
|||||||
// that its argument is certainly dead, for use when the liveness analysis
|
// that its argument is certainly dead, for use when the liveness analysis
|
||||||
// would not otherwise be able to deduce that fact.
|
// 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.
|
// BlockEffects summarizes the liveness effects on an SSA block.
|
||||||
type BlockEffects struct {
|
type BlockEffects struct {
|
||||||
// Computed during Liveness.prologue using only the content of
|
// 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)
|
// uevar: upward exposed variables (used before set in block)
|
||||||
// varkill: killed variables (set in block)
|
// varkill: killed variables (set in block)
|
||||||
// avarinit: addrtaken variables set or used (proof of initialization)
|
uevar varRegVec
|
||||||
uevar varRegVec
|
varkill varRegVec
|
||||||
varkill varRegVec
|
|
||||||
avarinit bvec
|
|
||||||
|
|
||||||
// Computed during Liveness.solve using control flow information:
|
// Computed during Liveness.solve using control flow information:
|
||||||
//
|
//
|
||||||
// livein: variables live at block entry
|
// livein: variables live at block entry
|
||||||
// liveout: variables live at block exit
|
// liveout: variables live at block exit
|
||||||
// avarinitany: addrtaken variables possibly initialized at block exit
|
livein varRegVec
|
||||||
// (initialized in block or at exit from any predecessor block)
|
liveout varRegVec
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A collection of global state used by liveness analysis.
|
// A collection of global state used by liveness analysis.
|
||||||
@ -186,10 +182,9 @@ func (idx LivenessIndex) Valid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type progeffectscache struct {
|
type progeffectscache struct {
|
||||||
textavarinit []int32
|
retuevar []int32
|
||||||
retuevar []int32
|
tailuevar []int32
|
||||||
tailuevar []int32
|
initialized bool
|
||||||
initialized bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// varRegVec contains liveness bitmaps for variables and registers.
|
// 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
|
// all the parameters for correctness, and similarly it must not
|
||||||
// read the out arguments - they won't be set until the new
|
// read the out arguments - they won't be set until the new
|
||||||
// function runs.
|
// function runs.
|
||||||
|
|
||||||
lv.cache.tailuevar = append(lv.cache.tailuevar, int32(i))
|
lv.cache.tailuevar = append(lv.cache.tailuevar, int32(i))
|
||||||
|
|
||||||
if node.Addrtaken() {
|
|
||||||
lv.cache.textavarinit = append(lv.cache.textavarinit, int32(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
case PPARAMOUT:
|
case PPARAMOUT:
|
||||||
// If the result had its address taken, it is being tracked
|
// All results are live at every return point.
|
||||||
// by the avarinit code, which does not use uevar.
|
// Note that this point is after escaping return values
|
||||||
// If we added it to uevar too, we'd not see any kill
|
// are copied back to the stack using their PAUTOHEAP references.
|
||||||
// and decide that the variable was live entry, which it is not.
|
lv.cache.retuevar = append(lv.cache.retuevar, int32(i))
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,21 +275,13 @@ func (lv *Liveness) initcache() {
|
|||||||
//
|
//
|
||||||
// The possible flags are:
|
// The possible flags are:
|
||||||
// uevar - used by the instruction
|
// uevar - used by the instruction
|
||||||
// varkill - killed by the instruction
|
// varkill - killed by the instruction (set)
|
||||||
// for variables without address taken, means variable was set
|
// A kill happens after the use (for an instruction that updates a value, for example).
|
||||||
// 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.
|
|
||||||
type liveEffect int
|
type liveEffect int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
uevar liveEffect = 1 << iota
|
uevar liveEffect = 1 << iota
|
||||||
varkill
|
varkill
|
||||||
avarinit
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// valueEffects returns the index of a variable in lv.vars and the
|
// 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
|
var effect liveEffect
|
||||||
if n.Addrtaken() {
|
// Read is a read, obviously.
|
||||||
if v.Op != ssa.OpVarKill {
|
//
|
||||||
effect |= avarinit
|
// 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 v.Op == ssa.OpVarDef || v.Op == ssa.OpVarKill {
|
if e&(ssa.SymRead|ssa.SymAddr) != 0 {
|
||||||
effect |= varkill
|
effect |= uevar
|
||||||
}
|
}
|
||||||
} else {
|
if e&ssa.SymWrite != 0 && (!isfat(n.Type) || v.Op == ssa.OpVarDef) {
|
||||||
// Read is a read, obviously.
|
effect |= varkill
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if effect == 0 {
|
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.varkill = varRegVec{vars: bulk.next()}
|
||||||
be.livein = varRegVec{vars: bulk.next()}
|
be.livein = varRegVec{vars: bulk.next()}
|
||||||
be.liveout = 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())
|
lv.livenessMap.reset(lv.f.NumValues())
|
||||||
|
|
||||||
@ -869,19 +830,6 @@ func (lv *Liveness) prologue() {
|
|||||||
}
|
}
|
||||||
be.uevar.regs |= regUevar
|
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))
|
nvars := int32(len(lv.vars))
|
||||||
newlivein := varRegVec{vars: bvalloc(nvars)}
|
newlivein := varRegVec{vars: bvalloc(nvars)}
|
||||||
newliveout := varRegVec{vars: bvalloc(nvars)}
|
newliveout := varRegVec{vars: bvalloc(nvars)}
|
||||||
any := bvalloc(nvars)
|
|
||||||
all := bvalloc(nvars)
|
|
||||||
|
|
||||||
// Push avarinitall, avarinitany forward.
|
// Walk blocks in postorder ordering. This improves convergence.
|
||||||
// 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.
|
|
||||||
po := lv.f.Postorder()
|
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
|
// Iterate through the blocks in reverse round-robin fashion. A work
|
||||||
// queue might be slightly faster. As is, the number of iterations is
|
// queue might be slightly faster. As is, the number of iterations is
|
||||||
// so low that it hardly seems to be worth the complexity.
|
// so low that it hardly seems to be worth the complexity.
|
||||||
@ -957,7 +864,7 @@ func (lv *Liveness) solve() {
|
|||||||
newliveout.vars.Set(pos)
|
newliveout.vars.Set(pos)
|
||||||
}
|
}
|
||||||
case ssa.BlockExit:
|
case ssa.BlockExit:
|
||||||
// nothing to do
|
// panic exit - nothing to do
|
||||||
default:
|
default:
|
||||||
// A variable is live on output from this block
|
// A variable is live on output from this block
|
||||||
// if it is live on input to some successor.
|
// 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
|
// 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.
|
// not set by the code in this block.
|
||||||
//
|
//
|
||||||
// in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
|
// in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
|
||||||
@ -990,8 +897,6 @@ func (lv *Liveness) solve() {
|
|||||||
func (lv *Liveness) epilogue() {
|
func (lv *Liveness) epilogue() {
|
||||||
nvars := int32(len(lv.vars))
|
nvars := int32(len(lv.vars))
|
||||||
liveout := varRegVec{vars: bvalloc(nvars)}
|
liveout := varRegVec{vars: bvalloc(nvars)}
|
||||||
any := bvalloc(nvars)
|
|
||||||
all := bvalloc(nvars)
|
|
||||||
livedefer := bvalloc(nvars) // always-live variables
|
livedefer := bvalloc(nvars) // always-live variables
|
||||||
|
|
||||||
// If there is a defer (that could recover), then all output
|
// If there is a defer (that could recover), then all output
|
||||||
@ -1017,6 +922,9 @@ func (lv *Liveness) epilogue() {
|
|||||||
livedefer.Set(int32(i))
|
livedefer.Set(int32(i))
|
||||||
}
|
}
|
||||||
if n.IsOutputParamHeapAddr() {
|
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)
|
n.Name.SetNeedzero(true)
|
||||||
livedefer.Set(int32(i))
|
livedefer.Set(int32(i))
|
||||||
}
|
}
|
||||||
@ -1033,9 +941,6 @@ func (lv *Liveness) epilogue() {
|
|||||||
{
|
{
|
||||||
// Reserve an entry for function entry.
|
// Reserve an entry for function entry.
|
||||||
live := bvalloc(nvars)
|
live := bvalloc(nvars)
|
||||||
for _, pos := range lv.cache.textavarinit {
|
|
||||||
live.Set(pos)
|
|
||||||
}
|
|
||||||
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1043,53 +948,14 @@ func (lv *Liveness) epilogue() {
|
|||||||
be := lv.blockEffects(b)
|
be := lv.blockEffects(b)
|
||||||
firstBitmapIndex := len(lv.livevars)
|
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
|
// Walk forward through the basic block instructions and
|
||||||
// allocate liveness maps for those instructions that need them.
|
// allocate liveness maps for those instructions that need them.
|
||||||
// Seed the maps with information about the addrtaken variables.
|
|
||||||
for _, v := range b.Values {
|
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) {
|
if !lv.issafepoint(v) {
|
||||||
continue
|
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 := bvalloc(nvars)
|
||||||
live.Copy(any)
|
|
||||||
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
lv.livevars = append(lv.livevars, varRegVec{vars: live})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1128,6 +994,17 @@ func (lv *Liveness) epilogue() {
|
|||||||
Fatalf("bad index for entry point: %v", index)
|
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.
|
// Record live variables.
|
||||||
live := &lv.livevars[index]
|
live := &lv.livevars[index]
|
||||||
live.Or(*live, liveout)
|
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)
|
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
|
// Compact coalesces identical bitmaps from lv.livevars into the sets
|
||||||
// lv.stackMapSet and lv.regMaps.
|
// lv.stackMapSet and lv.regMaps.
|
||||||
//
|
//
|
||||||
@ -1559,7 +1414,6 @@ func (lv *Liveness) printDebug() {
|
|||||||
printed = false
|
printed = false
|
||||||
printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0, regUevar)
|
printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0, regUevar)
|
||||||
printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0, regKill)
|
printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0, regKill)
|
||||||
printed = lv.printeffect(printed, "avarinit", pos, effect&avarinit != 0, 0)
|
|
||||||
if printed {
|
if printed {
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
@ -1596,9 +1450,6 @@ func (lv *Liveness) printDebug() {
|
|||||||
printed = false
|
printed = false
|
||||||
printed = lv.printbvec(printed, "varkill", be.varkill)
|
printed = lv.printbvec(printed, "varkill", be.varkill)
|
||||||
printed = lv.printbvec(printed, "liveout", be.liveout)
|
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 {
|
if printed {
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
|
@ -839,6 +839,10 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
|
|||||||
a = nod(OAS, x, nil)
|
a = nod(OAS, x, nil)
|
||||||
a = typecheck(a, Etop)
|
a = typecheck(a, Etop)
|
||||||
init.Append(a) // zero new temp
|
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)
|
a = nod(OADDR, x, nil)
|
||||||
@ -849,6 +853,8 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
|
|||||||
a = typecheck(a, Etop)
|
a = typecheck(a, Etop)
|
||||||
init.Append(a) // zero new temp
|
init.Append(a) // zero new temp
|
||||||
a = a.Left
|
a = a.Left
|
||||||
|
} else {
|
||||||
|
init.Append(nod(OVARDEF, a, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
a = nod(OADDR, a, nil)
|
a = nod(OADDR, a, nil)
|
||||||
|
@ -755,8 +755,8 @@ func (s *state) stmtList(l Nodes) {
|
|||||||
|
|
||||||
// stmt converts the statement n to SSA and adds it to s.
|
// stmt converts the statement n to SSA and adds it to s.
|
||||||
func (s *state) stmt(n *Node) {
|
func (s *state) stmt(n *Node) {
|
||||||
if !(n.Op == OVARKILL || n.Op == OVARLIVE) {
|
if !(n.Op == OVARKILL || n.Op == OVARLIVE || n.Op == OVARDEF) {
|
||||||
// OVARKILL and OVARLIVE are invisible to the programmer, so we don't use their line numbers to avoid confusion in debugging.
|
// 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)
|
s.pushLine(n.Pos)
|
||||||
defer s.popLine()
|
defer s.popLine()
|
||||||
}
|
}
|
||||||
@ -1170,6 +1170,10 @@ func (s *state) stmt(n *Node) {
|
|||||||
}
|
}
|
||||||
s.startBlock(bEnd)
|
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:
|
case OVARKILL:
|
||||||
// Insert a varkill op to record that a variable is no longer live.
|
// 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
|
// 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.Type = obj.TYPE_MEM
|
||||||
p.To.Name = obj.NAME_EXTERN
|
p.To.Name = obj.NAME_EXTERN
|
||||||
p.To.Sym = x
|
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.
|
// genssa appends entries to pp for each instruction in f.
|
||||||
@ -5056,24 +5066,8 @@ func genssa(f *ssa.Func, pp *Progs) {
|
|||||||
case ssa.OpGetG:
|
case ssa.OpGetG:
|
||||||
// nothing to do when there's a g register,
|
// nothing to do when there's a g register,
|
||||||
// and checkLower complains if there's not
|
// 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
|
// 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:
|
case ssa.OpPhi:
|
||||||
CheckLoweredPhi(v)
|
CheckLoweredPhi(v)
|
||||||
case ssa.OpConvert:
|
case ssa.OpConvert:
|
||||||
|
@ -739,6 +739,7 @@ const (
|
|||||||
OCLOSUREVAR // variable reference at beginning of closure function
|
OCLOSUREVAR // variable reference at beginning of closure function
|
||||||
OCFUNC // reference to c function pointer (not go func value)
|
OCFUNC // reference to c function pointer (not go func value)
|
||||||
OCHECKNIL // emit code to ensure pointer/interface not nil
|
OCHECKNIL // emit code to ensure pointer/interface not nil
|
||||||
|
OVARDEF // variable is about to be fully initialized
|
||||||
OVARKILL // variable is dead
|
OVARKILL // variable is dead
|
||||||
OVARLIVE // variable is alive
|
OVARLIVE // variable is alive
|
||||||
OINDREGSP // offset plus indirect of REGSP, such as 8(SP).
|
OINDREGSP // offset plus indirect of REGSP, such as 8(SP).
|
||||||
|
@ -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: "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: "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
|
// TODO: what's the difference betweeen VarLive and KeepAlive?
|
||||||
{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
|
{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
|
// Ops for breaking 64-bit operations on 32-bit architectures
|
||||||
{name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
|
{name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
|
||||||
|
@ -232,6 +232,11 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
|
|||||||
gp.param = nil
|
gp.param = nil
|
||||||
c.sendq.enqueue(mysg)
|
c.sendq.enqueue(mysg)
|
||||||
goparkunlock(&c.lock, waitReasonChanSend, traceEvGoBlockSend, 3)
|
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.
|
// someone woke us up.
|
||||||
if mysg != gp.waiting {
|
if mysg != gp.waiting {
|
||||||
|
@ -35,14 +35,46 @@ func TestGCInfo(t *testing.T) {
|
|||||||
verifyGCInfo(t, "data eface", &dataEface, infoEface)
|
verifyGCInfo(t, "data eface", &dataEface, infoEface)
|
||||||
verifyGCInfo(t, "data iface", &dataIface, infoIface)
|
verifyGCInfo(t, "data iface", &dataIface, infoIface)
|
||||||
|
|
||||||
verifyGCInfo(t, "stack Ptr", new(Ptr), infoPtr)
|
{
|
||||||
verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
|
var x Ptr
|
||||||
verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
|
verifyGCInfo(t, "stack Ptr", &x, infoPtr)
|
||||||
verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
|
runtime.KeepAlive(x)
|
||||||
verifyGCInfo(t, "stack string", new(string), infoString)
|
}
|
||||||
verifyGCInfo(t, "stack slice", new([]string), infoSlice)
|
{
|
||||||
verifyGCInfo(t, "stack eface", new(interface{}), infoEface)
|
var x ScalarPtr
|
||||||
verifyGCInfo(t, "stack iface", new(Iface), infoIface)
|
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++ {
|
for i := 0; i < 10; i++ {
|
||||||
verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(padDead(infoPtr)))
|
verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(padDead(infoPtr)))
|
||||||
|
@ -1994,7 +1994,9 @@ func reflect_gcbits(x interface{}) []byte {
|
|||||||
return ret
|
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) {
|
func getgcmask(ep interface{}) (mask []byte) {
|
||||||
e := *efaceOf(&ep)
|
e := *efaceOf(&ep)
|
||||||
p := e.data
|
p := e.data
|
||||||
@ -2051,11 +2053,6 @@ func getgcmask(ep interface{}) (mask []byte) {
|
|||||||
_g_ := getg()
|
_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)
|
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() {
|
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)
|
locals, _, _ := getStackMap(&frame, nil, false)
|
||||||
if locals.n == 0 {
|
if locals.n == 0 {
|
||||||
return
|
return
|
||||||
|
@ -15,10 +15,10 @@ type T struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func f(a T) { // ERROR "live at entry to f: a"
|
func f(a T) { // ERROR "live at entry to f: a"
|
||||||
var e interface{}
|
var e interface{} // ERROR "stack object e interface \{\}$"
|
||||||
func() { // ERROR "live at entry to f.func1: a &e"
|
func() { // ERROR "live at entry to f.func1: a &e"
|
||||||
e = a.s // ERROR "live at call to convT2Estring: a &e"
|
e = a.s // ERROR "live at call to convT2Estring: &e" "stack object a T$"
|
||||||
}() // ERROR "live at call to f.func1: e$"
|
}()
|
||||||
// Before the fix, both a and e were live at the previous line.
|
// Before the fix, both a and e were live at the previous line.
|
||||||
_ = e
|
_ = e
|
||||||
}
|
}
|
||||||
|
175
test/live.go
175
test/live.go
@ -32,9 +32,9 @@ func printbytepointer(*byte)
|
|||||||
func printint(int)
|
func printint(int)
|
||||||
|
|
||||||
func f1() {
|
func f1() {
|
||||||
var x *int
|
var x *int // ERROR "stack object x \*int$"
|
||||||
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
|
||||||
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
||||||
|
printpointer(&x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func f2(b bool) {
|
func f2(b bool) {
|
||||||
@ -42,9 +42,9 @@ func f2(b bool) {
|
|||||||
printint(0) // nothing live here
|
printint(0) // nothing live here
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var x *int
|
var x *int // ERROR "stack object x \*int$"
|
||||||
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
|
||||||
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
||||||
|
printpointer(&x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func f3(b1, b2 bool) {
|
func f3(b1, b2 bool) {
|
||||||
@ -60,15 +60,15 @@ func f3(b1, b2 bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if b2 {
|
if b2 {
|
||||||
var x *int
|
var x *int // ERROR "stack object x \*int$"
|
||||||
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
|
||||||
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
printpointer(&x) // ERROR "live at call to printpointer: x$"
|
||||||
|
printpointer(&x)
|
||||||
} else {
|
} else {
|
||||||
var y *int
|
var y *int // ERROR "stack object y \*int$"
|
||||||
printpointer(&y) // ERROR "live at call to printpointer: y$"
|
|
||||||
printpointer(&y) // ERROR "live at call to printpointer: y$"
|
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
|
// 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
|
return
|
||||||
}
|
}
|
||||||
var z **int
|
var z **int
|
||||||
x := new(int)
|
x := new(int) // ERROR "stack object x \*int$"
|
||||||
*x = 42
|
*x = 42
|
||||||
z = &x
|
z = &x
|
||||||
printint(**z) // ERROR "live at call to printint: 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) {
|
func f5(b1 bool) {
|
||||||
var z **int
|
var z **int
|
||||||
if b1 {
|
if b1 {
|
||||||
x := new(int)
|
x := new(int) // ERROR "stack object x \*int$"
|
||||||
*x = 42
|
*x = 42
|
||||||
z = &x
|
z = &x
|
||||||
} else {
|
} else {
|
||||||
y := new(int)
|
y := new(int) // ERROR "stack object y \*int$"
|
||||||
*y = 54
|
*y = 54
|
||||||
z = &y
|
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: _".
|
// 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".
|
// 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
|
||||||
x = "hello"
|
x = "hello"
|
||||||
return
|
return
|
||||||
@ -141,7 +141,7 @@ var i9 interface{}
|
|||||||
func f9() bool {
|
func f9() bool {
|
||||||
g8()
|
g8()
|
||||||
x := i9
|
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
|
i9 = y // make y escape so the line above has to call convT2E
|
||||||
return x != y
|
return x != y
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ var b bool
|
|||||||
|
|
||||||
// this used to have a spurious "live at entry to f11a: ~r0"
|
// this used to have a spurious "live at entry to f11a: ~r0"
|
||||||
func f11a() *int {
|
func f11a() *int {
|
||||||
select { // ERROR "live at call to selectgo: .autotmp_[0-9]+$"
|
select { // ERROR "stack object .autotmp_[0-9]+ \[2\]struct"
|
||||||
case <-c:
|
case <-c:
|
||||||
return nil
|
return nil
|
||||||
case <-c:
|
case <-c:
|
||||||
@ -178,7 +178,7 @@ func f11b() *int {
|
|||||||
// get to the bottom of the function.
|
// get to the bottom of the function.
|
||||||
// This used to have a spurious "live at call to printint: p".
|
// This used to have a spurious "live at call to printint: p".
|
||||||
printint(1) // nothing live here!
|
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:
|
case <-c:
|
||||||
return nil
|
return nil
|
||||||
case <-c:
|
case <-c:
|
||||||
@ -198,7 +198,7 @@ func f11c() *int {
|
|||||||
// Unlike previous, the cases in this select fall through,
|
// Unlike previous, the cases in this select fall through,
|
||||||
// so we can get to the println, so p is not dead.
|
// so we can get to the println, so p is not dead.
|
||||||
printint(1) // ERROR "live at call to printint: p$"
|
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:
|
||||||
case <-c:
|
case <-c:
|
||||||
}
|
}
|
||||||
@ -233,8 +233,8 @@ func h13(string, string) string
|
|||||||
// more incorrectly placed VARDEF.
|
// more incorrectly placed VARDEF.
|
||||||
|
|
||||||
func f14() {
|
func f14() {
|
||||||
x := g14()
|
x := g14() // ERROR "stack object x string$"
|
||||||
printstringpointer(&x) // ERROR "live at call to printstringpointer: x$"
|
printstringpointer(&x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func g14() string
|
func g14() string
|
||||||
@ -254,10 +254,10 @@ func iface() interface{}
|
|||||||
|
|
||||||
func f16() {
|
func f16() {
|
||||||
if b {
|
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 "stack object .autotmp_[0-9]+ interface \{\}$"
|
||||||
delete(mi, iface()) // ERROR "live at call to mapdelete: .autotmp_[0-9]+$"
|
delete(mi, iface()) // ERROR "stack object .autotmp_[0-9]+ interface \{\}$"
|
||||||
}
|
}
|
||||||
|
|
||||||
var m2s map[string]*byte
|
var m2s map[string]*byte
|
||||||
@ -300,10 +300,10 @@ func f18() {
|
|||||||
// temporary introduced by orderexpr.
|
// temporary introduced by orderexpr.
|
||||||
var z *byte
|
var z *byte
|
||||||
if b {
|
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 "stack object .autotmp_[0-9]+ \[2\]string$"
|
||||||
z = m2[g18()] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
|
z = m2[g18()] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
|
||||||
printbytepointer(z)
|
printbytepointer(z)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,30 +317,30 @@ func f19() {
|
|||||||
var z *byte
|
var z *byte
|
||||||
|
|
||||||
if b {
|
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 "stack object .autotmp_[0-9]+ \*byte$"
|
||||||
z = <-ch // ERROR "live at call to chanrecv1: .autotmp_[0-9]+$"
|
z = <-ch // ERROR "stack object .autotmp_[0-9]+ \*byte$" "live at call to chanrecv1: .autotmp_[0-9]+$"
|
||||||
printbytepointer(z)
|
printbytepointer(z)
|
||||||
}
|
}
|
||||||
|
|
||||||
func f20() {
|
func f20() {
|
||||||
// src temporary for channel send
|
// src temporary for channel send
|
||||||
if b {
|
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 "stack object .autotmp_[0-9]+ \*byte$"
|
||||||
ch <- byteptr() // ERROR "live at call to chansend1: .autotmp_[0-9]+$"
|
ch <- byteptr() // ERROR "stack object .autotmp_[0-9]+ \*byte$"
|
||||||
}
|
}
|
||||||
|
|
||||||
func f21() {
|
func f21() {
|
||||||
// key temporary for mapaccess using array literal key.
|
// key temporary for mapaccess using array literal key.
|
||||||
var z *byte
|
var z *byte
|
||||||
if b {
|
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 "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 "stack object .autotmp_[0-9]+ \[2\]string$"
|
||||||
printbytepointer(z)
|
printbytepointer(z)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,10 +349,10 @@ func f23() {
|
|||||||
var z *byte
|
var z *byte
|
||||||
var ok bool
|
var ok bool
|
||||||
if b {
|
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 "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 "stack object .autotmp_[0-9]+ \[2\]string$"
|
||||||
printbytepointer(z)
|
printbytepointer(z)
|
||||||
print(ok)
|
print(ok)
|
||||||
}
|
}
|
||||||
@ -361,10 +361,10 @@ func f24() {
|
|||||||
// key temporary for map access using array literal key.
|
// key temporary for map access using array literal key.
|
||||||
// value temporary too.
|
// value temporary too.
|
||||||
if b {
|
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 "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 "stack object .autotmp_[0-9]+ \[2\]string$"
|
||||||
}
|
}
|
||||||
|
|
||||||
// defer should not cause spurious ambiguously live variables
|
// defer should not cause spurious ambiguously live variables
|
||||||
@ -387,10 +387,10 @@ func g25()
|
|||||||
|
|
||||||
func f26(b bool) {
|
func f26(b bool) {
|
||||||
if b {
|
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 "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 "stack object .autotmp_[0-9]+ \[3\]interface \{\}$"
|
||||||
printnl()
|
printnl()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,10 +402,10 @@ func print26(...interface{})
|
|||||||
func f27(b bool) {
|
func f27(b bool) {
|
||||||
x := 0
|
x := 0
|
||||||
if b {
|
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 "stack object .autotmp_[0-9]+ struct \{"
|
||||||
call27(func() { x++ }) // ERROR "live at call to call27: .autotmp_[0-9]+$"
|
call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
|
||||||
printnl()
|
printnl()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,11 +414,11 @@ func f27(b bool) {
|
|||||||
func f27defer(b bool) {
|
func f27defer(b bool) {
|
||||||
x := 0
|
x := 0
|
||||||
if b {
|
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]+$"
|
defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
|
||||||
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ .autotmp_[0-9]+$"
|
printnl()
|
||||||
} // ERROR "live at call to deferreturn: .autotmp_[0-9]+ .autotmp_[0-9]+$"
|
}
|
||||||
|
|
||||||
// and newproc (go) escapes to the heap
|
// 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) {
|
func f28(b bool) {
|
||||||
if b {
|
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 "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 "stack object .autotmp_[0-9]+ \[10\]string$"
|
||||||
}
|
}
|
||||||
|
|
||||||
// map iterator should die on end of range loop
|
// map iterator should die on end of range loop
|
||||||
|
|
||||||
func f29(b bool) {
|
func f29(b bool) {
|
||||||
if b {
|
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]+$"
|
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]+$"
|
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]+$"
|
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,20 +473,19 @@ type pstruct struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func f30(b bool) {
|
func f30(b bool) {
|
||||||
// two live temps during printintpointer(p):
|
// live temp during printintpointer(p):
|
||||||
// in the copy of p.intp and
|
|
||||||
// the internal iterator pointer if a pointer to pstruct in pstructarr
|
// the internal iterator pointer if a pointer to pstruct in pstructarr
|
||||||
// can not be easily computed by strength reduction.
|
// can not be easily computed by strength reduction.
|
||||||
if b {
|
if b {
|
||||||
for _, p := range pstructarr {
|
for _, p := range pstructarr { // ERROR "stack object .autotmp_[0-9]+ \[10\]pstruct$"
|
||||||
printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
|
printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, p := range pstructarr {
|
for _, p := range pstructarr { // ERROR "stack object .autotmp_[0-9]+ \[10\]pstruct$"
|
||||||
printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
|
printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+$"
|
||||||
}
|
}
|
||||||
for _, p := range pstructarr {
|
for _, p := range pstructarr { // ERROR "stack object .autotmp_[0-9]+ \[10\]pstruct$"
|
||||||
printintpointer(p.intp) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
|
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) {
|
func f31(b1, b2, b3 bool) {
|
||||||
if b1 {
|
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 {
|
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 {
|
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)
|
print(b3)
|
||||||
}
|
}
|
||||||
@ -520,10 +519,10 @@ var t32 T32
|
|||||||
|
|
||||||
func f32(b bool) {
|
func f32(b bool) {
|
||||||
if b {
|
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 "stack object .autotmp_[0-9]+ struct \{"
|
||||||
call32(t32.Inc) // ERROR "live at call to call32: .autotmp_[0-9]+$"
|
call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{"
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
@ -535,7 +534,7 @@ func call32(func())
|
|||||||
var m33 map[interface{}]int
|
var m33 map[interface{}]int
|
||||||
|
|
||||||
func f33() {
|
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()
|
printnl()
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@ -545,7 +544,7 @@ func f33() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func f34() {
|
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()
|
printnl()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -553,7 +552,8 @@ func f34() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func f35() {
|
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()
|
printnl()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -561,7 +561,8 @@ func f35() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func f36() {
|
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()
|
printnl()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -569,7 +570,9 @@ func f36() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func f37() {
|
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()
|
printnl()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -589,14 +592,14 @@ func f38(b bool) {
|
|||||||
// we care that the println lines have no live variables
|
// we care that the println lines have no live variables
|
||||||
// and therefore no output.
|
// and therefore no output.
|
||||||
if b {
|
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():
|
case <-fc38():
|
||||||
printnl()
|
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()
|
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()
|
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()
|
||||||
}
|
}
|
||||||
printnl()
|
printnl()
|
||||||
@ -655,15 +658,17 @@ func bad40() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func good40() {
|
func good40() {
|
||||||
ret := T40{}
|
ret := T40{} // ERROR "stack object ret T40$"
|
||||||
ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+ ret$"
|
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
|
t := &ret
|
||||||
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
|
printnl() // ERROR "live at call to printnl: ret$"
|
||||||
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ 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$"
|
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()
|
printnl()
|
||||||
// Note: no .?autotmp live at printnl. See issue 16996.
|
// Note: no .?autotmp live at printnl. See issue 16996.
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
// issue 8142: lost 'addrtaken' bit on inlined variables.
|
// issue 8142: lost 'addrtaken' bit on inlined variables.
|
||||||
// no inlining in this test, so just checking that non-inlined works.
|
|
||||||
|
|
||||||
func printnl()
|
func printnl()
|
||||||
|
|
||||||
@ -28,15 +27,15 @@ 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: ret$" "stack object ret T40$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$"
|
||||||
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
|
printnl() // ERROR "live at call to printnl: ret$"
|
||||||
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
|
useT40(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func good40() {
|
func good40() {
|
||||||
ret := T40{}
|
ret := T40{} // ERROR "stack object ret T40$"
|
||||||
ret.m = make(map[int]int, 42) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
|
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
|
t := &ret
|
||||||
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
|
printnl() // ERROR "live at call to printnl: ret$"
|
||||||
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
|
useT40(t)
|
||||||
}
|
}
|
||||||
|
@ -19,22 +19,22 @@ func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr"
|
|||||||
|
|
||||||
func g() {
|
func g() {
|
||||||
var t int
|
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() {
|
func h() {
|
||||||
var v int
|
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() {
|
func i() {
|
||||||
var t int
|
var t int
|
||||||
p := unsafe.Pointer(&t) // ERROR "i &t does not escape"
|
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() {
|
func j() {
|
||||||
var v int
|
var v int
|
||||||
p := unsafe.Pointer(&v) // ERROR "j &v does not escape"
|
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$"
|
||||||
}
|
}
|
||||||
|
57
test/stackobj.go
Normal file
57
test/stackobj.go
Normal file
@ -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
|
||||||
|
}
|
93
test/stackobj3.go
Normal file
93
test/stackobj3.go
Normal file
@ -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()
|
||||||
|
}
|
@ -30,14 +30,14 @@ func F4(...uintptr) {} // ERROR "escaping ...uintptr"
|
|||||||
|
|
||||||
func G() {
|
func G() {
|
||||||
var t int // ERROR "moved to heap"
|
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"
|
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() {
|
func H() {
|
||||||
var v int // ERROR "moved to heap"
|
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"
|
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$"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user