go.tools/go/pointer: node renumbering

  This change renumbers nodes so that addressable ones
  (that may appear in a points-to set) all have lower
  numbers than non-addressable ones----initially at least:
  reflection, SetFinalizer, etc add new nodes during
  solving.

  This improves the efficiency of sparse PTS
  representations (to be added later).  The largest int in
  a PTS is now about 20% of the previous max.

  Overview:
  - move constraint stuff into constraint.go.
  - add two methods to constraint:
    (1) renumber(): renumbers all nodeids.  The
        implementations are very repetitive but simple.  I
        thought hard about other ways (mixins, reflection)
        but decided this one was fine.
	(2) indirect(): report the set of nodeids whose
        points-to relations depend on the solver, not just
        the initial constraint graph.
        (This method is currently unused and is logically
        part of a forthcoming change to implement PE/LE
        presolver optimizations. (Perhaps I should comment
        it out/remove it for now.)
  - split up the population of the intrinsics map by file.
  - delete analysis.probes (unused field)
  - remove state="..." from panic message; unnecessary.

LGTM=crawshaw
R=crawshaw
CC=golang-codereviews
https://golang.org/cl/73320043
diff --git a/go/pointer/analysis.go b/go/pointer/analysis.go
index 1464982..42deb0b 100644
--- a/go/pointer/analysis.go
+++ b/go/pointer/analysis.go
@@ -102,91 +102,6 @@
 	complex constraintset
 }
 
-type constraint interface {
-	String() string
-
-	// For a complex constraint, returns the nodeid of the pointer
-	// to which it is attached.
-	ptr() nodeid
-
-	// 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)
-}
-
-// dst = &src
-// pts(dst) ⊇ {src}
-// A base constraint used to initialize the solver's pt sets
-type addrConstraint struct {
-	dst nodeid // (ptr)
-	src nodeid
-}
-
-// dst = src
-// A simple constraint represented directly as a copyTo graph edge.
-type copyConstraint struct {
-	dst nodeid
-	src nodeid // (ptr)
-}
-
-// dst = src[offset]
-// A complex constraint attached to src (the pointer)
-type loadConstraint struct {
-	offset uint32
-	dst    nodeid
-	src    nodeid // (ptr)
-}
-
-// dst[offset] = src
-// A complex constraint attached to dst (the pointer)
-type storeConstraint struct {
-	offset uint32
-	dst    nodeid // (ptr)
-	src    nodeid
-}
-
-// dst = &src.f  or  dst = &src[0]
-// A complex constraint attached to dst (the pointer)
-type offsetAddrConstraint struct {
-	offset uint32
-	dst    nodeid
-	src    nodeid // (ptr)
-}
-
-// dst = src.(typ)  where typ is an interface
-// A complex constraint attached to src (the interface).
-// No representation change: pts(dst) and pts(src) contains tagged objects.
-type typeFilterConstraint struct {
-	typ types.Type // an interface type
-	dst nodeid
-	src nodeid // (ptr)
-}
-
-// dst = src.(typ)  where typ is a concrete type
-// A complex constraint attached to src (the interface).
-//
-// If exact, only tagged objects identical to typ are untagged.
-// If !exact, tagged objects assignable to typ are untagged too.
-// The latter is needed for various reflect operators, e.g. Send.
-//
-// This entails a representation change:
-// pts(src) contains tagged objects,
-// pts(dst) contains their payloads.
-type untagConstraint struct {
-	typ   types.Type // a concrete type
-	dst   nodeid
-	src   nodeid // (ptr)
-	exact bool
-}
-
-// src.method(params...)
-// A complex constraint attached to iface.
-type invokeConstraint struct {
-	method *types.Func // the abstract method
-	iface  nodeid      // (ptr) the interface
-	params nodeid      // the first parameter in the params/results block
-}
-
 // An analysis instance holds the state of a single pointer analysis problem.
 type analysis struct {
 	config      *Config                     // the client's control/observer interface
@@ -200,7 +115,6 @@
 	cgnodes     []*cgnode                   // all cgnodes
 	genq        []*cgnode                   // queue of functions to generate constraints for
 	intrinsics  map[*ssa.Function]intrinsic // non-nil values are summaries for intrinsic fns
-	probes      map[*ssa.CallCommon]nodeid  // maps call to print() to argument variable
 	globalval   map[ssa.Value]nodeid        // node for each global ssa.Value
 	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
@@ -291,10 +205,9 @@
 // always succeed.  An error can occur only due to an internal bug.
 //
 func Analyze(config *Config) (result *Result, err error) {
-	stage := "setup"
 	defer func() {
 		if p := recover(); p != nil {
-			err = fmt.Errorf("internal error in pointer analysis %s: %v (please report this bug)", stage, p)
+			err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p)
 			fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:")
 			debug.PrintStack()
 		}
@@ -360,7 +273,6 @@
 	}
 	a.computeTrackBits()
 
-	stage = "constraint generation"
 	a.generate()
 
 	if a.log != nil {
@@ -376,23 +288,11 @@
 		fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
 	}
 
-	// stage = "constraint optimization"
-	// a.optimize()
+	a.optimize()
 
-	stage = "solver"
 	a.solve()
 
-	if a.log != nil {
-		// 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)
-			}
-		}
-	}
-
 	// Create callgraph.Nodes in deterministic order.
