lineno = lno
}()
- if k.derefs >= 0 && !types.Haspointers(n.Type) {
+ uintptrEscapesHack := k.uintptrEscapesHack
+ k.uintptrEscapesHack = false
+
+ if uintptrEscapesHack && n.Op == OCONVNOP && n.Left.Type.IsUnsafePtr() {
+ // nop
+ } else if k.derefs >= 0 && !types.Haspointers(n.Type) {
k = e.discardHole()
}
case OSLICELIT:
k = e.spill(k, n)
+ k.uintptrEscapesHack = uintptrEscapesHack // for ...uintptr parameters
for _, elt := range n.List.Slice() {
if elt.Op == OKEY {
// should contain the holes representing where the function callee's
// results flows; where is the OGO/ODEFER context of the call, if any.
func (e *Escape) call(ks []EscHole, call, where *Node) {
- // First, pick out the function callee (if statically known),
- // its type, and receiver (if any) and normal arguments list.
- var fn, recv *Node
- var fntype *types.Type
- args := call.List.Slice()
- switch call.Op {
- case OCALLFUNC:
- fn = call.Left
- if fn.Op == OCLOSURE {
- fn = fn.Func.Closure.Func.Nname
- }
- fntype = fn.Type
- if !(fn.Op == ONAME && fn.Class() == PFUNC) {
- fn = nil // dynamic call
+ topLevelDefer := where != nil && where.Op == ODEFER && e.loopDepth == 1
+ if topLevelDefer {
+ // force stack allocation of defer record, unless
+ // open-coded defers are used (see ssa.go)
+ where.Esc = EscNever
+ }
+
+ argument := func(k EscHole, arg *Node) {
+ if topLevelDefer {
+ // Top level defers arguments don't escape to
+ // heap, but they do need to last until end of
+ // function.
+ k = e.later(k)
+ } else if where != nil {
+ k = e.heapHole()
}
- case OCALLMETH:
- fn = asNode(call.Left.Type.FuncType().Nname)
- fntype = fn.Type
- recv = call.Left.Left
- case OCALLINTER:
- fntype = call.Left.Type
- recv = call.Left.Left
- case OAPPEND, ODELETE, OPRINT, OPRINTN, ORECOVER:
- // ok
- case OLEN, OCAP, OREAL, OIMAG, OCLOSE, OPANIC:
- args = []*Node{call.Left}
- case OCOMPLEX, OCOPY:
- args = []*Node{call.Left, call.Right}
+
+ e.expr(k.note(call, "call parameter"), arg)
+ }
+
+ switch call.Op {
default:
Fatalf("unexpected call op: %v", call.Op)
- }
- // Setup evaluation holes for each receiver/argument.
- var recvK EscHole
- var paramKs []EscHole
+ case OCALLFUNC, OCALLMETH, OCALLINTER:
+ fixVariadicCall(call)
+
+ // Pick out the function callee, if statically known.
+ var fn *Node
+ switch call.Op {
+ case OCALLFUNC:
+ if call.Left.Op == ONAME && call.Left.Class() == PFUNC {
+ fn = call.Left
+ } else if call.Left.Op == OCLOSURE {
+ fn = call.Left.Func.Closure.Func.Nname
+ }
+ case OCALLMETH:
+ fn = asNode(call.Left.Type.FuncType().Nname)
+ }
+
+ fntype := call.Left.Type
+ if fn != nil {
+ fntype = fn.Type
+ }
- if call.Op == OCALLFUNC || call.Op == OCALLMETH || call.Op == OCALLINTER {
if ks != nil && fn != nil && e.inMutualBatch(fn) {
for i, result := range fn.Type.Results().FieldSlice() {
e.expr(ks[i], asNode(result.Nname))
}
if r := fntype.Recv(); r != nil {
- recvK = e.tagHole(ks, fn, r)
- }
- for _, param := range fntype.Params().FieldSlice() {
- paramKs = append(paramKs, e.tagHole(ks, fn, param))
- }
- } else {
- // Handle escape analysis for builtins.
- // By default, we just discard everything.
- for range args {
- paramKs = append(paramKs, e.discardHole())
+ argument(e.tagHole(ks, fn, r), call.Left.Left)
+ } else {
+ // Evaluate callee function expression.
+ argument(e.discardHole(), call.Left)
}
- switch call.Op {
- case OAPPEND:
- // Appendee slice may flow directly to the
- // result, if it has enough capacity.
- // Alternatively, a new heap slice might be
- // allocated, and all slice elements might
- // flow to heap.
- paramKs[0] = e.teeHole(paramKs[0], ks[0])
- if types.Haspointers(args[0].Type.Elem()) {
- paramKs[0] = e.teeHole(paramKs[0], e.heapHole().deref(call, "appendee slice"))
- }
-
- if call.IsDDD() {
- if args[1].Type.IsSlice() && types.Haspointers(args[1].Type.Elem()) {
- paramKs[1] = e.teeHole(paramKs[1], e.heapHole().deref(call, "appended slice..."))
- }
- } else {
- for i := 1; i < len(args); i++ {
- paramKs[i] = e.heapHole()
- }
- }
-
- case OCOPY:
- if call.Right.Type.IsSlice() && types.Haspointers(call.Right.Type.Elem()) {
- paramKs[1] = e.teeHole(paramKs[1], e.heapHole().deref(call, "copied slice"))
- }
-
- case OPANIC:
- paramKs[0] = e.heapHole()
+ args := call.List.Slice()
+ for i, param := range fntype.Params().FieldSlice() {
+ argument(e.tagHole(ks, fn, param), args[i])
}
- }
-
- if call.Op == OCALLFUNC {
- // Evaluate callee function expression.
- e.expr(e.augmentParamHole(e.discardHole(), call, where), call.Left)
- }
-
- if recv != nil {
- // TODO(mdempsky): Handle go:uintptrescapes here too?
- e.expr(e.augmentParamHole(recvK, call, where), recv)
- }
-
- // Apply augmentParamHole before ODDDARG so that it affects
- // the implicit slice allocation for variadic calls, if any.
- for i, paramK := range paramKs {
- paramKs[i] = e.augmentParamHole(paramK, call, where)
- }
-
- // TODO(mdempsky): Remove after early ddd-ification.
- if fntype != nil && fntype.IsVariadic() && !call.IsDDD() {
- vi := fntype.NumParams() - 1
- elt := fntype.Params().Field(vi).Type.Elem()
- nva := call.List.Len()
- nva -= vi
+ case OAPPEND:
+ args := call.List.Slice()
- // Introduce ODDDARG node to represent ... allocation.
- ddd := nodl(call.Pos, ODDDARG, nil, nil)
- ddd.Type = types.NewPtr(types.NewArray(elt, int64(nva)))
- call.Right = ddd
-
- dddK := e.spill(paramKs[vi], ddd)
- paramKs = paramKs[:vi]
- for i := 0; i < nva; i++ {
- paramKs = append(paramKs, dddK)
+ // Appendee slice may flow directly to the result, if
+ // it has enough capacity. Alternatively, a new heap
+ // slice might be allocated, and all slice elements
+ // might flow to heap.
+ appendeeK := ks[0]
+ if types.Haspointers(args[0].Type.Elem()) {
+ appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
}
- }
+ argument(appendeeK, args[0])
- for i, arg := range args {
- // For arguments to go:uintptrescapes, peel
- // away an unsafe.Pointer->uintptr conversion,
- // if present.
- if fn != nil && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR {
- x := i
- if fntype.IsVariadic() && x >= fntype.NumParams() {
- x = fntype.NumParams() - 1
+ if call.IsDDD() {
+ appendedK := e.discardHole()
+ if args[1].Type.IsSlice() && types.Haspointers(args[1].Type.Elem()) {
+ appendedK = e.heapHole().deref(call, "appended slice...")
}
- if fntype.Params().Field(x).Note == uintptrEscapesTag {
- arg = arg.Left
+ argument(appendedK, args[1])
+ } else {
+ for _, arg := range args[1:] {
+ argument(e.heapHole(), arg)
}
}
- // no augmentParamHole here; handled in loop before ODDDARG
- e.expr(paramKs[i], arg)
- }
-}
+ case OCOPY:
+ argument(e.discardHole(), call.Left)
-// augmentParamHole augments parameter holes as necessary for use in
-// go/defer statements.
-func (e *Escape) augmentParamHole(k EscHole, call, where *Node) EscHole {
- k = k.note(call, "call parameter")
- if where == nil {
- return k
- }
+ copiedK := e.discardHole()
+ if call.Right.Type.IsSlice() && types.Haspointers(call.Right.Type.Elem()) {
+ copiedK = e.heapHole().deref(call, "copied slice")
+ }
+ argument(copiedK, call.Right)
- // Top level defers arguments don't escape to heap, but they
- // do need to last until end of function. Tee with a
- // non-transient location to avoid arguments from being
- // transiently allocated.
- if where.Op == ODEFER && e.loopDepth == 1 {
- // force stack allocation of defer record, unless open-coded
- // defers are used (see ssa.go)
- where.Esc = EscNever
- return e.later(k)
- }
+ case OPANIC:
+ argument(e.heapHole(), call.Left)
- return e.heapHole().note(where, "call parameter")
+ case OCOMPLEX:
+ argument(e.discardHole(), call.Left)
+ argument(e.discardHole(), call.Right)
+ case ODELETE, OPRINT, OPRINTN, ORECOVER:
+ for _, arg := range call.List.Slice() {
+ argument(e.discardHole(), arg)
+ }
+ case OLEN, OCAP, OREAL, OIMAG, OCLOSE:
+ argument(e.discardHole(), call.Left)
+ }
}
// tagHole returns a hole for evaluating an argument passed to param.
}
// Call to previously tagged function.
+
+ if param.Note == uintptrEscapesTag {
+ k := e.heapHole()
+ k.uintptrEscapesHack = true
+ return k
+ }
+
var tagKs []EscHole
esc := ParseLeaks(param.Note)
dst *EscLocation
derefs int // >= -1
notes *EscNote
+
+ // uintptrEscapesHack indicates this context is evaluating an
+ // argument for a //go:uintptrescapes function.
+ uintptrEscapesHack bool
}
type EscNote struct {