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  }