4
4
5
5
package ssa
6
6
7
- import (
8
- "fmt"
9
- "sort"
10
- )
11
-
12
- // balanceExprTree repurposes all nodes and leafs into a
13
- // balanced expression tree
7
+ // balanceExprTree repurposes all nodes and leafs into a well-balanced expression tree.
8
+ // It doesn't truly balance the tree in the sense of a BST, rather it
9
+ // prioritizes pairing up innermost (rightmost) expressions and their results and only
10
+ // pairing results of outermost (leftmost) expressions up with them when no other nice pairing exists
14
11
func balanceExprTree (v * Value , visited map [* Value ]bool , nodes , leafs []* Value ) {
15
12
// reset all arguments of nodes to help rebalancing
16
13
for i , n := range nodes {
@@ -34,27 +31,27 @@ func balanceExprTree(v *Value, visited map[*Value]bool, nodes, leafs []*Value) {
34
31
nodes [i ], nodes [j ] = nodes [j ], nodes [i ]
35
32
}
36
33
37
- // push all leafs which are constants as far off to the
38
- // right as possible to give the constant folder more opportunities
39
- sort .Slice (leafs , func (i , j int ) bool {
40
- switch leafs [j ].Op {
41
- case OpConst8 , OpConst16 , OpConst32 , OpConst64 :
42
- return false
43
- default :
44
- return true
34
+ // rebuild expression trees from the bottom up, prioritizing
35
+ // right grouping.
36
+ // if the number of leaves is not even, skip the first leaf
37
+ // and add it to be paired up later
38
+ i := 0
39
+ subTrees := leafs
40
+ for len (subTrees ) != 1 {
41
+ nextSubTrees := make ([]* Value , 0 , (len (subTrees )+ 1 )/ 2 )
42
+
43
+ start := len (subTrees )% 2
44
+ if start != 0 {
45
+ nextSubTrees = append (nextSubTrees , subTrees [0 ])
45
46
}
46
- })
47
-
48
- // build tree in reverse topological order
49
- for i := 0 ; i < len (nodes ); i ++ {
50
- if len (leafs ) < 2 { // we need at least two leafs per node, balance went very wrong
51
- panic (fmt .Sprint ("leafs needs to be >= 2, got" , len (leafs )))
47
+
48
+ for j := start ; j < len (subTrees )- 1 ; j += 2 {
49
+ nodes [i ].AddArg2 (subTrees [j ], subTrees [j + 1 ])
50
+ nextSubTrees = append (nextSubTrees , nodes [i ])
51
+ i ++
52
52
}
53
-
54
- // Take two leaves out and attach them to a node,
55
- // use the node as a new leaf in the "next layer" of the tree
56
- nodes [i ].AddArg2 (leafs [0 ], leafs [1 ])
57
- leafs = append (leafs [2 :], nodes [i ])
53
+
54
+ subTrees = nextSubTrees
58
55
}
59
56
}
60
57
@@ -72,7 +69,7 @@ func isOr(op Op) bool {
72
69
//
73
70
// (l | l << 8 | l << 18 | l << 24)
74
71
//
75
- // which cannot be rebalanced or else it won't fire rewrite rules
72
+ // which cannot be rebalanced or else it won't fire load widening rewrite rules
76
73
func probablyMemcombine (op Op , leafs []* Value ) bool {
77
74
if ! isOr (op ) {
78
75
return false
@@ -89,7 +86,11 @@ func probablyMemcombine(op Op, leafs []*Value) bool {
89
86
}
90
87
}
91
88
92
- return lshCount == len (leafs )- 1
89
+ // there are a few algorithms in the std lib expressed as two 32 bit loads
90
+ // which can get turned into a 64 bit load
91
+ // conservatively estimate that if there are more shifts than not then it is
92
+ // some sort of load waiting to be widened
93
+ return lshCount > len (leafs )/ 2
93
94
}
94
95
95
96
// rebalance balances associative computation to better help CPU instruction pipelining (#49331)
@@ -145,7 +146,7 @@ func rebalance(v *Value, visited map[*Value]bool) {
145
146
}
146
147
147
148
// we need at least 4 leafs for this expression to be rebalanceable,
148
- // and we can't balance a potential load widening (memcombine)
149
+ // and we can't balance a potential load widening (see memcombine)
149
150
if len (leafs ) < 4 || probablyMemcombine (v .Op , leafs ) {
150
151
return
151
152
}
@@ -154,8 +155,8 @@ func rebalance(v *Value, visited map[*Value]bool) {
154
155
}
155
156
156
157
// reassociate balances trees of commutative computation
157
- // to better group expressions for better constant folding,
158
- // cse, etc.
158
+ // to better group expressions to expose easy optimizations in
159
+ // cse, cancelling/counting/factoring expressions, etc.
159
160
func reassociate (f * Func ) {
160
161
visited := make (map [* Value ]bool )
161
162
0 commit comments