1
2
3
4
5 package ssa
6
7 import (
8 "cmd/internal/src"
9 "fmt"
10 )
11
12
13 func fuseEarly(f *Func) { fuse(f, fuseTypePlain|fuseTypeIntInRange) }
14
15
16 func fuseLate(f *Func) { fuse(f, fuseTypePlain|fuseTypeIf|fuseTypeBranchRedirect) }
17
18 type fuseType uint8
19
20 const (
21 fuseTypePlain fuseType = 1 << iota
22 fuseTypeIf
23 fuseTypeIntInRange
24 fuseTypeBranchRedirect
25 fuseTypeShortCircuit
26 )
27
28
29 func fuse(f *Func, typ fuseType) {
30 for changed := true; changed; {
31 changed = false
32
33
34
35 for i := len(f.Blocks) - 1; i >= 0; i-- {
36 b := f.Blocks[i]
37 if typ&fuseTypeIf != 0 {
38 changed = fuseBlockIf(b) || changed
39 }
40 if typ&fuseTypeIntInRange != 0 {
41 changed = fuseIntegerComparisons(b) || changed
42 }
43 if typ&fuseTypePlain != 0 {
44 changed = fuseBlockPlain(b) || changed
45 }
46 if typ&fuseTypeShortCircuit != 0 {
47 changed = shortcircuitBlock(b) || changed
48 }
49 }
50
51 if typ&fuseTypeBranchRedirect != 0 {
52 changed = fuseBranchRedirect(f) || changed
53 }
54 if changed {
55 f.invalidateCFG()
56 }
57 }
58 }
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 func fuseBlockIf(b *Block) bool {
79 if b.Kind != BlockIf {
80 return false
81 }
82
83 var ss0, ss1 *Block
84 s0 := b.Succs[0].b
85 i0 := b.Succs[0].i
86 if s0.Kind != BlockPlain || !isEmpty(s0) {
87 s0, ss0 = b, s0
88 } else {
89 ss0 = s0.Succs[0].b
90 i0 = s0.Succs[0].i
91 }
92 s1 := b.Succs[1].b
93 i1 := b.Succs[1].i
94 if s1.Kind != BlockPlain || !isEmpty(s1) {
95 s1, ss1 = b, s1
96 } else {
97 ss1 = s1.Succs[0].b
98 i1 = s1.Succs[0].i
99 }
100 if ss0 != ss1 {
101 if s0.Kind == BlockPlain && isEmpty(s0) && s1.Kind == BlockPlain && isEmpty(s1) {
102
103 if s0 == ss1 {
104 s0, ss0 = b, ss1
105 } else if ss0 == s1 {
106 s1, ss1 = b, ss0
107 } else {
108 return false
109 }
110 } else {
111 return false
112 }
113 }
114 ss := ss0
115
116
117
118
119 for _, v := range ss.Values {
120 if v.Op == OpPhi && v.Uses > 0 && v.Args[i0] != v.Args[i1] {
121 return false
122 }
123 }
124
125
126
127 b.removeEdge(0)
128 if s0 != b && len(s0.Preds) == 0 {
129 s0.removeEdge(0)
130
131
132 for _, v := range s0.Values {
133 v.Block = b
134 }
135 b.Values = append(b.Values, s0.Values...)
136
137 s0.Kind = BlockInvalid
138 s0.Values = nil
139 s0.Succs = nil
140 s0.Preds = nil
141 }
142
143 b.Kind = BlockPlain
144 b.Likely = BranchUnknown
145 b.ResetControls()
146
147
148
149
150
151 walkValues := []*Value{}
152 for _, v := range b.Values {
153 if v.Uses == 0 && v.removeable() {
154 walkValues = append(walkValues, v)
155 }
156 }
157 for len(walkValues) != 0 {
158 v := walkValues[len(walkValues)-1]
159 walkValues = walkValues[:len(walkValues)-1]
160 if v.Uses == 0 && v.removeable() {
161 walkValues = append(walkValues, v.Args...)
162 v.reset(OpInvalid)
163 }
164 }
165 return true
166 }
167
168
169
170 func isEmpty(b *Block) bool {
171 for _, v := range b.Values {
172 if v.Uses > 0 || v.Op.IsCall() || v.Op.HasSideEffects() || v.Type.IsVoid() || opcodeTable[v.Op].nilCheck {
173 return false
174 }
175 }
176 return true
177 }
178
179
180
181
182
183
184 func fuseBlockPlain(b *Block) bool {
185 if b.Kind != BlockPlain {
186 return false
187 }
188
189 c := b.Succs[0].b
190 if len(c.Preds) != 1 || c == b {
191 return false
192 }
193
194
195 for len(b.Preds) == 1 && b.Preds[0].b != c && b.Preds[0].b.Kind == BlockPlain {
196 b = b.Preds[0].b
197 }
198
199
200 for {
201 if c.Kind != BlockPlain {
202 break
203 }
204 cNext := c.Succs[0].b
205 if cNext == b {
206 break
207 }
208 if len(cNext.Preds) != 1 {
209 break
210 }
211 c = cNext
212 }
213
214
215 var b_next *Block
216 for bx := b; bx != c; bx = b_next {
217
218
219
220 b_next = bx.Succs[0].b
221 if bx.Pos.IsStmt() == src.PosIsStmt {
222 l := bx.Pos.Line()
223 outOfOrder := false
224 for _, v := range b_next.Values {
225 if v.Pos.IsStmt() == src.PosNotStmt {
226 continue
227 }
228 if l == v.Pos.Line() {
229 v.Pos = v.Pos.WithIsStmt()
230 l = 0
231 break
232 }
233 if l < v.Pos.Line() {
234
235
236 outOfOrder = true
237 }
238 }
239 if l != 0 && !outOfOrder && (b_next.Pos.Line() == l || b_next.Pos.IsStmt() != src.PosIsStmt) {
240 b_next.Pos = bx.Pos.WithIsStmt()
241 }
242 }
243
244 for _, v := range bx.Values {
245 v.Block = c
246 }
247 }
248
249
250 total := 0
251 totalBeforeMax := 0
252 max_b := b
253
254 for bx := b; ; bx = bx.Succs[0].b {
255 if cap(bx.Values) > cap(max_b.Values) {
256 totalBeforeMax = total
257 max_b = bx
258 }
259 total += len(bx.Values)
260 if bx == c {
261 break
262 }
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 var t []*Value
279 if total <= len(c.valstorage) {
280 t = c.valstorage[:total]
281 max_b = c
282 totalBeforeMax = total - len(c.Values)
283 copy(t[totalBeforeMax:], c.Values)
284 } else if total <= cap(max_b.Values) {
285 t = max_b.Values[0:total]
286 copy(t[totalBeforeMax:], max_b.Values)
287 } else {
288 t = make([]*Value, total)
289 max_b = nil
290 }
291
292
293 copyTo := 0
294 for bx := b; ; bx = bx.Succs[0].b {
295 if bx != max_b {
296 copy(t[copyTo:], bx.Values)
297 } else if copyTo != totalBeforeMax {
298 panic(fmt.Errorf("totalBeforeMax (%d) != copyTo (%d), max_b=%v, b=%v, c=%v", totalBeforeMax, copyTo, max_b, b, c))
299 }
300 if bx == c {
301 break
302 }
303 copyTo += len(bx.Values)
304 }
305 c.Values = t
306
307
308 c.predstorage[0] = Edge{}
309 if len(b.Preds) > len(b.predstorage) {
310 c.Preds = b.Preds
311 } else {
312 c.Preds = append(c.predstorage[:0], b.Preds...)
313 }
314 for i, e := range c.Preds {
315 p := e.b
316 p.Succs[e.i] = Edge{c, i}
317 }
318 f := b.Func
319 if f.Entry == b {
320 f.Entry = c
321 }
322
323
324 for bx := b; bx != c; bx = b_next {
325 b_next = bx.Succs[0].b
326
327 bx.Kind = BlockInvalid
328 bx.Values = nil
329 bx.Preds = nil
330 bx.Succs = nil
331 }
332 return true
333 }
334
View as plain text