mirror of
https://github.com/golang/go.git
synced 2025-05-06 16:13:04 +00:00
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:
parent
5d70784aca
commit
26d5173f5e
@ -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 {
|
||||||
|
5
oracle/testdata/src/main/calls.go
vendored
5
oracle/testdata/src/main/calls.go
vendored
@ -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.
|
||||||
|
6
oracle/testdata/src/main/calls.golden
vendored
6
oracle/testdata/src/main/calls.golden
vendored
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user