Source file src/pkg/cmd/compile/internal/ssa/branchelim.go
1
2
3
4
5 package ssa
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 func branchelim(f *Func) {
21
22 switch f.Config.arch {
23 case "arm64", "amd64":
24
25 default:
26 return
27 }
28
29
30
31 loadAddr := f.newSparseSet(f.NumValues())
32 defer f.retSparseSet(loadAddr)
33 for _, b := range f.Blocks {
34 for _, v := range b.Values {
35 switch v.Op {
36 case OpLoad, OpAtomicLoad8, OpAtomicLoad32, OpAtomicLoad64, OpAtomicLoadPtr, OpAtomicLoadAcq32:
37 loadAddr.add(v.Args[0].ID)
38 case OpMove:
39 loadAddr.add(v.Args[1].ID)
40 }
41 }
42 }
43 po := f.postorder()
44 for {
45 n := loadAddr.size()
46 for _, b := range po {
47 for i := len(b.Values) - 1; i >= 0; i-- {
48 v := b.Values[i]
49 if !loadAddr.contains(v.ID) {
50 continue
51 }
52 for _, a := range v.Args {
53 if a.Type.IsInteger() || a.Type.IsPtr() || a.Type.IsUnsafePtr() {
54 loadAddr.add(a.ID)
55 }
56 }
57 }
58 }
59 if loadAddr.size() == n {
60 break
61 }
62 }
63
64 change := true
65 for change {
66 change = false
67 for _, b := range f.Blocks {
68 change = elimIf(f, loadAddr, b) || elimIfElse(f, loadAddr, b) || change
69 }
70 }
71 }
72
73 func canCondSelect(v *Value, arch string, loadAddr *sparseSet) bool {
74 if loadAddr.contains(v.ID) {
75
76
77
78
79
80
81
82 return false
83 }
84
85 switch {
86 case v.Type.Size() > v.Block.Func.Config.RegSize:
87 return false
88 case v.Type.IsPtrShaped():
89 return true
90 case v.Type.IsInteger():
91 if arch == "amd64" && v.Type.Size() < 2 {
92
93 return false
94 }
95 return true
96 default:
97 return false
98 }
99 }
100
101
102
103
104 func elimIf(f *Func, loadAddr *sparseSet, dom *Block) bool {
105
106
107
108 if dom.Kind != BlockIf || dom.Likely != BranchUnknown {
109 return false
110 }
111 var simple, post *Block
112 for i := range dom.Succs {
113 bb, other := dom.Succs[i].Block(), dom.Succs[i^1].Block()
114 if isLeafPlain(bb) && bb.Succs[0].Block() == other {
115 simple = bb
116 post = other
117 break
118 }
119 }
120 if simple == nil || len(post.Preds) != 2 || post == dom {
121 return false
122 }
123
124
125
126
127
128
129
130 hasphis := false
131 for _, v := range post.Values {
132 if v.Op == OpPhi {
133 hasphis = true
134 if !canCondSelect(v, f.Config.arch, loadAddr) {
135 return false
136 }
137 }
138 }
139 if !hasphis {
140 return false
141 }
142
143
144
145
146
147 const maxfuseinsts = 2
148
149 if len(simple.Values) > maxfuseinsts || !allTrivial(simple) {
150 return false
151 }
152
153
154 swap := (post.Preds[0].Block() == dom) != (dom.Succs[0].Block() == post)
155 for _, v := range post.Values {
156 if v.Op != OpPhi {
157 continue
158 }
159 v.Op = OpCondSelect
160 if swap {
161 v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
162 }
163 v.AddArg(dom.Control)
164 }
165
166
167
168 dom.Kind = post.Kind
169 dom.SetControl(post.Control)
170 dom.Aux = post.Aux
171 dom.Succs = append(dom.Succs[:0], post.Succs...)
172 for i := range dom.Succs {
173 e := dom.Succs[i]
174 e.b.Preds[e.i].b = dom
175 }
176
177 for i := range simple.Values {
178 simple.Values[i].Block = dom
179 }
180 for i := range post.Values {
181 post.Values[i].Block = dom
182 }
183 dom.Values = append(dom.Values, simple.Values...)
184 dom.Values = append(dom.Values, post.Values...)
185
186
187 clobberBlock(post)
188 clobberBlock(simple)
189
190 f.invalidateCFG()
191 return true
192 }
193
194
195 func isLeafPlain(b *Block) bool {
196 return b.Kind == BlockPlain && len(b.Preds) == 1
197 }
198
199 func clobberBlock(b *Block) {
200 b.Values = nil
201 b.Preds = nil
202 b.Succs = nil
203 b.Aux = nil
204 b.SetControl(nil)
205 b.Likely = BranchUnknown
206 b.Kind = BlockInvalid
207 }
208
209
210
211
212 func elimIfElse(f *Func, loadAddr *sparseSet, b *Block) bool {
213
214
215
216 if b.Kind != BlockIf || b.Likely != BranchUnknown {
217 return false
218 }
219 yes, no := b.Succs[0].Block(), b.Succs[1].Block()
220 if !isLeafPlain(yes) || len(yes.Values) > 1 || !allTrivial(yes) {
221 return false
222 }
223 if !isLeafPlain(no) || len(no.Values) > 1 || !allTrivial(no) {
224 return false
225 }
226 if b.Succs[0].Block().Succs[0].Block() != b.Succs[1].Block().Succs[0].Block() {
227 return false
228 }
229
230 post := b.Succs[0].Block().Succs[0].Block()
231 if len(post.Preds) != 2 || post == b {
232 return false
233 }
234 hasphis := false
235 for _, v := range post.Values {
236 if v.Op == OpPhi {
237 hasphis = true
238 if !canCondSelect(v, f.Config.arch, loadAddr) {
239 return false
240 }
241 }
242 }
243 if !hasphis {
244 return false
245 }
246
247
248 if !shouldElimIfElse(no, yes, post, f.Config.arch) {
249 return false
250 }
251
252
253 swap := post.Preds[0].Block() != b.Succs[0].Block()
254 for _, v := range post.Values {
255 if v.Op != OpPhi {
256 continue
257 }
258 v.Op = OpCondSelect
259 if swap {
260 v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
261 }
262 v.AddArg(b.Control)
263 }
264
265
266
267 b.Kind = post.Kind
268 b.SetControl(post.Control)
269 b.Aux = post.Aux
270 b.Succs = append(b.Succs[:0], post.Succs...)
271 for i := range b.Succs {
272 e := b.Succs[i]
273 e.b.Preds[e.i].b = b
274 }
275 for i := range post.Values {
276 post.Values[i].Block = b
277 }
278 for i := range yes.Values {
279 yes.Values[i].Block = b
280 }
281 for i := range no.Values {
282 no.Values[i].Block = b
283 }
284 b.Values = append(b.Values, yes.Values...)
285 b.Values = append(b.Values, no.Values...)
286 b.Values = append(b.Values, post.Values...)
287
288
289 clobberBlock(yes)
290 clobberBlock(no)
291 clobberBlock(post)
292
293 f.invalidateCFG()
294 return true
295 }
296
297
298
299 func shouldElimIfElse(no, yes, post *Block, arch string) bool {
300 switch arch {
301 default:
302 return true
303 case "amd64":
304 const maxcost = 2
305 phi := 0
306 other := 0
307 for _, v := range post.Values {
308 if v.Op == OpPhi {
309
310
311 phi++
312 }
313 for _, x := range v.Args {
314 if x.Block == no || x.Block == yes {
315 other++
316 }
317 }
318 }
319 cost := phi * 1
320 if phi > 1 {
321
322
323
324 cost += other * 1
325 }
326 return cost < maxcost
327 }
328 }
329
330 func allTrivial(b *Block) bool {
331
332
333 for _, v := range b.Values {
334 if v.Op == OpPhi || isDivMod(v.Op) || v.Type.IsMemory() ||
335 v.MemoryArg() != nil || opcodeTable[v.Op].hasSideEffects {
336 return false
337 }
338 }
339 return true
340 }
341
342 func isDivMod(op Op) bool {
343 switch op {
344 case OpDiv8, OpDiv8u, OpDiv16, OpDiv16u,
345 OpDiv32, OpDiv32u, OpDiv64, OpDiv64u, OpDiv128u,
346 OpDiv32F, OpDiv64F,
347 OpMod8, OpMod8u, OpMod16, OpMod16u,
348 OpMod32, OpMod32u, OpMod64, OpMod64u:
349 return true
350 default:
351 return false
352 }
353 }
354
View as plain text