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:
|
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.
|
- implement native intrinsics. These vary by platform.
|
||||||
- unsafe.Pointer conversions. Three options:
|
- unsafe.Pointer conversions. Three options:
|
||||||
1) unsoundly (but type-safely) treat p=unsafe.Pointer(x) conversions as
|
1) unsoundly (but type-safely) treat p=unsafe.Pointer(x) conversions as
|
||||||
|
|
@ -17,21 +19,8 @@ CONSTRAINT GENERATION:
|
||||||
allocations that identifies the object.
|
allocations that identifies the object.
|
||||||
|
|
||||||
OPTIMISATIONS
|
OPTIMISATIONS
|
||||||
- pre-solver: PE and LE via HVN/HRU.
|
- pre-solver: PE via HVN/HRU and LE.
|
||||||
- solver: HCD, LCD.
|
- 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:
|
MISC:
|
||||||
- Test on all platforms.
|
- Test on all platforms.
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ type node struct {
|
||||||
// - *typeFilterConstraint y=x.(I)
|
// - *typeFilterConstraint y=x.(I)
|
||||||
// - *untagConstraint y=x.(C)
|
// - *untagConstraint y=x.(C)
|
||||||
// - *invokeConstraint y=x.f(params...)
|
// - *invokeConstraint y=x.f(params...)
|
||||||
complex constraintset
|
complex []constraint
|
||||||
}
|
}
|
||||||
|
|
||||||
// An analysis instance holds the state of a single pointer analysis problem.
|
// 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
|
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
|
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
|
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
|
result *Result // results of the analysis
|
||||||
track track // pointerlike types whose aliasing we track
|
track track // pointerlike types whose aliasing we track
|
||||||
|
deltaSpace []int // working space for iterating over PTS deltas
|
||||||
|
|
||||||
// Reflection & intrinsics:
|
// Reflection & intrinsics:
|
||||||
hasher typeutil.Hasher // cache of type hashes
|
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),
|
trackTypes: make(map[types.Type]bool),
|
||||||
hasher: typeutil.MakeHasher(),
|
hasher: typeutil.MakeHasher(),
|
||||||
intrinsics: make(map[*ssa.Function]intrinsic),
|
intrinsics: make(map[*ssa.Function]intrinsic),
|
||||||
work: makeMapWorklist(),
|
|
||||||
result: &Result{
|
result: &Result{
|
||||||
Queries: make(map[ssa.Value]Pointer),
|
Queries: make(map[ssa.Value]Pointer),
|
||||||
IndirectQueries: make(map[ssa.Value]Pointer),
|
IndirectQueries: make(map[ssa.Value]Pointer),
|
||||||
},
|
},
|
||||||
|
deltaSpace: make([]int, 0, 100),
|
||||||
}
|
}
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
|
|
@ -300,10 +301,11 @@ func Analyze(config *Config) (result *Result, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add dynamic edges to call graph.
|
// Add dynamic edges to call graph.
|
||||||
|
var space [100]int
|
||||||
for _, caller := range a.cgnodes {
|
for _, caller := range a.cgnodes {
|
||||||
for _, site := range caller.sites {
|
for _, site := range caller.sites {
|
||||||
for callee := range a.nodes[site.targets].pts {
|
for _, callee := range a.nodes[site.targets].pts.AppendTo(space[:0]) {
|
||||||
a.callEdge(caller, site, callee)
|
a.callEdge(caller, site, nodeid(callee))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"go/token"
|
"go/token"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/container/intsets"
|
||||||
"code.google.com/p/go.tools/go/callgraph"
|
"code.google.com/p/go.tools/go/callgraph"
|
||||||
"code.google.com/p/go.tools/go/ssa"
|
"code.google.com/p/go.tools/go/ssa"
|
||||||
"code.google.com/p/go.tools/go/types/typeutil"
|
"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).
|
// A PointsToSet is a set of labels (locations or allocations).
|
||||||
type PointsToSet struct {
|
type PointsToSet struct {
|
||||||
a *analysis // may be nil if pts is nil
|
a *analysis // may be nil if pts is nil
|
||||||
pts nodeset
|
pts *nodeset
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s PointsToSet) String() string {
|
func (s PointsToSet) String() string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
fmt.Fprintf(&buf, "[")
|
buf.WriteByte('[')
|
||||||
sep := ""
|
if s.pts != nil {
|
||||||
for l := range s.pts {
|
var space [50]int
|
||||||
fmt.Fprintf(&buf, "%s%s", sep, s.a.labelFor(l))
|
for i, l := range s.pts.AppendTo(space[:0]) {
|
||||||
sep = ", "
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(s.a.labelFor(nodeid(l)).String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&buf, "]")
|
buf.WriteByte(']')
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,8 +158,11 @@ func (s PointsToSet) String() string {
|
||||||
// contains.
|
// contains.
|
||||||
func (s PointsToSet) Labels() []*Label {
|
func (s PointsToSet) Labels() []*Label {
|
||||||
var labels []*Label
|
var labels []*Label
|
||||||
for l := range s.pts {
|
if s.pts != nil {
|
||||||
labels = append(labels, s.a.labelFor(l))
|
var space [50]int
|
||||||
|
for _, l := range s.pts.AppendTo(space[:0]) {
|
||||||
|
labels = append(labels, s.a.labelFor(nodeid(l)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return labels
|
return labels
|
||||||
}
|
}
|
||||||
|
|
@ -173,20 +181,24 @@ func (s PointsToSet) Labels() []*Label {
|
||||||
func (s PointsToSet) DynamicTypes() *typeutil.Map {
|
func (s PointsToSet) DynamicTypes() *typeutil.Map {
|
||||||
var tmap typeutil.Map
|
var tmap typeutil.Map
|
||||||
tmap.SetHasher(s.a.hasher)
|
tmap.SetHasher(s.a.hasher)
|
||||||
for ifaceObjId := range s.pts {
|
if s.pts != nil {
|
||||||
if !s.a.isTaggedObject(ifaceObjId) {
|
var space [50]int
|
||||||
continue // !CanHaveDynamicTypes(tDyn)
|
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
|
return &tmap
|
||||||
}
|
}
|
||||||
|
|
@ -194,12 +206,13 @@ func (s PointsToSet) DynamicTypes() *typeutil.Map {
|
||||||
// Intersects reports whether this points-to set and the
|
// Intersects reports whether this points-to set and the
|
||||||
// argument points-to set contain common members.
|
// argument points-to set contain common members.
|
||||||
func (x PointsToSet) Intersects(y PointsToSet) bool {
|
func (x PointsToSet) Intersects(y PointsToSet) bool {
|
||||||
for l := range x.pts {
|
if x.pts == nil || y.pts == nil {
|
||||||
if _, ok := y.pts[l]; ok {
|
return false
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 {
|
func (p Pointer) String() string {
|
||||||
|
|
@ -208,7 +221,10 @@ func (p Pointer) String() string {
|
||||||
|
|
||||||
// PointsTo returns the points-to set of this pointer.
|
// PointsTo returns the points-to set of this pointer.
|
||||||
func (p Pointer) PointsTo() PointsToSet {
|
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
|
// 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
|
// solve is called for complex constraints when the pts for
|
||||||
// the node to which they are attached has changed.
|
// the node to which they are attached has changed.
|
||||||
solve(a *analysis, n *node, delta nodeset)
|
solve(a *analysis, delta *nodeset)
|
||||||
|
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -272,9 +272,9 @@ func (c *runtimeSetFinalizerConstraint) String() string {
|
||||||
return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f)
|
return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *runtimeSetFinalizerConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *runtimeSetFinalizerConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for fObj := range delta {
|
for _, fObj := range delta.AppendTo(a.deltaSpace) {
|
||||||
tDyn, f, indirect := a.taggedValue(fObj)
|
tDyn, f, indirect := a.taggedValue(nodeid(fObj))
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// TODO(adonovan): we'll need to implement this
|
||||||
// when we start creating indirect tagged objects.
|
// when we start creating indirect tagged objects.
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@ import (
|
||||||
func (a *analysis) optimize() {
|
func (a *analysis) optimize() {
|
||||||
a.renumber()
|
a.renumber()
|
||||||
|
|
||||||
// TODO(adonovan): opt:
|
// TODO(adonovan): opt: PE (HVN, HRU), LE, etc.
|
||||||
// PE, LE, HVN, HRU, sparse bitsets, etc.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// renumber permutes a.nodes so that all nodes within an addressable
|
// 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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, slice, indirect := a.taggedValue(vObj)
|
tDyn, slice, indirect := a.taggedValue(vObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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)
|
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 {
|
if c.targets == 0 {
|
||||||
panic("no targets")
|
panic("no targets")
|
||||||
}
|
}
|
||||||
|
|
||||||
changed := false
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, fn, indirect := a.taggedValue(vObj)
|
tDyn, fn, indirect := a.taggedValue(vObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, m, indirect := a.taggedValue(vObj)
|
tDyn, m, indirect := a.taggedValue(vObj)
|
||||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||||
if tMap == nil {
|
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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, m, indirect := a.taggedValue(vObj)
|
tDyn, m, indirect := a.taggedValue(vObj)
|
||||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||||
if tMap == nil {
|
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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, ch, indirect := a.taggedValue(vObj)
|
tDyn, ch, indirect := a.taggedValue(vObj)
|
||||||
tChan, _ := tDyn.Underlying().(*types.Chan)
|
tChan, _ := tDyn.Underlying().(*types.Chan)
|
||||||
if tChan == nil {
|
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)
|
return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVSendConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, ch, indirect := a.taggedValue(vObj)
|
tDyn, ch, indirect := a.taggedValue(vObj)
|
||||||
tChan, _ := tDyn.Underlying().(*types.Chan)
|
tChan, _ := tDyn.Underlying().(*types.Chan)
|
||||||
if tChan == nil {
|
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)
|
return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rVSetBytesConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, slice, indirect := a.taggedValue(vObj)
|
tDyn, slice, indirect := a.taggedValue(vObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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)
|
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) {
|
func (c *rVSetMapIndexConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, m, indirect := a.taggedValue(vObj)
|
tDyn, m, indirect := a.taggedValue(vObj)
|
||||||
tMap, _ := tDyn.Underlying().(*types.Map)
|
tMap, _ := tDyn.Underlying().(*types.Map)
|
||||||
if tMap == nil {
|
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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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)
|
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
|
changed := false
|
||||||
for tObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
tObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(tObj)
|
T := a.rtypeTaggedValue(tObj)
|
||||||
|
|
||||||
if typeTooHigh(T) {
|
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)
|
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
|
changed := false
|
||||||
for vObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
vObj := nodeid(x)
|
||||||
tDyn, _, _ := a.taggedValue(vObj)
|
tDyn, _, _ := a.taggedValue(vObj)
|
||||||
var res nodeid
|
var res nodeid
|
||||||
if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok {
|
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)
|
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
|
changed := false
|
||||||
for typObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
typObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(typObj)
|
T := a.rtypeTaggedValue(typObj)
|
||||||
tChan, ok := T.Underlying().(*types.Chan)
|
tChan, ok := T.Underlying().(*types.Chan)
|
||||||
if !ok || tChan.Dir() != types.SendRecv {
|
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)
|
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
|
changed := false
|
||||||
for typObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
typObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(typObj)
|
T := a.rtypeTaggedValue(typObj)
|
||||||
tMap, ok := T.Underlying().(*types.Map)
|
tMap, ok := T.Underlying().(*types.Map)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -1190,9 +1206,10 @@ func (c *reflectMakeSliceConstraint) String() string {
|
||||||
return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ)
|
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
|
changed := false
|
||||||
for typObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
typObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(typObj)
|
T := a.rtypeTaggedValue(typObj)
|
||||||
if _, ok := T.Underlying().(*types.Slice); !ok {
|
if _, ok := T.Underlying().(*types.Slice); !ok {
|
||||||
continue // not a slice type
|
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)
|
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
|
changed := false
|
||||||
for typObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
typObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(typObj)
|
T := a.rtypeTaggedValue(typObj)
|
||||||
|
|
||||||
// allocate new T object
|
// 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)
|
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
|
changed := false
|
||||||
for tObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
tObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(tObj)
|
T := a.rtypeTaggedValue(tObj)
|
||||||
|
|
||||||
if typeTooHigh(T) {
|
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)
|
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
|
changed := false
|
||||||
for tObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
tObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(tObj)
|
T := a.rtypeTaggedValue(tObj)
|
||||||
|
|
||||||
if typeTooHigh(T) {
|
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)
|
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
|
changed := false
|
||||||
for iObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
iObj := nodeid(x)
|
||||||
tDyn, _, _ := a.taggedValue(iObj)
|
tDyn, _, _ := a.taggedValue(iObj)
|
||||||
if a.addLabel(c.result, a.makeRtype(tDyn)) {
|
if a.addLabel(c.result, a.makeRtype(tDyn)) {
|
||||||
changed = true
|
changed = true
|
||||||
|
|
@ -1451,9 +1472,10 @@ func (c *reflectZeroConstraint) String() string {
|
||||||
return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
|
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
|
changed := false
|
||||||
for typObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
typObj := nodeid(x)
|
||||||
T := a.rtypeTaggedValue(typObj)
|
T := a.rtypeTaggedValue(typObj)
|
||||||
|
|
||||||
// TODO(adonovan): if T is an interface type, we need
|
// 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)
|
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}.
|
// Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
|
||||||
type hasElem interface {
|
type hasElem interface {
|
||||||
Elem() types.Type
|
Elem() types.Type
|
||||||
}
|
}
|
||||||
changed := false
|
changed := false
|
||||||
for tObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
tObj := nodeid(x)
|
||||||
T := a.nodes[tObj].obj.data.(types.Type)
|
T := a.nodes[tObj].obj.data.(types.Type)
|
||||||
if tHasElem, ok := T.Underlying().(hasElem); ok {
|
if tHasElem, ok := T.Underlying().(hasElem); ok {
|
||||||
if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
|
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)
|
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 {
|
// type StructField struct {
|
||||||
// 0 __identity__
|
// 0 __identity__
|
||||||
// 1 Name string
|
// 1 Name string
|
||||||
|
|
@ -1572,7 +1595,8 @@ func (c *rtypeFieldByNameConstraint) solve(a *analysis, _ *node, delta nodeset)
|
||||||
// 7 Anonymous bool
|
// 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)
|
T := a.nodes[tObj].obj.data.(types.Type)
|
||||||
tStruct, ok := T.Underlying().(*types.Struct)
|
tStruct, ok := T.Underlying().(*types.Struct)
|
||||||
if !ok {
|
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)
|
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
|
changed := false
|
||||||
for tObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
tObj := nodeid(x)
|
||||||
T := a.nodes[tObj].obj.data.(types.Type)
|
T := a.nodes[tObj].obj.data.(types.Type)
|
||||||
sig, ok := T.Underlying().(*types.Signature)
|
sig, ok := T.Underlying().(*types.Signature)
|
||||||
if !ok {
|
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)
|
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
|
changed := false
|
||||||
for tObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
tObj := nodeid(x)
|
||||||
T := a.nodes[tObj].obj.data.(types.Type)
|
T := a.nodes[tObj].obj.data.(types.Type)
|
||||||
if tMap, ok := T.Underlying().(*types.Map); ok {
|
if tMap, ok := T.Underlying().(*types.Map); ok {
|
||||||
if a.addLabel(c.result, a.makeRtype(tMap.Key())) {
|
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())
|
return types.NewSignature(nil, nil, types.NewTuple(p2...), sig.Results(), sig.Variadic())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rtypeMethodByNameConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for tObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
tObj := nodeid(x)
|
||||||
T := a.nodes[tObj].obj.data.(types.Type)
|
T := a.nodes[tObj].obj.data.(types.Type)
|
||||||
|
|
||||||
isIface := isInterface(T)
|
isIface := isInterface(T)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *analysis) solve() {
|
func (a *analysis) solve() {
|
||||||
|
var delta nodeset
|
||||||
|
|
||||||
// Solver main loop.
|
// Solver main loop.
|
||||||
for round := 1; ; round++ {
|
for round := 1; ; round++ {
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
|
|
@ -25,10 +27,11 @@ func (a *analysis) solve() {
|
||||||
// dynamic constraints from reflection thereafter.
|
// dynamic constraints from reflection thereafter.
|
||||||
a.processNewConstraints()
|
a.processNewConstraints()
|
||||||
|
|
||||||
id := a.work.take()
|
var x int
|
||||||
if id == empty {
|
if !a.work.TakeMin(&x) {
|
||||||
break
|
break // empty
|
||||||
}
|
}
|
||||||
|
id := nodeid(x)
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
fmt.Fprintf(a.log, "\tnode n%d\n", id)
|
fmt.Fprintf(a.log, "\tnode n%d\n", id)
|
||||||
}
|
}
|
||||||
|
|
@ -36,22 +39,22 @@ func (a *analysis) solve() {
|
||||||
n := a.nodes[id]
|
n := a.nodes[id]
|
||||||
|
|
||||||
// Difference propagation.
|
// Difference propagation.
|
||||||
delta := n.pts.diff(n.prevPts)
|
delta.Difference(&n.pts.Sparse, &n.prevPts.Sparse)
|
||||||
if delta == nil {
|
if delta.IsEmpty() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n.prevPts = n.pts.clone()
|
n.prevPts.Copy(&n.pts.Sparse)
|
||||||
|
|
||||||
// Apply all resolution rules attached to n.
|
// Apply all resolution rules attached to n.
|
||||||
a.solveConstraints(n, delta)
|
a.solveConstraints(n, &delta)
|
||||||
|
|
||||||
if a.log != nil {
|
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 {
|
if !a.nodes[0].pts.IsEmpty() {
|
||||||
panic(fmt.Sprintf("pts(0) is nonempty: %s", a.nodes[0].pts))
|
panic(fmt.Sprintf("pts(0) is nonempty: %s", &a.nodes[0].pts))
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
|
|
@ -59,8 +62,8 @@ func (a *analysis) solve() {
|
||||||
|
|
||||||
// Dump solution.
|
// Dump solution.
|
||||||
for i, n := range a.nodes {
|
for i, n := range a.nodes {
|
||||||
if n.pts != nil {
|
if !n.pts.IsEmpty() {
|
||||||
fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, n.pts, n.typ)
|
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
|
// something initially (due to addrConstraints) and
|
||||||
// have other constraints attached.
|
// have other constraints attached.
|
||||||
// (A no-op in round 1.)
|
// (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)
|
a.addWork(c.dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,20 +111,22 @@ func (a *analysis) processNewConstraints() {
|
||||||
default:
|
default:
|
||||||
// complex constraint
|
// complex constraint
|
||||||
id = c.ptr()
|
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 n := a.nodes[id]; !n.pts.IsEmpty() {
|
||||||
if len(n.prevPts) > 0 {
|
if !n.prevPts.IsEmpty() {
|
||||||
stale.add(id)
|
stale.add(id)
|
||||||
}
|
}
|
||||||
a.addWork(id)
|
a.addWork(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Apply new constraints to pre-existing PTS labels.
|
// 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]
|
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
|
// the set of labels delta. It may generate new constraints in
|
||||||
// a.constraints.
|
// a.constraints.
|
||||||
//
|
//
|
||||||
func (a *analysis) solveConstraints(n *node, delta nodeset) {
|
func (a *analysis) solveConstraints(n *node, delta *nodeset) {
|
||||||
if delta == nil {
|
if delta.IsEmpty() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process complex constraints dependent on n.
|
// Process complex constraints dependent on n.
|
||||||
for c := range n.complex {
|
for _, c := range n.complex {
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
fmt.Fprintf(a.log, "\t\tconstraint %s\n", c)
|
fmt.Fprintf(a.log, "\t\tconstraint %s\n", c)
|
||||||
}
|
}
|
||||||
// TODO(adonovan): parameter n is never needed, since
|
c.solve(a, delta)
|
||||||
// it's equal to c.ptr(). Remove.
|
|
||||||
c.solve(a, n, delta)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process copy constraints.
|
// Process copy constraints.
|
||||||
var copySeen nodeset
|
var copySeen nodeset
|
||||||
for mid := range n.copyTo {
|
for _, x := range n.copyTo.AppendTo(a.deltaSpace) {
|
||||||
|
mid := nodeid(x)
|
||||||
if copySeen.add(mid) {
|
if copySeen.add(mid) {
|
||||||
if a.nodes[mid].pts.addAll(delta) {
|
if a.nodes[mid].pts.addAll(delta) {
|
||||||
a.addWork(mid)
|
a.addWork(mid)
|
||||||
|
|
@ -161,7 +165,7 @@ func (a *analysis) addLabel(ptr, label nodeid) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *analysis) addWork(id nodeid) {
|
func (a *analysis) addWork(id nodeid) {
|
||||||
a.work.add(id)
|
a.work.Insert(int(id))
|
||||||
if a.log != nil {
|
if a.log != nil {
|
||||||
fmt.Fprintf(a.log, "\t\twork: n%d\n", id)
|
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
|
// are followed by addWork, possibly batched
|
||||||
// via a 'changed' flag; see if there's a
|
// via a 'changed' flag; see if there's a
|
||||||
// noticeable penalty to calling addWork here.
|
// noticeable penalty to calling addWork here.
|
||||||
return a.nodes[dst].pts.addAll(nsrc.pts)
|
return a.nodes[dst].pts.addAll(&nsrc.pts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
@ -207,9 +211,10 @@ func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
|
||||||
return sizeof
|
return sizeof
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *loadConstraint) solve(a *analysis, n *node, delta nodeset) {
|
func (c *loadConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
var changed bool
|
var changed bool
|
||||||
for k := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
k := nodeid(x)
|
||||||
koff := k + nodeid(c.offset)
|
koff := k + nodeid(c.offset)
|
||||||
if a.onlineCopy(c.dst, koff) {
|
if a.onlineCopy(c.dst, koff) {
|
||||||
changed = true
|
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) {
|
func (c *storeConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for k := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
k := nodeid(x)
|
||||||
koff := k + nodeid(c.offset)
|
koff := k + nodeid(c.offset)
|
||||||
if a.onlineCopy(koff, c.src) {
|
if a.onlineCopy(koff, c.src) {
|
||||||
a.addWork(koff)
|
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]
|
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)) {
|
if dst.pts.add(k + nodeid(c.offset)) {
|
||||||
a.addWork(c.dst)
|
a.addWork(c.dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
|
func (c *typeFilterConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for ifaceObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
ifaceObj := nodeid(x)
|
||||||
tDyn, _, indirect := a.taggedValue(ifaceObj)
|
tDyn, _, indirect := a.taggedValue(ifaceObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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
|
predicate := types.AssignableTo
|
||||||
if c.exact {
|
if c.exact {
|
||||||
predicate = types.Identical
|
predicate = types.Identical
|
||||||
}
|
}
|
||||||
for ifaceObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
ifaceObj := nodeid(x)
|
||||||
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we'll need to implement this
|
// 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) {
|
if predicate(tDyn, c.typ) {
|
||||||
// Copy payload sans tag to dst.
|
// Copy payload sans tag to dst.
|
||||||
//
|
//
|
||||||
// TODO(adonovan): opt: if tConc is
|
// TODO(adonovan): opt: if tDyn is
|
||||||
// nonpointerlike we can skip this entire
|
// nonpointerlike we can skip this entire
|
||||||
// constraint, perhaps. We only care about
|
// constraint, perhaps. We only care about
|
||||||
// pointers among the fields.
|
// 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) {
|
func (c *invokeConstraint) solve(a *analysis, delta *nodeset) {
|
||||||
for ifaceObj := range delta {
|
for _, x := range delta.AppendTo(a.deltaSpace) {
|
||||||
|
ifaceObj := nodeid(x)
|
||||||
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
||||||
if indirect {
|
if indirect {
|
||||||
// TODO(adonovan): we may need to implement this if
|
// 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")
|
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")
|
panic("copy is not a complex constraint")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/container/intsets"
|
||||||
"code.google.com/p/go.tools/go/types"
|
"code.google.com/p/go.tools/go/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -154,10 +155,17 @@ func (a *analysis) flatten(t types.Type) []*fieldInfo {
|
||||||
|
|
||||||
case *types.Tuple:
|
case *types.Tuple:
|
||||||
// No identity node: tuples are never address-taken.
|
// No identity node: tuples are never address-taken.
|
||||||
for i, n := 0, t.Len(); i < n; i++ {
|
n := t.Len()
|
||||||
f := t.At(i)
|
if n == 1 {
|
||||||
for _, fi := range a.flatten(f.Type()) {
|
// Don't add a fieldInfo link for singletons,
|
||||||
fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
|
// 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 -------------------------------------------------------------------
|
// Node set -------------------------------------------------------------------
|
||||||
|
|
||||||
// NB, mutator methods are attached to *nodeset.
|
type nodeset struct {
|
||||||
// nodeset may be a reference, but its address matters!
|
intsets.Sparse
|
||||||
type nodeset map[nodeid]struct{}
|
}
|
||||||
|
|
||||||
// ---- Accessors ----
|
func (ns *nodeset) String() string {
|
||||||
|
|
||||||
func (ns nodeset) String() string {
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteRune('{')
|
buf.WriteRune('{')
|
||||||
var sep string
|
var space [50]int
|
||||||
for n := range ns {
|
for i, n := range ns.AppendTo(space[:0]) {
|
||||||
fmt.Fprintf(&buf, "%sn%d", sep, n)
|
if i > 0 {
|
||||||
sep = ", "
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteRune('n')
|
||||||
|
fmt.Fprintf(&buf, "%d", n)
|
||||||
}
|
}
|
||||||
buf.WriteRune('}')
|
buf.WriteRune('}')
|
||||||
return buf.String()
|
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 {
|
func (ns *nodeset) add(n nodeid) bool {
|
||||||
sz := len(*ns)
|
return ns.Sparse.Insert(int(n))
|
||||||
if *ns == nil {
|
|
||||||
*ns = make(nodeset)
|
|
||||||
}
|
|
||||||
(*ns)[n] = struct{}{}
|
|
||||||
return len(*ns) > sz
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *nodeset) addAll(y nodeset) bool {
|
func (x *nodeset) addAll(y *nodeset) bool {
|
||||||
if y == nil {
|
return x.UnionWith(&y.Sparse)
|
||||||
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)}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue