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