1
2
3
4
5 package ssa
6
7 import "cmd/internal/src"
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 func branchelim(f *Func) {
23
24 switch f.Config.arch {
25 case "arm64", "ppc64le", "ppc64", "amd64", "wasm":
26
27 default:
28 return
29 }
30
31
32
33 loadAddr := f.newSparseSet(f.NumValues())
34 defer f.retSparseSet(loadAddr)
35 for _, b := range f.Blocks {
36 for _, v := range b.Values {
37 switch v.Op {
38 case OpLoad, OpAtomicLoad8, OpAtomicLoad32, OpAtomicLoad64, OpAtomicLoadPtr, OpAtomicLoadAcq32, OpAtomicLoadAcq64:
39 loadAddr.add(v.Args[0].ID)
40 case OpMove:
41 loadAddr.add(v.Args[1].ID)
42 }
43 }
44 }
45 po := f.postorder()
46 for {
47 n := loadAddr.size()
48 for _, b := range po {
49 for i := len(b.Values) - 1; i >= 0; i-- {
50 v := b.Values[i]
51 if !loadAddr.contains(v.ID) {
52 continue
53 }
54 for _, a := range v.Args {
55 if a.Type.IsInteger() || a.Type.IsPtr() || a.Type.IsUnsafePtr() {
56 loadAddr.add(a.ID)
57 }
58 }
59 }
60 }
61 if loadAddr.size() == n {
62 break
63 }
64 }
65
66 change := true
67 for change {
68 change = false
69 for _, b := range f.Blocks {
70 change = elimIf(f, loadAddr, b) || elimIfElse(f, loadAddr, b) || change
71 }
72 }
73 }
74
75 func canCondSelect(v *Value, arch string, loadAddr *sparseSet) bool {
76 if loadAddr.contains(v.ID) {
77
78
79
80
81
82
83
84 return false
85 }
86
87 switch {
88 case v.Type.Size() > v.Block.Func.Config.RegSize:
89 return false
90 case v.Type.IsPtrShaped():
91 return true
92 case v.Type.IsInteger():
93 if arch == "amd64" && v.Type.Size() < 2 {
94
95 return false
96 }
97 return true
98 default:
99 return false
100 }
101 }
102
103
104
105
106 func elimIf(f *Func, loadAddr *sparseSet, dom *Block) bool {
107
108
109
110 if dom.Kind != BlockIf || dom.Likely != BranchUnknown {
111 return false
112 }
113 var simple, post *Block
114 for i := range dom.Succs {
115 bb, other := dom.Succs[i].Block(), dom.Succs[i^1].Block()
116 if isLeafPlain(bb) && bb.Succs[0].Block() == other {
117 simple = bb
118 post = other
119 break
120 }
121 }
122 if simple == nil || len(post.Preds) != 2 || post == dom {
123 return false
124 }
125
126
127
128
129
130
131
132 hasphis := false
133 for _, v := range post.Values {
134 if v.Op == OpPhi {
135 hasphis = true
136 if !canCondSelect(v, f.Config.arch, loadAddr) {
137 return false
138 }
139 }
140 }
141 if !hasphis {
142 return false
143 }
144
145
146
147
148
149 const maxfuseinsts = 2
150
151 if len(simple.Values) > maxfuseinsts || !canSpeculativelyExecute(simple) {
152 return false
153 }
154
155
156 swap := (post.Preds[0].Block() == dom) != (dom.Succs[0].Block() == post)
157 for _, v := range post.Values {
158 if v.Op != OpPhi {
159 continue
160 }
161 v.Op = OpCondSelect
162 if swap {
163 v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
164 }
165 v.AddArg(dom.Controls[0])
166 }
167
168
169
170 dom.Kind = post.Kind
171 dom.CopyControls(post)
172 dom.Aux = post.Aux
173 dom.Succs = append(dom.Succs[:0], post.Succs...)
174 for i := range dom.Succs {
175 e := dom.Succs[i]
176 e.b.Preds[e.i].b = dom
177 }
178
179
180 simplePos := simple.Pos
181 postPos := post.Pos
182 simpleStmt := simplePos.IsStmt() == src.PosIsStmt
183 postStmt := postPos.IsStmt() == src.PosIsStmt
184
185 for _, v := range simple.Values {
186 v.Block = dom
187 }
188 for _, v := range post.Values {
189 v.Block = dom
190 }
191
192
193
194
195 findBlockPos := func(b *Block) bool {
196 pos := b.Pos
197 for _, v := range b.Values {
198
199 if pos.SameFileAndLine(v.Pos) && v.Pos.IsStmt() == src.PosIsStmt {
200 return true
201 }
202 }
203 return false
204 }
205 if simpleStmt {
206 simpleStmt = !findBlockPos(simple)
207 if !simpleStmt && simplePos.SameFileAndLine(postPos) {
208 postStmt = false
209 }
210
211 }
212 if postStmt {
213 postStmt = !findBlockPos(post)
214 }
215
216
217
218
219
220
221
222
223 setBlockPos := func(b *Block) bool {
224 pos := b.Pos
225 for _, v := range b.Values {
226 if pos.SameFileAndLine(v.Pos) && !isPoorStatementOp(v.Op) {
227 v.Pos = v.Pos.WithIsStmt()
228 return true
229 }
230 }
231 return false
232 }
233
234 if simpleStmt {
235 if setBlockPos(simple) && simplePos.SameFileAndLine(postPos) {
236 postStmt = false
237 }
238 }
239
240 if postStmt {
241 postStmt = !setBlockPos(post)
242 }
243
244
245
246 if postStmt {
247 if dom.Pos.IsStmt() != src.PosIsStmt {
248 dom.Pos = postPos
249 } else {
250
251 if len(dom.Succs) == 1 && len(dom.Succs[0].Block().Preds) == 1 {
252 succ := dom.Succs[0].Block()
253 for _, v := range succ.Values {
254 if isPoorStatementOp(v.Op) {
255 continue
256 }
257 if postPos.SameFileAndLine(v.Pos) {
258 v.Pos = v.Pos.WithIsStmt()
259 }
260 postStmt = false
261 break
262 }
263
264 if postStmt && succ.Pos.IsStmt() != src.PosIsStmt {
265 succ.Pos = postPos
266 }
267 }
268 }
269 }
270
271 dom.Values = append(dom.Values, simple.Values...)
272 dom.Values = append(dom.Values, post.Values...)
273
274
275 clobberBlock(post)
276 clobberBlock(simple)
277
278 f.invalidateCFG()
279 return true
280 }
281
282
283 func isLeafPlain(b *Block) bool {
284 return b.Kind == BlockPlain && len(b.Preds) == 1
285 }
286
287 func clobberBlock(b *Block) {
288 b.Values = nil
289 b.Preds = nil
290 b.Succs = nil
291 b.Aux = nil
292 b.ResetControls()
293 b.Likely = BranchUnknown
294 b.Kind = BlockInvalid
295 }
296
297
298
299
300 func elimIfElse(f *Func, loadAddr *sparseSet, b *Block) bool {
301
302
303
304 if b.Kind != BlockIf || b.Likely != BranchUnknown {
305 return false
306 }
307 yes, no := b.Succs[0].Block(), b.Succs[1].Block()
308 if !isLeafPlain(yes) || len(yes.Values) > 1 || !canSpeculativelyExecute(yes) {
309 return false
310 }
311 if !isLeafPlain(no) || len(no.Values) > 1 || !canSpeculativelyExecute(no) {
312 return false
313 }
314 if b.Succs[0].Block().Succs[0].Block() != b.Succs[1].Block().Succs[0].Block() {
315 return false
316 }
317
318 post := b.Succs[0].Block().Succs[0].Block()
319 if len(post.Preds) != 2 || post == b {
320 return false
321 }
322 hasphis := false
323 for _, v := range post.Values {
324 if v.Op == OpPhi {
325 hasphis = true
326 if !canCondSelect(v, f.Config.arch, loadAddr) {
327 return false
328 }
329 }
330 }
331 if !hasphis {
332 return false
333 }
334
335
336 if !shouldElimIfElse(no, yes, post, f.Config.arch) {
337 return false
338 }
339
340
341 swap := post.Preds[0].Block() != b.Succs[0].Block()
342 for _, v := range post.Values {
343 if v.Op != OpPhi {
344 continue
345 }
346 v.Op = OpCondSelect
347 if swap {
348 v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
349 }
350 v.AddArg(b.Controls[0])
351 }
352
353
354
355 b.Kind = post.Kind
356 b.CopyControls(post)
357 b.Aux = post.Aux
358 b.Succs = append(b.Succs[:0], post.Succs...)
359 for i := range b.Succs {
360 e := b.Succs[i]
361 e.b.Preds[e.i].b = b
362 }
363 for i := range post.Values {
364 post.Values[i].Block = b
365 }
366 for i := range yes.Values {
367 yes.Values[i].Block = b
368 }
369 for i := range no.Values {
370 no.Values[i].Block = b
371 }
372 b.Values = append(b.Values, yes.Values...)
373 b.Values = append(b.Values, no.Values...)
374 b.Values = append(b.Values, post.Values...)
375
376
377 clobberBlock(yes)
378 clobberBlock(no)
379 clobberBlock(post)
380
381 f.invalidateCFG()
382 return true
383 }
384
385
386
387 func shouldElimIfElse(no, yes, post *Block, arch string) bool {
388 switch arch {
389 default:
390 return true
391 case "amd64":
392 const maxcost = 2
393 phi := 0
394 other := 0
395 for _, v := range post.Values {
396 if v.Op == OpPhi {
397
398
399 phi++
400 }
401 for _, x := range v.Args {
402 if x.Block == no || x.Block == yes {
403 other++
404 }
405 }
406 }
407 cost := phi * 1
408 if phi > 1 {
409
410
411
412 cost += other * 1
413 }
414 return cost < maxcost
415 }
416 }
417
418
419
420
421
422
423
424
425
426 func canSpeculativelyExecute(b *Block) bool {
427
428
429 for _, v := range b.Values {
430 if v.Op == OpPhi || isDivMod(v.Op) || v.Type.IsMemory() ||
431 v.MemoryArg() != nil || opcodeTable[v.Op].hasSideEffects {
432 return false
433 }
434 }
435 return true
436 }
437
438 func isDivMod(op Op) bool {
439 switch op {
440 case OpDiv8, OpDiv8u, OpDiv16, OpDiv16u,
441 OpDiv32, OpDiv32u, OpDiv64, OpDiv64u, OpDiv128u,
442 OpDiv32F, OpDiv64F,
443 OpMod8, OpMod8u, OpMod16, OpMod16u,
444 OpMod32, OpMod32u, OpMod64, OpMod64u:
445 return true
446 default:
447 return false
448 }
449 }
450
View as plain text