-	stage = "callgraph construction"
 	if cg := a.result.CallGraph; cg != nil {
 		for _, caller := range a.cgnodes {
 			cg.CreateNode(caller.fn)
diff --git a/go/pointer/constraint.go b/go/pointer/constraint.go
new file mode 100644
index 0000000..f7603cb
--- /dev/null
+++ b/go/pointer/constraint.go
@@ -0,0 +1,167 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pointer
+
+import (
+	"code.google.com/p/go.tools/go/types"
+)
+
+type constraint interface {
+	// For a complex constraint, returns the nodeid of the pointer
+	// to which it is attached.
+	ptr() nodeid
+
+	// indirect returns (by appending to the argument) the constraint's
+	// "indirect" nodes as defined in (Hardekopf 2007b):
+	// nodes whose points-to relations are not completely
+	// represented in the initial constraint graph.
+	//
+	// TODO(adonovan): I think we need >1 results in some obscure
+	// cases.  If not, just return a nodeid, like ptr().
+	//
+	indirect(nodes []nodeid) []nodeid
+
+	// renumber replaces each nodeid n in the constraint by mapping[n].
+	renumber(mapping []nodeid)
+
+	// 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)
+
+	String() string
+}
+
+// dst = &src
+// pts(dst) ⊇ {src}
+// A base constraint used to initialize the solver's pt sets
+type addrConstraint struct {
+	dst nodeid // (ptr)
+	src nodeid
+}
+
+func (c *addrConstraint) ptr() nodeid { panic("addrConstraint: not a complex constraint") }
+func (c *addrConstraint) indirect(nodes []nodeid) []nodeid {
+	panic("addrConstraint: not a complex constraint")
+}
+func (c *addrConstraint) renumber(mapping []nodeid) {
+	c.dst = mapping[c.dst]
+	c.src = mapping[c.src]
+}
+
+// dst = src
+// A simple constraint represented directly as a copyTo graph edge.
+type copyConstraint struct {
+	dst nodeid
+	src nodeid // (ptr)
+}
+
+func (c *copyConstraint) ptr() nodeid { panic("copyConstraint: not a complex constraint") }
+func (c *copyConstraint) indirect(nodes []nodeid) []nodeid {
+	panic("copyConstraint: not a complex constraint")
+}
+func (c *copyConstraint) renumber(mapping []nodeid) {
+	c.dst = mapping[c.dst]
+	c.src = mapping[c.src]
+}
+
+// dst = src[offset]
+// A complex constraint attached to src (the pointer)
+type loadConstraint struct {
+	offset uint32
+	dst    nodeid // (indirect)
+	src    nodeid // (ptr)
+}
+
+func (c *loadConstraint) ptr() nodeid                      { return c.src }
+func (c *loadConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.dst) }
+func (c *loadConstraint) renumber(mapping []nodeid) {
+	c.dst = mapping[c.dst]
+	c.src = mapping[c.src]
+}
+
+// dst[offset] = src
+// A complex constraint attached to dst (the pointer)
+type storeConstraint struct {
+	offset uint32
+	dst    nodeid // (ptr)
+	src    nodeid
+}
+
+func (c *storeConstraint) ptr() nodeid                      { return c.dst }
+func (c *storeConstraint) indirect(nodes []nodeid) []nodeid { return nodes }
+func (c *storeConstraint) renumber(mapping []nodeid) {
+	c.dst = mapping[c.dst]
+	c.src = mapping[c.src]
+}
+
+// dst = &src.f  or  dst = &src[0]
+// A complex constraint attached to dst (the pointer)
+type offsetAddrConstraint struct {
+	offset uint32
+	dst    nodeid // (indirect)
+	src    nodeid // (ptr)
+}
+
+func (c *offsetAddrConstraint) ptr() nodeid                      { return c.src }
+func (c *offsetAddrConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.dst) }
+func (c *offsetAddrConstraint) renumber(mapping []nodeid) {
+	c.dst = mapping[c.dst]
+	c.src = mapping[c.src]
+}
+
+// dst = src.(typ)  where typ is an interface
+// A complex constraint attached to src (the interface).
+// No representation change: pts(dst) and pts(src) contains tagged objects.
+type typeFilterConstraint struct {
+	typ types.Type // an interface type
+	dst nodeid     // (indirect)
+	src nodeid     // (ptr)
+}
+
+func (c *typeFilterConstraint) ptr() nodeid                      { return c.src }
+func (c *typeFilterConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.dst) }
+func (c *typeFilterConstraint) renumber(mapping []nodeid) {
+	c.dst = mapping[c.dst]
+	c.src = mapping[c.src]
+}
+
+// dst = src.(typ)  where typ is a concrete type
+// A complex constraint attached to src (the interface).
+//
+// If exact, only tagged objects identical to typ are untagged.
+// If !exact, tagged objects assignable to typ are untagged too.
+// The latter is needed for various reflect operators, e.g. Send.
+//
+// This entails a representation change:
+// pts(src) contains tagged objects,
+// pts(dst) contains their payloads.
+type untagConstraint struct {
+	typ   types.Type // a concrete type
+	dst   nodeid     // (indirect)
+	src   nodeid     // (ptr)
+	exact bool
+}
+
+func (c *untagConstraint) ptr() nodeid                      { return c.src }
+func (c *untagConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.dst) }
+func (c *untagConstraint) renumber(mapping []nodeid) {
+	c.dst = mapping[c.dst]
+	c.src = mapping[c.src]
+}
+
+// src.method(params...)
+// A complex constraint attached to iface.
+type invokeConstraint struct {
+	method *types.Func // the abstract method
+	iface  nodeid      // (ptr) the interface
+	params nodeid      // (indirect) the first param in the params/results block
+}
+
+func (c *invokeConstraint) ptr() nodeid                      { return c.iface }
+func (c *invokeConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.params) }
+func (c *invokeConstraint) renumber(mapping []nodeid) {
+	c.iface = mapping[c.iface]
+	c.params = mapping[c.params]
+}
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index fdd2370..c451c8d 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -1260,4 +1260,10 @@
 		a.endObject(obj, nil, "<command-line args>")
 		a.addressOf(T, a.objectNode(nil, os.Var("Args")), obj)
 	}
+
+	// Discard generation state, to avoid confusion after node renumbering.
+	a.panicNode = 0
+	a.globalval = nil
+	a.localval = nil
+	a.localobj = nil
 }
diff --git a/go/pointer/intrinsics.go b/go/pointer/intrinsics.go
index 69ce878..a2257fe 100644
--- a/go/pointer/intrinsics.go
+++ b/go/pointer/intrinsics.go
@@ -30,124 +30,13 @@
 
 // Initialized in explicit init() to defeat (spurious) initialization
 // cycle error.
-var intrinsicsByName map[string]intrinsic
+var intrinsicsByName = make(map[string]intrinsic)
 
 func init() {
 	// Key strings are from Function.String().
 	// That little dot ۰ is an Arabic zero numeral (U+06F0),
 	// categories [Nd].
-	intrinsicsByName = map[string]intrinsic{
-		// reflect.Value methods.
-		"(reflect.Value).Addr":            ext۰reflect۰Value۰Addr,
-		"(reflect.Value).Bool":            ext۰NoEffect,
-		"(reflect.Value).Bytes":           ext۰reflect۰Value۰Bytes,
-		"(reflect.Value).Call":            ext۰reflect۰Value۰Call,
-		"(reflect.Value).CallSlice":       ext۰reflect۰Value۰CallSlice,
-		"(reflect.Value).CanAddr":         ext۰NoEffect,
-		"(reflect.Value).CanInterface":    ext۰NoEffect,
-		"(reflect.Value).CanSet":          ext۰NoEffect,
-		"(reflect.Value).Cap":             ext۰NoEffect,
-		"(reflect.Value).Close":           ext۰NoEffect,
-		"(reflect.Value).Complex":         ext۰NoEffect,
-		"(reflect.Value).Convert":         ext۰reflect۰Value۰Convert,
-		"(reflect.Value).Elem":            ext۰reflect۰Value۰Elem,
-		"(reflect.Value).Field":           ext۰reflect۰Value۰Field,
-		"(reflect.Value).FieldByIndex":    ext۰reflect۰Value۰FieldByIndex,
-		"(reflect.Value).FieldByName":     ext۰reflect۰Value۰FieldByName,
-		"(reflect.Value).FieldByNameFunc": ext۰reflect۰Value۰FieldByNameFunc,
-		"(reflect.Value).Float":           ext۰NoEffect,
-		"(reflect.Value).Index":           ext۰reflect۰Value۰Index,
-		"(reflect.Value).Int":             ext۰NoEffect,
-		"(reflect.Value).Interface":       ext۰reflect۰Value۰Interface,
-		"(reflect.Value).InterfaceData":   ext۰NoEffect,
-		"(reflect.Value).IsNil":           ext۰NoEffect,
-		"(reflect.Value).IsValid":         ext۰NoEffect,
-		"(reflect.Value).Kind":            ext۰NoEffect,
-		"(reflect.Value).Len":             ext۰NoEffect,
-		"(reflect.Value).MapIndex":        ext۰reflect۰Value۰MapIndex,
-		"(reflect.Value).MapKeys":         ext۰reflect۰Value۰MapKeys,
-		"(reflect.Value).Method":          ext۰reflect۰Value۰Method,
-		"(reflect.Value).MethodByName":    ext۰reflect۰Value۰MethodByName,
-		"(reflect.Value).NumField":        ext۰NoEffect,
-		"(reflect.Value).NumMethod":       ext۰NoEffect,
-		"(reflect.Value).OverflowComplex": ext۰NoEffect,
-		"(reflect.Value).OverflowFloat":   ext۰NoEffect,
-		"(reflect.Value).OverflowInt":     ext۰NoEffect,
-		"(reflect.Value).OverflowUint":    ext۰NoEffect,
-		"(reflect.Value).Pointer":         ext۰NoEffect,
-		"(reflect.Value).Recv":            ext۰reflect۰Value۰Recv,
-		"(reflect.Value).Send":            ext۰reflect۰Value۰Send,
-		"(reflect.Value).Set":             ext۰reflect۰Value۰Set,
-		"(reflect.Value).SetBool":         ext۰NoEffect,
-		"(reflect.Value).SetBytes":        ext۰reflect۰Value۰SetBytes,
-		"(reflect.Value).SetComplex":      ext۰NoEffect,
-		"(reflect.Value).SetFloat":        ext۰NoEffect,
-		"(reflect.Value).SetInt":          ext۰NoEffect,
-		"(reflect.Value).SetLen":          ext۰NoEffect,
-		"(reflect.Value).SetMapIndex":     ext۰reflect۰Value۰SetMapIndex,
-		"(reflect.Value).SetPointer":      ext۰reflect۰Value۰SetPointer,
-		"(reflect.Value).SetString":       ext۰NoEffect,
-		"(reflect.Value).SetUint":         ext۰NoEffect,
-		"(reflect.Value).Slice":           ext۰reflect۰Value۰Slice,
-		"(reflect.Value).String":          ext۰NoEffect,
-		"(reflect.Value).TryRecv":         ext۰reflect۰Value۰Recv,
-		"(reflect.Value).TrySend":         ext۰reflect۰Value۰Send,
-		"(reflect.Value).Type":            ext۰NoEffect,
-		"(reflect.Value).Uint":            ext۰NoEffect,
-		"(reflect.Value).UnsafeAddr":      ext۰NoEffect,
-
-		// Standalone reflect.* functions.
-		"reflect.Append":      ext۰reflect۰Append,
-		"reflect.AppendSlice": ext۰reflect۰AppendSlice,
-		"reflect.Copy":        ext۰reflect۰Copy,
-		"reflect.ChanOf":      ext۰reflect۰ChanOf,
-		"reflect.DeepEqual":   ext۰NoEffect,
-		"reflect.Indirect":    ext۰reflect۰Indirect,
-		"reflect.MakeChan":    ext۰reflect۰MakeChan,
-		"reflect.MakeFunc":    ext۰reflect۰MakeFunc,
-		"reflect.MakeMap":     ext۰reflect۰MakeMap,
-		"reflect.MakeSlice":   ext۰reflect۰MakeSlice,
-		"reflect.MapOf":       ext۰reflect۰MapOf,
-		"reflect.New":         ext۰reflect۰New,
-		"reflect.NewAt":       ext۰reflect۰NewAt,
-		"reflect.PtrTo":       ext۰reflect۰PtrTo,
-		"reflect.Select":      ext۰reflect۰Select,
-		"reflect.SliceOf":     ext۰reflect۰SliceOf,
-		"reflect.TypeOf":      ext۰reflect۰TypeOf,
-		"reflect.ValueOf":     ext۰reflect۰ValueOf,
-		"reflect.Zero":        ext۰reflect۰Zero,
-		"reflect.init":        ext۰NoEffect,
-
-		// *reflect.rtype methods
-		"(*reflect.rtype).Align":           ext۰NoEffect,
-		"(*reflect.rtype).AssignableTo":    ext۰NoEffect,
-		"(*reflect.rtype).Bits":            ext۰NoEffect,
-		"(*reflect.rtype).ChanDir":         ext۰NoEffect,
-		"(*reflect.rtype).ConvertibleTo":   ext۰NoEffect,
-		"(*reflect.rtype).Elem":            ext۰reflect۰rtype۰Elem,
-		"(*reflect.rtype).Field":           ext۰reflect۰rtype۰Field,
-		"(*reflect.rtype).FieldAlign":      ext۰NoEffect,
-		"(*reflect.rtype).FieldByIndex":    ext۰reflect۰rtype۰FieldByIndex,
-		"(*reflect.rtype).FieldByName":     ext۰reflect۰rtype۰FieldByName,
-		"(*reflect.rtype).FieldByNameFunc": ext۰reflect۰rtype۰FieldByNameFunc,
-		"(*reflect.rtype).Implements":      ext۰NoEffect,
-		"(*reflect.rtype).In":              ext۰reflect۰rtype۰In,
-		"(*reflect.rtype).IsVariadic":      ext۰NoEffect,
-		"(*reflect.rtype).Key":             ext۰reflect۰rtype۰Key,
-		"(*reflect.rtype).Kind":            ext۰NoEffect,
-		"(*reflect.rtype).Len":             ext۰NoEffect,
-		"(*reflect.rtype).Method":          ext۰reflect۰rtype۰Method,
-		"(*reflect.rtype).MethodByName":    ext۰reflect۰rtype۰MethodByName,
-		"(*reflect.rtype).Name":            ext۰NoEffect,
-		"(*reflect.rtype).NumField":        ext۰NoEffect,
-		"(*reflect.rtype).NumIn":           ext۰NoEffect,
-		"(*reflect.rtype).NumMethod":       ext۰NoEffect,
-		"(*reflect.rtype).NumOut":          ext۰NoEffect,
-		"(*reflect.rtype).Out":             ext۰reflect۰rtype۰Out,
-		"(*reflect.rtype).PkgPath":         ext۰NoEffect,
-		"(*reflect.rtype).Size":            ext۰NoEffect,
-		"(*reflect.rtype).String":          ext۰NoEffect,
-
+	for name, fn := range map[string]intrinsic{
 		// Other packages.
 		"bytes.Equal":                           ext۰NoEffect,
 		"bytes.IndexByte":                       ext۰NoEffect,
@@ -287,6 +176,8 @@
 		"time.now":                              ext۰NoEffect,
 		"time.startTimer":                       ext۰NoEffect,
 		"time.stopTimer":                        ext۰NoEffect,
+	} {
+		intrinsicsByName[name] = fn
 	}
 }
 
@@ -369,12 +260,16 @@
 	x       nodeid
 }
 
-func (c *runtimeSetFinalizerConstraint) String() string {
-	return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f)
+func (c *runtimeSetFinalizerConstraint) ptr() nodeid                      { return c.f }
+func (c *runtimeSetFinalizerConstraint) indirect(nodes []nodeid) []nodeid { return nodes }
+func (c *runtimeSetFinalizerConstraint) renumber(mapping []nodeid) {
+	c.targets = mapping[c.targets]
+	c.f = mapping[c.f]
+	c.x = mapping[c.x]
 }
 
-func (c *runtimeSetFinalizerConstraint) ptr() nodeid {
-	return c.f
+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) {
diff --git a/go/pointer/opt.go b/go/pointer/opt.go
new file mode 100644
index 0000000..e27508a
--- /dev/null
+++ b/go/pointer/opt.go
@@ -0,0 +1,124 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pointer
+
+// This file defines the constraint optimiser ("pre-solver").
+
+import (
+	"fmt"
+)
+
+func (a *analysis) optimize() {
+	a.renumber()
+
+	// TODO(adonovan): opt:
+	// PE, LE, HVN, HRU, sparse bitsets, etc.
+}
+
+// renumber permutes a.nodes so that all nodes within an addressable
+// object appear before all non-addressable nodes, maintaining the
+// order of nodes within the same object (as required by offsetAddr).
+//
+// renumber must update every nodeid in the analysis (constraints,
+// Pointers, callgraph, etc) to reflect the new ordering.
+//
+// This is an optimisation to increase the locality and efficiency of
+// sparse representations of points-to sets.  (Typically only about
+// 20% of nodes are within an object.)
+//
+// NB: nodes added during solving (e.g. for reflection, SetFinalizer)
+// will be appended to the end.
+//
+func (a *analysis) renumber() {
+	N := nodeid(len(a.nodes))
+	newNodes := make([]*node, N, N)
+	renumbering := make([]nodeid, N, N) // maps old to new
+
+	var i, j nodeid
+
+	// The zero node is special.
+	newNodes[j] = a.nodes[i]
+	renumbering[i] = j
+	i++
+	j++
+
+	// Pass 1: object nodes.
+	for i < N {
+		obj := a.nodes[i].obj
+		if obj == nil {
+			i++
+			continue
+		}
+
+		end := i + nodeid(obj.size)
+		for i < end {
+			newNodes[j] = a.nodes[i]
+			renumbering[i] = j
+			i++
+			j++
+		}
+	}
+	nobj := j
+
+	// Pass 2: non-object nodes.
+	for i = 1; i < N; {
+		obj := a.nodes[i].obj
+		if obj != nil {
+			i += nodeid(obj.size)
+			continue
+		}
+
+		newNodes[j] = a.nodes[i]
+		renumbering[i] = j
+		i++
+		j++
+	}
+
+	if j != N {
+		panic(fmt.Sprintf("internal error: j=%d, N=%d", j, N))
+	}
+
+	// Log the remapping table.
+	if a.log != nil {
+		fmt.Fprintf(a.log, "Renumbering nodes to improve density:\n")
+		fmt.Fprintf(a.log, "(%d object nodes of %d total)\n", nobj, N)
+		for old, new := range renumbering {
+			fmt.Fprintf(a.log, "\tn%d -> n%d\n", old, new)
+		}
+	}
+
+	// Now renumber all existing nodeids to use the new node permutation.
+	// It is critical that all reachable nodeids are accounted for!
+
+	// Renumber nodeids in queried Pointers.
+	for v, ptr := range a.result.Queries {
+		ptr.n = renumbering[ptr.n]
+		a.result.Queries[v] = ptr
+	}
+	for v, ptr := range a.result.IndirectQueries {
+		ptr.n = renumbering[ptr.n]
+		a.result.IndirectQueries[v] = ptr
+	}
+
+	// Renumber nodeids in global objects.
+	for v, id := range a.globalobj {
+		a.globalobj[v] = renumbering[id]
+	}
+
+	// Renumber nodeids in constraints.
+	for _, c := range a.constraints {
+		c.renumber(renumbering)
+	}
+
+	// Renumber nodeids in the call graph.
+	for _, cgn := range a.cgnodes {
+		cgn.obj = renumbering[cgn.obj]
+		for _, site := range cgn.sites {
+			site.targets = renumbering[site.targets]
+		}
+	}
+
+	a.nodes = newNodes
+}
diff --git a/go/pointer/reflect.go b/go/pointer/reflect.go
index b0dd9d0..ed45177 100644
--- a/go/pointer/reflect.go
+++ b/go/pointer/reflect.go
@@ -32,6 +32,123 @@
 	"code.google.com/p/go.tools/go/types"
 )
 
