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/compile/internal/types"
14 )
15
16
17
18
19 type edgeMem struct {
20 e ssa.Edge
21 m *ssa.Value
22 }
23
24
25
26
27
28 type rewriteTarget struct {
29 v *ssa.Value
30 i int
31 }
32
33 type rewrite struct {
34 before, after *ssa.Value
35 rewrites []rewriteTarget
36 }
37
38 func (r *rewrite) String() string {
39 s := "\n\tbefore=" + r.before.String() + ", after=" + r.after.String()
40 for _, rw := range r.rewrites {
41 s += ", (i=" + fmt.Sprint(rw.i) + ", v=" + rw.v.LongString() + ")"
42 }
43 s += "\n"
44 return s
45 }
46
47
48 func insertLoopReschedChecks(f *ssa.Func) {
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 if f.NoSplit {
69 return
70 }
71
72 backedges := backedges(f)
73 if len(backedges) == 0 {
74 return
75 }
76
77 lastMems := findLastMems(f)
78 defer f.Cache.FreeValueSlice(lastMems)
79
80 idom := f.Idom()
81 po := f.Postorder()
82 sdom := f.Sdom()
83
84 if f.Pass.Debug > 1 {
85 fmt.Printf("before %s = %s\n", f.Name, sdom.Treestructure(f.Entry))
86 }
87
88 tofixBackedges := []edgeMem{}
89
90 for _, e := range backedges {
91 tofixBackedges = append(tofixBackedges, edgeMem{e, nil})
92 }
93
94
95 if lastMems[f.Entry.ID] == nil {
96 lastMems[f.Entry.ID] = f.Entry.NewValue0(f.Entry.Pos, ssaop.OpInitMem, types.TypeMem)
97 }
98
99 memDefsAtBlockEnds := f.Cache.AllocValueSlice(f.NumBlocks())
100 defer f.Cache.FreeValueSlice(memDefsAtBlockEnds)
101
102
103 for i := len(po) - 1; i >= 0; i-- {
104 b := po[i]
105 mem := lastMems[b.ID]
106 for j := 0; mem == nil; j++ {
107
108 mem = memDefsAtBlockEnds[b.Preds[j].B.ID]
109 }
110 memDefsAtBlockEnds[b.ID] = mem
111 if f.Pass.Debug > 2 {
112 fmt.Printf("memDefsAtBlockEnds[%s] = %s\n", b, mem)
113 }
114 }
115
116
117 newmemphis := make(map[*ssa.Block]rewrite)
118
119
120 for i, emc := range tofixBackedges {
121 e := emc.e
122 h := e.B
123
124
125 var headerMemPhi *ssa.Value
126
127 for _, v := range h.Values {
128 if v.Op == ssaop.OpPhi && v.Type.IsMemory() {
129 headerMemPhi = v
130 }
131 }
132
133 if headerMemPhi == nil {
134
135 mem0 := memDefsAtBlockEnds[idom[h.ID].ID]
136 headerMemPhi = newPhiFor(h, mem0)
137 newmemphis[h] = rewrite{before: mem0, after: headerMemPhi}
138 addDFphis(mem0, h, h, f, memDefsAtBlockEnds, newmemphis, sdom)
139
140 }
141 tofixBackedges[i].m = headerMemPhi
142
143 }
144 if f.Pass.Debug > 0 {
145 for b, r := range newmemphis {
146 fmt.Printf("before b=%s, rewrite=%s\n", b, r.String())
147 }
148 }
149
150
151
152 dfPhiTargets := make(map[rewriteTarget]bool)
153
154 rewriteNewPhis(f.Entry, f.Entry, f, memDefsAtBlockEnds, newmemphis, dfPhiTargets, sdom)
155
156 if f.Pass.Debug > 0 {
157 for b, r := range newmemphis {
158 fmt.Printf("after b=%s, rewrite=%s\n", b, r.String())
159 }
160 }
161
162
163 for _, r := range newmemphis {
164 for _, rw := range r.rewrites {
165 rw.v.SetArg(rw.i, r.after)
166 }
167 }
168
169
170 for _, emc := range tofixBackedges {
171 e := emc.e
172 headerMemPhi := emc.m
173 h := e.B
174 i := e.I
175 p := h.Preds[i]
176 bb := p.B
177 mem0 := headerMemPhi.Args[i]
178
179
180
181 likely := ssa.BranchLikely
182 if p.I != 0 {
183 likely = ssa.BranchUnlikely
184 }
185 if bb.Kind != block.BlockPlain {
186 bb.Likely = likely
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 test := f.NewBlock(block.BlockIf)
215 sched := f.NewBlock(block.BlockPlain)
216
217 test.Pos = bb.Pos
218 sched.Pos = bb.Pos
219
220
221
222
223 cfgtypes := &f.Config.Types
224 pt := cfgtypes.Uintptr
225 g := test.NewValue1(bb.Pos, ssaop.OpGetG, pt, mem0)
226 sp := test.NewValue0(bb.Pos, ssaop.OpSP, pt)
227 cmpOp := ssaop.OpLess64U
228 if pt.Size() == 4 {
229 cmpOp = ssaop.OpLess32U
230 }
231 limaddr := test.NewValue1I(bb.Pos, ssaop.OpOffPtr, pt, 2*pt.Size(), g)
232 lim := test.NewValue2(bb.Pos, ssaop.OpLoad, pt, limaddr, mem0)
233 cmp := test.NewValue2(bb.Pos, cmpOp, cfgtypes.Bool, sp, lim)
234 test.SetControl(cmp)
235
236
237 test.AddEdgeTo(sched)
238
239
240
241
242 test.Succs = append(test.Succs, ssa.Edge{B: h, I: i})
243 h.Preds[i] = ssa.Edge{B: test, I: 1}
244 headerMemPhi.SetArg(i, mem0)
245
246 test.Likely = ssa.BranchUnlikely
247
248
249
250
251 resched := f.Fe.Syslook("goschedguarded")
252 call := sched.NewValue1A(bb.Pos, ssaop.OpStaticCall, types.TypeResultMem, ssa.StaticAuxCall(resched, bb.Func.ABIDefault.ABIAnalyzeTypes(nil, nil)), mem0)
253 mem1 := sched.NewValue1I(bb.Pos, ssaop.OpSelectN, types.TypeMem, 0, call)
254 sched.AddEdgeTo(h)
255 headerMemPhi.AddArg(mem1)
256
257 bb.Succs[p.I] = ssa.Edge{B: test, I: 0}
258 test.Preds = append(test.Preds, ssa.Edge{B: bb, I: p.I})
259
260
261
262
263 for _, v := range h.Values {
264 if v.Op == ssaop.OpPhi && v != headerMemPhi {
265 v.AddArg(v.Args[i])
266 }
267 }
268 }
269
270 f.InvalidateCFG()
271
272 if f.Pass.Debug > 1 {
273 sdom = ssa.NewSparseTree(f, f.Idom())
274 fmt.Printf("after %s = %s\n", f.Name, sdom.Treestructure(f.Entry))
275 }
276 }
277
278
279
280 func newPhiFor(b *ssa.Block, v *ssa.Value) *ssa.Value {
281 phiV := b.NewValue0(b.Pos, ssaop.OpPhi, v.Type)
282
283 for range b.Preds {
284 phiV.AddArg(v)
285 }
286 return phiV
287 }
288
289
290
291
292
293
294
295
296
297 func rewriteNewPhis(h, b *ssa.Block, f *ssa.Func, defsForUses []*ssa.Value, newphis map[*ssa.Block]rewrite, dfPhiTargets map[rewriteTarget]bool, sdom ssa.SparseTree) {
298
299 if _, ok := newphis[b]; ok {
300 h = b
301 }
302 change := newphis[h]
303 x := change.before
304 y := change.after
305
306
307 if x != nil {
308 p := &change.rewrites
309 for _, v := range b.Values {
310 if v == y {
311 continue
312 }
313 for i, w := range v.Args {
314 if w != x {
315 continue
316 }
317 tgt := rewriteTarget{v, i}
318
319
320
321
322 if dfPhiTargets[tgt] {
323 continue
324 }
325 *p = append(*p, tgt)
326 if f.Pass.Debug > 1 {
327 fmt.Printf("added block target for h=%v, b=%v, x=%v, y=%v, tgt.v=%s, tgt.i=%d\n",
328 h, b, x, y, v, i)
329 }
330 }
331 }
332
333
334
335
336
337
338 if dfu := defsForUses[b.ID]; dfu != nil && dfu.Block != b {
339 for _, e := range b.Succs {
340 s := e.B
341
342 for _, v := range s.Values {
343 if v.Op == ssaop.OpPhi && v.Args[e.I] == x {
344 tgt := rewriteTarget{v, e.I}
345 *p = append(*p, tgt)
346 dfPhiTargets[tgt] = true
347 if f.Pass.Debug > 1 {
348 fmt.Printf("added phi target for h=%v, b=%v, s=%v, x=%v, y=%v, tgt.v=%s, tgt.i=%d\n",
349 h, b, s, x, y, v.LongString(), e.I)
350 }
351 break
352 }
353 }
354 }
355 }
356 newphis[h] = change
357 }
358
359 for c := sdom[b.ID].Child; c != nil; c = sdom[c.ID].Sibling {
360 rewriteNewPhis(h, c, f, defsForUses, newphis, dfPhiTargets, sdom)
361 }
362 }
363
364
365
366
367
368
369
370
371 func addDFphis(x *ssa.Value, h, b *ssa.Block, f *ssa.Func, defForUses []*ssa.Value, newphis map[*ssa.Block]rewrite, sdom ssa.SparseTree) {
372 oldv := defForUses[b.ID]
373 if oldv != x {
374 return
375 }
376 idom := f.Idom()
377 outer:
378 for _, e := range b.Succs {
379 s := e.B
380
381 if sdom.IsAncestor(h, s) {
382 continue
383 }
384 if _, ok := newphis[s]; ok {
385 continue
386 }
387 if x != nil {
388 for _, v := range s.Values {
389 if v.Op == ssaop.OpPhi && v.Args[e.I] == x {
390 continue outer
391 }
392 }
393 }
394
395 old := defForUses[idom[s.ID].ID]
396 headerPhi := newPhiFor(s, old)
397
398 newphis[s] = rewrite{before: old, after: headerPhi}
399 addDFphis(old, s, s, f, defForUses, newphis, sdom)
400 }
401 for c := sdom[b.ID].Child; c != nil; c = sdom[c.ID].Sibling {
402 addDFphis(x, h, c, f, defForUses, newphis, sdom)
403 }
404 }
405
406
407 func findLastMems(f *ssa.Func) []*ssa.Value {
408
409 var stores []*ssa.Value
410 lastMems := f.Cache.AllocValueSlice(f.NumBlocks())
411 storeUse := f.NewSparseSet(f.NumValues())
412 defer f.RetSparseSet(storeUse)
413 for _, b := range f.Blocks {
414
415
416 storeUse.Clear()
417 stores = stores[:0]
418 var memPhi *ssa.Value
419 for _, v := range b.Values {
420 if v.Op == ssaop.OpPhi {
421 if v.Type.IsMemory() {
422 memPhi = v
423 }
424 continue
425 }
426 if v.Type.IsMemory() {
427 stores = append(stores, v)
428 for _, a := range v.Args {
429 if a.Block == b && a.Type.IsMemory() {
430 storeUse.Add(a.ID)
431 }
432 }
433 }
434 }
435 if len(stores) == 0 {
436 lastMems[b.ID] = memPhi
437 continue
438 }
439
440
441 var last *ssa.Value
442 for _, v := range stores {
443 if storeUse.Contains(v.ID) {
444 continue
445 }
446 if last != nil {
447 b.Fatalf("two final stores - simultaneous live stores %s %s", last, v)
448 }
449 last = v
450 }
451 if last == nil {
452 b.Fatalf("no last store found - cycle?")
453 }
454
455
456
457
458 if last.Type.IsTuple() {
459 last = b.NewValue1(last.Pos, ssaop.OpSelect1, types.TypeMem, last)
460 } else if last.Type.IsResults() {
461 last = b.NewValue1I(last.Pos, ssaop.OpSelectN, types.TypeMem, int64(last.Type.NumFields()-1), last)
462 }
463
464 lastMems[b.ID] = last
465 }
466 return lastMems
467 }
468
469
470 type markKind uint8
471
472 const (
473 notFound markKind = iota
474 notExplored
475 explored
476 done
477 )
478
479 type backedgesState struct {
480 b *ssa.Block
481 i int
482 }
483
484
485
486 func backedges(f *ssa.Func) []ssa.Edge {
487 edges := []ssa.Edge{}
488 mark := make([]markKind, f.NumBlocks())
489 stack := []backedgesState{}
490
491 mark[f.Entry.ID] = notExplored
492 stack = append(stack, backedgesState{f.Entry, 0})
493
494 for len(stack) > 0 {
495 l := len(stack)
496 x := stack[l-1]
497 if x.i < len(x.b.Succs) {
498 e := x.b.Succs[x.i]
499 stack[l-1].i++
500 s := e.B
501 if mark[s.ID] == notFound {
502 mark[s.ID] = notExplored
503 stack = append(stack, backedgesState{s, 0})
504 } else if mark[s.ID] == notExplored {
505 edges = append(edges, e)
506 }
507 } else {
508 mark[x.b.ID] = done
509 stack = stack[0 : l-1]
510 }
511 }
512 return edges
513 }
514
View as plain text