github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/pointer/solve.go (about) 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 5 package pointer 6 7 // This file defines a naive Andersen-style solver for the inclusion 8 // constraint system. 9 10 import ( 11 "fmt" 12 "go/types" 13 ) 14 15 type solverState struct { 16 complex []constraint // complex constraints attached to this node 17 copyTo nodeset // simple copy constraint edges 18 pts nodeset // points-to set of this node 19 prevPTS nodeset // pts(n) in previous iteration (for difference propagation) 20 } 21 22 func (a *analysis) solve() { 23 start("Solving") 24 if a.log != nil { 25 fmt.Fprintf(a.log, "\n\n==== Solving constraints\n\n") 26 } 27 28 // Solver main loop. 29 var delta nodeset 30 for { 31 // Add new constraints to the graph: 32 // static constraints from SSA on round 1, 33 // dynamic constraints from reflection thereafter. 34 a.processNewConstraints() 35 36 var x int 37 if !a.work.TakeMin(&x) { 38 break // empty 39 } 40 id := nodeid(x) 41 if a.log != nil { 42 fmt.Fprintf(a.log, "\tnode n%d\n", id) 43 } 44 45 n := a.nodes[id] 46 47 // Difference propagation. 48 delta.Difference(&n.solve.pts.Sparse, &n.solve.prevPTS.Sparse) 49 if delta.IsEmpty() { 50 continue 51 } 52 if a.log != nil { 53 fmt.Fprintf(a.log, "\t\tpts(n%d : %s) = %s + %s\n", 54 id, n.typ, &delta, &n.solve.prevPTS) 55 } 56 n.solve.prevPTS.Copy(&n.solve.pts.Sparse) 57 58 // Apply all resolution rules attached to n. 59 a.solveConstraints(n, &delta) 60 61 if a.log != nil { 62 fmt.Fprintf(a.log, "\t\tpts(n%d) = %s\n", id, &n.solve.pts) 63 } 64 } 65 66 if !a.nodes[0].solve.pts.IsEmpty() { 67 panic(fmt.Sprintf("pts(0) is nonempty: %s", &a.nodes[0].solve.pts)) 68 } 69 70 // Release working state (but keep final PTS). 71 for _, n := range a.nodes { 72 n.solve.complex = nil 73 n.solve.copyTo.Clear() 74 n.solve.prevPTS.Clear() 75 } 76 77 if a.log != nil { 78 fmt.Fprintf(a.log, "Solver done\n") 79 80 // Dump solution. 81 for i, n := range a.nodes { 82 if !n.solve.pts.IsEmpty() { 83 fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, &n.solve.pts, n.typ) 84 } 85 } 86 } 87 stop("Solving") 88 } 89 90 // processNewConstraints takes the new constraints from a.constraints 91 // and adds them to the graph, ensuring 92 // that new constraints are applied to pre-existing labels and 93 // that pre-existing constraints are applied to new labels. 94 // 95 func (a *analysis) processNewConstraints() { 96 // Take the slice of new constraints. 97 // (May grow during call to solveConstraints.) 98 constraints := a.constraints 99 a.constraints = nil 100 101 // Initialize points-to sets from addr-of (base) constraints. 102 for _, c := range constraints { 103 if c, ok := c.(*addrConstraint); ok { 104 dst := a.nodes[c.dst] 105 dst.solve.pts.add(c.src) 106 107 // Populate the worklist with nodes that point to 108 // something initially (due to addrConstraints) and 109 // have other constraints attached. 110 // (A no-op in round 1.) 111 if !dst.solve.copyTo.IsEmpty() || len(dst.solve.complex) > 0 { 112 a.addWork(c.dst) 113 } 114 } 115 } 116 117 // Attach simple (copy) and complex constraints to nodes. 118 var stale nodeset 119 for _, c := range constraints { 120 var id nodeid 121 switch c := c.(type) { 122 case *addrConstraint: 123 // base constraints handled in previous loop 124 continue 125 case *copyConstraint: 126 // simple (copy) constraint 127 id = c.src 128 a.nodes[id].solve.copyTo.add(c.dst) 129 default: 130 // complex constraint 131 id = c.ptr() 132 solve := a.nodes[id].solve 133 solve.complex = append(solve.complex, c) 134 } 135 136 if n := a.nodes[id]; !n.solve.pts.IsEmpty() { 137 if !n.solve.prevPTS.IsEmpty() { 138 stale.add(id) 139 } 140 a.addWork(id) 141 } 142 } 143 // Apply new constraints to pre-existing PTS labels. 144 var space [50]int 145 for _, id := range stale.AppendTo(space[:0]) { 146 n := a.nodes[nodeid(id)] 147 a.solveConstraints(n, &n.solve.prevPTS) 148 } 149 } 150 151 // solveConstraints applies each resolution rule attached to node n to 152 // the set of labels delta. It may generate new constraints in 153 // a.constraints. 154 // 155 func (a *analysis) solveConstraints(n *node, delta *nodeset) { 156 if delta.IsEmpty() { 157 return 158 } 159 160 // Process complex constraints dependent on n. 161 for _, c := range n.solve.complex { 162 if a.log != nil { 163 fmt.Fprintf(a.log, "\t\tconstraint %s\n", c) 164 } 165 c.solve(a, delta) 166 } 167 168 // Process copy constraints. 169 var copySeen nodeset 170 for _, x := range n.solve.copyTo.AppendTo(a.deltaSpace) { 171 mid := nodeid(x) 172 if copySeen.add(mid) { 173 if a.nodes[mid].solve.pts.addAll(delta) { 174 a.addWork(mid) 175 } 176 } 177 } 178 } 179 180 // addLabel adds label to the points-to set of ptr and reports whether the set grew. 181 func (a *analysis) addLabel(ptr, label nodeid) bool { 182 b := a.nodes[ptr].solve.pts.add(label) 183 if b && a.log != nil { 184 fmt.Fprintf(a.log, "\t\tpts(n%d) += n%d\n", ptr, label) 185 } 186 return b 187 } 188 189 func (a *analysis) addWork(id nodeid) { 190 a.work.Insert(int(id)) 191 if a.log != nil { 192 fmt.Fprintf(a.log, "\t\twork: n%d\n", id) 193 } 194 } 195 196 // onlineCopy adds a copy edge. It is called online, i.e. during 197 // solving, so it adds edges and pts members directly rather than by 198 // instantiating a 'constraint'. 199 // 200 // The size of the copy is implicitly 1. 201 // It returns true if pts(dst) changed. 202 // 203 func (a *analysis) onlineCopy(dst, src nodeid) bool { 204 if dst != src { 205 if nsrc := a.nodes[src]; nsrc.solve.copyTo.add(dst) { 206 if a.log != nil { 207 fmt.Fprintf(a.log, "\t\t\tdynamic copy n%d <- n%d\n", dst, src) 208 } 209 // TODO(adonovan): most calls to onlineCopy 210 // are followed by addWork, possibly batched 211 // via a 'changed' flag; see if there's a 212 // noticeable penalty to calling addWork here. 213 return a.nodes[dst].solve.pts.addAll(&nsrc.solve.pts) 214 } 215 } 216 return false 217 } 218 219 // Returns sizeof. 220 // Implicitly adds nodes to worklist. 221 // 222 // TODO(adonovan): now that we support a.copy() during solving, we 223 // could eliminate onlineCopyN, but it's much slower. Investigate. 224 // 225 func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 { 226 for i := uint32(0); i < sizeof; i++ { 227 if a.onlineCopy(dst, src) { 228 a.addWork(dst) 229 } 230 src++ 231 dst++ 232 } 233 return sizeof 234 } 235 236 func (c *loadConstraint) solve(a *analysis, delta *nodeset) { 237 var changed bool 238 for _, x := range delta.AppendTo(a.deltaSpace) { 239 k := nodeid(x) 240 koff := k + nodeid(c.offset) 241 if a.onlineCopy(c.dst, koff) { 242 changed = true 243 } 244 } 245 if changed { 246 a.addWork(c.dst) 247 } 248 } 249 250 func (c *storeConstraint) solve(a *analysis, delta *nodeset) { 251 for _, x := range delta.AppendTo(a.deltaSpace) { 252 k := nodeid(x) 253 koff := k + nodeid(c.offset) 254 if a.onlineCopy(koff, c.src) { 255 a.addWork(koff) 256 } 257 } 258 } 259 260 func (c *offsetAddrConstraint) solve(a *analysis, delta *nodeset) { 261 dst := a.nodes[c.dst] 262 for _, x := range delta.AppendTo(a.deltaSpace) { 263 k := nodeid(x) 264 if dst.solve.pts.add(k + nodeid(c.offset)) { 265 a.addWork(c.dst) 266 } 267 } 268 } 269 270 func (c *typeFilterConstraint) solve(a *analysis, delta *nodeset) { 271 for _, x := range delta.AppendTo(a.deltaSpace) { 272 ifaceObj := nodeid(x) 273 tDyn, _, indirect := a.taggedValue(ifaceObj) 274 if indirect { 275 // TODO(adonovan): we'll need to implement this 276 // when we start creating indirect tagged objects. 277 panic("indirect tagged object") 278 } 279 280 if types.AssignableTo(tDyn, c.typ) { 281 if a.addLabel(c.dst, ifaceObj) { 282 a.addWork(c.dst) 283 } 284 } 285 } 286 } 287 288 func (c *untagConstraint) solve(a *analysis, delta *nodeset) { 289 predicate := types.AssignableTo 290 if c.exact { 291 predicate = types.Identical 292 } 293 for _, x := range delta.AppendTo(a.deltaSpace) { 294 ifaceObj := nodeid(x) 295 tDyn, v, indirect := a.taggedValue(ifaceObj) 296 if indirect { 297 // TODO(adonovan): we'll need to implement this 298 // when we start creating indirect tagged objects. 299 panic("indirect tagged object") 300 } 301 302 if predicate(tDyn, c.typ) { 303 // Copy payload sans tag to dst. 304 // 305 // TODO(adonovan): opt: if tDyn is 306 // nonpointerlike we can skip this entire 307 // constraint, perhaps. We only care about 308 // pointers among the fields. 309 a.onlineCopyN(c.dst, v, a.sizeof(tDyn)) 310 } 311 } 312 } 313 314 func (c *invokeConstraint) solve(a *analysis, delta *nodeset) { 315 for _, x := range delta.AppendTo(a.deltaSpace) { 316 ifaceObj := nodeid(x) 317 tDyn, v, indirect := a.taggedValue(ifaceObj) 318 if indirect { 319 // TODO(adonovan): we may need to implement this if 320 // we ever apply invokeConstraints to reflect.Value PTSs, 321 // e.g. for (reflect.Value).Call. 322 panic("indirect tagged object") 323 } 324 325 // Look up the concrete method. 326 fn := a.prog.LookupMethod(tDyn, c.method.Pkg(), c.method.Name()) 327 if fn == nil { 328 panic(fmt.Sprintf("n%d: no ssa.Function for %s", c.iface, c.method)) 329 } 330 sig := fn.Signature 331 332 fnObj := a.globalobj[fn] // dynamic calls use shared contour 333 if fnObj == 0 { 334 // a.objectNode(fn) was not called during gen phase. 335 panic(fmt.Sprintf("a.globalobj[%s]==nil", fn)) 336 } 337 338 // Make callsite's fn variable point to identity of 339 // concrete method. (There's no need to add it to 340 // worklist since it never has attached constraints.) 341 a.addLabel(c.params, fnObj) 342 343 // Extract value and connect to method's receiver. 344 // Copy payload to method's receiver param (arg0). 345 arg0 := a.funcParams(fnObj) 346 recvSize := a.sizeof(sig.Recv().Type()) 347 a.onlineCopyN(arg0, v, recvSize) 348 349 src := c.params + 1 // skip past identity 350 dst := arg0 + nodeid(recvSize) 351 352 // Copy caller's argument block to method formal parameters. 353 paramsSize := a.sizeof(sig.Params()) 354 a.onlineCopyN(dst, src, paramsSize) 355 src += nodeid(paramsSize) 356 dst += nodeid(paramsSize) 357 358 // Copy method results to caller's result block. 359 resultsSize := a.sizeof(sig.Results()) 360 a.onlineCopyN(src, dst, resultsSize) 361 } 362 } 363 364 func (c *addrConstraint) solve(a *analysis, delta *nodeset) { 365 panic("addr is not a complex constraint") 366 } 367 368 func (c *copyConstraint) solve(a *analysis, delta *nodeset) { 369 panic("copy is not a complex constraint") 370 }