1
2
3
4
5 package ssacompile
6
7 import (
8 "cmd/compile/internal/ssa"
9 blockpkg "cmd/compile/internal/ssa/block"
10 "cmd/compile/internal/ssa/ssaop"
11 "cmd/compile/internal/ssarewrite/rewritegeneric"
12 )
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 const (
47 top int8 = iota
48 constant
49 bottom
50 )
51
52 type lattice struct {
53 tag int8
54 val *ssa.Value
55 }
56
57 type worklist struct {
58 f *ssa.Func
59 edges []ssa.Edge
60 inUses *ssa.SparseSet
61 uses []*ssa.Value
62 visited map[ssa.Edge]bool
63 latticeCells map[*ssa.Value]lattice
64 defUse map[*ssa.Value][]*ssa.Value
65 defBlock map[*ssa.Value][]*ssa.Block
66 visitedBlock []bool
67 }
68
69
70
71
72 func sccp(f *ssa.Func) {
73 var t worklist
74 t.f = f
75 t.edges = make([]ssa.Edge, 0)
76 t.visited = make(map[ssa.Edge]bool)
77 t.edges = append(t.edges, ssa.Edge{B: f.Entry, I: 0})
78 t.defUse = make(map[*ssa.Value][]*ssa.Value)
79 t.defBlock = make(map[*ssa.Value][]*ssa.Block)
80 t.latticeCells = make(map[*ssa.Value]lattice)
81 t.visitedBlock = f.Cache.AllocBoolSlice(f.NumBlocks())
82 t.inUses = f.NewSparseSet(f.NumValues())
83 defer f.RetSparseSet(t.inUses)
84 defer f.Cache.FreeBoolSlice(t.visitedBlock)
85
86
87 t.buildDefUses()
88
89
90 for {
91 if len(t.edges) > 0 {
92 edge := t.edges[0]
93 t.edges = t.edges[1:]
94 if _, exist := t.visited[edge]; !exist {
95 dest := edge.B
96 destVisited := t.visitedBlock[dest.ID]
97
98
99 t.visited[edge] = true
100 t.visitedBlock[dest.ID] = true
101 for _, val := range dest.Values {
102 if val.Op == ssaop.OpPhi || !destVisited {
103 t.visitValue(val)
104 }
105 }
106
107
108 if !destVisited {
109 t.propagate(dest)
110 }
111 }
112 continue
113 }
114 if len(t.uses) > 0 {
115 use := t.uses[0]
116 t.uses = t.uses[1:]
117 t.inUses.Remove(use.ID)
118 t.visitValue(use)
119 continue
120 }
121 break
122 }
123
124
125 constCnt, rewireCnt := t.replaceConst()
126 if f.Pass.Debug > 0 {
127 if constCnt > 0 || rewireCnt > 0 {
128 f.Warnl(f.Entry.Pos, "Phase SCCP for %v : %v constants, %v dce", f.Name, constCnt, rewireCnt)
129 }
130 }
131 }
132
133 func equals(a, b lattice) bool {
134 if a == b {
135
136 return true
137 }
138 if a.tag != b.tag {
139 return false
140 }
141 if a.tag == constant {
142
143
144 v1 := a.val
145 v2 := b.val
146 if v1.Op == v2.Op && v1.AuxInt == v2.AuxInt {
147 return true
148 } else {
149 return false
150 }
151 }
152 return true
153 }
154
155
156
157 func possibleConst(val *ssa.Value) bool {
158 if isConst(val) {
159 return true
160 }
161 switch val.Op {
162 case ssaop.OpCopy:
163 return true
164 case ssaop.OpPhi:
165 return true
166 case
167
168 ssaop.OpNeg8, ssaop.OpNeg16, ssaop.OpNeg32, ssaop.OpNeg64, ssaop.OpNeg32F, ssaop.OpNeg64F,
169 ssaop.OpCom8, ssaop.OpCom16, ssaop.OpCom32, ssaop.OpCom64,
170
171 ssaop.OpFloor, ssaop.OpCeil, ssaop.OpTrunc, ssaop.OpRoundToEven, ssaop.OpSqrt,
172
173 ssaop.OpTrunc16to8, ssaop.OpTrunc32to8, ssaop.OpTrunc32to16, ssaop.OpTrunc64to8,
174 ssaop.OpTrunc64to16, ssaop.OpTrunc64to32, ssaop.OpCvt32to32F, ssaop.OpCvt32to64F,
175 ssaop.OpCvt64to32F, ssaop.OpCvt64to64F, ssaop.OpCvt32Fto32, ssaop.OpCvt32Fto64,
176 ssaop.OpCvt64Fto32, ssaop.OpCvt64Fto64, ssaop.OpCvt32Fto64F, ssaop.OpCvt64Fto32F,
177 ssaop.OpCvtBoolToUint8,
178 ssaop.OpZeroExt8to16, ssaop.OpZeroExt8to32, ssaop.OpZeroExt8to64, ssaop.OpZeroExt16to32,
179 ssaop.OpZeroExt16to64, ssaop.OpZeroExt32to64, ssaop.OpSignExt8to16, ssaop.OpSignExt8to32,
180 ssaop.OpSignExt8to64, ssaop.OpSignExt16to32, ssaop.OpSignExt16to64, ssaop.OpSignExt32to64,
181
182 ssaop.OpCtz8, ssaop.OpCtz16, ssaop.OpCtz32, ssaop.OpCtz64,
183
184 ssaop.OpSlicemask,
185
186 ssaop.OpIsNonNil,
187
188 ssaop.OpNot:
189 return true
190 case
191
192 ssaop.OpAdd64, ssaop.OpAdd32, ssaop.OpAdd16, ssaop.OpAdd8,
193 ssaop.OpAdd32F, ssaop.OpAdd64F,
194
195 ssaop.OpSub64, ssaop.OpSub32, ssaop.OpSub16, ssaop.OpSub8,
196 ssaop.OpSub32F, ssaop.OpSub64F,
197
198 ssaop.OpMul64, ssaop.OpMul32, ssaop.OpMul16, ssaop.OpMul8,
199 ssaop.OpMul32F, ssaop.OpMul64F,
200
201 ssaop.OpDiv32F, ssaop.OpDiv64F,
202 ssaop.OpDiv8, ssaop.OpDiv16, ssaop.OpDiv32, ssaop.OpDiv64,
203 ssaop.OpDiv8u, ssaop.OpDiv16u, ssaop.OpDiv32u, ssaop.OpDiv64u,
204 ssaop.OpMod8, ssaop.OpMod16, ssaop.OpMod32, ssaop.OpMod64,
205 ssaop.OpMod8u, ssaop.OpMod16u, ssaop.OpMod32u, ssaop.OpMod64u,
206
207 ssaop.OpEq64, ssaop.OpEq32, ssaop.OpEq16, ssaop.OpEq8,
208 ssaop.OpEq32F, ssaop.OpEq64F,
209 ssaop.OpLess64, ssaop.OpLess32, ssaop.OpLess16, ssaop.OpLess8,
210 ssaop.OpLess64U, ssaop.OpLess32U, ssaop.OpLess16U, ssaop.OpLess8U,
211 ssaop.OpLess32F, ssaop.OpLess64F,
212 ssaop.OpLeq64, ssaop.OpLeq32, ssaop.OpLeq16, ssaop.OpLeq8,
213 ssaop.OpLeq64U, ssaop.OpLeq32U, ssaop.OpLeq16U, ssaop.OpLeq8U,
214 ssaop.OpLeq32F, ssaop.OpLeq64F,
215 ssaop.OpEqB, ssaop.OpNeqB,
216
217 ssaop.OpLsh64x64, ssaop.OpRsh64x64, ssaop.OpRsh64Ux64, ssaop.OpLsh32x64,
218 ssaop.OpRsh32x64, ssaop.OpRsh32Ux64, ssaop.OpLsh16x64, ssaop.OpRsh16x64,
219 ssaop.OpRsh16Ux64, ssaop.OpLsh8x64, ssaop.OpRsh8x64, ssaop.OpRsh8Ux64,
220
221 ssaop.OpIsInBounds, ssaop.OpIsSliceInBounds,
222
223 ssaop.OpAnd8, ssaop.OpAnd16, ssaop.OpAnd32, ssaop.OpAnd64,
224 ssaop.OpOr8, ssaop.OpOr16, ssaop.OpOr32, ssaop.OpOr64,
225 ssaop.OpXor8, ssaop.OpXor16, ssaop.OpXor32, ssaop.OpXor64:
226 return true
227 default:
228 return false
229 }
230 }
231
232 func (t *worklist) getLatticeCell(val *ssa.Value) lattice {
233 if !possibleConst(val) {
234
235 return lattice{bottom, nil}
236 }
237 lt, exist := t.latticeCells[val]
238 if !exist {
239 return lattice{top, nil}
240 }
241 return lt
242 }
243
244 func isConst(val *ssa.Value) bool {
245 switch val.Op {
246 case ssaop.OpConst64, ssaop.OpConst32, ssaop.OpConst16, ssaop.OpConst8,
247 ssaop.OpConstBool, ssaop.OpConst32F, ssaop.OpConst64F:
248 return true
249 default:
250 return false
251 }
252 }
253
254
255
256
257
258
259 func (t *worklist) buildDefUses() {
260 for _, block := range t.f.Blocks {
261 for _, val := range block.Values {
262 for _, arg := range val.Args {
263
264 if possibleConst(arg) && possibleConst(val) {
265
266 if arg == val {
267 continue
268 }
269 if _, exist := t.defUse[arg]; !exist {
270 t.defUse[arg] = make([]*ssa.Value, 0, arg.Uses)
271 }
272 t.defUse[arg] = append(t.defUse[arg], val)
273 }
274 }
275 }
276 for _, ctl := range block.ControlValues() {
277
278 if possibleConst(ctl) {
279 t.defBlock[ctl] = append(t.defBlock[ctl], block)
280 }
281 }
282 }
283 }
284
285
286 func (t *worklist) addUses(val *ssa.Value) {
287 for _, use := range t.defUse[val] {
288
289 useLt := t.getLatticeCell(use)
290 if useLt.tag == bottom {
291 continue
292 }
293
294 if !t.inUses.Contains(use.ID) {
295 t.inUses.Add(use.ID)
296 t.uses = append(t.uses, use)
297 }
298 }
299 for _, block := range t.defBlock[val] {
300 if t.visitedBlock[block.ID] {
301 t.propagate(block)
302 }
303 }
304 }
305
306
307 func (t *worklist) meet(val *ssa.Value) lattice {
308 optimisticLt := lattice{top, nil}
309 for i := 0; i < len(val.Args); i++ {
310 edge := ssa.Edge{B: val.Block, I: i}
311
312
313
314
315
316 if _, exist := t.visited[edge]; exist {
317 lt := t.getLatticeCell(val.Args[i])
318 if lt.tag == constant {
319 if optimisticLt.tag == top {
320 optimisticLt = lt
321 } else {
322 if !equals(optimisticLt, lt) {
323
324 return lattice{bottom, nil}
325 }
326 }
327 } else if lt.tag == bottom {
328
329 return lattice{bottom, nil}
330 } else {
331
332 }
333 } else {
334
335 }
336 }
337
338
339 return optimisticLt
340 }
341
342 func computeLattice(f *ssa.Func, val *ssa.Value, args ...*ssa.Value) lattice {
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368 constValue := f.NewValue(val.Op, val.Type, f.Entry, val.Pos)
369 constValue.AddArgs(args...)
370 matched := rewritegeneric.RewriteValue(constValue)
371 if matched {
372 if isConst(constValue) {
373 return lattice{constant, constValue}
374 }
375 }
376
377
378
379 constValue.Reset(ssaop.OpInvalid)
380 return lattice{bottom, nil}
381 }
382
383 func (t *worklist) visitValue(val *ssa.Value) {
384
385 if !possibleConst(val) {
386 return
387 }
388
389
390 oldLt := t.getLatticeCell(val)
391 if oldLt.tag == bottom {
392 return
393 }
394
395
396 defer func() {
397 newLt := t.getLatticeCell(val)
398 if !equals(newLt, oldLt) {
399 if oldLt.tag > newLt.tag {
400 t.f.Fatalf("Must lower lattice\n")
401 }
402 t.addUses(val)
403 }
404 }()
405
406 switch val.Op {
407
408 case ssaop.OpConst64, ssaop.OpConst32, ssaop.OpConst16, ssaop.OpConst8,
409 ssaop.OpConstBool, ssaop.OpConst32F, ssaop.OpConst64F:
410 t.latticeCells[val] = lattice{constant, val}
411
412 case ssaop.OpCopy:
413 t.latticeCells[val] = t.getLatticeCell(val.Args[0])
414
415 case ssaop.OpPhi:
416 t.latticeCells[val] = t.meet(val)
417
418 case
419
420 ssaop.OpNeg8, ssaop.OpNeg16, ssaop.OpNeg32, ssaop.OpNeg64, ssaop.OpNeg32F, ssaop.OpNeg64F,
421 ssaop.OpCom8, ssaop.OpCom16, ssaop.OpCom32, ssaop.OpCom64,
422
423 ssaop.OpFloor, ssaop.OpCeil, ssaop.OpTrunc, ssaop.OpRoundToEven, ssaop.OpSqrt,
424
425 ssaop.OpTrunc16to8, ssaop.OpTrunc32to8, ssaop.OpTrunc32to16, ssaop.OpTrunc64to8,
426 ssaop.OpTrunc64to16, ssaop.OpTrunc64to32, ssaop.OpCvt32to32F, ssaop.OpCvt32to64F,
427 ssaop.OpCvt64to32F, ssaop.OpCvt64to64F, ssaop.OpCvt32Fto32, ssaop.OpCvt32Fto64,
428 ssaop.OpCvt64Fto32, ssaop.OpCvt64Fto64, ssaop.OpCvt32Fto64F, ssaop.OpCvt64Fto32F,
429 ssaop.OpCvtBoolToUint8,
430 ssaop.OpZeroExt8to16, ssaop.OpZeroExt8to32, ssaop.OpZeroExt8to64, ssaop.OpZeroExt16to32,
431 ssaop.OpZeroExt16to64, ssaop.OpZeroExt32to64, ssaop.OpSignExt8to16, ssaop.OpSignExt8to32,
432 ssaop.OpSignExt8to64, ssaop.OpSignExt16to32, ssaop.OpSignExt16to64, ssaop.OpSignExt32to64,
433
434 ssaop.OpCtz8, ssaop.OpCtz16, ssaop.OpCtz32, ssaop.OpCtz64,
435
436 ssaop.OpSlicemask,
437
438 ssaop.OpIsNonNil,
439
440 ssaop.OpNot:
441 lt1 := t.getLatticeCell(val.Args[0])
442
443 if lt1.tag == constant {
444
445 t.latticeCells[val] = computeLattice(t.f, val, lt1.val)
446 } else {
447 t.latticeCells[val] = lattice{lt1.tag, nil}
448 }
449
450 case
451
452 ssaop.OpAdd64, ssaop.OpAdd32, ssaop.OpAdd16, ssaop.OpAdd8,
453 ssaop.OpAdd32F, ssaop.OpAdd64F,
454
455 ssaop.OpSub64, ssaop.OpSub32, ssaop.OpSub16, ssaop.OpSub8,
456 ssaop.OpSub32F, ssaop.OpSub64F,
457
458 ssaop.OpMul64, ssaop.OpMul32, ssaop.OpMul16, ssaop.OpMul8,
459 ssaop.OpMul32F, ssaop.OpMul64F,
460
461 ssaop.OpDiv32F, ssaop.OpDiv64F,
462 ssaop.OpDiv8, ssaop.OpDiv16, ssaop.OpDiv32, ssaop.OpDiv64,
463 ssaop.OpDiv8u, ssaop.OpDiv16u, ssaop.OpDiv32u, ssaop.OpDiv64u,
464
465 ssaop.OpMod8, ssaop.OpMod16, ssaop.OpMod32, ssaop.OpMod64,
466 ssaop.OpMod8u, ssaop.OpMod16u, ssaop.OpMod32u, ssaop.OpMod64u,
467
468 ssaop.OpEq64, ssaop.OpEq32, ssaop.OpEq16, ssaop.OpEq8,
469 ssaop.OpEq32F, ssaop.OpEq64F,
470 ssaop.OpLess64, ssaop.OpLess32, ssaop.OpLess16, ssaop.OpLess8,
471 ssaop.OpLess64U, ssaop.OpLess32U, ssaop.OpLess16U, ssaop.OpLess8U,
472 ssaop.OpLess32F, ssaop.OpLess64F,
473 ssaop.OpLeq64, ssaop.OpLeq32, ssaop.OpLeq16, ssaop.OpLeq8,
474 ssaop.OpLeq64U, ssaop.OpLeq32U, ssaop.OpLeq16U, ssaop.OpLeq8U,
475 ssaop.OpLeq32F, ssaop.OpLeq64F,
476 ssaop.OpEqB, ssaop.OpNeqB,
477
478 ssaop.OpLsh64x64, ssaop.OpRsh64x64, ssaop.OpRsh64Ux64, ssaop.OpLsh32x64,
479 ssaop.OpRsh32x64, ssaop.OpRsh32Ux64, ssaop.OpLsh16x64, ssaop.OpRsh16x64,
480 ssaop.OpRsh16Ux64, ssaop.OpLsh8x64, ssaop.OpRsh8x64, ssaop.OpRsh8Ux64,
481
482 ssaop.OpIsInBounds, ssaop.OpIsSliceInBounds,
483
484 ssaop.OpAnd8, ssaop.OpAnd16, ssaop.OpAnd32, ssaop.OpAnd64,
485 ssaop.OpOr8, ssaop.OpOr16, ssaop.OpOr32, ssaop.OpOr64,
486 ssaop.OpXor8, ssaop.OpXor16, ssaop.OpXor32, ssaop.OpXor64:
487 lt1 := t.getLatticeCell(val.Args[0])
488 lt2 := t.getLatticeCell(val.Args[1])
489
490 if lt1.tag == constant && lt2.tag == constant {
491
492 t.latticeCells[val] = computeLattice(t.f, val, lt1.val, lt2.val)
493 } else {
494 if lt1.tag == bottom || lt2.tag == bottom {
495 t.latticeCells[val] = lattice{bottom, nil}
496 } else {
497 t.latticeCells[val] = lattice{top, nil}
498 }
499 }
500 default:
501
502 }
503 }
504
505
506
507
508 func (t *worklist) propagate(block *ssa.Block) {
509 switch block.Kind {
510 case blockpkg.BlockExit, blockpkg.BlockRet, blockpkg.BlockRetJmp, blockpkg.BlockInvalid:
511
512 break
513 case blockpkg.BlockDefer:
514
515 t.edges = append(t.edges, block.Succs...)
516 case blockpkg.BlockFirst:
517 fallthrough
518 case blockpkg.BlockPlain:
519 t.edges = append(t.edges, block.Succs[0])
520 case blockpkg.BlockIf, blockpkg.BlockJumpTable:
521 cond := block.ControlValues()[0]
522 condLattice := t.getLatticeCell(cond)
523 if condLattice.tag == bottom {
524
525 t.edges = append(t.edges, block.Succs...)
526 } else if condLattice.tag == constant {
527
528 var branchIdx int64
529 if block.Kind == blockpkg.BlockIf {
530 branchIdx = 1 - condLattice.val.AuxInt
531 } else {
532 branchIdx = condLattice.val.AuxInt
533 if branchIdx < 0 || branchIdx >= int64(len(block.Succs)) {
534
535 break
536 }
537 }
538 t.edges = append(t.edges, block.Succs[branchIdx])
539 } else {
540
541 }
542 default:
543 t.f.Fatalf("All kind of block should be processed above.")
544 }
545 }
546
547
548
549
550 func rewireSuccessor(block *ssa.Block, constVal *ssa.Value) bool {
551 switch block.Kind {
552 case blockpkg.BlockIf:
553 block.RemoveEdge(int(constVal.AuxInt))
554 block.Kind = blockpkg.BlockPlain
555 block.Likely = ssa.BranchUnknown
556 block.ResetControls()
557 return true
558 case blockpkg.BlockJumpTable:
559
560 idx := int(constVal.AuxInt)
561 if idx < 0 || idx >= len(block.Succs) {
562
563
564
565
566 return false
567 }
568 block.SwapSuccessorsByIdx(0, idx)
569 for len(block.Succs) > 1 {
570 block.RemoveEdge(1)
571 }
572 block.Kind = blockpkg.BlockPlain
573 block.Likely = ssa.BranchUnknown
574 block.ResetControls()
575 return true
576 default:
577 return false
578 }
579 }
580
581
582
583 func (t *worklist) replaceConst() (int, int) {
584 constCnt, rewireCnt := 0, 0
585 for val, lt := range t.latticeCells {
586 if lt.tag == constant {
587 if !isConst(val) {
588 if t.f.Pass.Debug > 0 {
589 t.f.Warnl(val.Pos, "Replace %v with %v", val.LongString(), lt.val.LongString())
590 }
591 val.Reset(lt.val.Op)
592 val.AuxInt = lt.val.AuxInt
593 constCnt++
594 }
595
596 ctrlBlock := t.defBlock[val]
597 for _, block := range ctrlBlock {
598 if rewireSuccessor(block, lt.val) {
599 rewireCnt++
600 if t.f.Pass.Debug > 0 {
601 t.f.Warnl(block.Pos, "Rewire %v %v successors", block.Kind, block)
602 }
603 }
604 }
605 }
606 }
607 return constCnt, rewireCnt
608 }
609
View as plain text