2019-03-14 13:54:05 -07:00
|
|
|
// Copyright 2011 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 gc
|
|
|
|
|
|
|
|
|
|
// Strongly connected components.
|
|
|
|
|
//
|
|
|
|
|
// Run analysis on minimal sets of mutually recursive functions
|
|
|
|
|
// or single non-recursive functions, bottom up.
|
|
|
|
|
//
|
|
|
|
|
// Finding these sets is finding strongly connected components
|
|
|
|
|
// by reverse topological order in the static call graph.
|
|
|
|
|
// The algorithm (known as Tarjan's algorithm) for doing that is taken from
|
|
|
|
|
// Sedgewick, Algorithms, Second Edition, p. 482, with two adaptations.
|
|
|
|
|
//
|
|
|
|
|
// First, a hidden closure function (n.Func.IsHiddenClosure()) cannot be the
|
|
|
|
|
// root of a connected component. Refusing to use it as a root
|
|
|
|
|
// forces it into the component of the function in which it appears.
|
|
|
|
|
// This is more convenient for escape analysis.
|
|
|
|
|
//
|
|
|
|
|
// Second, each function becomes two virtual nodes in the graph,
|
|
|
|
|
// with numbers n and n+1. We record the function's node number as n
|
|
|
|
|
// but search from node n+1. If the search tells us that the component
|
|
|
|
|
// number (min) is n+1, we know that this is a trivial component: one function
|
|
|
|
|
// plus its closures. If the search tells us that the component number is
|
|
|
|
|
// n, then there was a path from node n+1 back to node n, meaning that
|
|
|
|
|
// the function set is mutually recursive. The escape analysis can be
|
|
|
|
|
// more precise when analyzing a single non-recursive function than
|
|
|
|
|
// when analyzing a set of mutually recursive functions.
|
|
|
|
|
|
|
|
|
|
type bottomUpVisitor struct {
|
|
|
|
|
analyze func([]*Node, bool)
|
|
|
|
|
visitgen uint32
|
|
|
|
|
nodeID map[*Node]uint32
|
|
|
|
|
stack []*Node
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
|
|
|
|
|
// It calls analyze with successive groups of functions, working from
|
|
|
|
|
// the bottom of the call graph upward. Each time analyze is called with
|
|
|
|
|
// a list of functions, every function on that list only calls other functions
|
|
|
|
|
// on the list or functions that have been passed in previous invocations of
|
|
|
|
|
// analyze. Closures appear in the same list as their outer functions.
|
|
|
|
|
// The lists are as short as possible while preserving those requirements.
|
|
|
|
|
// (In a typical program, many invocations of analyze will be passed just
|
|
|
|
|
// a single function.) The boolean argument 'recursive' passed to analyze
|
|
|
|
|
// specifies whether the functions on the list are mutually recursive.
|
|
|
|
|
// If recursive is false, the list consists of only a single function and its closures.
|
|
|
|
|
// If recursive is true, the list may still contain only a single function,
|
|
|
|
|
// if that function is itself recursive.
|
|
|
|
|
func visitBottomUp(list []*Node, analyze func(list []*Node, recursive bool)) {
|
|
|
|
|
var v bottomUpVisitor
|
|
|
|
|
v.analyze = analyze
|
|
|
|
|
v.nodeID = make(map[*Node]uint32)
|
|
|
|
|
for _, n := range list {
|
|
|
|
|
if n.Op == ODCLFUNC && !n.Func.IsHiddenClosure() {
|
|
|
|
|
v.visit(n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *bottomUpVisitor) visit(n *Node) uint32 {
|
|
|
|
|
if id := v.nodeID[n]; id > 0 {
|
|
|
|
|
// already visited
|
|
|
|
|
return id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v.visitgen++
|
|
|
|
|
id := v.visitgen
|
|
|
|
|
v.nodeID[n] = id
|
|
|
|
|
v.visitgen++
|
|
|
|
|
min := v.visitgen
|
|
|
|
|
v.stack = append(v.stack, n)
|
2019-04-22 14:22:25 -07:00
|
|
|
|
|
|
|
|
inspectList(n.Nbody, func(n *Node) bool {
|
|
|
|
|
switch n.Op {
|
2020-10-24 02:08:06 -07:00
|
|
|
case ONAME:
|
|
|
|
|
if n.Class() == PFUNC {
|
|
|
|
|
if n.isMethodExpression() {
|
2020-11-22 20:43:16 -08:00
|
|
|
n = n.MethodName()
|
2020-10-24 02:08:06 -07:00
|
|
|
}
|
|
|
|
|
if n != nil && n.Name.Defn != nil {
|
|
|
|
|
if m := v.visit(n.Name.Defn); m < min {
|
|
|
|
|
min = m
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case ODOTMETH:
|
2020-11-22 20:43:16 -08:00
|
|
|
fn := n.MethodName()
|
2019-04-22 14:22:25 -07:00
|
|
|
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
|
|
|
|
|
if m := v.visit(fn.Name.Defn); m < min {
|
2020-04-20 11:14:36 -07:00
|
|
|
min = m
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case OCALLPART:
|
2020-11-22 20:43:16 -08:00
|
|
|
fn := asNode(callpartMethod(n).Nname)
|
2020-04-20 11:14:36 -07:00
|
|
|
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
|
|
|
|
|
if m := v.visit(fn.Name.Defn); m < min {
|
2019-04-22 14:22:25 -07:00
|
|
|
min = m
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case OCLOSURE:
|
[dev.regabi] cmd/compile: clean up Node.Func
The original meaning of type Func was "extra fields factored out
of a few cases of type Node having to do with functions",
but those specific cases didn't necessarily have any relation.
A typical declared function is represented by an ODCLFUNC Node
at its declaration and an ONAME node at its uses, and both those
have a .Func field, but they are *different* Funcs.
Similarly, a closure is represented both by an OCLOSURE Node for
the value itself and an ODCLFUNC Node for the underlying function
implementing the closure. Those too have *different* Funcs,
and the Func.Closure field in one points to the other and vice versa.
This has led to no end of confusion over the years.
This CL elevates type Func to be the canonical identifier for
a given Go function.
This looks like a trivial CL but in fact is the result of a lot of
scaffolding and rewriting, discarded once the result was achieved, to
separate out the three different kinds of Func nodes into three
separate fields, limited in use to each specific Node type, to
understand which Func fields are used by which Node types and what the
possible overlaps are. There were a few overlaps, most notably around
closures, which led to more fields being added to type Func to keep
them separate even though there is now a single Func instead of two
different ones for each function.
A future CL can and should change Curfn to be a *Func instead of
a *Node, finally eliminating the confusion about whether Curfn
is an ODCLFUNC node (as it is most of the time) or an ONAME node
(as it is when type-checking an inlined function body).
Although sizeof_test.go makes it look like Func is growing by two
words, there are now half as many Funcs in a running compilation,
so the memory footprint has actually been reduced substantially.
Change-Id: I598bd96c95728093dc769a835d48f2154a406a61
Reviewed-on: https://go-review.googlesource.com/c/go/+/272253
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-16 17:00:10 -05:00
|
|
|
if m := v.visit(n.Func.Decl); m < min {
|
2019-04-22 14:22:25 -07:00
|
|
|
min = m
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
|
2019-03-14 13:54:05 -07:00
|
|
|
if (min == id || min == id+1) && !n.Func.IsHiddenClosure() {
|
|
|
|
|
// This node is the root of a strongly connected component.
|
|
|
|
|
|
|
|
|
|
// The original min passed to visitcodelist was v.nodeID[n]+1.
|
|
|
|
|
// If visitcodelist found its way back to v.nodeID[n], then this
|
|
|
|
|
// block is a set of mutually recursive functions.
|
|
|
|
|
// Otherwise it's just a lone function that does not recurse.
|
|
|
|
|
recursive := min == id
|
|
|
|
|
|
|
|
|
|
// Remove connected component from stack.
|
|
|
|
|
// Mark walkgen so that future visits return a large number
|
|
|
|
|
// so as not to affect the caller's min.
|
|
|
|
|
|
|
|
|
|
var i int
|
|
|
|
|
for i = len(v.stack) - 1; i >= 0; i-- {
|
|
|
|
|
x := v.stack[i]
|
|
|
|
|
if x == n {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
v.nodeID[x] = ^uint32(0)
|
|
|
|
|
}
|
|
|
|
|
v.nodeID[n] = ^uint32(0)
|
|
|
|
|
block := v.stack[i:]
|
|
|
|
|
// Run escape analysis on this set of functions.
|
|
|
|
|
v.stack = v.stack[:i]
|
|
|
|
|
v.analyze(block, recursive)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return min
|
|
|
|
|
}
|