GoPLS Viewer

Home|gopls/go/pointer/solve.go
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
5package pointer
6
7// This file defines a naive Andersen-style solver for the inclusion
8// constraint system.
9
10import (
11    "fmt"
12    "go/types"
13)
14
15type 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
22func (a *analysissolve() {
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                idn.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 in := range a.nodes {
82            if !n.solve.pts.IsEmpty() {
83                fmt.Fprintf(a.log"pts(n%d) = %s : %s\n"i, &n.solve.ptsn.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.
94func (a *analysisprocessNewConstraints() {
95    // Take the slice of new constraints.
96    // (May grow during call to solveConstraints.)
97    constraints := a.constraints
98    a.constraints = nil
99
100    // Initialize points-to sets from addr-of (base) constraints.
101    for _c := range constraints {
102        if cok := c.(*addrConstraint); ok {
103            dst := a.nodes[c.dst]
104            dst.solve.pts.add(c.src)
105
106            // Populate the worklist with nodes that point to
107            // something initially (due to addrConstraints) and
108            // have other constraints attached.
109            // (A no-op in round 1.)
110            if !dst.solve.copyTo.IsEmpty() || len(dst.solve.complex) > 0 {
111                a.addWork(c.dst)
112            }
113        }
114    }
115
116    // Attach simple (copy) and complex constraints to nodes.
117    var stale nodeset
118    for _c := range constraints {
119        var id nodeid
120        switch c := c.(type) {
121        case *addrConstraint:
122            // base constraints handled in previous loop
123            continue
124        case *copyConstraint:
125            // simple (copy) constraint
126            id = c.src
127            a.nodes[id].solve.copyTo.add(c.dst)
128        default:
129            // complex constraint
130            id = c.ptr()
131            solve := a.nodes[id].solve
132            solve.complex = append(solve.complexc)
133        }
134
135        if n := a.nodes[id]; !n.solve.pts.IsEmpty() {
136            if !n.solve.prevPTS.IsEmpty() {
137                stale.add(id)
138            }
139            a.addWork(id)
140        }
141    }
142    // Apply new constraints to pre-existing PTS labels.
143    var space [50]int
144    for _id := range stale.AppendTo(space[:0]) {
145        n := a.nodes[nodeid(id)]
146        a.solveConstraints(n, &n.solve.prevPTS)
147    }
148}
149
150// solveConstraints applies each resolution rule attached to node n to
151// the set of labels delta.  It may generate new constraints in
152// a.constraints.
153func (a *analysissolveConstraints(n *nodedelta *nodeset) {
154    if delta.IsEmpty() {
155        return
156    }
157
158    // Process complex constraints dependent on n.
159    for _c := range n.solve.complex {
160        if a.log != nil {
161            fmt.Fprintf(a.log"\t\tconstraint %s\n"c)
162        }
163        c.solve(adelta)
164    }
165
166    // Process copy constraints.
167    var copySeen nodeset
168    for _x := range n.solve.copyTo.AppendTo(a.deltaSpace) {
169        mid := nodeid(x)
170        if copySeen.add(mid) {
171            if a.nodes[mid].solve.pts.addAll(delta) {
172                a.addWork(mid)
173            }
174        }
175    }
176}
177
178// addLabel adds label to the points-to set of ptr and reports whether the set grew.
179func (a *analysisaddLabel(ptrlabel nodeidbool {
180    b := a.nodes[ptr].solve.pts.add(label)
181    if b && a.log != nil {
182        fmt.Fprintf(a.log"\t\tpts(n%d) += n%d\n"ptrlabel)
183    }
184    return b
185}
186
187func (a *analysisaddWork(id nodeid) {
188    a.work.Insert(int(id))
189    if a.log != nil {
190        fmt.Fprintf(a.log"\t\twork: n%d\n"id)
191    }
192}
193
194// onlineCopy adds a copy edge.  It is called online, i.e. during
195// solving, so it adds edges and pts members directly rather than by
196// instantiating a 'constraint'.
197//
198// The size of the copy is implicitly 1.
199// It returns true if pts(dst) changed.
200func (a *analysisonlineCopy(dstsrc nodeidbool {
201    if dst != src {
202        if nsrc := a.nodes[src]; nsrc.solve.copyTo.add(dst) {
203            if a.log != nil {
204                fmt.Fprintf(a.log"\t\t\tdynamic copy n%d <- n%d\n"dstsrc)
205            }
206            // TODO(adonovan): most calls to onlineCopy
207            // are followed by addWork, possibly batched
208            // via a 'changed' flag; see if there's a
209            // noticeable penalty to calling addWork here.
210            return a.nodes[dst].solve.pts.addAll(&nsrc.solve.pts)
211        }
212    }
213    return false
214}
215
216// Returns sizeof.
217// Implicitly adds nodes to worklist.
218//
219// TODO(adonovan): now that we support a.copy() during solving, we
220// could eliminate onlineCopyN, but it's much slower.  Investigate.
221func (a *analysisonlineCopyN(dstsrc nodeidsizeof uint32uint32 {
222    for i := uint32(0); i < sizeofi++ {
223        if a.onlineCopy(dstsrc) {
224            a.addWork(dst)
225        }
226        src++
227        dst++
228    }
229    return sizeof
230}
231
232func (c *loadConstraintsolve(a *analysisdelta *nodeset) {
233    var changed bool
234    for _x := range delta.AppendTo(a.deltaSpace) {
235        k := nodeid(x)
236        koff := k + nodeid(c.offset)
237        if a.onlineCopy(c.dstkoff) {
238            changed = true
239        }
240    }
241    if changed {
242        a.addWork(c.dst)
243    }
244}
245
246func (c *storeConstraintsolve(a *analysisdelta *nodeset) {
247    for _x := range delta.AppendTo(a.deltaSpace) {
248        k := nodeid(x)
249        koff := k + nodeid(c.offset)
250        if a.onlineCopy(koffc.src) {
251            a.addWork(koff)
252        }
253    }
254}
255
256func (c *offsetAddrConstraintsolve(a *analysisdelta *nodeset) {
257    dst := a.nodes[c.dst]
258    for _x := range delta.AppendTo(a.deltaSpace) {
259        k := nodeid(x)
260        if dst.solve.pts.add(k + nodeid(c.offset)) {
261            a.addWork(c.dst)
262        }
263    }
264}
265
266func (c *typeFilterConstraintsolve(a *analysisdelta *nodeset) {
267    for _x := range delta.AppendTo(a.deltaSpace) {
268        ifaceObj := nodeid(x)
269        tDyn_indirect := a.taggedValue(ifaceObj)
270        if indirect {
271            // TODO(adonovan): we'll need to implement this
272            // when we start creating indirect tagged objects.
273            panic("indirect tagged object")
274        }
275
276        if types.AssignableTo(tDync.typ) {
277            if a.addLabel(c.dstifaceObj) {
278                a.addWork(c.dst)
279            }
280        }
281    }
282}
283
284func (c *untagConstraintsolve(a *analysisdelta *nodeset) {
285    predicate := types.AssignableTo
286    if c.exact {
287        predicate = types.Identical
288    }
289    for _x := range delta.AppendTo(a.deltaSpace) {
290        ifaceObj := nodeid(x)
291        tDynvindirect := a.taggedValue(ifaceObj)
292        if indirect {
293            // TODO(adonovan): we'll need to implement this
294            // when we start creating indirect tagged objects.
295            panic("indirect tagged object")
296        }
297
298        if predicate(tDync.typ) {
299            // Copy payload sans tag to dst.
300            //
301            // TODO(adonovan): opt: if tDyn is
302            // nonpointerlike we can skip this entire
303            // constraint, perhaps.  We only care about
304            // pointers among the fields.
305            a.onlineCopyN(c.dstva.sizeof(tDyn))
306        }
307    }
308}
309
310func (c *invokeConstraintsolve(a *analysisdelta *nodeset) {
311    for _x := range delta.AppendTo(a.deltaSpace) {
312        ifaceObj := nodeid(x)
313        tDynvindirect := a.taggedValue(ifaceObj)
314        if indirect {
315            // TODO(adonovan): we may need to implement this if
316            // we ever apply invokeConstraints to reflect.Value PTSs,
317            // e.g. for (reflect.Value).Call.
318            panic("indirect tagged object")
319        }
320
321        // Look up the concrete method.
322        fn := a.prog.LookupMethod(tDync.method.Pkg(), c.method.Name())
323        if fn == nil {
324            panic(fmt.Sprintf("n%d: no ssa.Function for %s"c.ifacec.method))
325        }
326        sig := fn.Signature
327
328        fnObj := a.globalobj[fn// dynamic calls use shared contour
329        if fnObj == 0 {
330            // a.objectNode(fn) was not called during gen phase.
331            panic(fmt.Sprintf("a.globalobj[%s]==nil"fn))
332        }
333
334        // Make callsite's fn variable point to identity of
335        // concrete method.  (There's no need to add it to
336        // worklist since it never has attached constraints.)
337        a.addLabel(c.paramsfnObj)
338
339        // Extract value and connect to method's receiver.
340        // Copy payload to method's receiver param (arg0).
341        arg0 := a.funcParams(fnObj)
342        recvSize := a.sizeof(sig.Recv().Type())
343        a.onlineCopyN(arg0vrecvSize)
344
345        src := c.params + 1 // skip past identity
346        dst := arg0 + nodeid(recvSize)
347
348        // Copy caller's argument block to method formal parameters.
349        paramsSize := a.sizeof(sig.Params())
350        a.onlineCopyN(dstsrcparamsSize)
351        src += nodeid(paramsSize)
352        dst += nodeid(paramsSize)
353
354        // Copy method results to caller's result block.
355        resultsSize := a.sizeof(sig.Results())
356        a.onlineCopyN(srcdstresultsSize)
357    }
358}
359
360func (c *addrConstraintsolve(a *analysisdelta *nodeset) {
361    panic("addr is not a complex constraint")
362}
363
364func (c *copyConstraintsolve(a *analysisdelta *nodeset) {
365    panic("copy is not a complex constraint")
366}
367
MembersX
analysis.addWork.id
loadConstraint.solve.c
invokeConstraint.solve.a
invokeConstraint.solve.RangeStmt_7942.BlockStmt.tDyn
invokeConstraint.solve.RangeStmt_7942.BlockStmt.ifaceObj
invokeConstraint.solve.RangeStmt_7942.BlockStmt.recvSize
addrConstraint.solve
analysis.processNewConstraints.RangeStmt_3033.BlockStmt.id
analysis.solveConstraints.copySeen
analysis.addLabel.b
loadConstraint.solve.delta
solverState
untagConstraint.solve.RangeStmt_7308.BlockStmt.v
loadConstraint.solve
analysis.solve.BlockStmt.x
analysis.solveConstraints.n
typeFilterConstraint.solve.RangeStmt_6772.x
solverState.copyTo
analysis.solve.BlockStmt.id
analysis.addLabel
analysis.onlineCopyN.dst
storeConstraint.solve.a
typeFilterConstraint.solve.RangeStmt_6772.BlockStmt.indirect
analysis.solveConstraints.RangeStmt_4055.c
storeConstraint.solve.c
offsetAddrConstraint.solve.delta
analysis.solve.BlockStmt.RangeStmt_1910.i
invokeConstraint.solve.delta
copyConstraint.solve.c
copyConstraint.solve
untagConstraint.solve.predicate
analysis.onlineCopy
typeFilterConstraint.solve
offsetAddrConstraint.solve.c
analysis.solve.RangeStmt_1723.n
analysis.solveConstraints.delta
analysis.onlineCopy.src
invokeConstraint.solve.c
solverState.prevPTS
analysis.solveConstraints.a
addrConstraint.solve.delta
analysis.processNewConstraints.RangeStmt_3633.id
analysis.solve
analysis.processNewConstraints.constraints
loadConstraint.solve.changed
typeFilterConstraint.solve.a
untagConstraint.solve.RangeStmt_7308.BlockStmt.ifaceObj
solverState.complex
untagConstraint.solve
untagConstraint.solve.RangeStmt_7308.BlockStmt.indirect
analysis.addLabel.label
analysis.onlineCopyN.i
untagConstraint.solve.RangeStmt_7308.BlockStmt.tDyn
invokeConstraint.solve.RangeStmt_7942.BlockStmt.sig
analysis.onlineCopyN
invokeConstraint.solve
analysis.processNewConstraints.RangeStmt_3033.BlockStmt.BlockStmt.solve
analysis.addLabel.a
analysis.addWork
untagConstraint.solve.RangeStmt_7308.x
copyConstraint.solve.a
analysis.solveConstraints.RangeStmt_4240.BlockStmt.mid
analysis.processNewConstraints.space
analysis.onlineCopyN.sizeof
offsetAddrConstraint.solve.RangeStmt_6560.BlockStmt.k
typeFilterConstraint.solve.RangeStmt_6772.BlockStmt.tDyn
invokeConstraint.solve.RangeStmt_7942.BlockStmt.v
analysis.onlineCopy.dst
loadConstraint.solve.RangeStmt_6051.x
typeFilterConstraint.solve.RangeStmt_6772.BlockStmt.ifaceObj
untagConstraint.solve.a
invokeConstraint.solve.RangeStmt_7942.BlockStmt.indirect
invokeConstraint.solve.RangeStmt_7942.BlockStmt.resultsSize
analysis.solve.delta
analysis.solveConstraints
analysis.onlineCopyN.a
loadConstraint.solve.a
storeConstraint.solve.delta
storeConstraint.solve.RangeStmt_6309.x
untagConstraint.solve.delta
invokeConstraint.solve.RangeStmt_7942.BlockStmt.paramsSize
analysis.processNewConstraints.stale
untagConstraint.solve.c
analysis.processNewConstraints
addrConstraint.solve.a
analysis.processNewConstraints.a
analysis.processNewConstraints.RangeStmt_3033.c
loadConstraint.solve.RangeStmt_6051.BlockStmt.k
storeConstraint.solve.RangeStmt_6309.BlockStmt.k
offsetAddrConstraint.solve
offsetAddrConstraint.solve.a
analysis.solve.BlockStmt.RangeStmt_1910.n
analysis.solve.a
solverState.pts
typeFilterConstraint.solve.RangeStmt_6772.BlockStmt._
invokeConstraint.solve.RangeStmt_7942.BlockStmt.fn
analysis.solveConstraints.RangeStmt_4240.x
analysis.onlineCopy.a
offsetAddrConstraint.solve.RangeStmt_6560.x
analysis.processNewConstraints.RangeStmt_2553.c
analysis.addWork.a
analysis.onlineCopyN.src
storeConstraint.solve
typeFilterConstraint.solve.c
typeFilterConstraint.solve.delta
invokeConstraint.solve.RangeStmt_7942.x
copyConstraint.solve.delta
analysis.addLabel.ptr
addrConstraint.solve.c
invokeConstraint.solve.RangeStmt_7942.BlockStmt.arg0
Members
X