// 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 }