diff options
Diffstat (limited to 'src/cmd/compile/internal/escape/call.go')
-rw-r--r-- | src/cmd/compile/internal/escape/call.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/escape/call.go b/src/cmd/compile/internal/escape/call.go new file mode 100644 index 0000000000..28a3b679a5 --- /dev/null +++ b/src/cmd/compile/internal/escape/call.go @@ -0,0 +1,182 @@ +// 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 escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" +) + +// call evaluates a call expressions, including builtin calls. ks +// 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 []hole, call, where ir.Node) { + topLevelDefer := where != nil && where.Op() == ir.ODEFER && e.loopDepth == 1 + if topLevelDefer { + // force stack allocation of defer record, unless + // open-coded defers are used (see ssa.go) + where.SetEsc(ir.EscNever) + } + + argument := func(k hole, arg ir.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() + } + + e.expr(k.note(call, "call parameter"), arg) + } + + switch call.Op() { + default: + ir.Dump("esc", call) + base.Fatalf("unexpected call op: %v", call.Op()) + + case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: + call := call.(*ir.CallExpr) + typecheck.FixVariadicCall(call) + + // Pick out the function callee, if statically known. + var fn *ir.Name + switch call.Op() { + case ir.OCALLFUNC: + switch v := ir.StaticValue(call.X); { + case v.Op() == ir.ONAME && v.(*ir.Name).Class == ir.PFUNC: + fn = v.(*ir.Name) + case v.Op() == ir.OCLOSURE: + fn = v.(*ir.ClosureExpr).Func.Nname + } + case ir.OCALLMETH: + fn = ir.MethodExprName(call.X) + } + + fntype := call.X.Type() + if fn != nil { + fntype = fn.Type() + } + + if ks != nil && fn != nil && e.inMutualBatch(fn) { + for i, result := range fn.Type().Results().FieldSlice() { + e.expr(ks[i], ir.AsNode(result.Nname)) + } + } + + if r := fntype.Recv(); r != nil { + argument(e.tagHole(ks, fn, r), call.X.(*ir.SelectorExpr).X) + } else { + // Evaluate callee function expression. + argument(e.discardHole(), call.X) + } + + args := call.Args + for i, param := range fntype.Params().FieldSlice() { + argument(e.tagHole(ks, fn, param), args[i]) + } + + case ir.OAPPEND: + call := call.(*ir.CallExpr) + args := call.Args + + // 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 args[0].Type().Elem().HasPointers() { + appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice")) + } + argument(appendeeK, args[0]) + + if call.IsDDD { + appendedK := e.discardHole() + if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() { + appendedK = e.heapHole().deref(call, "appended slice...") + } + argument(appendedK, args[1]) + } else { + for _, arg := range args[1:] { + argument(e.heapHole(), arg) + } + } + + case ir.OCOPY: + call := call.(*ir.BinaryExpr) + argument(e.discardHole(), call.X) + + copiedK := e.discardHole() + if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() { + copiedK = e.heapHole().deref(call, "copied slice") + } + argument(copiedK, call.Y) + + case ir.OPANIC: + call := call.(*ir.UnaryExpr) + argument(e.heapHole(), call.X) + + case ir.OCOMPLEX: + call := call.(*ir.BinaryExpr) + argument(e.discardHole(), call.X) + argument(e.discardHole(), call.Y) + case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + call := call.(*ir.CallExpr) + for _, arg := range call.Args { + argument(e.discardHole(), arg) + } + case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE: + call := call.(*ir.UnaryExpr) + argument(e.discardHole(), call.X) + + case ir.OUNSAFEADD, ir.OUNSAFESLICE: + call := call.(*ir.BinaryExpr) + argument(ks[0], call.X) + argument(e.discardHole(), call.Y) + } +} + +// tagHole returns a hole for evaluating an argument passed to param. +// ks should contain the holes representing where the function +// callee's results flows. fn is the statically-known callee function, +// if any. +func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole { + // If this is a dynamic call, we can't rely on param.Note. + if fn == nil { + return e.heapHole() + } + + if e.inMutualBatch(fn) { + return e.addr(ir.AsNode(param.Nname)) + } + + // Call to previously tagged function. + + if fn.Func != nil && fn.Func.Pragma&ir.UintptrEscapes != 0 && (param.Type.IsUintptr() || param.IsDDD() && param.Type.Elem().IsUintptr()) { + k := e.heapHole() + k.uintptrEscapesHack = true + return k + } + + var tagKs []hole + + esc := parseLeaks(param.Note) + if x := esc.Heap(); x >= 0 { + tagKs = append(tagKs, e.heapHole().shift(x)) + } + + if ks != nil { + for i := 0; i < numEscResults; i++ { + if x := esc.Result(i); x >= 0 { + tagKs = append(tagKs, ks[i].shift(x)) + } + } + } + + return e.teeHole(tagKs...) +} |