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