+func init() {
+	for name, fn := range map[string]intrinsic{
+		// reflect.Value methods.
+		"(reflect.Value).Addr":            ext۰reflect۰Value۰Addr,
+		"(reflect.Value).Bool":            ext۰NoEffect,
+		"(reflect.Value).Bytes":           ext۰reflect۰Value۰Bytes,
+		"(reflect.Value).Call":            ext۰reflect۰Value۰Call,
+		"(reflect.Value).CallSlice":       ext۰reflect۰Value۰CallSlice,
+		"(reflect.Value).CanAddr":         ext۰NoEffect,
+		"(reflect.Value).CanInterface":    ext۰NoEffect,
+		"(reflect.Value).CanSet":          ext۰NoEffect,
+		"(reflect.Value).Cap":             ext۰NoEffect,
+		"(reflect.Value).Close":           ext۰NoEffect,
+		"(reflect.Value).Complex":         ext۰NoEffect,
+		"(reflect.Value).Convert":         ext۰reflect۰Value۰Convert,
+		"(reflect.Value).Elem":            ext۰reflect۰Value۰Elem,
+		"(reflect.Value).Field":           ext۰reflect۰Value۰Field,
+		"(reflect.Value).FieldByIndex":    ext۰reflect۰Value۰FieldByIndex,
+		"(reflect.Value).FieldByName":     ext۰reflect۰Value۰FieldByName,
+		"(reflect.Value).FieldByNameFunc": ext۰reflect۰Value۰FieldByNameFunc,
+		"(reflect.Value).Float":           ext۰NoEffect,
+		"(reflect.Value).Index":           ext۰reflect۰Value۰Index,
+		"(reflect.Value).Int":             ext۰NoEffect,
+		"(reflect.Value).Interface":       ext۰reflect۰Value۰Interface,
+		"(reflect.Value).InterfaceData":   ext۰NoEffect,
+		"(reflect.Value).IsNil":           ext۰NoEffect,
+		"(reflect.Value).IsValid":         ext۰NoEffect,
+		"(reflect.Value).Kind":            ext۰NoEffect,
+		"(reflect.Value).Len":             ext۰NoEffect,
+		"(reflect.Value).MapIndex":        ext۰reflect۰Value۰MapIndex,
+		"(reflect.Value).MapKeys":         ext۰reflect۰Value۰MapKeys,
+		"(reflect.Value).Method":          ext۰reflect۰Value۰Method,
+		"(reflect.Value).MethodByName":    ext۰reflect۰Value۰MethodByName,
+		"(reflect.Value).NumField":        ext۰NoEffect,
+		"(reflect.Value).NumMethod":       ext۰NoEffect,
+		"(reflect.Value).OverflowComplex": ext۰NoEffect,
+		"(reflect.Value).OverflowFloat":   ext۰NoEffect,
+		"(reflect.Value).OverflowInt":     ext۰NoEffect,
+		"(reflect.Value).OverflowUint":    ext۰NoEffect,
+		"(reflect.Value).Pointer":         ext۰NoEffect,
+		"(reflect.Value).Recv":            ext۰reflect۰Value۰Recv,
+		"(reflect.Value).Send":            ext۰reflect۰Value۰Send,
+		"(reflect.Value).Set":             ext۰reflect۰Value۰Set,
+		"(reflect.Value).SetBool":         ext۰NoEffect,
+		"(reflect.Value).SetBytes":        ext۰reflect۰Value۰SetBytes,
+		"(reflect.Value).SetComplex":      ext۰NoEffect,
+		"(reflect.Value).SetFloat":        ext۰NoEffect,
+		"(reflect.Value).SetInt":          ext۰NoEffect,
+		"(reflect.Value).SetLen":          ext۰NoEffect,
+		"(reflect.Value).SetMapIndex":     ext۰reflect۰Value۰SetMapIndex,
+		"(reflect.Value).SetPointer":      ext۰reflect۰Value۰SetPointer,
+		"(reflect.Value).SetString":       ext۰NoEffect,
+		"(reflect.Value).SetUint":         ext۰NoEffect,
+		"(reflect.Value).Slice":           ext۰reflect۰Value۰Slice,
+		"(reflect.Value).String":          ext۰NoEffect,
+		"(reflect.Value).TryRecv":         ext۰reflect۰Value۰Recv,
+		"(reflect.Value).TrySend":         ext۰reflect۰Value۰Send,
+		"(reflect.Value).Type":            ext۰NoEffect,
+		"(reflect.Value).Uint":            ext۰NoEffect,
+		"(reflect.Value).UnsafeAddr":      ext۰NoEffect,
+
+		// Standalone reflect.* functions.
+		"reflect.Append":      ext۰reflect۰Append,
+		"reflect.AppendSlice": ext۰reflect۰AppendSlice,
+		"reflect.Copy":        ext۰reflect۰Copy,
+		"reflect.ChanOf":      ext۰reflect۰ChanOf,
+		"reflect.DeepEqual":   ext۰NoEffect,
+		"reflect.Indirect":    ext۰reflect۰Indirect,
+		"reflect.MakeChan":    ext۰reflect۰MakeChan,
+		"reflect.MakeFunc":    ext۰reflect۰MakeFunc,
+		"reflect.MakeMap":     ext۰reflect۰MakeMap,
+		"reflect.MakeSlice":   ext۰reflect۰MakeSlice,
+		"reflect.MapOf":       ext۰reflect۰MapOf,
+		"reflect.New":         ext۰reflect۰New,
+		"reflect.NewAt":       ext۰reflect۰NewAt,
+		"reflect.PtrTo":       ext۰reflect۰PtrTo,
+		"reflect.Select":      ext۰reflect۰Select,
+		"reflect.SliceOf":     ext۰reflect۰SliceOf,
+		"reflect.TypeOf":      ext۰reflect۰TypeOf,
+		"reflect.ValueOf":     ext۰reflect۰ValueOf,
+		"reflect.Zero":        ext۰reflect۰Zero,
+		"reflect.init":        ext۰NoEffect,
+
+		// *reflect.rtype methods
+		"(*reflect.rtype).Align":           ext۰NoEffect,
+		"(*reflect.rtype).AssignableTo":    ext۰NoEffect,
+		"(*reflect.rtype).Bits":            ext۰NoEffect,
+		"(*reflect.rtype).ChanDir":         ext۰NoEffect,
+		"(*reflect.rtype).ConvertibleTo":   ext۰NoEffect,
+		"(*reflect.rtype).Elem":            ext۰reflect۰rtype۰Elem,
+		"(*reflect.rtype).Field":           ext۰reflect۰rtype۰Field,
+		"(*reflect.rtype).FieldAlign":      ext۰NoEffect,
+		"(*reflect.rtype).FieldByIndex":    ext۰reflect۰rtype۰FieldByIndex,
+		"(*reflect.rtype).FieldByName":     ext۰reflect۰rtype۰FieldByName,
+		"(*reflect.rtype).FieldByNameFunc": ext۰reflect۰rtype۰FieldByNameFunc,
+		"(*reflect.rtype).Implements":      ext۰NoEffect,
+		"(*reflect.rtype).In":              ext۰reflect۰rtype۰In,
+		"(*reflect.rtype).IsVariadic":      ext۰NoEffect,
+		"(*reflect.rtype).Key":             ext۰reflect۰rtype۰Key,
+		"(*reflect.rtype).Kind":            ext۰NoEffect,
+		"(*reflect.rtype).Len":             ext۰NoEffect,
+		"(*reflect.rtype).Method":          ext۰reflect۰rtype۰Method,
+		"(*reflect.rtype).MethodByName":    ext۰reflect۰rtype۰MethodByName,
+		"(*reflect.rtype).Name":            ext۰NoEffect,
+		"(*reflect.rtype).NumField":        ext۰NoEffect,
+		"(*reflect.rtype).NumIn":           ext۰NoEffect,
+		"(*reflect.rtype).NumMethod":       ext۰NoEffect,
+		"(*reflect.rtype).NumOut":          ext۰NoEffect,
+		"(*reflect.rtype).Out":             ext۰reflect۰rtype۰Out,
+		"(*reflect.rtype).PkgPath":         ext۰NoEffect,
+		"(*reflect.rtype).Size":            ext۰NoEffect,
+		"(*reflect.rtype).String":          ext۰NoEffect,
+	} {
+		intrinsicsByName[name] = fn
+	}
+}
+
 // -------------------- (reflect.Value) --------------------
 
 func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {}
@@ -41,17 +158,20 @@
 // result = v.Bytes()
 type rVBytesConstraint struct {
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVBytesConstraint) ptr() nodeid                      { return c.v }
+func (c *rVBytesConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVBytesConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVBytesConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v)
 }
 
-func (c *rVBytesConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVBytesConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -89,18 +209,30 @@
 	targets   nodeid
 	v         nodeid // (ptr)
 	arg       nodeid // = in[*]
-	result    nodeid
-	dotdotdot bool // interpret last arg as a "..." slice
+	result    nodeid // (indirect)
+	dotdotdot bool   // interpret last arg as a "..." slice
+}
+
+func (c *rVCallConstraint) ptr() nodeid { return c.v }
+func (c *rVCallConstraint) indirect(nodes []nodeid) []nodeid {
+	nodes = append(nodes, c.result)
+	// TODO(adonovan): we may be able to handle 'targets' out-of-band
+	// so that all implementations indirect() return a single value.
+	// We can then dispense with the slice.
+	nodes = append(nodes, c.targets)
+	return nodes
+}
+func (c *rVCallConstraint) renumber(mapping []nodeid) {
+	c.targets = mapping[c.targets]
+	c.v = mapping[c.v]
+	c.arg = mapping[c.arg]
+	c.result = mapping[c.result]
 }
 
 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) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVCallConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	if c.targets == 0 {
 		panic("no targets")
@@ -228,17 +360,20 @@
 type rVElemConstraint struct {
 	cgn    *cgnode
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVElemConstraint) ptr() nodeid                      { return c.v }
+func (c *rVElemConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVElemConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVElemConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v)
 }
 
-func (c *rVElemConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVElemConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -287,17 +422,20 @@
 type rVIndexConstraint struct {
 	cgn    *cgnode
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVIndexConstraint) ptr() nodeid                      { return c.v }
+func (c *rVIndexConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVIndexConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVIndexConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v)
 }
 
-func (c *rVIndexConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -345,19 +483,21 @@
 // result = v.Interface()
 type rVInterfaceConstraint struct {
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVInterfaceConstraint) ptr() nodeid                      { return c.v }
+func (c *rVInterfaceConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVInterfaceConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVInterfaceConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v)
 }
 
-func (c *rVInterfaceConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
-	resultPts := &a.nodes[c.result].pts
 	changed := false
 	for vObj := range delta {
 		tDyn, payload, indirect := a.taggedValue(vObj)
@@ -372,7 +512,7 @@
 				a.addWork(c.result)
 			}
 		} else {
-			if resultPts.add(vObj) {
+			if a.addLabel(c.result, vObj) {
 				changed = true
 			}
 		}
@@ -395,17 +535,20 @@
 type rVMapIndexConstraint struct {
 	cgn    *cgnode
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVMapIndexConstraint) ptr() nodeid                      { return c.v }
+func (c *rVMapIndexConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVMapIndexConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVMapIndexConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v)
 }
 
