github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gotools/go/callgraph/cha/cha.go (about) 1 // Package cha computes the call graph of a Go program using the Class 2 // Hierarchy Analysis (CHA) algorithm. 3 // 4 // CHA was first described in "Optimization of Object-Oriented Programs 5 // Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove, 6 // and Craig Chambers, ECOOP'95. 7 // 8 // CHA is related to RTA (see go/callgraph/rta); the difference is that 9 // CHA conservatively computes the entire "implements" relation between 10 // interfaces and concrete types ahead of time, whereas RTA uses dynamic 11 // programming to construct it on the fly as it encounters new functions 12 // reachable from main. CHA may thus include spurious call edges for 13 // types that haven't been instantiated yet, or types that are never 14 // instantiated. 15 // 16 // Since CHA conservatively assumes that all functions are address-taken 17 // and all concrete types are put into interfaces, it is sound to run on 18 // partial programs, such as libraries without a main or test function. 19 // 20 package cha // import "llvm.org/llgo/third_party/gotools/go/callgraph/cha" 21 22 import ( 23 "llvm.org/llgo/third_party/gotools/go/callgraph" 24 "llvm.org/llgo/third_party/gotools/go/ssa" 25 "llvm.org/llgo/third_party/gotools/go/ssa/ssautil" 26 "llvm.org/llgo/third_party/gotools/go/types" 27 "llvm.org/llgo/third_party/gotools/go/types/typeutil" 28 ) 29 30 // CallGraph computes the call graph of the specified program using the 31 // Class Hierarchy Analysis algorithm. 32 // 33 func CallGraph(prog *ssa.Program) *callgraph.Graph { 34 cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph 35 36 allFuncs := ssautil.AllFunctions(prog) 37 38 // funcsBySig contains all functions, keyed by signature. It is 39 // the effective set of address-taken functions used to resolve 40 // a dynamic call of a particular signature. 41 var funcsBySig typeutil.Map // value is []*ssa.Function 42 43 // methodsByName contains all methods, 44 // grouped by name for efficient lookup. 45 methodsByName := make(map[string][]*ssa.Function) 46 47 // methodsMemo records, for every abstract method call call I.f on 48 // interface type I, the set of concrete methods C.f of all 49 // types C that satisfy interface I. 50 methodsMemo := make(map[*types.Func][]*ssa.Function) 51 lookupMethods := func(m *types.Func) []*ssa.Function { 52 methods, ok := methodsMemo[m] 53 if !ok { 54 I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface) 55 for _, f := range methodsByName[m.Name()] { 56 C := f.Signature.Recv().Type() // named or *named 57 if types.Implements(C, I) { 58 methods = append(methods, f) 59 } 60 } 61 methodsMemo[m] = methods 62 } 63 return methods 64 } 65 66 for f := range allFuncs { 67 if f.Signature.Recv() == nil { 68 // Package initializers can never be address-taken. 69 if f.Name() == "init" && f.Synthetic == "package initializer" { 70 continue 71 } 72 funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function) 73 funcs = append(funcs, f) 74 funcsBySig.Set(f.Signature, funcs) 75 } else { 76 methodsByName[f.Name()] = append(methodsByName[f.Name()], f) 77 } 78 } 79 80 addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) { 81 gnode := cg.CreateNode(g) 82 callgraph.AddEdge(fnode, site, gnode) 83 } 84 85 addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) { 86 // Because every call to a highly polymorphic and 87 // frequently used abstract method such as 88 // (io.Writer).Write is assumed to call every concrete 89 // Write method in the program, the call graph can 90 // contain a lot of duplication. 91 // 92 // TODO(adonovan): opt: consider factoring the callgraph 93 // API so that the Callers component of each edge is a 94 // slice of nodes, not a singleton. 95 for _, g := range callees { 96 addEdge(fnode, site, g) 97 } 98 } 99 100 for f := range allFuncs { 101 fnode := cg.CreateNode(f) 102 for _, b := range f.Blocks { 103 for _, instr := range b.Instrs { 104 if site, ok := instr.(ssa.CallInstruction); ok { 105 call := site.Common() 106 if call.IsInvoke() { 107 addEdges(fnode, site, lookupMethods(call.Method)) 108 } else if g := call.StaticCallee(); g != nil { 109 addEdge(fnode, site, g) 110 } else if _, ok := call.Value.(*ssa.Builtin); !ok { 111 callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function) 112 addEdges(fnode, site, callees) 113 } 114 } 115 } 116 } 117 } 118 119 return cg 120 }