| // 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 implements renumbering, a pre-solver optimization to |
| // improve the efficiency of the solver's points-to set representation. |
| // |
| // TODO(adonovan): rename file "renumber.go" |
| |
| import "fmt" |
| |
| // 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. |
| // |
| // Renumbering makes the PTA log inscrutable. To aid debugging, later |
| // phases (e.g. HVN) must not rely on it having occurred. |
| // |
| func (a *analysis) renumber() { |
| if a.log != nil { |
| fmt.Fprintf(a.log, "\n\n==== Renumbering\n\n") |
| } |
| |
| 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 |
| } |
| for _, queries := range a.config.extendedQueries { |
| for _, query := range queries { |
| if query.ptr != nil { |
| query.ptr.n = renumbering[query.ptr.n] |
| } |
| } |
| } |
| |
| // 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 |
| } |