// 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, its type, and receiver
- // (if any) and normal arguments list.
+ // 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()
fn = fn.Func.Closure.Func.Nname
}
fntype = fn.Type
+ if !(fn.Op == ONAME && fn.Class() == PFUNC) {
+ fn = nil // dynamic call
+ }
case OCALLMETH:
fn = asNode(call.Left.Type.FuncType().Nname)
fntype = fn.Type
Fatalf("unexpected call op: %v", call.Op)
}
- static := fn != nil && fn.Op == ONAME && fn.Class() == PFUNC
-
// Setup evaluation holes for each receiver/argument.
var recvK EscHole
var paramKs []EscHole
- if static && fn.Name.Defn != nil && fn.Name.Defn.Esc < EscFuncTagged {
- // Static call to function in same mutually recursive
- // group; incorporate into data flow graph.
-
- if fn.Name.Defn.Esc == EscFuncUnknown {
- Fatalf("graph inconsistency")
- }
-
- if ks != nil {
- for i, result := range fntype.Results().FieldSlice() {
+ 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.addr(asNode(r.Nname))
- }
- for _, param := range fntype.Params().FieldSlice() {
- paramKs = append(paramKs, e.addr(asNode(param.Nname)))
- }
- } else if call.Op == OCALLFUNC || call.Op == OCALLMETH || call.Op == OCALLINTER {
- // Dynamic call, or call to previously tagged
- // function. Setup flows to heap and/or ks according
- // to parameter tags.
- if r := fntype.Recv(); r != nil {
- recvK = e.tagHole(ks, r, static)
+ recvK = e.tagHole(ks, fn, r)
}
for _, param := range fntype.Params().FieldSlice() {
- paramKs = append(paramKs, e.tagHole(ks, param, static))
+ paramKs = append(paramKs, e.tagHole(ks, fn, param))
}
} else {
// Handle escape analysis for builtins.
// For arguments to go:uintptrescapes, peel
// away an unsafe.Pointer->uintptr conversion,
// if present.
- if static && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR {
+ 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
// tagHole returns a hole for evaluating an argument passed to param.
// ks should contain the holes representing where the function
-// callee's results flows; static indicates whether this is a static
-// call.
-func (e *Escape) tagHole(ks []EscHole, param *types.Field, static bool) EscHole {
+// callee's results flows. fn is the statically-known callee function,
+// if any.
+func (e *Escape) tagHole(ks []EscHole, fn *Node, param *types.Field) EscHole {
// If this is a dynamic call, we can't rely on param.Note.
- if !static {
+ if fn == nil {
return e.heapHole()
}
+ if e.inMutualBatch(fn) {
+ return e.addr(asNode(param.Nname))
+ }
+
+ // Call to previously tagged function.
var tagKs []EscHole
esc := ParseLeaks(param.Note)
return e.teeHole(tagKs...)
}
+// inMutualBatch reports whether function fn is in the batch of
+// mutually recursive functions being analyzed. When this is true,
+// fn has not yet been analyzed, so its parameters and results
+// should be incorporated directly into the flow graph instead of
+// relying on its escape analysis tagging.
+func (e *Escape) inMutualBatch(fn *Node) bool {
+ if fn.Name.Defn != nil && fn.Name.Defn.Esc < EscFuncTagged {
+ if fn.Name.Defn.Esc == EscFuncUnknown {
+ Fatalf("graph inconsistency")
+ }
+ return true
+ }
+ return false
+}
+
// An EscHole represents a context for evaluation a Go
// expression. E.g., when evaluating p in "x = **p", we'd have a hole
// with dst==x and derefs==2.