Alan Donovan | 713699d | 2013-08-27 18:49:13 -0400 | [diff] [blame] | 1 | // 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 Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 5 | package pointer |
| 6 | |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 7 | // This file defines the main datatypes and Analyze function of the pointer analysis. |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 8 | |
| 9 | import ( |
| 10 | "fmt" |
| 11 | "go/token" |
Alan Donovan | 542ffc7 | 2015-12-29 13:06:30 -0500 | [diff] [blame] | 12 | "go/types" |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 13 | "io" |
| 14 | "os" |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 15 | "reflect" |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 16 | "runtime" |
Alan Donovan | c509cf1 | 2014-02-27 14:13:52 -0500 | [diff] [blame] | 17 | "runtime/debug" |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 18 | "sort" |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 19 | "strings" |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 20 | |
Andrew Gerrand | 5ebbcd1 | 2014-11-10 08:50:40 +1100 | [diff] [blame] | 21 | "golang.org/x/tools/go/callgraph" |
| 22 | "golang.org/x/tools/go/ssa" |
Andrew Gerrand | 5ebbcd1 | 2014-11-10 08:50:40 +1100 | [diff] [blame] | 23 | "golang.org/x/tools/go/types/typeutil" |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 24 | ) |
| 25 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 26 | const ( |
| 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 Donovan | f032426 | 2014-06-16 16:31:30 -0400 | [diff] [blame] | 35 | debugTimers = false // show running time of each phase |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 36 | ) |
| 37 | |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 38 | // object.flags bitmask values. |
| 39 | const ( |
| 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 Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 50 | type 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 Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 58 | // 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 Kortschak | 4077921 | 2022-03-12 10:35:13 +1030 | [diff] [blame] | 62 | // string for an intrinsic object, e.g. the array behind os.Args. |
| 63 | // nil for an object allocated by an intrinsic. |
Alan Donovan | ae060fe | 2013-10-01 09:46:33 -0400 | [diff] [blame] | 64 | // (cgn provides the identity of the intrinsic.) |
| 65 | data interface{} |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 66 | |
| 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 Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 70 | } |
| 71 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 72 | // 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. |
| 78 | type nodeid uint32 |
| 79 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 80 | // A node is an equivalence class of memory locations. |
| 81 | // Nodes may be pointers, pointed-to locations, neither, or both. |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 82 | // |
| 83 | // Nodes that are pointed-to locations ("labels") have an enclosing |
| 84 | // object (see analysis.enclosingObject). |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 85 | type node struct { |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 86 | // If non-nil, this node is the start of an object |
| 87 | // (addressable memory location). |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 88 | // The following obj.size nodes implicitly belong to the object; |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 89 | // they locate their object by scanning back. |
| 90 | obj *object |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 91 | |
| 92 | // The type of the field denoted by this node. Non-aggregate, |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 93 | // unless this is an tagged.T node (i.e. the thing |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 94 | // pointed to by an interface) in which case typ is that type. |
| 95 | typ types.Type |
| 96 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 97 | // 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 Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 101 | // 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 Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 105 | } |
| 106 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 107 | // An analysis instance holds the state of a single pointer analysis problem. |
| 108 | type analysis struct { |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 109 | 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 Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 115 | trackTypes map[types.Type]bool // memoization of shouldTrack() |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 116 | constraints []constraint // set of constraints |
Alan Donovan | 785cfaa | 2013-09-25 17:17:42 -0400 | [diff] [blame] | 117 | cgnodes []*cgnode // all cgnodes |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 118 | genq []*cgnode // queue of functions to generate constraints for |
| 119 | intrinsics map[*ssa.Function]intrinsic // non-nil values are summaries for intrinsic fns |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 120 | 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 Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 124 | atFuncs map[*ssa.Function]bool // address-taken functions (for presolver) |
| 125 | mapValues []nodeid // values of makemap objects (indirect in HVN) |
Alan Donovan | 74117bc | 2014-06-11 13:12:15 -0400 | [diff] [blame] | 126 | work nodeset // solver's worklist |
Alan Donovan | 06c4192 | 2013-09-30 12:39:54 -0400 | [diff] [blame] | 127 | result *Result // results of the analysis |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 128 | track track // pointerlike types whose aliasing we track |
Alan Donovan | 74117bc | 2014-06-11 13:12:15 -0400 | [diff] [blame] | 129 | deltaSpace []int // working space for iterating over PTS deltas |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 130 | |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 131 | // Reflection & intrinsics: |
Alan Donovan | 03ca00d | 2014-02-19 13:32:36 -0500 | [diff] [blame] | 132 | 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 Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 141 | } |
| 142 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 143 | // 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. |
| 146 | func (a *analysis) enclosingObj(id nodeid) nodeid { |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 147 | // 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 Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 154 | return i |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 155 | } |
| 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. |
| 162 | func (a *analysis) labelFor(id nodeid) *Label { |
| 163 | return &Label{ |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 164 | obj: a.nodes[a.enclosingObj(id)].obj, |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 165 | subelement: a.nodes[id].subelement, |
| 166 | } |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) { |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 170 | 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. |
| 178 | func (a *analysis) computeTrackBits() { |
Dominik Honnef | a99f4ec | 2017-03-01 20:43:03 +0100 | [diff] [blame] | 179 | 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 Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 184 | 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 Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 210 | } |
| 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 Donovan | c509cf1 | 2014-02-27 14:13:52 -0500 | [diff] [blame] | 215 | // Pointer analysis of a transitively closed well-typed program should |
| 216 | // always succeed. An error can occur only due to an internal bug. |
Alan Donovan | c509cf1 | 2014-02-27 14:13:52 -0500 | [diff] [blame] | 217 | func Analyze(config *Config) (result *Result, err error) { |
Alan Donovan | 5c5c4f4 | 2014-06-19 15:30:51 -0400 | [diff] [blame] | 218 | if config.Mains == nil { |
| 219 | return nil, fmt.Errorf("no main/test packages to analyze (check $GOROOT/$GOPATH)") |
| 220 | } |
Alan Donovan | c509cf1 | 2014-02-27 14:13:52 -0500 | [diff] [blame] | 221 | defer func() { |
| 222 | if p := recover(); p != nil { |
Alan Donovan | 98ed3d3 | 2014-03-11 18:37:19 -0400 | [diff] [blame] | 223 | err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p) |
Alan Donovan | c509cf1 | 2014-02-27 14:13:52 -0500 | [diff] [blame] | 224 | fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:") |
| 225 | debug.PrintStack() |
| 226 | } |
| 227 | }() |
| 228 | |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 229 | a := &analysis{ |
| 230 | config: config, |
| 231 | log: config.Log, |
| 232 | prog: config.prog(), |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 233 | globalval: make(map[ssa.Value]nodeid), |
| 234 | globalobj: make(map[ssa.Value]nodeid), |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 235 | flattenMemo: make(map[types.Type][]*fieldInfo), |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 236 | trackTypes: make(map[types.Type]bool), |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 237 | atFuncs: make(map[*ssa.Function]bool), |
Alan Donovan | 03ca00d | 2014-02-19 13:32:36 -0500 | [diff] [blame] | 238 | hasher: typeutil.MakeHasher(), |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 239 | intrinsics: make(map[*ssa.Function]intrinsic), |
Alan Donovan | 06c4192 | 2013-09-30 12:39:54 -0400 | [diff] [blame] | 240 | result: &Result{ |
Alan Donovan | 28104d2 | 2014-02-20 11:35:09 -0500 | [diff] [blame] | 241 | Queries: make(map[ssa.Value]Pointer), |
| 242 | IndirectQueries: make(map[ssa.Value]Pointer), |
Alan Donovan | 06c4192 | 2013-09-30 12:39:54 -0400 | [diff] [blame] | 243 | }, |
Alan Donovan | 74117bc | 2014-06-11 13:12:15 -0400 | [diff] [blame] | 244 | deltaSpace: make([]int, 0, 100), |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 245 | } |
| 246 | |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 247 | if false { |
| 248 | a.log = os.Stderr // for debugging crashes; extremely verbose |
| 249 | } |
| 250 | |
| 251 | if a.log != nil { |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 252 | fmt.Fprintln(a.log, "==== Starting analysis") |
Alan Donovan | 5b55a71 | 2013-09-27 11:33:01 -0400 | [diff] [blame] | 253 | } |
| 254 | |
Alan Donovan | 0c9517d | 2014-02-19 13:08:06 -0500 | [diff] [blame] | 255 | // 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 Donovan | afcda55 | 2015-08-31 17:50:03 -0400 | [diff] [blame] | 260 | if !pkg.Pkg.Complete() { |
| 261 | return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Pkg.Path()) |
Alan Donovan | 0c9517d | 2014-02-19 13:08:06 -0500 | [diff] [blame] | 262 | } |
| 263 | } |
| 264 | |
Alan Donovan | 3f2f9a7 | 2013-09-06 18:13:57 -0400 | [diff] [blame] | 265 | if reflect := a.prog.ImportedPackage("reflect"); reflect != nil { |
Alan Donovan | afcda55 | 2015-08-31 17:50:03 -0400 | [diff] [blame] | 266 | rV := reflect.Pkg.Scope().Lookup("Value") |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 267 | a.reflectValueObj = rV |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 268 | a.reflectValueCall = a.prog.LookupMethod(rV.Type(), nil, "Call") |
Alan Donovan | afcda55 | 2015-08-31 17:50:03 -0400 | [diff] [blame] | 269 | a.reflectType = reflect.Pkg.Scope().Lookup("Type").Type().(*types.Named) |
| 270 | a.reflectRtypeObj = reflect.Pkg.Scope().Lookup("rtype") |
Alan Donovan | 3371b79 | 2013-09-23 16:13:01 -0400 | [diff] [blame] | 271 | a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type()) |
Alan Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 272 | |
| 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 Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 277 | // 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 Donovan | 3b5de06 | 2013-09-16 09:49:10 -0400 | [diff] [blame] | 282 | a.rtypes.SetHasher(a.hasher) |
| 283 | a.reflectZeros.SetHasher(a.hasher) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 284 | } |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 285 | if runtime := a.prog.ImportedPackage("runtime"); runtime != nil { |
| 286 | a.runtimeSetFinalizer = runtime.Func("SetFinalizer") |
| 287 | } |
Alan Donovan | 5f96644 | 2014-02-18 12:40:44 -0800 | [diff] [blame] | 288 | a.computeTrackBits() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 289 | |
Alan Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 290 | a.generate() |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 291 | a.showCounts() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 292 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 293 | if optRenumber { |
| 294 | a.renumber() |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 295 | } |
| 296 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 297 | 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 Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 328 | |
| 329 | a.solve() |
| 330 | |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 331 | // 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 Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 340 | // 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 Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 347 | // Add dynamic edges to call graph. |
Alan Donovan | 74117bc | 2014-06-11 13:12:15 -0400 | [diff] [blame] | 348 | var space [100]int |
Alan Donovan | 785cfaa | 2013-09-25 17:17:42 -0400 | [diff] [blame] | 349 | for _, caller := range a.cgnodes { |
| 350 | for _, site := range caller.sites { |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 351 | for _, callee := range a.nodes[site.targets].solve.pts.AppendTo(space[:0]) { |
Alan Donovan | 74117bc | 2014-06-11 13:12:15 -0400 | [diff] [blame] | 352 | a.callEdge(caller, site, nodeid(callee)) |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 353 | } |
| 354 | } |
| 355 | } |
| 356 | |
Alan Donovan | c509cf1 | 2014-02-27 14:13:52 -0500 | [diff] [blame] | 357 | return a.result, nil |
Alan Donovan | 6643abb | 2013-08-22 12:27:55 -0400 | [diff] [blame] | 358 | } |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 359 | |
| 360 | // callEdge is called for each edge in the callgraph. |
| 361 | // calleeid is the callee's object node (has otFunction flag). |
Alan Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 362 | func (a *analysis) callEdge(caller *cgnode, site *callsite, calleeid nodeid) { |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 363 | 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 Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 369 | if cg := a.result.CallGraph; cg != nil { |
Alan Donovan | 9531aca | 2014-04-15 15:41:02 -0400 | [diff] [blame] | 370 | // 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 Donovan | 829d52f | 2014-02-20 11:57:48 -0500 | [diff] [blame] | 374 | callgraph.AddEdge(cg.CreateNode(caller.fn), site.instr, cg.CreateNode(callee.fn)) |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 375 | } |
| 376 | |
| 377 | if a.log != nil { |
| 378 | fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee) |
| 379 | } |
| 380 | |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 381 | // Warn about calls to functions that are handled unsoundly. |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 382 | // TODO(adonovan): de-dup these messages. |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 383 | fn := callee.fn |
| 384 | |
| 385 | // Warn about calls to non-intrinsic external functions. |
| 386 | if fn.Blocks == nil && a.findIntrinsic(fn) == nil { |
Alan Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 387 | a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn) |
| 388 | a.warnf(fn.Pos(), " (declared here)") |
| 389 | } |
Tim King | 36a5c6a | 2022-08-25 11:09:24 -0400 | [diff] [blame] | 390 | |
| 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 Donovan | 8bb20b8 | 2013-10-17 09:26:44 -0400 | [diff] [blame] | 402 | } |
Alan Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 403 | |
| 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 Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 409 | func (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 Donovan | 9b38eaf | 2014-06-16 15:46:07 -0400 | [diff] [blame] | 436 | func (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 | } |