Capture analysis is now part of escape analysis.
Passes toolstash -cmp.
Change-Id: Ifcd3ecc342074c590e0db1ff0646dfa1ea2ff57b
Reviewed-on: https://go-review.googlesource.com/c/go/+/281543
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
}
// Capture by value for variables <= 128 bytes that are never reassigned.
- byval := !loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128
- if byval != n.Byval() {
- base.FatalfAt(cv.Pos(), "byval mismatch: %v: %v != %v", cv, byval, n.Byval())
+ n.SetByval(!loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128)
+ if !n.Byval() {
+ n.SetAddrtaken(true)
+ }
+
+ if base.Flag.LowerM > 1 {
+ how := "ref"
+ if n.Byval() {
+ how = "value"
+ }
+ base.WarnfAt(n.Pos(), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", n.Curfn, how, n, loc.addrtaken, loc.reassigned, n.Type().Size())
}
// Flow captured variables to closure.
}
typecheck.IncrementalAddrtaken = true
- // Decide how to capture closed variables.
- // This needs to run before escape analysis,
- // because variables captured by value do not escape.
- base.Timer.Start("fe", "capturevars")
- for _, n := range typecheck.Target.Decls {
- if n.Op() == ir.ODCLFUNC {
- n := n.(*ir.Func)
- if n.OClosure != nil {
- ir.CurFunc = n
- typecheck.CaptureVars(n)
- }
- }
- }
- typecheck.CaptureVarsComplete = true
- ir.CurFunc = nil
-
if base.Debug.TypecheckInl != 0 {
// Typecheck imported function bodies if Debug.l > 1,
// otherwise lazily when used or re-exported.
// (results) are numbered starting at one, followed by function inputs
// (parameters), and then local variables. Vargen is used to distinguish
// local variables/params with the same name.
- Vargen int32
- Decldepth int32 // declaration loop depth, increased for every loop or label
+ Vargen int32
Ntype Ntype
Heapaddr *Name // temp holding heap address of param
func (n *Name) SetAlias(alias bool) { n.flags.set(nameAlias, alias) }
const (
- nameCaptured = 1 << iota // is the variable captured by a closure
- nameReadonly
+ nameReadonly = 1 << iota
nameByval // is the variable captured by value or by reference
nameNeedzero // if it contains pointers, needs to be zeroed on function entry
nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
nameUsed // for variable declared and not used error
nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn
nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy
- nameAssigned // is the variable ever assigned to
nameAddrtaken // address taken, even if not moved to heap
nameInlFormal // PAUTO created by inliner, derived from callee formal
nameInlLocal // PAUTO created by inliner, derived from callee local
nameAlias // is type name an alias
)
-func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 }
func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 }
func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 }
func (n *Name) Used() bool { return n.flags&nameUsed != 0 }
func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 }
func (n *Name) IsOutputParamHeapAddr() bool { return n.flags&nameIsOutputParamHeapAddr != 0 }
-func (n *Name) Assigned() bool { return n.flags&nameAssigned != 0 }
func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken != 0 }
func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 }
func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 }
func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 }
func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 }
-func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) }
func (n *Name) setReadonly(b bool) { n.flags.set(nameReadonly, b) }
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) }
func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) }
func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) }
func (n *Name) SetIsOutputParamHeapAddr(b bool) { n.flags.set(nameIsOutputParamHeapAddr, b) }
-func (n *Name) SetAssigned(b bool) { n.flags.set(nameAssigned, b) }
func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken, b) }
func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) }
func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) }
_64bit uintptr // size on 64bit platforms
}{
{Func{}, 184, 320},
- {Name{}, 124, 216},
+ {Name{}, 120, 216},
}
for _, tt := range tests {
return t
}
-// CaptureVars is called in a separate phase after all typechecking is done.
-// It decides whether each variable captured by a closure should be captured
-// by value or by reference.
-// We use value capturing for values <= 128 bytes that are never reassigned
-// after capturing (effectively constant).
-func CaptureVars(fn *ir.Func) {
- for _, v := range fn.ClosureVars {
- outermost := v.Defn.(*ir.Name)
-
- // out parameters will be assigned to implicitly upon return.
- if outermost.Class != ir.PPARAMOUT && !outermost.Addrtaken() && !outermost.Assigned() && outermost.Type().Size() <= 128 {
- outermost.SetByval(true)
- } else {
- outermost.SetAddrtaken(true)
- }
-
- if base.Flag.LowerM > 1 {
- how := "ref"
- if v.Byval() {
- how = "value"
- }
- base.WarnfAt(v.Pos(), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", v.Curfn, how, v, outermost.Addrtaken(), outermost.Assigned(), v.Type().Size())
- }
- }
-}
-
// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
// because they're a copy of an already checked body.
func ImportedBody(fn *ir.Func) {
return fn.Sym().Pkg
}
-// CaptureVarsComplete is set to true when the capturevars phase is done.
-var CaptureVarsComplete bool
-
// closurename generates a new unique name for a closure within
// outerfunc.
func closurename(outerfunc *ir.Func) *types.Sym {
return
}
- for _, ln := range fn.ClosureVars {
- n := ln.Defn
- if !n.Name().Captured() {
- n.Name().SetCaptured(true)
- if n.Name().Decldepth == 0 {
- base.Fatalf("typecheckclosure: var %v does not have decldepth assigned", n)
- }
-
- // Ignore assignments to the variable in straightline code
- // preceding the first capturing by a closure.
- if n.Name().Decldepth == decldepth {
- n.Name().SetAssigned(false)
- }
- }
- }
-
fn.Nname.SetSym(closurename(ir.CurFunc))
ir.MarkFunc(fn.Nname)
Func(fn)
if ir.CurFunc != nil && clo.Type() != nil {
oldfn := ir.CurFunc
ir.CurFunc = fn
- olddd := decldepth
- decldepth = 1
Stmts(fn.Body)
- decldepth = olddd
ir.CurFunc = oldfn
}
defer tracePrint("typecheckfunc", n)(nil)
}
- for _, ln := range n.Dcl {
- if ln.Op() == ir.ONAME && (ln.Class == ir.PPARAM || ln.Class == ir.PPARAMOUT) {
- ln.Decldepth = 1
- }
- }
-
n.Nname = AssignExpr(n.Nname).(*ir.Name)
t := n.Nname.Type()
if t == nil {
// tcFor typechecks an OFOR node.
func tcFor(n *ir.ForStmt) ir.Node {
Stmts(n.Init())
- decldepth++
n.Cond = Expr(n.Cond)
n.Cond = DefaultLit(n.Cond, nil)
if n.Cond != nil {
Stmts(n.Late)
}
Stmts(n.Body)
- decldepth--
return n
}
n.Value = AssignExpr(n.Value)
}
- decldepth++
Stmts(n.Body)
- decldepth--
}
// tcReturn typechecks an ORETURN node.
var inimport bool // set during import
-var decldepth int32
-
var TypecheckAllowed bool
var (
func FuncBody(n *ir.Func) {
ir.CurFunc = n
- decldepth = 1
errorsBefore := base.Errors()
Stmts(n.Body)
CheckUnused(n)
case ir.ONAME:
n := n.(*ir.Name)
- if n.Decldepth == 0 {
- n.Decldepth = decldepth
- }
if n.BuiltinOp != 0 {
if top&ctxCallee == 0 {
base.Errorf("use of builtin %v not in function call", n.Sym())
return n
case ir.OLABEL:
- decldepth++
if n.Sym().IsBlank() {
// Empty identifier is valid but useless.
// Eliminate now to simplify life later.
return
}
- // Variables declared in ORANGE are assigned on every iteration.
- if !ir.DeclaredBy(n, stmt) || stmt.Op() == ir.ORANGE {
- r := ir.OuterValue(n)
- if r.Op() == ir.ONAME {
- r := r.(*ir.Name)
- r.SetAssigned(true)
- if r.IsClosureVar() {
- r.Defn.Name().SetAssigned(true)
- }
- }
- }
-
if ir.IsAddressable(n) {
return
}