| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package pointer |
| |
| // This file defines a naive Andersen-style solver for the inclusion |
| // constraint system. |
| |
| import ( |
| "fmt" |
| |
| "code.google.com/p/go.tools/go/types" |
| ) |
| |
| func (a *analysis) solve() { |
| // Initialize points-to sets and complex constraint sets. |
| for _, c := range a.constraints { |
| c.init(a) |
| } |
| a.constraints = nil // aid GC |
| |
| work := a.work |
| |
| // Now we've initialized all constraints, we populate the |
| // worklist with nodes that point to something initially (due |
| // to addrConstraints) and have other constraints attached. |
| for id, n := range a.nodes { |
| if len(n.pts) > 0 && (n.copyTo != nil || n.complex != nil) { |
| if a.log != nil { |
| fmt.Fprintf(a.log, "Adding to worklist n%d\n", id) |
| } |
| a.addWork(nodeid(id)) |
| } |
| } |
| work.swap() |
| |
| // Solver main loop. |
| for round := 1; ; round++ { |
| if work.swap() { |
| if a.log != nil { |
| fmt.Fprintf(a.log, "Solving, round %d\n", round) |
| } |
| |
| // Next iteration. |
| if work.empty() { |
| break // done |
| } |
| } |
| |
| id := work.take() |
| n := a.nodes[id] |
| |
| if a.log != nil { |
| fmt.Fprintf(a.log, "\tnode n%d\n", id) |
| } |
| |
| // Difference propagation. |
| delta := n.pts.diff(n.prevPts) |
| if delta == nil { |
| continue |
| } |
| n.prevPts = n.pts.clone() |
| |
| // Process complex constraints dependent on n. |
| for c := range n.complex { |
| if a.log != nil { |
| fmt.Fprintf(a.log, "\t\tconstraint %s\n", c) |
| } |
| c.solve(a, n, delta) |
| } |
| |
| // Process copy constraints. |
| var copySeen nodeset |
| for mid := range n.copyTo { |
| if copySeen.add(mid) { |
| if a.nodes[mid].pts.addAll(delta) { |
| a.addWork(mid) |
| } |
| } |
| } |
| |
| if a.log != nil { |
| fmt.Fprintf(a.log, "\t\tpts(n%d) = %s\n", id, n.pts) |
| } |
| } |
| |
| if a.log != nil { |
| fmt.Fprintf(a.log, "Solver done\n") |
| } |
| } |
| |
| func (a *analysis) addWork(id nodeid) { |
| a.work.add(id) |
| if a.log != nil { |
| fmt.Fprintf(a.log, "\t\twork: n%d\n", id) |
| } |
| } |
| |
| func (c *addrConstraint) init(a *analysis) { |
| a.nodes[c.dst].pts.add(c.src) |
| } |
| func (c *copyConstraint) init(a *analysis) { |
| a.nodes[c.src].copyTo.add(c.dst) |
| } |
| |
| // Complex constraints attach themselves to the relevant pointer node. |
| |
| func (c *storeConstraint) init(a *analysis) { |
| a.nodes[c.dst].complex.add(c) |
| } |
| func (c *loadConstraint) init(a *analysis) { |
| a.nodes[c.src].complex.add(c) |
| } |
| func (c *offsetAddrConstraint) init(a *analysis) { |
| a.nodes[c.src].complex.add(c) |
| } |
| func (c *typeAssertConstraint) init(a *analysis) { |
| a.nodes[c.src].complex.add(c) |
| } |
| func (c *invokeConstraint) init(a *analysis) { |
| a.nodes[c.iface].complex.add(c) |
| } |
| |
| // onlineCopy adds a copy edge. It is called online, i.e. during |
| // solving, so it adds edges and pts members directly rather than by |
| // instantiating a 'constraint'. |
| // |
| // The size of the copy is implicitly 1. |
| // It returns true if pts(dst) changed. |
| // |
| func (a *analysis) onlineCopy(dst, src nodeid) bool { |
| if dst != src { |
| if nsrc := a.nodes[src]; nsrc.copyTo.add(dst) { |
| if a.log != nil { |
| fmt.Fprintf(a.log, "\t\t\tdynamic copy n%d <- n%d\n", dst, src) |
| } |
| return a.nodes[dst].pts.addAll(nsrc.pts) |
| } |
| } |
| return false |
| } |
| |
| // Returns sizeof. |
| // Implicitly adds nodes to worklist. |
| func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 { |
| for i := uint32(0); i < sizeof; i++ { |
| if a.onlineCopy(dst, src) { |
| a.addWork(dst) |
| } |
| src++ |
| dst++ |
| } |
| return sizeof |
| } |
| |
| func (c *loadConstraint) solve(a *analysis, n *node, delta nodeset) { |
| var changed bool |
| for k := range delta { |
| koff := k + nodeid(c.offset) |
| if a.onlineCopy(c.dst, koff) { |
| changed = true |
| } |
| } |
| if changed { |
| a.addWork(c.dst) |
| } |
| } |
| |
| func (c *storeConstraint) solve(a *analysis, n *node, delta nodeset) { |
| for k := range delta { |
| koff := k + nodeid(c.offset) |
| if a.onlineCopy(koff, c.src) { |
| a.addWork(koff) |
| } |
| } |
| } |
| |
| func (c *offsetAddrConstraint) solve(a *analysis, n *node, delta nodeset) { |
| dst := a.nodes[c.dst] |
| for k := range delta { |
| if dst.pts.add(k + nodeid(c.offset)) { |
| a.addWork(c.dst) |
| } |
| } |
| } |
| |
| func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) { |
| tIface, _ := c.typ.Underlying().(*types.Interface) |
| |
| for ifaceObj := range delta { |
| ifaceValue, tConc := a.interfaceValue(ifaceObj) |
| |
| if tIface != nil { |
| if types.IsAssignableTo(tConc, tIface) { |
| if a.nodes[c.dst].pts.add(ifaceObj) { |
| a.addWork(c.dst) |
| } |
| } |
| } else { |
| if types.IsIdentical(tConc, c.typ) { |
| // Copy entire payload to dst. |
| // |
| // TODO(adonovan): opt: if tConc is |
| // nonpointerlike we can skip this |
| // entire constraint, perhaps. We |
| // only care about pointers among the |
| // fields. |
| a.onlineCopyN(c.dst, ifaceValue, a.sizeof(tConc)) |
| } |
| } |
| } |
| } |
| |
| func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) { |
| for ifaceObj := range delta { |
| ifaceValue, tConc := a.interfaceValue(ifaceObj) |
| |
| // Look up the concrete method. |
| meth := tConc.MethodSet().Lookup(c.method.Pkg(), c.method.Name()) |
| if meth == nil { |
| panic(fmt.Sprintf("n%d: type %s has no method %s (iface=n%d)", |
| c.iface, tConc, c.method, ifaceObj)) |
| } |
| fn := a.prog.Method(meth) |
| if fn == nil { |
| panic(fmt.Sprintf("n%d: no ssa.Function for %s", c.iface, meth)) |
| } |
| sig := fn.Signature |
| |
| fnObj := a.funcObj[fn] |
| |
| // Make callsite's fn variable point to identity of |
| // concrete method. (There's no need to add it to |
| // worklist since it never has attached constraints.) |
| a.nodes[c.params].pts.add(fnObj) |
| |
| // Extract value and connect to method's receiver. |
| // Copy payload to method's receiver param (arg0). |
| arg0 := a.funcParams(fnObj) |
| recvSize := a.sizeof(sig.Recv().Type()) |
| a.onlineCopyN(arg0, ifaceValue, recvSize) |
| |
| // Copy iface object payload to method receiver. |
| src := c.params + 1 // skip past identity |
| dst := arg0 + nodeid(recvSize) |
| |
| // Copy caller's argument block to method formal parameters. |
| paramsSize := a.sizeof(sig.Params()) |
| a.onlineCopyN(dst, src, paramsSize) |
| src += nodeid(paramsSize) |
| dst += nodeid(paramsSize) |
| |
| // Copy method results to caller's result block. |
| resultsSize := a.sizeof(sig.Results()) |
| a.onlineCopyN(src, dst, resultsSize) |
| } |
| } |
| |
| func (c *addrConstraint) solve(a *analysis, n *node, delta nodeset) { |
| panic("addr is not a complex constraint") |
| } |
| |
| func (c *copyConstraint) solve(a *analysis, n *node, delta nodeset) { |
| panic("copy is not a complex constraint") |
| } |