-func (c *rVMapIndexConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -445,17 +588,20 @@
 type rVMapKeysConstraint struct {
 	cgn    *cgnode
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVMapKeysConstraint) ptr() nodeid                      { return c.v }
+func (c *rVMapKeysConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVMapKeysConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVMapKeysConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v)
 }
 
-func (c *rVMapKeysConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -505,17 +651,20 @@
 type rVRecvConstraint struct {
 	cgn    *cgnode
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVRecvConstraint) ptr() nodeid                      { return c.v }
+func (c *rVRecvConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVRecvConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVRecvConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v)
 }
 
-func (c *rVRecvConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVRecvConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -559,12 +708,15 @@
 	x   nodeid
 }
 
-func (c *rVSendConstraint) String() string {
-	return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
+func (c *rVSendConstraint) ptr() nodeid                      { return c.v }
+func (c *rVSendConstraint) indirect(nodes []nodeid) []nodeid { return nodes }
+func (c *rVSendConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.x = mapping[c.x]
 }
 
-func (c *rVSendConstraint) ptr() nodeid {
-	return c.v
+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) {
@@ -608,12 +760,15 @@
 	x   nodeid
 }
 
-func (c *rVSetBytesConstraint) String() string {
-	return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x)
+func (c *rVSetBytesConstraint) ptr() nodeid                      { return c.v }
+func (c *rVSetBytesConstraint) indirect(nodes []nodeid) []nodeid { return nodes }
+func (c *rVSetBytesConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.x = mapping[c.x]
 }
 
-func (c *rVSetBytesConstraint) ptr() nodeid {
-	return c.v
+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) {
@@ -653,12 +808,16 @@
 	val nodeid
 }
 
-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) ptr() nodeid                      { return c.v }
+func (c *rVSetMapIndexConstraint) indirect(nodes []nodeid) []nodeid { return nodes }
+func (c *rVSetMapIndexConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.key = mapping[c.key]
+	c.val = mapping[c.val]
 }
 
-func (c *rVSetMapIndexConstraint) ptr() nodeid {
-	return c.v
+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) {
@@ -706,17 +865,20 @@
 type rVSliceConstraint struct {
 	cgn    *cgnode
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rVSliceConstraint) ptr() nodeid                      { return c.v }
+func (c *rVSliceConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rVSliceConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *rVSliceConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v)
 }
 
-func (c *rVSliceConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *rVSliceConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -780,18 +942,21 @@
 type reflectChanOfConstraint struct {
 	cgn    *cgnode
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
 	dirs   []types.ChanDir
 }
 
+func (c *reflectChanOfConstraint) ptr() nodeid                      { return c.t }
+func (c *reflectChanOfConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectChanOfConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
+}
+
 func (c *reflectChanOfConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t)
 }
 
