From 26d5173f5ed96df14ecdeb75b7cd723e6245246b Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Tue, 10 Dec 2013 10:16:35 -0500 Subject: [PATCH] go.tools/oracle: "callees": skip pointer analysis at static call sites. This improves both performance (most calls are static) and precision (e.g. for static calls in dead code). Also, break callees() function into smaller ones. R=crawshaw CC=golang-dev https://golang.org/cl/38740045 --- oracle/callees.go | 72 ++++++++++++++++++--------- oracle/testdata/src/main/calls.go | 5 ++ oracle/testdata/src/main/calls.golden | 6 ++- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/oracle/callees.go b/oracle/callees.go index 3d6a3b6b17..a80772d066 100644 --- a/oracle/callees.go +++ b/oracle/callees.go @@ -60,27 +60,57 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) { return nil, fmt.Errorf("no SSA function built for this location (dead code?)") } + // Find the call site. + site, err := findCallSite(callerFn, e.Lparen) + if err != nil { + return nil, err + } + + funcs, err := findCallees(o, site) + if err != nil { + return nil, err + } + + return &calleesResult{ + site: site, + funcs: funcs, + }, nil +} + +func findCallSite(fn *ssa.Function, lparen token.Pos) (ssa.CallInstruction, error) { + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + if site, ok := instr.(ssa.CallInstruction); ok && instr.Pos() == lparen { + return site, nil + } + } + } + return nil, fmt.Errorf("this call site is unreachable in this analysis") +} + +func findCallees(o *Oracle, site ssa.CallInstruction) ([]*ssa.Function, error) { + // Avoid running the pointer analysis for static calls. + if callee := site.Common().StaticCallee(); callee != nil { + switch callee.String() { + case "runtime.SetFinalizer", "(reflect.Value).Call": + // The PTA treats calls to these intrinsics as dynamic. + // TODO(adonovan): avoid reliance on PTA internals. + + default: + return []*ssa.Function{callee}, nil // singleton + } + } + + // Dynamic call: use pointer analysis. o.config.BuildCallGraph = true callgraph := ptrAnalysis(o).CallGraph - // Find the call site and all edges from it. - var site ssa.CallInstruction + // Find all call edges from the site. calleesMap := make(map[*ssa.Function]bool) + var foundCGNode bool for _, n := range callgraph.Nodes() { - if n.Func() == callerFn { - if site == nil { - // First node for callerFn: identify the site. - for _, s := range n.Sites() { - if s.Pos() == e.Lparen { - site = s - break - } - } - if site == nil { - return nil, fmt.Errorf("this call site is unreachable in this analysis") - } - } - + if n.Func() == site.Parent() { + foundCGNode = true for _, edge := range n.Edges() { if edge.Site == site { calleesMap[edge.Callee.Func()] = true @@ -88,8 +118,8 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) { } } } - if site == nil { - return nil, fmt.Errorf("this function is unreachable in this analysis") + if !foundCGNode { + return nil, fmt.Errorf("this call site is unreachable in this analysis") } // Discard context, de-duplicate and sort. @@ -98,11 +128,7 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) { funcs = append(funcs, f) } sort.Sort(byFuncPos(funcs)) - - return &calleesResult{ - site: site, - funcs: funcs, - }, nil + return funcs, nil } type calleesResult struct { diff --git a/oracle/testdata/src/main/calls.go b/oracle/testdata/src/main/calls.go index 93974aeec3..bd117d74f2 100644 --- a/oracle/testdata/src/main/calls.go +++ b/oracle/testdata/src/main/calls.go @@ -69,10 +69,15 @@ func main() { i.f() // @callees callees-err-nil-interface "i.f" } +var dynamic = func() {} + +// Within dead code, dynamic calls have no callees. func deadcode() { main() // @callees callees-err-deadcode2 "main" // @callers callers-err-deadcode "^" // @callstack callstack-err-deadcode "^" + + dynamic() // @callees callees-err-deadcode3 "dynamic" } // This code belongs to init. diff --git a/oracle/testdata/src/main/calls.golden b/oracle/testdata/src/main/calls.golden index 20194370b7..364da94b70 100644 --- a/oracle/testdata/src/main/calls.golden +++ b/oracle/testdata/src/main/calls.golden @@ -87,11 +87,15 @@ dynamic function call on nil value dynamic method call on nil value -------- @callees callees-err-deadcode2 -------- +this static function call dispatches to: + main.main -Error: this function is unreachable in this analysis -------- @callstack callstack-err-deadcode -------- main.deadcode is unreachable in this analysis scope +-------- @callees callees-err-deadcode3 -------- + +Error: this call site is unreachable in this analysis -------- @callers callers-global -------- main.init is called from these 1 sites: the root of the call graph