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  }