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