mirror of
https://github.com/golang/go
synced 2024-11-18 16:14:46 -07:00
go/pointer: use sparse bit vectors to represent points-to sets in solver.
This optimization reduces solve time (typically >90% of the total) by about 78% when analysing real programs. It also makes the solver 100% deterministic since all iterations are ordered. Also: - remove unnecessary nodeid parameter to solve() method. - don't add a fieldInfo for singleton tuples (cosmetic fix). - inline+simplify "worklist" type. - replace "constraintset" type by a slice. LGTM=crawshaw R=crawshaw CC=golang-codereviews https://golang.org/cl/95240043
This commit is contained in:
parent
fec252214b
commit
74117bcfd8
@ -4,7 +4,9 @@ Pointer analysis to-do list
|
||||
===========================
|
||||
|
||||
CONSTRAINT GENERATION:
|
||||
- support reflection
|
||||
- support reflection:
|
||||
- a couple of operators are missing
|
||||
- reflect.Values may contain lvalues (CanAddr)
|
||||
- implement native intrinsics. These vary by platform.
|
||||
- unsafe.Pointer conversions. Three options:
|
||||
1) unsoundly (but type-safely) treat p=unsafe.Pointer(x) conversions as
|
||||
@ -17,21 +19,8 @@ CONSTRAINT GENERATION:
|
||||
allocations that identifies the object.
|
||||
|
||||
OPTIMISATIONS
|
||||
- pre-solver: PE and LE via HVN/HRU.
|
||||
- pre-solver: PE via HVN/HRU and LE.
|
||||
- solver: HCD, LCD.
|
||||
- use sparse bitvectors for ptsets
|
||||
- use sparse bitvectors for graph edges
|
||||
- experiment with different worklist algorithms:
|
||||
priority queue (solver visit-time order)
|
||||
red-black tree (node id order)
|
||||
double-ended queue (insertion order)
|
||||
fast doubly-linked list (See Zhanh et al PLDI'13)
|
||||
(insertion order with fast membership test)
|
||||
dannyb recommends sparse bitmap.
|
||||
|
||||
API:
|
||||
- Some optimisations (e.g. LE, PE) may change the API.
|
||||
Think about them sooner rather than later.
|
||||
|
||||
MISC:
|
||||
- Test on all platforms.
|
||||
|
@ -99,7 +99,7 @@ type node struct {
|
||||
// - *typeFilterConstraint y=x.(I)
|
||||
// - *untagConstraint y=x.(C)
|
||||
// - *invokeConstraint y=x.f(params...)
|
||||
complex constraintset
|
||||
complex []constraint
|
||||
}
|
||||
|
||||
// An analysis instance holds the state of a single pointer analysis problem.
|
||||
@ -119,9 +119,10 @@ type analysis struct {
|
||||
globalobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
|
||||
localval map[ssa.Value]nodeid // node for each local ssa.Value
|
||||
localobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
|
||||
work worklist // solver's worklist
|
||||
work nodeset // solver's worklist
|
||||
result *Result // results of the analysis
|
||||
track track // pointerlike types whose aliasing we track
|
||||
deltaSpace []int // working space for iterating over PTS deltas
|
||||
|
||||
// Reflection & intrinsics:
|
||||
hasher typeutil.Hasher // cache of type hashes
|
||||
@ -223,11 +224,11 @@ func Analyze(config *Config) (result *Result, err error) {
|
||||
trackTypes: make(map[types.Type]bool),
|
||||
hasher: typeutil.MakeHasher(),
|
||||
intrinsics: make(map[*ssa.Function]intrinsic),
|
||||
work: makeMapWorklist(),
|
||||
result: &Result{
|
||||
Queries: make(map[ssa.Value]Pointer),
|
||||
IndirectQueries: make(map[ssa.Value]Pointer),
|
||||
},
|
||||
deltaSpace: make([]int, 0, 100),
|
||||
}
|
||||
|
||||
if false {
|
||||
@ -300,10 +301,11 @@ func Analyze(config *Config) (result *Result, err error) {
|
||||
}
|
||||
|
||||
// Add dynamic edges to call graph.
|
||||
var space [100]int
|
||||
for _, caller := range a.cgnodes {
|
||||
for _, site := range caller.sites {
|
||||
for callee := range a.nodes[site.targets].pts {
|
||||
a.callEdge(caller, site, callee)
|
||||
for _, callee := range a.nodes[site.targets].pts.AppendTo(space[:0]) {
|
||||
a.callEdge(caller, site, nodeid(callee))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"go/token"
|
||||
"io"
|
||||
|
||||
"code.google.com/p/go.tools/container/intsets"
|
||||
"code.google.com/p/go.tools/go/callgraph"
|
||||
"code.google.com/p/go.tools/go/ssa"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
@ -134,18 +135,22 @@ type Pointer struct {
|
||||
// A PointsToSet is a set of labels (locations or allocations).
|
||||
type PointsToSet struct {
|
||||
a *analysis // may be nil if pts is nil
|
||||
pts nodeset
|
||||
pts *nodeset
|
||||
}
|
||||
|
||||
func (s PointsToSet) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "[")
|
||||
sep := ""
|
||||
for l := range s.pts {
|
||||
fmt.Fprintf(&buf, "%s%s", sep, s.a.labelFor(l))
|
||||
sep = ", "
|
||||
buf.WriteByte('[')
|
||||
if s.pts != nil {
|
||||
var space [50]int
|
||||
for i, l := range s.pts.AppendTo(space[:0]) {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(s.a.labelFor(nodeid(l)).String())
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&buf, "]")
|
||||
buf.WriteByte(']')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@ -153,8 +158,11 @@ func (s PointsToSet) String() string {
|
||||
// contains.
|
||||
func (s PointsToSet) Labels() []*Label {
|
||||
var labels []*Label
|
||||
for l := range s.pts {
|
||||
labels = append(labels, s.a.labelFor(l))
|
||||
if s.pts != nil {
|
||||
var space [50]int
|
||||
for _, l := range s.pts.AppendTo(space[:0]) {
|
||||
labels = append(labels, s.a.labelFor(nodeid(l)))
|
||||
}
|
||||
}
|
||||
return labels
|
||||
}
|
||||
@ -173,20 +181,24 @@ func (s PointsToSet) Labels() []*Label {
|
||||
func (s PointsToSet) DynamicTypes() *typeutil.Map {
|
||||
var tmap typeutil.Map
|
||||
tmap.SetHasher(s.a.hasher)
|
||||
for ifaceObjId := range s.pts {
|
||||
if !s.a.isTaggedObject(ifaceObjId) {
|
||||
continue // !CanHaveDynamicTypes(tDyn)
|
||||
if s.pts != nil {
|
||||
var space [50]int
|
||||
for _, x := range s.pts.AppendTo(space[:0]) {
|
||||
ifaceObjId := nodeid(x)
|
||||
if !s.a.isTaggedObject(ifaceObjId) {
|
||||
continue // !CanHaveDynamicTypes(tDyn)
|
||||
}
|
||||
tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
|
||||
if indirect {
|
||||
panic("indirect tagged object") // implement later
|
||||
}
|
||||
pts, ok := tmap.At(tDyn).(PointsToSet)
|
||||
if !ok {
|
||||
pts = PointsToSet{s.a, new(nodeset)}
|
||||
tmap.Set(tDyn, pts)
|
||||
}
|
||||
pts.pts.addAll(&s.a.nodes[v].pts)
|
||||
}
|
||||
tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
|
||||
if indirect {
|
||||
panic("indirect tagged object") // implement later
|
||||
}
|
||||
pts, ok := tmap.At(tDyn).(PointsToSet)
|
||||
if !ok {
|
||||
pts = PointsToSet{s.a, make(nodeset)}
|
||||
tmap.Set(tDyn, pts)
|
||||
}
|
||||
pts.pts.addAll(s.a.nodes[v].pts)
|
||||
}
|
||||
return &tmap
|
||||
}
|
||||
@ -194,12 +206,13 @@ func (s PointsToSet) DynamicTypes() *typeutil.Map {
|
||||
// Intersects reports whether this points-to set and the
|
||||
// argument points-to set contain common members.
|
||||
func (x PointsToSet) Intersects(y PointsToSet) bool {
|
||||
for l := range x.pts {
|
||||
if _, ok := y.pts[l]; ok {
|
||||
return true
|
||||
}
|
||||
if x.pts == nil || y.pts == nil {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
// This takes Θ(|x|+|y|) time.
|
||||
var z intsets.Sparse
|
||||
z.Intersection(&x.pts.Sparse, &y.pts.Sparse)
|
||||
return !z.IsEmpty()
|
||||
}
|
||||
|
||||
func (p Pointer) String() string {
|
||||
@ -208,7 +221,10 @@ func (p Pointer) String() string {
|
||||
|
||||
// PointsTo returns the points-to set of this pointer.
|
||||
func (p Pointer) PointsTo() PointsToSet {
|
||||
return PointsToSet{p.a, p.a.nodes[p.n].pts}
|
||||
if p.n == 0 {
|
||||
return PointsToSet{}
|
||||
}
|
||||
return PointsToSet{p.a, &p.a.nodes[p.n].pts}
|
||||
}
|
||||
|
||||
// MayAlias reports whether the receiver pointer may alias
|
||||
|
@ -28,7 +28,7 @@ type constraint interface {
|
||||
|
||||
// solve is called for complex constraints when the pts for
|
||||
// the node to which they are attached has changed.
|
||||
solve(a *analysis, n *node, delta nodeset)
|
||||
solve(a *analysis, delta *nodeset)
|
||||
|
||||
String() string
|
||||
}
|
||||
|
@ -272,9 +272,9 @@ func (c *runtimeSetFinalizerConstraint) String() string {
|
||||
return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f)
|
||||
}
|
||||
|
||||
func (c *runtimeSetFinalizerConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for fObj := range delta {
|
||||
tDyn, f, indirect := a.taggedValue(fObj)
|
||||
func (c *runtimeSetFinalizerConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, fObj := range delta.AppendTo(a.deltaSpace) {
|
||||
tDyn, f, indirect := a.taggedValue(nodeid(fObj))
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
// when we start creating indirect tagged objects.
|
||||
|
@ -13,8 +13,7 @@ import (
|
||||
func (a *analysis) optimize() {
|
||||
a.renumber()
|
||||
|
||||
// TODO(adonovan): opt:
|
||||
// PE, LE, HVN, HRU, sparse bitsets, etc.
|
||||
// TODO(adonovan): opt: PE (HVN, HRU), LE, etc.
|
||||
}
|
||||
|
||||
// renumber permutes a.nodes so that all nodes within an addressable
|
||||
|
@ -170,9 +170,10 @@ func (c *rVBytesConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVBytesConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVBytesConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, slice, indirect := a.taggedValue(vObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -231,13 +232,14 @@ func (c *rVCallConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Call(n%d)", c.result, c.v, c.arg)
|
||||
}
|
||||
|
||||
func (c *rVCallConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVCallConstraint) solve(a *analysis, delta *nodeset) {
|
||||
if c.targets == 0 {
|
||||
panic("no targets")
|
||||
}
|
||||
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, fn, indirect := a.taggedValue(vObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -372,9 +374,10 @@ func (c *rVElemConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVElemConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVElemConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -434,9 +437,10 @@ func (c *rVIndexConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVIndexConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -495,9 +499,10 @@ func (c *rVInterfaceConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVInterfaceConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -547,9 +552,10 @@ func (c *rVMapIndexConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVMapIndexConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, m, indirect := a.taggedValue(vObj)
|
||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||
if tMap == nil {
|
||||
@ -600,9 +606,10 @@ func (c *rVMapKeysConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVMapKeysConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, m, indirect := a.taggedValue(vObj)
|
||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||
if tMap == nil {
|
||||
@ -663,9 +670,10 @@ func (c *rVRecvConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVRecvConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVRecvConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, ch, indirect := a.taggedValue(vObj)
|
||||
tChan, _ := tDyn.Underlying().(*types.Chan)
|
||||
if tChan == nil {
|
||||
@ -717,8 +725,9 @@ func (c *rVSendConstraint) String() string {
|
||||
return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
|
||||
}
|
||||
|
||||
func (c *rVSendConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for vObj := range delta {
|
||||
func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, ch, indirect := a.taggedValue(vObj)
|
||||
tChan, _ := tDyn.Underlying().(*types.Chan)
|
||||
if tChan == nil {
|
||||
@ -769,8 +778,9 @@ func (c *rVSetBytesConstraint) String() string {
|
||||
return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x)
|
||||
}
|
||||
|
||||
func (c *rVSetBytesConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for vObj := range delta {
|
||||
func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, slice, indirect := a.taggedValue(vObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -818,8 +828,9 @@ func (c *rVSetMapIndexConstraint) String() string {
|
||||
return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val)
|
||||
}
|
||||
|
||||
func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for vObj := range delta {
|
||||
func (c *rVSetMapIndexConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, m, indirect := a.taggedValue(vObj)
|
||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||
if tMap == nil {
|
||||
@ -877,9 +888,10 @@ func (c *rVSliceConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *rVSliceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rVSliceConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -955,9 +967,10 @@ func (c *reflectChanOfConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t)
|
||||
}
|
||||
|
||||
func (c *reflectChanOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectChanOfConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(tObj)
|
||||
|
||||
if typeTooHigh(T) {
|
||||
@ -1026,9 +1039,10 @@ func (c *reflectIndirectConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v)
|
||||
}
|
||||
|
||||
func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectIndirectConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
vObj := nodeid(x)
|
||||
tDyn, _, _ := a.taggedValue(vObj)
|
||||
var res nodeid
|
||||
if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok {
|
||||
@ -1077,9 +1091,10 @@ func (c *reflectMakeChanConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectMakeChanConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectMakeChanConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
typObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
tChan, ok := T.Underlying().(*types.Chan)
|
||||
if !ok || tChan.Dir() != types.SendRecv {
|
||||
@ -1134,9 +1149,10 @@ func (c *reflectMakeMapConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectMakeMapConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectMakeMapConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
typObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
tMap, ok := T.Underlying().(*types.Map)
|
||||
if !ok {
|
||||
@ -1190,9 +1206,10 @@ func (c *reflectMakeSliceConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectMakeSliceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectMakeSliceConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
typObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
if _, ok := T.Underlying().(*types.Slice); !ok {
|
||||
continue // not a slice type
|
||||
@ -1246,9 +1263,10 @@ func (c *reflectNewConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectNewConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectNewConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
typObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
|
||||
// allocate new T object
|
||||
@ -1307,9 +1325,10 @@ func (c *reflectPtrToConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t)
|
||||
}
|
||||
|
||||
func (c *reflectPtrToConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectPtrToConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(tObj)
|
||||
|
||||
if typeTooHigh(T) {
|
||||
@ -1355,9 +1374,10 @@ func (c *reflectSliceOfConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t)
|
||||
}
|
||||
|
||||
func (c *reflectSliceOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectSliceOfConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(tObj)
|
||||
|
||||
if typeTooHigh(T) {
|
||||
@ -1401,9 +1421,10 @@ func (c *reflectTypeOfConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i)
|
||||
}
|
||||
|
||||
func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectTypeOfConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for iObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
iObj := nodeid(x)
|
||||
tDyn, _, _ := a.taggedValue(iObj)
|
||||
if a.addLabel(c.result, a.makeRtype(tDyn)) {
|
||||
changed = true
|
||||
@ -1451,9 +1472,10 @@ func (c *reflectZeroConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
|
||||
}
|
||||
|
||||
func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *reflectZeroConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for typObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
typObj := nodeid(x)
|
||||
T := a.rtypeTaggedValue(typObj)
|
||||
|
||||
// TODO(adonovan): if T is an interface type, we need
|
||||
@ -1510,13 +1532,14 @@ func (c *rtypeElemConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t)
|
||||
}
|
||||
|
||||
func (c *rtypeElemConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rtypeElemConstraint) solve(a *analysis, delta *nodeset) {
|
||||
// Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
|
||||
type hasElem interface {
|
||||
Elem() types.Type
|
||||
}
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.nodes[tObj].obj.data.(types.Type)
|
||||
if tHasElem, ok := T.Underlying().(hasElem); ok {
|
||||
if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
|
||||
@ -1560,7 +1583,7 @@ func (c *rtypeFieldByNameConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = (*reflect.rtype).FieldByName(n%d, %q)", c.result, c.t, c.name)
|
||||
}
|
||||
|
||||
func (c *rtypeFieldByNameConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rtypeFieldByNameConstraint) solve(a *analysis, delta *nodeset) {
|
||||
// type StructField struct {
|
||||
// 0 __identity__
|
||||
// 1 Name string
|
||||
@ -1572,7 +1595,8 @@ func (c *rtypeFieldByNameConstraint) solve(a *analysis, _ *node, delta nodeset)
|
||||
// 7 Anonymous bool
|
||||
// }
|
||||
|
||||
for tObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.nodes[tObj].obj.data.(types.Type)
|
||||
tStruct, ok := T.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
@ -1648,9 +1672,10 @@ func (c *rtypeInOutConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i)
|
||||
}
|
||||
|
||||
func (c *rtypeInOutConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rtypeInOutConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.nodes[tObj].obj.data.(types.Type)
|
||||
sig, ok := T.Underlying().(*types.Signature)
|
||||
if !ok {
|
||||
@ -1722,9 +1747,10 @@ func (c *rtypeKeyConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t)
|
||||
}
|
||||
|
||||
func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
func (c *rtypeKeyConstraint) solve(a *analysis, delta *nodeset) {
|
||||
changed := false
|
||||
for tObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.nodes[tObj].obj.data.(types.Type)
|
||||
if tMap, ok := T.Underlying().(*types.Map); ok {
|
||||
if a.addLabel(c.result, a.makeRtype(tMap.Key())) {
|
||||
@ -1782,8 +1808,9 @@ func changeRecv(sig *types.Signature) *types.Signature {
|
||||
return types.NewSignature(nil, nil, types.NewTuple(p2...), sig.Results(), sig.Variadic())
|
||||
}
|
||||
|
||||
func (c *rtypeMethodByNameConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for tObj := range delta {
|
||||
func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
tObj := nodeid(x)
|
||||
T := a.nodes[tObj].obj.data.(types.Type)
|
||||
|
||||
isIface := isInterface(T)
|
||||
|
@ -14,6 +14,8 @@ import (
|
||||
)
|
||||
|
||||
func (a *analysis) solve() {
|
||||
var delta nodeset
|
||||
|
||||
// Solver main loop.
|
||||
for round := 1; ; round++ {
|
||||
if a.log != nil {
|
||||
@ -25,10 +27,11 @@ func (a *analysis) solve() {
|
||||
// dynamic constraints from reflection thereafter.
|
||||
a.processNewConstraints()
|
||||
|
||||
id := a.work.take()
|
||||
if id == empty {
|
||||
break
|
||||
var x int
|
||||
if !a.work.TakeMin(&x) {
|
||||
break // empty
|
||||
}
|
||||
id := nodeid(x)
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "\tnode n%d\n", id)
|
||||
}
|
||||
@ -36,22 +39,22 @@ func (a *analysis) solve() {
|
||||
n := a.nodes[id]
|
||||
|
||||
// Difference propagation.
|
||||
delta := n.pts.diff(n.prevPts)
|
||||
if delta == nil {
|
||||
delta.Difference(&n.pts.Sparse, &n.prevPts.Sparse)
|
||||
if delta.IsEmpty() {
|
||||
continue
|
||||
}
|
||||
n.prevPts = n.pts.clone()
|
||||
n.prevPts.Copy(&n.pts.Sparse)
|
||||
|
||||
// Apply all resolution rules attached to n.
|
||||
a.solveConstraints(n, delta)
|
||||
a.solveConstraints(n, &delta)
|
||||
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "\t\tpts(n%d) = %s\n", id, n.pts)
|
||||
fmt.Fprintf(a.log, "\t\tpts(n%d) = %s\n", id, &n.pts)
|
||||
}
|
||||
}
|
||||
|
||||
if len(a.nodes[0].pts) > 0 {
|
||||
panic(fmt.Sprintf("pts(0) is nonempty: %s", a.nodes[0].pts))
|
||||
if !a.nodes[0].pts.IsEmpty() {
|
||||
panic(fmt.Sprintf("pts(0) is nonempty: %s", &a.nodes[0].pts))
|
||||
}
|
||||
|
||||
if a.log != nil {
|
||||
@ -59,8 +62,8 @@ func (a *analysis) solve() {
|
||||
|
||||
// Dump solution.
|
||||
for i, n := range a.nodes {
|
||||
if n.pts != nil {
|
||||
fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, n.pts, n.typ)
|
||||
if !n.pts.IsEmpty() {
|
||||
fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, &n.pts, n.typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,7 +90,7 @@ func (a *analysis) processNewConstraints() {
|
||||
// something initially (due to addrConstraints) and
|
||||
// have other constraints attached.
|
||||
// (A no-op in round 1.)
|
||||
if dst.copyTo != nil || dst.complex != nil {
|
||||
if !dst.copyTo.IsEmpty() || len(dst.complex) > 0 {
|
||||
a.addWork(c.dst)
|
||||
}
|
||||
}
|
||||
@ -108,20 +111,22 @@ func (a *analysis) processNewConstraints() {
|
||||
default:
|
||||
// complex constraint
|
||||
id = c.ptr()
|
||||
a.nodes[id].complex.add(c)
|
||||
ptr := a.nodes[id]
|
||||
ptr.complex = append(ptr.complex, c)
|
||||
}
|
||||
|
||||
if n := a.nodes[id]; len(n.pts) > 0 {
|
||||
if len(n.prevPts) > 0 {
|
||||
if n := a.nodes[id]; !n.pts.IsEmpty() {
|
||||
if !n.prevPts.IsEmpty() {
|
||||
stale.add(id)
|
||||
}
|
||||
a.addWork(id)
|
||||
}
|
||||
}
|
||||
// Apply new constraints to pre-existing PTS labels.
|
||||
for id := range stale {
|
||||
var space [50]int
|
||||
for _, id := range stale.AppendTo(space[:0]) {
|
||||
n := a.nodes[id]
|
||||
a.solveConstraints(n, n.prevPts)
|
||||
a.solveConstraints(n, &n.prevPts)
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,24 +134,23 @@ func (a *analysis) processNewConstraints() {
|
||||
// the set of labels delta. It may generate new constraints in
|
||||
// a.constraints.
|
||||
//
|
||||
func (a *analysis) solveConstraints(n *node, delta nodeset) {
|
||||
if delta == nil {
|
||||
func (a *analysis) solveConstraints(n *node, delta *nodeset) {
|
||||
if delta.IsEmpty() {
|
||||
return
|
||||
}
|
||||
|
||||
// Process complex constraints dependent on n.
|
||||
for c := range n.complex {
|
||||
for _, c := range n.complex {
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "\t\tconstraint %s\n", c)
|
||||
}
|
||||
// TODO(adonovan): parameter n is never needed, since
|
||||
// it's equal to c.ptr(). Remove.
|
||||
c.solve(a, n, delta)
|
||||
c.solve(a, delta)
|
||||
}
|
||||
|
||||
// Process copy constraints.
|
||||
var copySeen nodeset
|
||||
for mid := range n.copyTo {
|
||||
for _, x := range n.copyTo.AppendTo(a.deltaSpace) {
|
||||
mid := nodeid(x)
|
||||
if copySeen.add(mid) {
|
||||
if a.nodes[mid].pts.addAll(delta) {
|
||||
a.addWork(mid)
|
||||
@ -161,7 +165,7 @@ func (a *analysis) addLabel(ptr, label nodeid) bool {
|
||||
}
|
||||
|
||||
func (a *analysis) addWork(id nodeid) {
|
||||
a.work.add(id)
|
||||
a.work.Insert(int(id))
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "\t\twork: n%d\n", id)
|
||||
}
|
||||
@ -184,7 +188,7 @@ func (a *analysis) onlineCopy(dst, src nodeid) bool {
|
||||
// are followed by addWork, possibly batched
|
||||
// via a 'changed' flag; see if there's a
|
||||
// noticeable penalty to calling addWork here.
|
||||
return a.nodes[dst].pts.addAll(nsrc.pts)
|
||||
return a.nodes[dst].pts.addAll(&nsrc.pts)
|
||||
}
|
||||
}
|
||||
return false
|
||||
@ -207,9 +211,10 @@ func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
|
||||
return sizeof
|
||||
}
|
||||
|
||||
func (c *loadConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
func (c *loadConstraint) solve(a *analysis, delta *nodeset) {
|
||||
var changed bool
|
||||
for k := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
k := nodeid(x)
|
||||
koff := k + nodeid(c.offset)
|
||||
if a.onlineCopy(c.dst, koff) {
|
||||
changed = true
|
||||
@ -220,8 +225,9 @@ func (c *loadConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *storeConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
for k := range delta {
|
||||
func (c *storeConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
k := nodeid(x)
|
||||
koff := k + nodeid(c.offset)
|
||||
if a.onlineCopy(koff, c.src) {
|
||||
a.addWork(koff)
|
||||
@ -229,17 +235,19 @@ func (c *storeConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *offsetAddrConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
func (c *offsetAddrConstraint) solve(a *analysis, delta *nodeset) {
|
||||
dst := a.nodes[c.dst]
|
||||
for k := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
k := nodeid(x)
|
||||
if dst.pts.add(k + nodeid(c.offset)) {
|
||||
a.addWork(c.dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
for ifaceObj := range delta {
|
||||
func (c *typeFilterConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
ifaceObj := nodeid(x)
|
||||
tDyn, _, indirect := a.taggedValue(ifaceObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -255,12 +263,13 @@ func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
func (c *untagConstraint) solve(a *analysis, delta *nodeset) {
|
||||
predicate := types.AssignableTo
|
||||
if c.exact {
|
||||
predicate = types.Identical
|
||||
}
|
||||
for ifaceObj := range delta {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
ifaceObj := nodeid(x)
|
||||
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
@ -271,7 +280,7 @@ func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
if predicate(tDyn, c.typ) {
|
||||
// Copy payload sans tag to dst.
|
||||
//
|
||||
// TODO(adonovan): opt: if tConc is
|
||||
// TODO(adonovan): opt: if tDyn is
|
||||
// nonpointerlike we can skip this entire
|
||||
// constraint, perhaps. We only care about
|
||||
// pointers among the fields.
|
||||
@ -280,8 +289,9 @@ func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
for ifaceObj := range delta {
|
||||
func (c *invokeConstraint) solve(a *analysis, delta *nodeset) {
|
||||
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||
ifaceObj := nodeid(x)
|
||||
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
||||
if indirect {
|
||||
// TODO(adonovan): we may need to implement this if
|
||||
@ -329,10 +339,10 @@ func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *addrConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
func (c *addrConstraint) solve(a *analysis, delta *nodeset) {
|
||||
panic("addr is not a complex constraint")
|
||||
}
|
||||
|
||||
func (c *copyConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
func (c *copyConstraint) solve(a *analysis, delta *nodeset) {
|
||||
panic("copy is not a complex constraint")
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"code.google.com/p/go.tools/container/intsets"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
@ -154,10 +155,17 @@ func (a *analysis) flatten(t types.Type) []*fieldInfo {
|
||||
|
||||
case *types.Tuple:
|
||||
// No identity node: tuples are never address-taken.
|
||||
for i, n := 0, t.Len(); i < n; i++ {
|
||||
f := t.At(i)
|
||||
for _, fi := range a.flatten(f.Type()) {
|
||||
fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
|
||||
n := t.Len()
|
||||
if n == 1 {
|
||||
// Don't add a fieldInfo link for singletons,
|
||||
// e.g. in params/results.
|
||||
fl = append(fl, a.flatten(t.At(0).Type())...)
|
||||
} else {
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.At(i)
|
||||
for _, fi := range a.flatten(f.Type()) {
|
||||
fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,107 +254,29 @@ func sliceToArray(slice types.Type) *types.Array {
|
||||
|
||||
// Node set -------------------------------------------------------------------
|
||||
|
||||
// NB, mutator methods are attached to *nodeset.
|
||||
// nodeset may be a reference, but its address matters!
|
||||
type nodeset map[nodeid]struct{}
|
||||
type nodeset struct {
|
||||
intsets.Sparse
|
||||
}
|
||||
|
||||
// ---- Accessors ----
|
||||
|
||||
func (ns nodeset) String() string {
|
||||
func (ns *nodeset) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune('{')
|
||||
var sep string
|
||||
for n := range ns {
|
||||
fmt.Fprintf(&buf, "%sn%d", sep, n)
|
||||
sep = ", "
|
||||
var space [50]int
|
||||
for i, n := range ns.AppendTo(space[:0]) {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteRune('n')
|
||||
fmt.Fprintf(&buf, "%d", n)
|
||||
}
|
||||
buf.WriteRune('}')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// diff returns the set-difference x - y. nil => empty.
|
||||
//
|
||||
// TODO(adonovan): opt: extremely inefficient. BDDs do this in
|
||||
// constant time. Sparse bitvectors are linear but very fast.
|
||||
func (x nodeset) diff(y nodeset) nodeset {
|
||||
var z nodeset
|
||||
for k := range x {
|
||||
if _, ok := y[k]; !ok {
|
||||
z.add(k)
|
||||
}
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
// clone() returns an unaliased copy of x.
|
||||
func (x nodeset) clone() nodeset {
|
||||
return x.diff(nil)
|
||||
}
|
||||
|
||||
// ---- Mutators ----
|
||||
|
||||
func (ns *nodeset) add(n nodeid) bool {
|
||||
sz := len(*ns)
|
||||
if *ns == nil {
|
||||
*ns = make(nodeset)
|
||||
}
|
||||
(*ns)[n] = struct{}{}
|
||||
return len(*ns) > sz
|
||||
return ns.Sparse.Insert(int(n))
|
||||
}
|
||||
|
||||
func (x *nodeset) addAll(y nodeset) bool {
|
||||
if y == nil {
|
||||
return false
|
||||
}
|
||||
sz := len(*x)
|
||||
if *x == nil {
|
||||
*x = make(nodeset)
|
||||
}
|
||||
for n := range y {
|
||||
(*x)[n] = struct{}{}
|
||||
}
|
||||
return len(*x) > sz
|
||||
}
|
||||
|
||||
// Constraint set -------------------------------------------------------------
|
||||
|
||||
type constraintset map[constraint]struct{}
|
||||
|
||||
func (cs *constraintset) add(c constraint) bool {
|
||||
sz := len(*cs)
|
||||
if *cs == nil {
|
||||
*cs = make(constraintset)
|
||||
}
|
||||
(*cs)[c] = struct{}{}
|
||||
return len(*cs) > sz
|
||||
}
|
||||
|
||||
// Worklist -------------------------------------------------------------------
|
||||
|
||||
const empty nodeid = 1<<32 - 1
|
||||
|
||||
type worklist interface {
|
||||
add(nodeid) // Adds a node to the set
|
||||
take() nodeid // Takes a node from the set and returns it, or empty
|
||||
}
|
||||
|
||||
// Simple nondeterministic worklist based on a built-in map.
|
||||
type mapWorklist struct {
|
||||
set nodeset
|
||||
}
|
||||
|
||||
func (w *mapWorklist) add(n nodeid) {
|
||||
w.set[n] = struct{}{}
|
||||
}
|
||||
|
||||
func (w *mapWorklist) take() nodeid {
|
||||
for k := range w.set {
|
||||
delete(w.set, k)
|
||||
return k
|
||||
}
|
||||
return empty
|
||||
}
|
||||
|
||||
func makeMapWorklist() worklist {
|
||||
return &mapWorklist{make(nodeset)}
|
||||
func (x *nodeset) addAll(y *nodeset) bool {
|
||||
return x.UnionWith(&y.Sparse)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user