-func (c *reflectChanOfConstraint) ptr() nodeid {
-	return c.t
-}
-
 func (c *reflectChanOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for tObj := range delta {
@@ -845,17 +1010,20 @@
 type reflectIndirectConstraint struct {
 	cgn    *cgnode
 	v      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectIndirectConstraint) ptr() nodeid                      { return c.v }
+func (c *reflectIndirectConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectIndirectConstraint) renumber(mapping []nodeid) {
+	c.v = mapping[c.v]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectIndirectConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v)
 }
 
-func (c *reflectIndirectConstraint) ptr() nodeid {
-	return c.v
-}
-
 func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for vObj := range delta {
@@ -893,17 +1061,20 @@
 type reflectMakeChanConstraint struct {
 	cgn    *cgnode
 	typ    nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectMakeChanConstraint) ptr() nodeid                      { return c.typ }
+func (c *reflectMakeChanConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectMakeChanConstraint) renumber(mapping []nodeid) {
+	c.typ = mapping[c.typ]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectMakeChanConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ)
 }
 
-func (c *reflectMakeChanConstraint) ptr() nodeid {
-	return c.typ
-}
-
 func (c *reflectMakeChanConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for typObj := range delta {
@@ -947,17 +1118,20 @@
 type reflectMakeMapConstraint struct {
 	cgn    *cgnode
 	typ    nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectMakeMapConstraint) ptr() nodeid                      { return c.typ }
+func (c *reflectMakeMapConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectMakeMapConstraint) renumber(mapping []nodeid) {
+	c.typ = mapping[c.typ]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectMakeMapConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ)
 }
 
-func (c *reflectMakeMapConstraint) ptr() nodeid {
-	return c.typ
-}
-
 func (c *reflectMakeMapConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for typObj := range delta {
@@ -1000,17 +1174,20 @@
 type reflectMakeSliceConstraint struct {
 	cgn    *cgnode
 	typ    nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectMakeSliceConstraint) ptr() nodeid                      { return c.typ }
+func (c *reflectMakeSliceConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectMakeSliceConstraint) renumber(mapping []nodeid) {
+	c.typ = mapping[c.typ]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectMakeSliceConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ)
 }
 
-func (c *reflectMakeSliceConstraint) ptr() nodeid {
-	return c.typ
-}
-
 func (c *reflectMakeSliceConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for typObj := range delta {
@@ -1053,17 +1230,20 @@
 type reflectNewConstraint struct {
 	cgn    *cgnode
 	typ    nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectNewConstraint) ptr() nodeid                      { return c.typ }
+func (c *reflectNewConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectNewConstraint) renumber(mapping []nodeid) {
+	c.typ = mapping[c.typ]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectNewConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ)
 }
 
-func (c *reflectNewConstraint) ptr() nodeid {
-	return c.typ
-}
-
 func (c *reflectNewConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for typObj := range delta {
@@ -1111,17 +1291,20 @@
 type reflectPtrToConstraint struct {
 	cgn    *cgnode
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectPtrToConstraint) ptr() nodeid                      { return c.t }
+func (c *reflectPtrToConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectPtrToConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectPtrToConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t)
 }
 
-func (c *reflectPtrToConstraint) ptr() nodeid {
-	return c.t
-}
-
 func (c *reflectPtrToConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for tObj := range delta {
@@ -1152,17 +1335,20 @@
 type reflectSliceOfConstraint struct {
 	cgn    *cgnode
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectSliceOfConstraint) ptr() nodeid                      { return c.t }
+func (c *reflectSliceOfConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectSliceOfConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectSliceOfConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t)
 }
 
-func (c *reflectSliceOfConstraint) ptr() nodeid {
-	return c.t
-}
-
 func (c *reflectSliceOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for tObj := range delta {
@@ -1191,17 +1377,20 @@
 type reflectTypeOfConstraint struct {
 	cgn    *cgnode
 	i      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectTypeOfConstraint) ptr() nodeid                      { return c.i }
+func (c *reflectTypeOfConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectTypeOfConstraint) renumber(mapping []nodeid) {
+	c.i = mapping[c.i]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectTypeOfConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i)
 }
 
-func (c *reflectTypeOfConstraint) ptr() nodeid {
-	return c.i
-}
-
 func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for iObj := range delta {
@@ -1238,17 +1427,20 @@
 type reflectZeroConstraint struct {
 	cgn    *cgnode
 	typ    nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *reflectZeroConstraint) ptr() nodeid                      { return c.typ }
+func (c *reflectZeroConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *reflectZeroConstraint) renumber(mapping []nodeid) {
+	c.typ = mapping[c.typ]
+	c.result = mapping[c.result]
 }
 
 func (c *reflectZeroConstraint) String() string {
 	return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
 }
 
-func (c *reflectZeroConstraint) ptr() nodeid {
-	return c.typ
-}
-
 func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for typObj := range delta {
@@ -1294,17 +1486,20 @@
 type rtypeElemConstraint struct {
 	cgn    *cgnode
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rtypeElemConstraint) ptr() nodeid                      { return c.t }
+func (c *rtypeElemConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rtypeElemConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
 }
 
 func (c *rtypeElemConstraint) String() string {
 	return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t)
 }
 
-func (c *rtypeElemConstraint) ptr() nodeid {
-	return c.t
-}
-
 func (c *rtypeElemConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	// Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
 	type hasElem interface {
@@ -1341,17 +1536,20 @@
 	cgn    *cgnode
 	name   string // name of field; "" for unknown
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rtypeFieldByNameConstraint) ptr() nodeid                      { return c.t }
+func (c *rtypeFieldByNameConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rtypeFieldByNameConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
 }
 
 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) ptr() nodeid {
-	return c.t
-}
-
 func (c *rtypeFieldByNameConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	// type StructField struct {
 	// 0	__identity__
@@ -1424,17 +1622,20 @@
 type rtypeInOutConstraint struct {
 	cgn    *cgnode
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
 	out    bool
 	i      int // -ve if not a constant
 }
 
-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) ptr() nodeid                      { return c.t }
+func (c *rtypeInOutConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rtypeInOutConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
 }
 
-func (c *rtypeInOutConstraint) ptr() nodeid {
-	return c.t
+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) {
@@ -1497,17 +1698,20 @@
 type rtypeKeyConstraint struct {
 	cgn    *cgnode
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rtypeKeyConstraint) ptr() nodeid                      { return c.t }
+func (c *rtypeKeyConstraint) indirect(nodes []nodeid) []nodeid { return append(nodes, c.result) }
+func (c *rtypeKeyConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
 }
 
 func (c *rtypeKeyConstraint) String() string {
 	return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t)
 }
 
-func (c *rtypeKeyConstraint) ptr() nodeid {
-	return c.t
-}
-
 func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) {
 	changed := false
 	for tObj := range delta {
@@ -1540,17 +1744,22 @@
 	cgn    *cgnode
 	name   string // name of method; "" for unknown
 	t      nodeid // (ptr)
-	result nodeid
+	result nodeid // (indirect)
+}
+
+func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t }
+func (c *rtypeMethodByNameConstraint) indirect(nodes []nodeid) []nodeid {
+	return append(nodes, c.result)
+}
+func (c *rtypeMethodByNameConstraint) renumber(mapping []nodeid) {
+	c.t = mapping[c.t]
+	c.result = mapping[c.result]
 }
 
 func (c *rtypeMethodByNameConstraint) String() string {
 	return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name)
 }
 
-func (c *rtypeMethodByNameConstraint) ptr() nodeid {
-	return c.t
-}
-
 // changeRecv returns sig with Recv prepended to Params().
 func changeRecv(sig *types.Signature) *types.Signature {
 	params := sig.Params()
diff --git a/go/pointer/solve.go b/go/pointer/solve.go
index 458e66c..6a6a5ee 100644
--- a/go/pointer/solve.go
+++ b/go/pointer/solve.go
@@ -56,6 +56,13 @@
 
 	if a.log != nil {
 		fmt.Fprintf(a.log, "Solver done\n")
+
+		// 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)
+			}
+		}
 	}
 }
 
@@ -160,34 +167,6 @@
 	}
 }
 
-func (c *addrConstraint) ptr() nodeid {
-	panic("addrConstraint: not a complex constraint")
-}
-func (c *copyConstraint) ptr() nodeid {
-	panic("addrConstraint: not a complex constraint")
-}
-
-// Complex constraints attach themselves to the relevant pointer node.
-
-func (c *storeConstraint) ptr() nodeid {
-	return c.dst
-}
-func (c *loadConstraint) ptr() nodeid {
-	return c.src
-}
-func (c *offsetAddrConstraint) ptr() nodeid {
-	return c.src
-}
-func (c *typeFilterConstraint) ptr() nodeid {
-	return c.src
-}
-func (c *untagConstraint) ptr() nodeid {
-	return c.src
-}
-func (c *invokeConstraint) ptr() nodeid {
-	return c.iface
-}
-
 // onlineCopy adds a copy edge.  It is called online, i.e. during
 // solving, so it adds edges and pts members directly rather than by
 // instantiating a 'constraint'.