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
This commit is contained in:
Alan Donovan 2013-12-10 10:16:35 -05:00
parent 5d70784aca
commit 26d5173f5e
3 changed files with 59 additions and 24 deletions

View File

@ -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?)") 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 o.config.BuildCallGraph = true
callgraph := ptrAnalysis(o).CallGraph callgraph := ptrAnalysis(o).CallGraph
// Find the call site and all edges from it. // Find all call edges from the site.
var site ssa.CallInstruction
calleesMap := make(map[*ssa.Function]bool) calleesMap := make(map[*ssa.Function]bool)
var foundCGNode bool
for _, n := range callgraph.Nodes() { for _, n := range callgraph.Nodes() {
if n.Func() == callerFn { if n.Func() == site.Parent() {
if site == nil { foundCGNode = true
// 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")
}
}
for _, edge := range n.Edges() { for _, edge := range n.Edges() {
if edge.Site == site { if edge.Site == site {
calleesMap[edge.Callee.Func()] = true calleesMap[edge.Callee.Func()] = true
@ -88,8 +118,8 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) {
} }
} }
} }
if site == nil { if !foundCGNode {
return nil, fmt.Errorf("this function is unreachable in this analysis") return nil, fmt.Errorf("this call site is unreachable in this analysis")
} }
// Discard context, de-duplicate and sort. // Discard context, de-duplicate and sort.
@ -98,11 +128,7 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) {
funcs = append(funcs, f) funcs = append(funcs, f)
} }
sort.Sort(byFuncPos(funcs)) sort.Sort(byFuncPos(funcs))
return funcs, nil
return &calleesResult{
site: site,
funcs: funcs,
}, nil
} }
type calleesResult struct { type calleesResult struct {

View File

@ -69,10 +69,15 @@ func main() {
i.f() // @callees callees-err-nil-interface "i.f" i.f() // @callees callees-err-nil-interface "i.f"
} }
var dynamic = func() {}
// Within dead code, dynamic calls have no callees.
func deadcode() { func deadcode() {
main() // @callees callees-err-deadcode2 "main" main() // @callees callees-err-deadcode2 "main"
// @callers callers-err-deadcode "^" // @callers callers-err-deadcode "^"
// @callstack callstack-err-deadcode "^" // @callstack callstack-err-deadcode "^"
dynamic() // @callees callees-err-deadcode3 "dynamic"
} }
// This code belongs to init. // This code belongs to init.

View File

@ -87,11 +87,15 @@ dynamic function call on nil value
dynamic method call on nil value dynamic method call on nil value
-------- @callees callees-err-deadcode2 -------- -------- @callees callees-err-deadcode2 --------
this static function call dispatches to:
main.main
Error: this function is unreachable in this analysis
-------- @callstack callstack-err-deadcode -------- -------- @callstack callstack-err-deadcode --------
main.deadcode is unreachable in this analysis scope main.deadcode is unreachable in this analysis scope
-------- @callees callees-err-deadcode3 --------
Error: this call site is unreachable in this analysis
-------- @callers callers-global -------- -------- @callers callers-global --------
main.init is called from these 1 sites: main.init is called from these 1 sites:
the root of the call graph the root of the call graph