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