Skip to content

Commit 0db0609

Browse files
committed
review changes
1 parent 5600637 commit 0db0609

File tree

1 file changed

+116
-114
lines changed
  • src/cmd/compile/internal/ssa

1 file changed

+116
-114
lines changed

src/cmd/compile/internal/ssa/sccp.go

Lines changed: 116 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
// It starts with optimistically assuming that all SSA values are initially Top
2626
// and then propagates constant facts only along reachable control flow paths.
2727
// Since some basic blocks are not visited yet, corresponding inputs of phi become
28-
// Top, we use the meet(args...) for phi to compute its lattice.
28+
// Top, we use the meet(phi) to compute its lattice.
2929
//
3030
// Top ∩ any = any
3131
// Bottom ∩ any = Bottom
@@ -60,6 +60,67 @@ type worklist struct {
6060
defBlock map[*Value][]*Block // use blocks of def
6161
}
6262

63+
// sccp stands for sparse conditional constant propagation, it propagates constants
64+
// through CFG conditionally and applies constant folding, constant replacement and
65+
// dead code elimination all together.
66+
func sccp(f *Func) {
67+
var t worklist
68+
t.f = f
69+
t.edges = make([]Edge, 0)
70+
t.visited = make(map[Edge]bool)
71+
t.edges = append(t.edges, Edge{f.Entry, 0})
72+
t.defUse = make(map[*Value][]*Value)
73+
t.defBlock = make(map[*Value][]*Block)
74+
t.latticeCells = make(map[*Value]lattice)
75+
76+
// build it early since we rely heavily on the def-use chain later
77+
t.buildDefUses()
78+
79+
visitedBlock := f.newSparseSet(f.NumBlocks())
80+
defer f.retSparseSet(visitedBlock)
81+
82+
// pick up either an edge or SSA value from worklilst, process it
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 := visitedBlock.contains(dest.ID)
90+
91+
// mark edge as visited
92+
t.visited[edge] = true
93+
visitedBlock.add(dest.ID)
94+
for _, val := range dest.Values {
95+
if val.Op == OpPhi || !destVisited {
96+
t.visitValue(val)
97+
}
98+
}
99+
// propagates constants facts through CFG, taking condition test into account
100+
if !destVisited {
101+
t.propagate(dest)
102+
}
103+
}
104+
continue
105+
}
106+
if len(t.uses) > 0 {
107+
use := t.uses[0]
108+
t.uses = t.uses[1:]
109+
t.visitValue(use)
110+
continue
111+
}
112+
break
113+
}
114+
115+
// apply optimizations based on discovered constants
116+
constCnt, rewireCnt := t.replaceConst()
117+
if f.pass.debug > 0 {
118+
if constCnt > 0 || rewireCnt > 0 {
119+
fmt.Printf("Phase SCCP for %v : %v constants, %v dce\n", f.Name, constCnt, rewireCnt)
120+
}
121+
}
122+
}
123+
63124
// possibleConst checks if Value can be fold to const. For those Values that can never become
64125
// constants(e.g. StaticCall), we don't make futile efforts.
65126
func possibleConst(val *Value) bool {
@@ -68,9 +129,9 @@ func possibleConst(val *Value) bool {
68129
}
69130
switch val.Op {
70131
case OpCopy:
71-
fallthrough
132+
return true
72133
case OpPhi:
73-
fallthrough
134+
return true
74135
case
75136
// negate
76137
OpNeg8, OpNeg16, OpNeg32, OpNeg64, OpNeg32F, OpNeg64F,
@@ -94,7 +155,7 @@ func possibleConst(val *Value) bool {
94155
OpIsNonNil,
95156
// not
96157
OpNot:
97-
fallthrough
158+
return true
98159
case
99160
// add
100161
OpAdd64, OpAdd32, OpAdd16, OpAdd8,
@@ -162,7 +223,8 @@ func isConst(val *Value) bool {
162223
// buildDefUses builds def-use chain for some values early, because once the lattice of
163224
// a value is changed, we need to update lattices of use. But we don't need all uses of
164225
// it, only uses that can become constants would be added into re-visit worklist since
165-
// no matter how many times they are revisited, their lattice remains unchanged(Bottom)
226+
// no matter how many times they are revisited, uses which can't become constants lattice
227+
// remains unchanged, i.e. Bottom.
166228
func (t *worklist) buildDefUses() {
167229
for _, block := range t.f.Blocks {
168230
for _, val := range block.Values {
@@ -177,11 +239,13 @@ func (t *worklist) buildDefUses() {
177239
}
178240
}
179241
for _, ctl := range block.ControlValues() {
180-
// for every control values, find their use blocks
181-
if _, exist := t.defBlock[ctl]; !exist {
182-
t.defBlock[ctl] = make([]*Block, 0)
242+
// for control values that can become constants, find their use blocks
243+
if possibleConst(ctl) {
244+
if _, exist := t.defBlock[ctl]; !exist {
245+
t.defBlock[ctl] = make([]*Block, 0)
246+
}
247+
t.defBlock[ctl] = append(t.defBlock[ctl], block)
183248
}
184-
t.defBlock[ctl] = append(t.defBlock[ctl], block)
185249
}
186250
}
187251
}
@@ -200,50 +264,46 @@ func (t *worklist) addUses(val *Value) {
200264
}
201265
}
202266

203-
func meet(arr []lattice) lattice {
204-
var lt = lattice{top, nil}
205-
for _, t := range arr {
206-
if t.tag == bottom {
207-
return lattice{bottom, nil}
208-
} else if t.tag == constant {
209-
if lt.tag == top {
210-
lt.tag = constant
211-
lt.val = t.val
212-
} else {
213-
if lt.val != t.val {
214-
return lattice{bottom, nil}
267+
// meet meets all of phi arguments and computes result lattice
268+
func (t *worklist) meet(val *Value) lattice {
269+
optimisticLt := lattice{top, nil}
270+
for i := 0; i < len(val.Args); i++ {
271+
edge := Edge{val.Block, i}
272+
// If incoming edge for phi is not visited, assume top optimistically.
273+
// According to rules of meet:
274+
// Top ∩ any = any
275+
// Top participates in meet() but does not affect the result, so here
276+
// we will ignore Top and only take other lattices into consideration.
277+
if _, exist := t.visited[edge]; exist {
278+
lt := t.getLatticeCell(val.Args[i])
279+
if lt.tag == constant {
280+
if optimisticLt.tag == top {
281+
optimisticLt = lt
282+
} else {
283+
if optimisticLt.val != lt.val {
284+
// ConstantA ∩ ConstantB = Bottom
285+
return lattice{bottom, nil}
286+
}
215287
}
288+
} else if lt.tag == bottom {
289+
// Bottom ∩ any = Bottom
290+
return lattice{bottom, nil}
291+
} else {
292+
// Top ∩ any = any
216293
}
217294
} else {
218295
// Top ∩ any = any
219296
}
220297
}
221-
return lt
222-
}
223298

224-
func (t *worklist) visitPhi(val *Value) {
225-
var argLattice = make([]lattice, 0)
226-
for i := 0; i < len(val.Args); i++ {
227-
var edge = Edge{val.Block, i}
228-
// If incoming edge for phi is not visited, assume top optimistically. According to rules
229-
// of meet:
230-
// Top ∩ any = any
231-
// Top participates in meet() but does not affect the result, so here we will ignore Top
232-
// and only take Bottom and Constant lattices into consideration.
233-
if _, exist := t.visited[edge]; exist {
234-
argLattice = append(argLattice, t.getLatticeCell(val.Args[i]))
235-
} else {
236-
// ignore Top intentionally
237-
}
238-
}
239-
// meet all of phi arguments
240-
t.latticeCells[val] = meet(argLattice)
299+
// ConstantA ∩ ConstantA = ConstantA or Top ∩ any = any
300+
return optimisticLt
241301
}
242302

243303
func computeLattice(f *Func, val *Value, args ...*Value) lattice {
244304
// In general, we need to perform constant evaluation based on two constant lattices:
245305
//
246-
// var res = lattice{constant, nil}
306+
// res := lattice{constant, nil}
247307
// switch op {
248308
// case OpAdd16:
249309
// res.val = newConst(argLt1.val.AuxInt16() + argLt2.val.AuxInt16())
@@ -265,9 +325,9 @@ func computeLattice(f *Func, val *Value, args ...*Value) lattice {
265325
// change it permanently, which can lead to errors. For example, We cannot change its value
266326
// immediately after visiting Phi, because some of its input edges may still not be visited
267327
// at this moment.
268-
var constValue = f.newValue(val.Op, val.Type, f.Entry, val.Pos)
328+
constValue := f.newValue(val.Op, val.Type, f.Entry, val.Pos)
269329
constValue.AddArgs(args...)
270-
var matched = rewriteValuegeneric(constValue)
330+
matched := rewriteValuegeneric(constValue)
271331
if matched {
272332
if isConst(constValue) {
273333
return lattice{constant, constValue}
@@ -284,16 +344,16 @@ func (t *worklist) visitValue(val *Value) {
284344
return
285345
}
286346

287-
var oldLt = t.getLatticeCell(val)
347+
oldLt := t.getLatticeCell(val)
288348
defer func() {
289349
// re-visit all uses of value if its lattice is changed
290-
var newLt = t.getLatticeCell(val)
350+
newLt := t.getLatticeCell(val)
291351
if newLt != oldLt {
292352
t.addUses(val)
293353
}
294354
}()
295355

296-
var worstLt = lattice{bottom, nil}
356+
worstLt := lattice{bottom, nil}
297357
switch val.Op {
298358
// they are constant values, aren't they?
299359
case OpConst64, OpConst32, OpConst16, OpConst8,
@@ -304,7 +364,7 @@ func (t *worklist) visitValue(val *Value) {
304364
t.latticeCells[val] = t.getLatticeCell(val.Args[0])
305365
// phi should be processed specially
306366
case OpPhi:
307-
t.visitPhi(val)
367+
t.latticeCells[val] = t.meet(val)
308368
// fold 1-input operations:
309369
case
310370
// negate
@@ -329,7 +389,7 @@ func (t *worklist) visitValue(val *Value) {
329389
OpIsNonNil,
330390
// not
331391
OpNot:
332-
var lt1 = t.getLatticeCell(val.Args[0])
392+
lt1 := t.getLatticeCell(val.Args[0])
333393
if lt1.tag != constant {
334394
t.latticeCells[val] = worstLt
335395
return
@@ -374,8 +434,8 @@ func (t *worklist) visitValue(val *Value) {
374434
OpAnd8, OpAnd16, OpAnd32, OpAnd64,
375435
OpOr8, OpOr16, OpOr32, OpOr64,
376436
OpXor8, OpXor16, OpXor32, OpXor64:
377-
var lt1 = t.getLatticeCell(val.Args[0])
378-
var lt2 = t.getLatticeCell(val.Args[1])
437+
lt1 := t.getLatticeCell(val.Args[0])
438+
lt2 := t.getLatticeCell(val.Args[1])
379439
if lt1.tag != constant || lt2.tag != constant {
380440
t.latticeCells[val] = worstLt
381441
return
@@ -404,8 +464,8 @@ func (t *worklist) propagate(block *Block) {
404464
case BlockPlain:
405465
t.edges = append(t.edges, block.Succs[0])
406466
case BlockIf, BlockJumpTable:
407-
var cond = block.ControlValues()[0]
408-
var condLattice = t.getLatticeCell(cond)
467+
cond := block.ControlValues()[0]
468+
condLattice := t.getLatticeCell(cond)
409469
if condLattice.tag == bottom {
410470
// we know nothing about control flow, add all branch destinations
411471
t.edges = append(t.edges, block.Succs...)
@@ -438,8 +498,8 @@ func rewireSuccessor(block *Block, constVal *Value) bool {
438498
block.ResetControls()
439499
return true
440500
case BlockJumpTable:
441-
var idx = int(constVal.AuxInt)
442-
var targetBlock = block.Succs[idx].b
501+
idx := int(constVal.AuxInt)
502+
targetBlock := block.Succs[idx].b
443503
for len(block.Succs) > 0 {
444504
block.removeEdge(0)
445505
}
@@ -456,7 +516,7 @@ func rewireSuccessor(block *Block, constVal *Value) bool {
456516
// replaceConst will replace non-constant values that have been proven by sccp to be
457517
// constants.
458518
func (t *worklist) replaceConst() (int, int) {
459-
var constCnt, rewireCnt = 0, 0
519+
constCnt, rewireCnt := 0, 0
460520
for val, lt := range t.latticeCells {
461521
if lt.tag == constant {
462522
if !isConst(val) {
@@ -468,7 +528,7 @@ func (t *worklist) replaceConst() (int, int) {
468528
constCnt++
469529
}
470530
// If const value controls this block, rewires successors according to its value
471-
var ctrlBlock = t.defBlock[val]
531+
ctrlBlock := t.defBlock[val]
472532
for _, block := range ctrlBlock {
473533
if rewireSuccessor(block, lt.val) {
474534
rewireCnt++
@@ -481,61 +541,3 @@ func (t *worklist) replaceConst() (int, int) {
481541
}
482542
return constCnt, rewireCnt
483543
}
484-
485-
func sccp(f *Func) {
486-
var t worklist
487-
t.f = f
488-
t.edges = make([]Edge, 0)
489-
t.visited = make(map[Edge]bool)
490-
t.edges = append(t.edges, Edge{f.Entry, 0})
491-
t.defUse = make(map[*Value][]*Value)
492-
t.defBlock = make(map[*Value][]*Block)
493-
t.latticeCells = make(map[*Value]lattice)
494-
495-
// build it early since we rely heavily on the def-use chain later
496-
t.buildDefUses()
497-
498-
var visitedBlock = f.newSparseSet(f.NumBlocks())
499-
defer f.retSparseSet(visitedBlock)
500-
501-
// pick up either an edge or SSA value from worklilst, process it
502-
for {
503-
if len(t.edges) > 0 {
504-
var edge = t.edges[0]
505-
t.edges = t.edges[1:]
506-
if _, exist := t.visited[edge]; !exist {
507-
var dest = edge.b
508-
var destVisited = visitedBlock.contains(dest.ID)
509-
510-
// mark edge as visited
511-
t.visited[edge] = true
512-
visitedBlock.add(dest.ID)
513-
for _, val := range dest.Values {
514-
if val.Op == OpPhi || !destVisited {
515-
t.visitValue(val)
516-
}
517-
}
518-
// propagates constants facts through CFG, taking condition test into account
519-
if !destVisited {
520-
t.propagate(dest)
521-
}
522-
}
523-
continue
524-
}
525-
if len(t.uses) > 0 {
526-
var use = t.uses[0]
527-
t.uses = t.uses[1:]
528-
t.visitValue(use)
529-
continue
530-
}
531-
break
532-
}
533-
534-
// apply optimizations based on discovered constants
535-
var constCnt, rewireCnt = t.replaceConst()
536-
if f.pass.debug > 0 {
537-
if constCnt > 0 || rewireCnt > 0 {
538-
fmt.Printf("Phase SCCP for %v : %v constants, %v dce\n", f.Name, constCnt, rewireCnt)
539-
}
540-
}
541-
}

0 commit comments

Comments
 (0)