mirror of
https://github.com/golang/go
synced 2024-11-26 21:11:57 -07:00
cmd/compile/internal/escape: change escapes and persists into bitset
This CL introduces a locAttr bitset type, which will make it easier to add additional attributes in the near future. Change-Id: I2689aa623097279dc1e7b7cf2adf5184d710c5a8 Reviewed-on: https://go-review.googlesource.com/c/go/+/520258 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Than McIntosh <thanm@google.com> Auto-Submit: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
4e336b8e1e
commit
1038fc207b
@ -129,8 +129,7 @@ func Batch(fns []*ir.Func, recursive bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var b batch
|
var b batch
|
||||||
b.heapLoc.escapes = true
|
b.heapLoc.attrs = attrEscapes | attrPersists
|
||||||
b.heapLoc.persists = true
|
|
||||||
|
|
||||||
// Construct data-flow graph from syntax trees.
|
// Construct data-flow graph from syntax trees.
|
||||||
for _, fn := range fns {
|
for _, fn := range fns {
|
||||||
@ -301,7 +300,7 @@ func (b *batch) finish(fns []*ir.Func) {
|
|||||||
// TODO(mdempsky): Update tests to expect this.
|
// TODO(mdempsky): Update tests to expect this.
|
||||||
goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper()
|
goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper()
|
||||||
|
|
||||||
if loc.escapes {
|
if loc.hasAttr(attrEscapes) {
|
||||||
if n.Op() == ir.ONAME {
|
if n.Op() == ir.ONAME {
|
||||||
if base.Flag.CompilingRuntime {
|
if base.Flag.CompilingRuntime {
|
||||||
base.ErrorfAt(n.Pos(), 0, "%v escapes to heap, not allowed in runtime", n)
|
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)
|
base.WarnfAt(n.Pos(), "%v does not escape", n)
|
||||||
}
|
}
|
||||||
n.SetEsc(ir.EscNone)
|
n.SetEsc(ir.EscNone)
|
||||||
if !loc.persists {
|
if !loc.hasAttr(attrPersists) {
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
case ir.OCLOSURE:
|
case ir.OCLOSURE:
|
||||||
n := n.(*ir.ClosureExpr)
|
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 := loc.paramEsc
|
||||||
esc.Optimize()
|
esc.Optimize()
|
||||||
|
|
||||||
if diagnose && !loc.escapes {
|
if diagnose && !loc.hasAttr(attrEscapes) {
|
||||||
if esc.Empty() {
|
if esc.Empty() {
|
||||||
base.WarnfAt(f.Pos, "%v does not escape", name())
|
base.WarnfAt(f.Pos, "%v does not escape", name())
|
||||||
}
|
}
|
||||||
|
@ -66,15 +66,8 @@ type location struct {
|
|||||||
// in the walk queue.
|
// in the walk queue.
|
||||||
queued bool
|
queued bool
|
||||||
|
|
||||||
// escapes reports whether the represented variable's address
|
// attrs is a bitset of location attributes.
|
||||||
// escapes; that is, whether the variable must be heap
|
attrs locAttr
|
||||||
// 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
|
|
||||||
|
|
||||||
// paramEsc records the represented parameter's leak set.
|
// paramEsc records the represented parameter's leak set.
|
||||||
paramEsc leaks
|
paramEsc leaks
|
||||||
@ -84,6 +77,21 @@ type location struct {
|
|||||||
addrtaken bool // has this variable's address been taken?
|
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.
|
// An edge represents an assignment edge between two Go variables.
|
||||||
type edge struct {
|
type edge struct {
|
||||||
src *location
|
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)
|
// If sink is a result parameter that doesn't escape (#44614)
|
||||||
// and we can fit return bits into the escape analysis tag,
|
// and we can fit return bits into the escape analysis tag,
|
||||||
// then record as a result leak.
|
// 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
|
ri := sink.resultIndex - 1
|
||||||
if ri < numEscResults {
|
if ri < numEscResults {
|
||||||
// Leak to result parameter.
|
// 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, ...
|
if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ...
|
||||||
return
|
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() {
|
if base.Flag.LowerM >= 2 || logopt.Enabled() {
|
||||||
pos := base.FmtPos(src.n.Pos())
|
pos := base.FmtPos(src.n.Pos())
|
||||||
if base.Flag.LowerM >= 2 {
|
if base.Flag.LowerM >= 2 {
|
||||||
@ -195,7 +203,7 @@ func (b *batch) flow(k hole, src *location) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
src.escapes = true
|
src.attrs |= attrEscapes
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +238,9 @@ func (e *escape) newLoc(n ir.Node, persists bool) *location {
|
|||||||
n: n,
|
n: n,
|
||||||
curfn: e.curfn,
|
curfn: e.curfn,
|
||||||
loopDepth: e.loopDepth,
|
loopDepth: e.loopDepth,
|
||||||
persists: persists,
|
}
|
||||||
|
if persists {
|
||||||
|
loc.attrs |= attrPersists
|
||||||
}
|
}
|
||||||
e.allLocs = append(e.allLocs, loc)
|
e.allLocs = append(e.allLocs, loc)
|
||||||
if n != nil {
|
if n != nil {
|
||||||
|
@ -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
|
// If l's address flows to a persistent location, then l needs
|
||||||
// to persist too.
|
// to persist too.
|
||||||
if root.persists && !l.persists {
|
if root.hasAttr(attrPersists) && !l.hasAttr(attrPersists) {
|
||||||
l.persists = true
|
l.attrs |= attrPersists
|
||||||
enqueue(l)
|
enqueue(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location))
|
|||||||
// that value flow for tagging the function
|
// that value flow for tagging the function
|
||||||
// later.
|
// later.
|
||||||
if l.isName(ir.PPARAM) {
|
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 {
|
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)
|
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
|
// If l's address flows somewhere that
|
||||||
// outlives it, then l needs to be heap
|
// outlives it, then l needs to be heap
|
||||||
// allocated.
|
// allocated.
|
||||||
if addressOf && !l.escapes {
|
if addressOf && !l.hasAttr(attrEscapes) {
|
||||||
if logopt.Enabled() || base.Flag.LowerM >= 2 {
|
if logopt.Enabled() || base.Flag.LowerM >= 2 {
|
||||||
if base.Flag.LowerM >= 2 {
|
if base.Flag.LowerM >= 2 {
|
||||||
fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
|
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)
|
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)
|
enqueue(l)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, edge := range l.edges {
|
for i, edge := range l.edges {
|
||||||
if edge.src.escapes {
|
if edge.src.hasAttr(attrEscapes) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
d := derefs + edge.derefs
|
d := derefs + edge.derefs
|
||||||
@ -227,7 +227,7 @@ func (b *batch) explainLoc(l *location) string {
|
|||||||
// other's lifetime if stack allocated.
|
// other's lifetime if stack allocated.
|
||||||
func (b *batch) outlives(l, other *location) bool {
|
func (b *batch) outlives(l, other *location) bool {
|
||||||
// The heap outlives everything.
|
// The heap outlives everything.
|
||||||
if l.escapes {
|
if l.hasAttr(attrEscapes) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user