Skip to content

Commit 829c140

Browse files
committed
cmd/compile: move Strongly Connected Components code into new file
This logic is used by the current escape analysis pass, but otherwise logically independent. Move (unchanged) into a separate file to make that clearer, and to make it easier to replace esc.go later. Updates #23109. Change-Id: Iec8c0c47ea04c0008165791731c11d9104d5a474 Reviewed-on: https://go-review.googlesource.com/c/go/+/167715 Reviewed-by: Robert Griesemer <[email protected]>
1 parent c0cfe96 commit 829c140

File tree

2 files changed

+145
-138
lines changed

2 files changed

+145
-138
lines changed

src/cmd/compile/internal/gc/esc.go

-138
Original file line numberDiff line numberDiff line change
@@ -11,144 +11,6 @@ import (
1111
"strings"
1212
)
1313

14-
// Run analysis on minimal sets of mutually recursive functions
15-
// or single non-recursive functions, bottom up.
16-
//
17-
// Finding these sets is finding strongly connected components
18-
// by reverse topological order in the static call graph.
19-
// The algorithm (known as Tarjan's algorithm) for doing that is taken from
20-
// Sedgewick, Algorithms, Second Edition, p. 482, with two adaptations.
21-
//
22-
// First, a hidden closure function (n.Func.IsHiddenClosure()) cannot be the
23-
// root of a connected component. Refusing to use it as a root
24-
// forces it into the component of the function in which it appears.
25-
// This is more convenient for escape analysis.
26-
//
27-
// Second, each function becomes two virtual nodes in the graph,
28-
// with numbers n and n+1. We record the function's node number as n
29-
// but search from node n+1. If the search tells us that the component
30-
// number (min) is n+1, we know that this is a trivial component: one function
31-
// plus its closures. If the search tells us that the component number is
32-
// n, then there was a path from node n+1 back to node n, meaning that
33-
// the function set is mutually recursive. The escape analysis can be
34-
// more precise when analyzing a single non-recursive function than
35-
// when analyzing a set of mutually recursive functions.
36-
37-
type bottomUpVisitor struct {
38-
analyze func([]*Node, bool)
39-
visitgen uint32
40-
nodeID map[*Node]uint32
41-
stack []*Node
42-
}
43-
44-
// visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
45-
// It calls analyze with successive groups of functions, working from
46-
// the bottom of the call graph upward. Each time analyze is called with
47-
// a list of functions, every function on that list only calls other functions
48-
// on the list or functions that have been passed in previous invocations of
49-
// analyze. Closures appear in the same list as their outer functions.
50-
// The lists are as short as possible while preserving those requirements.
51-
// (In a typical program, many invocations of analyze will be passed just
52-
// a single function.) The boolean argument 'recursive' passed to analyze
53-
// specifies whether the functions on the list are mutually recursive.
54-
// If recursive is false, the list consists of only a single function and its closures.
55-
// If recursive is true, the list may still contain only a single function,
56-
// if that function is itself recursive.
57-
func visitBottomUp(list []*Node, analyze func(list []*Node, recursive bool)) {
58-
var v bottomUpVisitor
59-
v.analyze = analyze
60-
v.nodeID = make(map[*Node]uint32)
61-
for _, n := range list {
62-
if n.Op == ODCLFUNC && !n.Func.IsHiddenClosure() {
63-
v.visit(n)
64-
}
65-
}
66-
}
67-
68-
func (v *bottomUpVisitor) visit(n *Node) uint32 {
69-
if id := v.nodeID[n]; id > 0 {
70-
// already visited
71-
return id
72-
}
73-
74-
v.visitgen++
75-
id := v.visitgen
76-
v.nodeID[n] = id
77-
v.visitgen++
78-
min := v.visitgen
79-
80-
v.stack = append(v.stack, n)
81-
min = v.visitcodelist(n.Nbody, min)
82-
if (min == id || min == id+1) && !n.Func.IsHiddenClosure() {
83-
// This node is the root of a strongly connected component.
84-
85-
// The original min passed to visitcodelist was v.nodeID[n]+1.
86-
// If visitcodelist found its way back to v.nodeID[n], then this
87-
// block is a set of mutually recursive functions.
88-
// Otherwise it's just a lone function that does not recurse.
89-
recursive := min == id
90-
91-
// Remove connected component from stack.
92-
// Mark walkgen so that future visits return a large number
93-
// so as not to affect the caller's min.
94-
95-
var i int
96-
for i = len(v.stack) - 1; i >= 0; i-- {
97-
x := v.stack[i]
98-
if x == n {
99-
break
100-
}
101-
v.nodeID[x] = ^uint32(0)
102-
}
103-
v.nodeID[n] = ^uint32(0)
104-
block := v.stack[i:]
105-
// Run escape analysis on this set of functions.
106-
v.stack = v.stack[:i]
107-
v.analyze(block, recursive)
108-
}
109-
110-
return min
111-
}
112-
113-
func (v *bottomUpVisitor) visitcodelist(l Nodes, min uint32) uint32 {
114-
for _, n := range l.Slice() {
115-
min = v.visitcode(n, min)
116-
}
117-
return min
118-
}
119-
120-
func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
121-
if n == nil {
122-
return min
123-
}
124-
125-
min = v.visitcodelist(n.Ninit, min)
126-
min = v.visitcode(n.Left, min)
127-
min = v.visitcode(n.Right, min)
128-
min = v.visitcodelist(n.List, min)
129-
min = v.visitcodelist(n.Nbody, min)
130-
min = v.visitcodelist(n.Rlist, min)
131-
132-
switch n.Op {
133-
case OCALLFUNC, OCALLMETH:
134-
fn := asNode(n.Left.Type.Nname())
135-
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
136-
m := v.visit(fn.Name.Defn)
137-
if m < min {
138-
min = m
139-
}
140-
}
141-
142-
case OCLOSURE:
143-
m := v.visit(n.Func.Closure)
144-
if m < min {
145-
min = m
146-
}
147-
}
148-
149-
return min
150-
}
151-
15214
// Escape analysis.
15315

