diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go index ea9287712c..5ad73bfc0d 100644 --- a/src/cmd/compile/internal/escape/escape.go +++ b/src/cmd/compile/internal/escape/escape.go @@ -129,8 +129,7 @@ func Batch(fns []*ir.Func, recursive bool) { } var b batch - b.heapLoc.escapes = true - b.heapLoc.persists = true + b.heapLoc.attrs = attrEscapes | attrPersists // Construct data-flow graph from syntax trees. for _, fn := range fns { @@ -301,7 +300,7 @@ func (b *batch) finish(fns []*ir.Func) { // TODO(mdempsky): Update tests to expect this. goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper() - if loc.escapes { + if loc.hasAttr(attrEscapes) { if n.Op() == ir.ONAME { if base.Flag.CompilingRuntime { base.ErrorfAt(n.Pos(), 0, "%v escapes to heap, not allowed in runtime", n) @@ -324,7 +323,7 @@ func (b *batch) finish(fns []*ir.Func) { base.WarnfAt(n.Pos(), "%v does not escape", n) } n.SetEsc(ir.EscNone) - if !loc.persists { + if !loc.hasAttr(attrPersists) { switch n.Op() { case ir.OCLOSURE: n := n.(*ir.ClosureExpr) @@ -453,7 +452,7 @@ func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string { esc := loc.paramEsc esc.Optimize() - if diagnose && !loc.escapes { + if diagnose && !loc.hasAttr(attrEscapes) { if esc.Empty() { base.WarnfAt(f.Pos, "%v does not escape", name()) } diff --git a/src/cmd/compile/internal/escape/graph.go b/src/cmd/compile/internal/escape/graph.go index ad97b7c28c..9b3a4558fb 100644 --- a/src/cmd/compile/internal/escape/graph.go +++ b/src/cmd/compile/internal/escape/graph.go @@ -66,15 +66,8 @@ type location struct { // in the walk queue. queued bool - // escapes reports whether the represented variable's address - // escapes; that is, whether the variable must be heap - // allocated. - escapes bool - - // persists reports whether the represented expression's address - // outlives the statement; that is, whether its storage cannot be - // immediately reused. - persists bool + // attrs is a bitset of location attributes. + attrs locAttr // paramEsc records the represented parameter's leak set. paramEsc leaks @@ -84,6 +77,21 @@ type location struct { addrtaken bool // has this variable's address been taken? } +type locAttr uint8 + +const ( + // attrEscapes indicates whether the represented variable's address + // escapes; that is, whether the variable must be heap allocated. + attrEscapes locAttr = 1 << iota + + // attrPersists indicates whether the represented expression's + // address outlives the statement; that is, whether its storage + // cannot be immediately reused. + attrPersists +) + +func (l *location) hasAttr(attr locAttr) bool { return l.attrs&attr != 0 } + // An edge represents an assignment edge between two Go variables. type edge struct { src *location @@ -100,7 +108,7 @@ func (l *location) leakTo(sink *location, derefs int) { // If sink is a result parameter that doesn't escape (#44614) // and we can fit return bits into the escape analysis tag, // then record as a result leak. - if !sink.escapes && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn { + if !sink.hasAttr(attrEscapes) && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn { ri := sink.resultIndex - 1 if ri < numEscResults { // Leak to result parameter. @@ -182,7 +190,7 @@ func (b *batch) flow(k hole, src *location) { if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ... return } - if dst.escapes && k.derefs < 0 { // dst = &src + if dst.hasAttr(attrEscapes) && k.derefs < 0 { // dst = &src if base.Flag.LowerM >= 2 || logopt.Enabled() { pos := base.FmtPos(src.n.Pos()) if base.Flag.LowerM >= 2 { @@ -195,7 +203,7 @@ func (b *batch) flow(k hole, src *location) { } } - src.escapes = true + src.attrs |= attrEscapes return } @@ -230,7 +238,9 @@ func (e *escape) newLoc(n ir.Node, persists bool) *location { n: n, curfn: e.curfn, loopDepth: e.loopDepth, - persists: persists, + } + if persists { + loc.attrs |= attrPersists } e.allLocs = append(e.allLocs, loc) if n != nil { diff --git a/src/cmd/compile/internal/escape/solve.go b/src/cmd/compile/internal/escape/solve.go index 2856c9c131..12866c0dc8 100644 --- a/src/cmd/compile/internal/escape/solve.go +++ b/src/cmd/compile/internal/escape/solve.go @@ -79,8 +79,8 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) // If l's address flows to a persistent location, then l needs // to persist too. - if root.persists && !l.persists { - l.persists = true + if root.hasAttr(attrPersists) && !l.hasAttr(attrPersists) { + l.attrs |= attrPersists enqueue(l) } } @@ -92,7 +92,7 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) // that value flow for tagging the function // later. if l.isName(ir.PPARAM) { - if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes { + if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.hasAttr(attrEscapes) { if base.Flag.LowerM >= 2 { fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs) } @@ -109,7 +109,7 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) // If l's address flows somewhere that // outlives it, then l needs to be heap // allocated. - if addressOf && !l.escapes { + if addressOf && !l.hasAttr(attrEscapes) { if logopt.Enabled() || base.Flag.LowerM >= 2 { if base.Flag.LowerM >= 2 { fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n) @@ -120,14 +120,14 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation) } } - l.escapes = true + l.attrs |= attrEscapes enqueue(l) continue } } for i, edge := range l.edges { - if edge.src.escapes { + if edge.src.hasAttr(attrEscapes) { continue } d := derefs + edge.derefs @@ -227,7 +227,7 @@ func (b *batch) explainLoc(l *location) string { // other's lifetime if stack allocated. func (b *batch) outlives(l, other *location) bool { // The heap outlives everything. - if l.escapes { + if l.hasAttr(attrEscapes) { return true }