blob: e3c85ede4f754e6ab599d275d13dbec37aa63d69 [file] [log] [blame]
Alan Donovan713699d2013-08-27 18:49:13 -04001// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Alan Donovan6643abb2013-08-22 12:27:55 -04005package pointer
6
Alan Donovan3b5de062013-09-16 09:49:10 -04007// This file defines the main datatypes and Analyze function of the pointer analysis.
Alan Donovan6643abb2013-08-22 12:27:55 -04008
9import (
10 "fmt"
11 "go/token"
Alan Donovan542ffc72015-12-29 13:06:30 -050012 "go/types"
Alan Donovan6643abb2013-08-22 12:27:55 -040013 "io"
14 "os"
Alan Donovan5b55a712013-09-27 11:33:01 -040015 "reflect"
Alan Donovan9b38eaf2014-06-16 15:46:07 -040016 "runtime"
Alan Donovanc509cf12014-02-27 14:13:52 -050017 "runtime/debug"
Alan Donovan9b38eaf2014-06-16 15:46:07 -040018 "sort"
Tim King36a5c6a2022-08-25 11:09:24 -040019 "strings"
Alan Donovan6643abb2013-08-22 12:27:55 -040020
Andrew Gerrand5ebbcd12014-11-10 08:50:40 +110021 "golang.org/x/tools/go/callgraph"
22 "golang.org/x/tools/go/ssa"
Andrew Gerrand5ebbcd12014-11-10 08:50:40 +110023 "golang.org/x/tools/go/types/typeutil"
Alan Donovan6643abb2013-08-22 12:27:55 -040024)
25
Alan Donovan9b38eaf2014-06-16 15:46:07 -040026const (
27 // optimization options; enable all when committing
28 optRenumber = true // enable renumbering optimization (makes logs hard to read)
29 optHVN = true // enable pointer equivalence via Hash-Value Numbering
30
31 // debugging options; disable all when committing
32 debugHVN = false // enable assertions in HVN
33 debugHVNVerbose = false // enable extra HVN logging
34 debugHVNCrossCheck = false // run solver with/without HVN and compare (caveats below)
Alan Donovanf0324262014-06-16 16:31:30 -040035 debugTimers = false // show running time of each phase
Alan Donovan9b38eaf2014-06-16 15:46:07 -040036)
37
Alan Donovan3b5de062013-09-16 09:49:10 -040038// object.flags bitmask values.
39const (
40 otTagged = 1 << iota // type-tagged object
41 otIndirect // type-tagged object with indirect payload
42 otFunction // function object
43)
44
45// An object represents a contiguous block of memory to which some
46// (generalized) pointer may point.
47//
48// (Note: most variables called 'obj' are not *objects but nodeids
49// such that a.nodes[obj].obj != nil.)
Alan Donovan3b5de062013-09-16 09:49:10 -040050type object struct {
51 // flags is a bitset of the node type (ot*) flags defined above.
52 flags uint32
53
54 // Number of following nodes belonging to the same "object"
55 // allocation. Zero for all other nodes.
56 size uint32
57
Alan Donovanae060fe2013-10-01 09:46:33 -040058 // data describes this object; it has one of these types:
59 //
60 // ssa.Value for an object allocated by an SSA operation.
61 // types.Type for an rtype instance object or *rtype-tagged object.
Dan Kortschak40779212022-03-12 10:35:13 +103062 // string for an intrinsic object, e.g. the array behind os.Args.
63 // nil for an object allocated by an intrinsic.
Alan Donovanae060fe2013-10-01 09:46:33 -040064 // (cgn provides the identity of the intrinsic.)
65 data interface{}
Alan Donovan3b5de062013-09-16 09:49:10 -040066
67 // The call-graph node (=context) in which this object was allocated.
68 // May be nil for global objects: Global, Const, some Functions.
69 cgn *cgnode
Alan Donovan3b5de062013-09-16 09:49:10 -040070}
71
Alan Donovan6643abb2013-08-22 12:27:55 -040072// nodeid denotes a node.
73// It is an index within analysis.nodes.
74// We use small integers, not *node pointers, for many reasons:
75// - they are smaller on 64-bit systems.
76// - sets of them can be represented compactly in bitvectors or BDDs.
77// - order matters; a field offset can be computed by simple addition.
78type nodeid uint32
79
Alan Donovan6643abb2013-08-22 12:27:55 -040080// A node is an equivalence class of memory locations.
81// Nodes may be pointers, pointed-to locations, neither, or both.
Alan Donovan3b5de062013-09-16 09:49:10 -040082//
83// Nodes that are pointed-to locations ("labels") have an enclosing
84// object (see analysis.enclosingObject).
Alan Donovan6643abb2013-08-22 12:27:55 -040085type node struct {
Alan Donovan3b5de062013-09-16 09:49:10 -040086 // If non-nil, this node is the start of an object
87 // (addressable memory location).
Alan Donovan9b38eaf2014-06-16 15:46:07 -040088 // The following obj.size nodes implicitly belong to the object;
Alan Donovan3b5de062013-09-16 09:49:10 -040089 // they locate their object by scanning back.
90 obj *object
Alan Donovan6643abb2013-08-22 12:27:55 -040091
92 // The type of the field denoted by this node. Non-aggregate,
Alan Donovan3b5de062013-09-16 09:49:10 -040093 // unless this is an tagged.T node (i.e. the thing
Alan Donovan6643abb2013-08-22 12:27:55 -040094 // pointed to by an interface) in which case typ is that type.
95 typ types.Type
96
Alan Donovan6643abb2013-08-22 12:27:55 -040097 // subelement indicates which directly embedded subelement of
98 // an object of aggregate type (struct, tuple, array) this is.
99 subelement *fieldInfo // e.g. ".a.b[*].c"
100
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400101 // Solver state for the canonical node of this pointer-
102 // equivalence class. Each node is created with its own state
103 // but they become shared after HVN.
104 solve *solverState
Alan Donovan6643abb2013-08-22 12:27:55 -0400105}
106
Alan Donovan6643abb2013-08-22 12:27:55 -0400107// An analysis instance holds the state of a single pointer analysis problem.
108type analysis struct {
Alan Donovan3b5de062013-09-16 09:49:10 -0400109 config *Config // the client's control/observer interface
110 prog *ssa.Program // the program being analyzed
111 log io.Writer // log stream; nil to disable
112 panicNode nodeid // sink for panic, source for recover
113 nodes []*node // indexed by nodeid
114 flattenMemo map[types.Type][]*fieldInfo // memoization of flatten()
Alan Donovan5f966442014-02-18 12:40:44 -0800115 trackTypes map[types.Type]bool // memoization of shouldTrack()
Alan Donovan3b5de062013-09-16 09:49:10 -0400116 constraints []constraint // set of constraints
Alan Donovan785cfaa2013-09-25 17:17:42 -0400117 cgnodes []*cgnode // all cgnodes
Alan Donovan3b5de062013-09-16 09:49:10 -0400118 genq []*cgnode // queue of functions to generate constraints for
119 intrinsics map[*ssa.Function]intrinsic // non-nil values are summaries for intrinsic fns
Alan Donovan5b55a712013-09-27 11:33:01 -0400120 globalval map[ssa.Value]nodeid // node for each global ssa.Value
121 globalobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
122 localval map[ssa.Value]nodeid // node for each local ssa.Value
123 localobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400124 atFuncs map[*ssa.Function]bool // address-taken functions (for presolver)
125 mapValues []nodeid // values of makemap objects (indirect in HVN)
Alan Donovan74117bc2014-06-11 13:12:15 -0400126 work nodeset // solver's worklist
Alan Donovan06c41922013-09-30 12:39:54 -0400127 result *Result // results of the analysis
Alan Donovan5f966442014-02-18 12:40:44 -0800128 track track // pointerlike types whose aliasing we track
Alan Donovan74117bc2014-06-11 13:12:15 -0400129 deltaSpace []int // working space for iterating over PTS deltas
Alan Donovan3b5de062013-09-16 09:49:10 -0400130
Alan Donovan8bb20b82013-10-17 09:26:44 -0400131 // Reflection & intrinsics:
Alan Donovan03ca00d2014-02-19 13:32:36 -0500132 hasher typeutil.Hasher // cache of type hashes
133 reflectValueObj types.Object // type symbol for reflect.Value (if present)
134 reflectValueCall *ssa.Function // (reflect.Value).Call
135 reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
136 reflectRtypePtr *types.Pointer // *reflect.rtype
137 reflectType *types.Named // reflect.Type
138 rtypes typeutil.Map // nodeid of canonical *rtype-tagged object for type T
139 reflectZeros typeutil.Map // nodeid of canonical T-tagged object for zero value
140 runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer
Alan Donovan3b5de062013-09-16 09:49:10 -0400141}
142
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400143// enclosingObj returns the first node of the addressable memory
144// object that encloses node id. Panic ensues if that node does not
145// belong to any object.
146func (a *analysis) enclosingObj(id nodeid) nodeid {
Alan Donovan3b5de062013-09-16 09:49:10 -0400147 // Find previous node with obj != nil.
148 for i := id; i >= 0; i-- {
149 n := a.nodes[i]
150 if obj := n.obj; obj != nil {
151 if i+nodeid(obj.size) <= id {
152 break // out of bounds
153 }
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400154 return i
Alan Donovan3b5de062013-09-16 09:49:10 -0400155 }
156 }
157 panic("node has no enclosing object")
158}
159
160// labelFor returns the Label for node id.
161// Panic ensues if that node is not addressable.
162func (a *analysis) labelFor(id nodeid) *Label {
163 return &Label{
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400164 obj: a.nodes[a.enclosingObj(id)].obj,
Alan Donovan3b5de062013-09-16 09:49:10 -0400165 subelement: a.nodes[id].subelement,
166 }
Alan Donovan6643abb2013-08-22 12:27:55 -0400167}
168
169func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) {
Alan Donovan5f966442014-02-18 12:40:44 -0800170 msg := fmt.Sprintf(format, args...)
171 if a.log != nil {
172 fmt.Fprintf(a.log, "%s: warning: %s\n", a.prog.Fset.Position(pos), msg)
173 }
174 a.result.Warnings = append(a.result.Warnings, Warning{pos, msg})
175}
176
177// computeTrackBits sets a.track to the necessary 'track' bits for the pointer queries.
178func (a *analysis) computeTrackBits() {
Dominik Honnefa99f4ec2017-03-01 20:43:03 +0100179 if len(a.config.extendedQueries) != 0 {
180 // TODO(dh): only track the types necessary for the query.
181 a.track = trackAll
182 return
183 }
Alan Donovan5f966442014-02-18 12:40:44 -0800184 var queryTypes []types.Type
185 for v := range a.config.Queries {
186 queryTypes = append(queryTypes, v.Type())
187 }
188 for v := range a.config.IndirectQueries {
189 queryTypes = append(queryTypes, mustDeref(v.Type()))
190 }
191 for _, t := range queryTypes {
192 switch t.Underlying().(type) {
193 case *types.Chan:
194 a.track |= trackChan
195 case *types.Map:
196 a.track |= trackMap
197 case *types.Pointer:
198 a.track |= trackPtr
199 case *types.Slice:
200 a.track |= trackSlice
201 case *types.Interface:
202 a.track = trackAll
203 return
204 }
205 if rVObj := a.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) {
206 a.track = trackAll
207 return
208 }
209 }
Alan Donovan6643abb2013-08-22 12:27:55 -0400210}
211
212// Analyze runs the pointer analysis with the scope and options
213// specified by config, and returns the (synthetic) root of the callgraph.
214//
Alan Donovanc509cf12014-02-27 14:13:52 -0500215// Pointer analysis of a transitively closed well-typed program should
216// always succeed. An error can occur only due to an internal bug.
Alan Donovanc509cf12014-02-27 14:13:52 -0500217func Analyze(config *Config) (result *Result, err error) {
Alan Donovan5c5c4f42014-06-19 15:30:51 -0400218 if config.Mains == nil {
219 return nil, fmt.Errorf("no main/test packages to analyze (check $GOROOT/$GOPATH)")
220 }
Alan Donovanc509cf12014-02-27 14:13:52 -0500221 defer func() {
222 if p := recover(); p != nil {
Alan Donovan98ed3d32014-03-11 18:37:19 -0400223 err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p)
Alan Donovanc509cf12014-02-27 14:13:52 -0500224 fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:")
225 debug.PrintStack()
226 }
227 }()
228
Alan Donovan6643abb2013-08-22 12:27:55 -0400229 a := &analysis{
230 config: config,
231 log: config.Log,
232 prog: config.prog(),
Alan Donovan5b55a712013-09-27 11:33:01 -0400233 globalval: make(map[ssa.Value]nodeid),
234 globalobj: make(map[ssa.Value]nodeid),
Alan Donovan6643abb2013-08-22 12:27:55 -0400235 flattenMemo: make(map[types.Type][]*fieldInfo),
Alan Donovan5f966442014-02-18 12:40:44 -0800236 trackTypes: make(map[types.Type]bool),
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400237 atFuncs: make(map[*ssa.Function]bool),
Alan Donovan03ca00d2014-02-19 13:32:36 -0500238 hasher: typeutil.MakeHasher(),
Alan Donovan6643abb2013-08-22 12:27:55 -0400239 intrinsics: make(map[*ssa.Function]intrinsic),
Alan Donovan06c41922013-09-30 12:39:54 -0400240 result: &Result{
Alan Donovan28104d22014-02-20 11:35:09 -0500241 Queries: make(map[ssa.Value]Pointer),
242 IndirectQueries: make(map[ssa.Value]Pointer),
Alan Donovan06c41922013-09-30 12:39:54 -0400243 },
Alan Donovan74117bc2014-06-11 13:12:15 -0400244 deltaSpace: make([]int, 0, 100),
Alan Donovan6643abb2013-08-22 12:27:55 -0400245 }
246
Alan Donovan5b55a712013-09-27 11:33:01 -0400247 if false {
248 a.log = os.Stderr // for debugging crashes; extremely verbose
249 }
250
251 if a.log != nil {
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400252 fmt.Fprintln(a.log, "==== Starting analysis")
Alan Donovan5b55a712013-09-27 11:33:01 -0400253 }
254
Alan Donovan0c9517d2014-02-19 13:08:06 -0500255 // Pointer analysis requires a complete program for soundness.
256 // Check to prevent accidental misconfiguration.
257 for _, pkg := range a.prog.AllPackages() {
258 // (This only checks that the package scope is complete,
259 // not that func bodies exist, but it's a good signal.)
Alan Donovanafcda552015-08-31 17:50:03 -0400260 if !pkg.Pkg.Complete() {
261 return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Pkg.Path())
Alan Donovan0c9517d2014-02-19 13:08:06 -0500262 }
263 }
264
Alan Donovan3f2f9a72013-09-06 18:13:57 -0400265 if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
Alan Donovanafcda552015-08-31 17:50:03 -0400266 rV := reflect.Pkg.Scope().Lookup("Value")
Alan Donovan8bb20b82013-10-17 09:26:44 -0400267 a.reflectValueObj = rV
Alan Donovan5f966442014-02-18 12:40:44 -0800268 a.reflectValueCall = a.prog.LookupMethod(rV.Type(), nil, "Call")
Alan Donovanafcda552015-08-31 17:50:03 -0400269 a.reflectType = reflect.Pkg.Scope().Lookup("Type").Type().(*types.Named)
270 a.reflectRtypeObj = reflect.Pkg.Scope().Lookup("rtype")
Alan Donovan3371b792013-09-23 16:13:01 -0400271 a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
Alan Donovan3b5de062013-09-16 09:49:10 -0400272
273 // Override flattening of reflect.Value, treating it like a basic type.
274 tReflectValue := a.reflectValueObj.Type()
275 a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}}
276
Alan Donovan5f966442014-02-18 12:40:44 -0800277 // Override shouldTrack of reflect.Value and *reflect.rtype.
278 // Always track pointers of these types.
279 a.trackTypes[tReflectValue] = true
280 a.trackTypes[a.reflectRtypePtr] = true
281
Alan Donovan3b5de062013-09-16 09:49:10 -0400282 a.rtypes.SetHasher(a.hasher)
283 a.reflectZeros.SetHasher(a.hasher)
Alan Donovan6643abb2013-08-22 12:27:55 -0400284 }
Alan Donovan8bb20b82013-10-17 09:26:44 -0400285 if runtime := a.prog.ImportedPackage("runtime"); runtime != nil {
286 a.runtimeSetFinalizer = runtime.Func("SetFinalizer")
287 }
Alan Donovan5f966442014-02-18 12:40:44 -0800288 a.computeTrackBits()
Alan Donovan6643abb2013-08-22 12:27:55 -0400289
Alan Donovan829d52f2014-02-20 11:57:48 -0500290 a.generate()
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400291 a.showCounts()
Alan Donovan6643abb2013-08-22 12:27:55 -0400292
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400293 if optRenumber {
294 a.renumber()
Alan Donovan6643abb2013-08-22 12:27:55 -0400295 }
296
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400297 N := len(a.nodes) // excludes solver-created nodes
298
299 if optHVN {
300 if debugHVNCrossCheck {
301 // Cross-check: run the solver once without
302 // optimization, once with, and compare the
303 // solutions.
304 savedConstraints := a.constraints
305
306 a.solve()
307 a.dumpSolution("A.pts", N)
308
309 // Restore.
310 a.constraints = savedConstraints
311 for _, n := range a.nodes {
312 n.solve = new(solverState)
313 }
314 a.nodes = a.nodes[:N]
315
316 // rtypes is effectively part of the solver state.
317 a.rtypes = typeutil.Map{}
318 a.rtypes.SetHasher(a.hasher)
319 }
320
321 a.hvn()
322 }
323
324 if debugHVNCrossCheck {
325 runtime.GC()
326 runtime.GC()
327 }
Alan Donovan6643abb2013-08-22 12:27:55 -0400328
329 a.solve()
330
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400331 // Compare solutions.
332 if optHVN && debugHVNCrossCheck {
333 a.dumpSolution("B.pts", N)
334
335 if !diff("A.pts", "B.pts") {
336 return nil, fmt.Errorf("internal error: optimization changed solution")
337 }
338 }
339
Alan Donovan829d52f2014-02-20 11:57:48 -0500340 // Create callgraph.Nodes in deterministic order.
341 if cg := a.result.CallGraph; cg != nil {
342 for _, caller := range a.cgnodes {
343 cg.CreateNode(caller.fn)
344 }
345 }
346
Alan Donovan8bb20b82013-10-17 09:26:44 -0400347 // Add dynamic edges to call graph.
Alan Donovan74117bc2014-06-11 13:12:15 -0400348 var space [100]int
Alan Donovan785cfaa2013-09-25 17:17:42 -0400349 for _, caller := range a.cgnodes {
350 for _, site := range caller.sites {
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400351 for _, callee := range a.nodes[site.targets].solve.pts.AppendTo(space[:0]) {
Alan Donovan74117bc2014-06-11 13:12:15 -0400352 a.callEdge(caller, site, nodeid(callee))
Alan Donovan6643abb2013-08-22 12:27:55 -0400353 }
354 }
355 }
356
Alan Donovanc509cf12014-02-27 14:13:52 -0500357 return a.result, nil
Alan Donovan6643abb2013-08-22 12:27:55 -0400358}
Alan Donovan8bb20b82013-10-17 09:26:44 -0400359
360// callEdge is called for each edge in the callgraph.
361// calleeid is the callee's object node (has otFunction flag).
Alan Donovan829d52f2014-02-20 11:57:48 -0500362func (a *analysis) callEdge(caller *cgnode, site *callsite, calleeid nodeid) {
Alan Donovan8bb20b82013-10-17 09:26:44 -0400363 obj := a.nodes[calleeid].obj
364 if obj.flags&otFunction == 0 {
365 panic(fmt.Sprintf("callEdge %s -> n%d: not a function object", site, calleeid))
366 }
367 callee := obj.cgn
368
Alan Donovan829d52f2014-02-20 11:57:48 -0500369 if cg := a.result.CallGraph; cg != nil {
Alan Donovan9531aca2014-04-15 15:41:02 -0400370 // TODO(adonovan): opt: I would expect duplicate edges
371 // (to wrappers) to arise due to the elimination of
372 // context information, but I haven't observed any.
373 // Understand this better.
Alan Donovan829d52f2014-02-20 11:57:48 -0500374 callgraph.AddEdge(cg.CreateNode(caller.fn), site.instr, cg.CreateNode(callee.fn))
Alan Donovan8bb20b82013-10-17 09:26:44 -0400375 }
376
377 if a.log != nil {
378 fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee)
379 }
380
Tim King36a5c6a2022-08-25 11:09:24 -0400381 // Warn about calls to functions that are handled unsoundly.
Alan Donovan8bb20b82013-10-17 09:26:44 -0400382 // TODO(adonovan): de-dup these messages.
Tim King36a5c6a2022-08-25 11:09:24 -0400383 fn := callee.fn
384
385 // Warn about calls to non-intrinsic external functions.
386 if fn.Blocks == nil && a.findIntrinsic(fn) == nil {
Alan Donovan8bb20b82013-10-17 09:26:44 -0400387 a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn)
388 a.warnf(fn.Pos(), " (declared here)")
389 }
Tim King36a5c6a2022-08-25 11:09:24 -0400390
391 // Warn about calls to generic function bodies.
392 if fn.TypeParams().Len() > 0 && len(fn.TypeArgs()) == 0 {
393 a.warnf(site.pos(), "unsound call to generic function body: %s (build with ssa.InstantiateGenerics)", fn)
394 a.warnf(fn.Pos(), " (declared here)")
395 }
396
397 // Warn about calls to instantiation wrappers of generics functions.
398 if fn.Origin() != nil && strings.HasPrefix(fn.Synthetic, "instantiation wrapper ") {
399 a.warnf(site.pos(), "unsound call to instantiation wrapper of generic: %s (build with ssa.InstantiateGenerics)", fn)
400 a.warnf(fn.Pos(), " (declared here)")
401 }
Alan Donovan8bb20b82013-10-17 09:26:44 -0400402}
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400403
404// dumpSolution writes the PTS solution to the specified file.
405//
406// It only dumps the nodes that existed before solving. The order in
407// which solver-created nodes are created depends on pre-solver
408// optimization, so we can't include them in the cross-check.
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400409func (a *analysis) dumpSolution(filename string, N int) {
410 f, err := os.Create(filename)
411 if err != nil {
412 panic(err)
413 }
414 for id, n := range a.nodes[:N] {
415 if _, err := fmt.Fprintf(f, "pts(n%d) = {", id); err != nil {
416 panic(err)
417 }
418 var sep string
419 for _, l := range n.solve.pts.AppendTo(a.deltaSpace) {
420 if l >= N {
421 break
422 }
423 fmt.Fprintf(f, "%s%d", sep, l)
424 sep = " "
425 }
426 fmt.Fprintf(f, "} : %s\n", n.typ)
427 }
428 if err := f.Close(); err != nil {
429 panic(err)
430 }
431}
432
433// showCounts logs the size of the constraint system. A typical
434// optimized distribution is 65% copy, 13% load, 11% addr, 5%
435// offsetAddr, 4% store, 2% others.
Alan Donovan9b38eaf2014-06-16 15:46:07 -0400436func (a *analysis) showCounts() {
437 if a.log != nil {
438 counts := make(map[reflect.Type]int)
439 for _, c := range a.constraints {
440 counts[reflect.TypeOf(c)]++
441 }
442 fmt.Fprintf(a.log, "# constraints:\t%d\n", len(a.constraints))
443 var lines []string
444 for t, n := range counts {
445 line := fmt.Sprintf("%7d (%2d%%)\t%s", n, 100*n/len(a.constraints), t)
446 lines = append(lines, line)
447 }
448 sort.Sort(sort.Reverse(sort.StringSlice(lines)))
449 for _, line := range lines {
450 fmt.Fprintf(a.log, "\t%s\n", line)
451 }
452
453 fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
454
455 // Show number of pointer equivalence classes.
456 m := make(map[*solverState]bool)
457 for _, n := range a.nodes {
458 m[n.solve] = true
459 }
460 fmt.Fprintf(a.log, "# ptsets:\t%d\n", len(m))
461 }
462}