github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/compile/internal/ssa/layout.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssa 6 7 // layout orders basic blocks in f with the goal of minimizing control flow instructions. 8 // After this phase returns, the order of f.Blocks matters and is the order 9 // in which those blocks will appear in the assembly output. 10 func layout(f *Func) { 11 f.Blocks = layoutOrder(f) 12 } 13 14 // Register allocation may use a different order which has constraints 15 // imposed by the linear-scan algorithm. Note that that f.pass here is 16 // regalloc, so the switch is conditional on -d=ssa/regalloc/test=N 17 func layoutRegallocOrder(f *Func) []*Block { 18 19 switch f.pass.test { 20 case 0: // layout order 21 return layoutOrder(f) 22 case 1: // existing block order 23 return f.Blocks 24 case 2: // reverse of postorder; legal, but usually not good. 25 po := f.postorder() 26 visitOrder := make([]*Block, len(po)) 27 for i, b := range po { 28 j := len(po) - i - 1 29 visitOrder[j] = b 30 } 31 return visitOrder 32 } 33 34 return nil 35 } 36 37 func layoutOrder(f *Func) []*Block { 38 order := make([]*Block, 0, f.NumBlocks()) 39 scheduled := make([]bool, f.NumBlocks()) 40 idToBlock := make([]*Block, f.NumBlocks()) 41 indegree := make([]int, f.NumBlocks()) 42 posdegree := f.newSparseSet(f.NumBlocks()) // blocks with positive remaining degree 43 defer f.retSparseSet(posdegree) 44 zerodegree := f.newSparseSet(f.NumBlocks()) // blocks with zero remaining degree 45 defer f.retSparseSet(zerodegree) 46 exit := f.newSparseSet(f.NumBlocks()) // exit blocks 47 defer f.retSparseSet(exit) 48 49 // Initialize indegree of each block 50 for _, b := range f.Blocks { 51 idToBlock[b.ID] = b 52 if b.Kind == BlockExit { 53 // exit blocks are always scheduled last 54 // TODO: also add blocks post-dominated by exit blocks 55 exit.add(b.ID) 56 continue 57 } 58 indegree[b.ID] = len(b.Preds) 59 if len(b.Preds) == 0 { 60 zerodegree.add(b.ID) 61 } else { 62 posdegree.add(b.ID) 63 } 64 } 65 66 bid := f.Entry.ID 67 blockloop: 68 for { 69 // add block to schedule 70 b := idToBlock[bid] 71 order = append(order, b) 72 scheduled[bid] = true 73 if len(order) == len(f.Blocks) { 74 break 75 } 76 77 for _, e := range b.Succs { 78 c := e.b 79 indegree[c.ID]-- 80 if indegree[c.ID] == 0 { 81 posdegree.remove(c.ID) 82 zerodegree.add(c.ID) 83 } 84 } 85 86 // Pick the next block to schedule 87 // Pick among the successor blocks that have not been scheduled yet. 88 89 // Use likely direction if we have it. 90 var likely *Block 91 switch b.Likely { 92 case BranchLikely: 93 likely = b.Succs[0].b 94 case BranchUnlikely: 95 likely = b.Succs[1].b 96 } 97 if likely != nil && !scheduled[likely.ID] { 98 bid = likely.ID 99 continue 100 } 101 102 // Use degree for now. 103 bid = 0 104 mindegree := f.NumBlocks() 105 for _, e := range order[len(order)-1].Succs { 106 c := e.b 107 if scheduled[c.ID] || c.Kind == BlockExit { 108 continue 109 } 110 if indegree[c.ID] < mindegree { 111 mindegree = indegree[c.ID] 112 bid = c.ID 113 } 114 } 115 if bid != 0 { 116 continue 117 } 118 // TODO: improve this part 119 // No successor of the previously scheduled block works. 120 // Pick a zero-degree block if we can. 121 for zerodegree.size() > 0 { 122 cid := zerodegree.pop() 123 if !scheduled[cid] { 124 bid = cid 125 continue blockloop 126 } 127 } 128 // Still nothing, pick any non-exit block. 129 for posdegree.size() > 0 { 130 cid := posdegree.pop() 131 if !scheduled[cid] { 132 bid = cid 133 continue blockloop 134 } 135 } 136 // Pick any exit block. 137 // TODO: Order these to minimize jump distances? 138 for { 139 cid := exit.pop() 140 if !scheduled[cid] { 141 bid = cid 142 continue blockloop 143 } 144 } 145 } 146 return order 147 }