15416
// An escape analysis pass for a set of functions. The

src/cmd/compile/internal/gc/scc.go

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright 2011 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package gc
6+
7+
// Strongly connected components.
8+
//
9+
// Run analysis on minimal sets of mutually recursive functions
10+
// or single non-recursive functions, bottom up.
11+
//
12+
// Finding these sets is finding strongly connected components
13+
// by reverse topological order in the static call graph.
14+
// The algorithm (known as Tarjan's algorithm) for doing that is taken from
15+
// Sedgewick, Algorithms, Second Edition, p. 482, with two adaptations.
16+
//
17+
// First, a hidden closure function (n.Func.IsHiddenClosure()) cannot be the
18+
// root of a connected component. Refusing to use it as a root
19+
// forces it into the component of the function in which it appears.
20+
// This is more convenient for escape analysis.
21+
//
22+
// Second, each function becomes two virtual nodes in the graph,
23+
// with numbers n and n+1. We record the function's node number as n
24+
// but search from node n+1. If the search tells us that the component
25+
// number (min) is n+1, we know that this is a trivial component: one function
26+
// plus its closures. If the search tells us that the component number is
27+
// n, then there was a path from node n+1 back to node n, meaning that
28+
// the function set is mutually recursive. The escape analysis can be
29+
// more precise when analyzing a single non-recursive function than
30+
// when analyzing a set of mutually recursive functions.
31+
32+
type bottomUpVisitor struct {
33+
analyze func([]*Node, bool)
34+
visitgen uint32
35+
nodeID map[*Node]uint32
36+
stack []*Node
37+
}
38+
39+
// visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
40+
// It calls analyze with successive groups of functions, working from
41+
// the bottom of the call graph upward. Each time analyze is called with
42+
// a list of functions, every function on that list only calls other functions
43+
// on the list or functions that have been passed in previous invocations of
44+
// analyze. Closures appear in the same list as their outer functions.
45+
// The lists are as short as possible while preserving those requirements.
46+
// (In a typical program, many invocations of analyze will be passed just
47+
// a single function.) The boolean argument 'recursive' passed to analyze
48+
// specifies whether the functions on the list are mutually recursive.
49+
// If recursive is false, the list consists of only a single function and its closures.
50+
// If recursive is true, the list may still contain only a single function,
51+
// if that function is itself recursive.
52+
func visitBottomUp(list []*Node, analyze func(list []*Node, recursive bool)) {
53+
var v bottomUpVisitor
54+
v.analyze = analyze
55+
v.nodeID = make(map[*Node]uint32)
56+
for _, n := range list {
57+
if n.Op == ODCLFUNC && !n.Func.IsHiddenClosure() {
58+
v.visit(n)
59+
}
60+
}
61+
}
62+
63+
func (v *bottomUpVisitor) visit(n *Node) uint32 {
64+
if id := v.nodeID[n]; id > 0 {
65+
// already visited
66+
return id
67+
}
68+
69+
v.visitgen++
70+
id := v.visitgen
71+
v.nodeID[n] = id
72+
v.visitgen++
73+
min := v.visitgen
74+
75+
v.stack = append(v.stack, n)
76+
min = v.visitcodelist(n.Nbody, min)
77+
if (min == id || min == id+1) && !n.Func.IsHiddenClosure() {
78+
// This node is the root of a strongly connected component.
79+
80+
// The original min passed to visitcodelist was v.nodeID[n]+1.
81+
// If visitcodelist found its way back to v.nodeID[n], then this
82+
// block is a set of mutually recursive functions.
83+
// Otherwise it's just a lone function that does not recurse.
84+
recursive := min == id
85+
86+
// Remove connected component from stack.
87+
// Mark walkgen so that future visits return a large number
88+
// so as not to affect the caller's min.
89+
90+
var i int
91+
for i = len(v.stack) - 1; i >= 0; i-- {
92+
x := v.stack[i]
93+
if x == n {
94+
break
95+
}
96+
v.nodeID[x] = ^uint32(0)
97+
}
98+
v.nodeID[n] = ^uint32(0)
99+
block := v.stack[i:]
100+
// Run escape analysis on this set of functions.
101+
v.stack = v.stack[:i]
102+
v.analyze(block, recursive)
103+
}
104+
105+
return min
106+
}
107+
108+
func (v *bottomUpVisitor) visitcodelist(l Nodes, min uint32) uint32 {
109+
for _, n := range l.Slice() {
110+
min = v.visitcode(n, min)
111+
}
112+
return min
113+
}
114+
115+
func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
116+
if n == nil {
117+
return min
118+
}
119+
120+
min = v.visitcodelist(n.Ninit, min)
121+
min = v.visitcode(n.Left, min)
122+
min = v.visitcode(n.Right, min)
123+
min = v.visitcodelist(n.List, min)
124+
min = v.visitcodelist(n.Nbody, min)
125+
min = v.visitcodelist(n.Rlist, min)
126+
127+
switch n.Op {
128+
case OCALLFUNC, OCALLMETH:
129+
fn := asNode(n.Left.Type.Nname())
130+
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
131+
m := v.visit(fn.Name.Defn)
132+
if m < min {
133+
min = m
134+
}
135+
}
136+
137+
case OCLOSURE:
138+
m := v.visit(n.Func.Closure)
139+
if m < min {
140+
min = m
141+
}
142+
}
143+
144+
return min
145+
}

0 commit comments

Comments
 (0)