github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/cmd/compile/internal/ssa/schedule.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 import "container/heap" 8 9 const ( 10 ScorePhi = iota // towards top of block 11 ScoreNilCheck 12 ScoreReadTuple 13 ScoreVarDef 14 ScoreMemory 15 ScoreDefault 16 ScoreFlags 17 ScoreControl // towards bottom of block 18 ) 19 20 type ValHeap struct { 21 a []*Value 22 score []int8 23 } 24 25 func (h ValHeap) Len() int { return len(h.a) } 26 func (h ValHeap) Swap(i, j int) { a := h.a; a[i], a[j] = a[j], a[i] } 27 28 func (h *ValHeap) Push(x interface{}) { 29 // Push and Pop use pointer receivers because they modify the slice's length, 30 // not just its contents. 31 v := x.(*Value) 32 h.a = append(h.a, v) 33 } 34 func (h *ValHeap) Pop() interface{} { 35 old := h.a 36 n := len(old) 37 x := old[n-1] 38 h.a = old[0 : n-1] 39 return x 40 } 41 func (h ValHeap) Less(i, j int) bool { 42 x := h.a[i] 43 y := h.a[j] 44 sx := h.score[x.ID] 45 sy := h.score[y.ID] 46 if c := sx - sy; c != 0 { 47 return c > 0 // higher score comes later. 48 } 49 if x.Pos != y.Pos { // Favor in-order line stepping 50 return x.Pos.After(y.Pos) 51 } 52 if x.Op != OpPhi { 53 if c := len(x.Args) - len(y.Args); c != 0 { 54 return c < 0 // smaller args comes later 55 } 56 } 57 return x.ID > y.ID 58 } 59 60 // Schedule the Values in each Block. After this phase returns, the 61 // order of b.Values matters and is the order in which those values 62 // will appear in the assembly output. For now it generates a 63 // reasonable valid schedule using a priority queue. TODO(khr): 64 // schedule smarter. 65 func schedule(f *Func) { 66 // For each value, the number of times it is used in the block 67 // by values that have not been scheduled yet. 68 uses := make([]int32, f.NumValues()) 69 70 // reusable priority queue 71 priq := new(ValHeap) 72 73 // "priority" for a value 74 score := make([]int8, f.NumValues()) 75 76 // scheduling order. We queue values in this list in reverse order. 77 var order []*Value 78 79 // maps mem values to the next live memory value 80 nextMem := make([]*Value, f.NumValues()) 81 // additional pretend arguments for each Value. Used to enforce load/store ordering. 82 additionalArgs := make([][]*Value, f.NumValues()) 83 84 for _, b := range f.Blocks { 85 // Compute score. Larger numbers are scheduled closer to the end of the block. 86 for _, v := range b.Values { 87 switch { 88 case v.Op == OpAMD64LoweredGetClosurePtr || v.Op == OpPPC64LoweredGetClosurePtr || 89 v.Op == OpARMLoweredGetClosurePtr || v.Op == OpARM64LoweredGetClosurePtr || 90 v.Op == Op386LoweredGetClosurePtr || v.Op == OpMIPS64LoweredGetClosurePtr || 91 v.Op == OpS390XLoweredGetClosurePtr || v.Op == OpMIPSLoweredGetClosurePtr || 92 v.Op == OpRISCVLoweredGetClosurePtr: 93 // We also score GetLoweredClosurePtr as early as possible to ensure that the 94 // context register is not stomped. GetLoweredClosurePtr should only appear 95 // in the entry block where there are no phi functions, so there is no 96 // conflict or ambiguity here. 97 if b != f.Entry { 98 f.Fatalf("LoweredGetClosurePtr appeared outside of entry block, b=%s", b.String()) 99 } 100 score[v.ID] = ScorePhi 101 case v.Op == OpAMD64LoweredNilCheck || v.Op == OpPPC64LoweredNilCheck || 102 v.Op == OpARMLoweredNilCheck || v.Op == OpARM64LoweredNilCheck || 103 v.Op == Op386LoweredNilCheck || v.Op == OpMIPS64LoweredNilCheck || 104 v.Op == OpS390XLoweredNilCheck || v.Op == OpMIPSLoweredNilCheck || 105 v.Op == OpRISCVLoweredNilCheck: 106 // Nil checks must come before loads from the same address. 107 score[v.ID] = ScoreNilCheck 108 case v.Op == OpPhi: 109 // We want all the phis first. 110 score[v.ID] = ScorePhi 111 case v.Op == OpVarDef: 112 // We want all the vardefs next. 113 score[v.ID] = ScoreVarDef 114 case v.Type.IsMemory(): 115 // Schedule stores as early as possible. This tends to 116 // reduce register pressure. It also helps make sure 117 // VARDEF ops are scheduled before the corresponding LEA. 118 score[v.ID] = ScoreMemory 119 case v.Op == OpSelect0 || v.Op == OpSelect1: 120 // Schedule the pseudo-op of reading part of a tuple 121 // immediately after the tuple-generating op, since 122 // this value is already live. This also removes its 123 // false dependency on the other part of the tuple. 124 // Also ensures tuple is never spilled. 125 score[v.ID] = ScoreReadTuple 126 case v.Type.IsFlags() || v.Type.IsTuple(): 127 // Schedule flag register generation as late as possible. 128 // This makes sure that we only have one live flags 129 // value at a time. 130 score[v.ID] = ScoreFlags 131 default: 132 score[v.ID] = ScoreDefault 133 } 134 } 135 } 136 137 for _, b := range f.Blocks { 138 // Find store chain for block. 139 // Store chains for different blocks overwrite each other, so 140 // the calculated store chain is good only for this block. 141 for _, v := range b.Values { 142 if v.Op != OpPhi && v.Type.IsMemory() { 143 mem := v 144 if v.Op == OpSelect1 { 145 v = v.Args[0] 146 } 147 for _, w := range v.Args { 148 if w.Type.IsMemory() { 149 nextMem[w.ID] = mem 150 } 151 } 152 } 153 } 154 155 // Compute uses. 156 for _, v := range b.Values { 157 if v.Op == OpPhi { 158 // If a value is used by a phi, it does not induce 159 // a scheduling edge because that use is from the 160 // previous iteration. 161 continue 162 } 163 for _, w := range v.Args { 164 if w.Block == b { 165 uses[w.ID]++ 166 } 167 // Any load must come before the following store. 168 if v.Type.IsMemory() || !w.Type.IsMemory() { 169 continue // not a load 170 } 171 s := nextMem[w.ID] 172 if s == nil || s.Block != b { 173 continue 174 } 175 additionalArgs[s.ID] = append(additionalArgs[s.ID], v) 176 uses[v.ID]++ 177 } 178 } 179 180 if b.Control != nil && b.Control.Op != OpPhi { 181 // Force the control value to be scheduled at the end, 182 // unless it is a phi value (which must be first). 183 score[b.Control.ID] = ScoreControl 184 185 // Schedule values dependent on the control value at the end. 186 // This reduces the number of register spills. We don't find 187 // all values that depend on the control, just values with a 188 // direct dependency. This is cheaper and in testing there 189 // was no difference in the number of spills. 190 for _, v := range b.Values { 191 if v.Op != OpPhi { 192 for _, a := range v.Args { 193 if a == b.Control { 194 score[v.ID] = ScoreControl 195 } 196 } 197 } 198 } 199 } 200 201 // To put things into a priority queue 202 // The values that should come last are least. 203 priq.score = score 204 priq.a = priq.a[:0] 205 206 // Initialize priority queue with schedulable values. 207 for _, v := range b.Values { 208 if uses[v.ID] == 0 { 209 heap.Push(priq, v) 210 } 211 } 212 213 // Schedule highest priority value, update use counts, repeat. 214 order = order[:0] 215 tuples := make(map[ID][]*Value) 216 for { 217 // Find highest priority schedulable value. 218 // Note that schedule is assembled backwards. 219 220 if priq.Len() == 0 { 221 break 222 } 223 224 v := heap.Pop(priq).(*Value) 225 226 // Add it to the schedule. 227 // Do not emit tuple-reading ops until we're ready to emit the tuple-generating op. 228 //TODO: maybe remove ReadTuple score above, if it does not help on performance 229 switch { 230 case v.Op == OpSelect0: 231 if tuples[v.Args[0].ID] == nil { 232 tuples[v.Args[0].ID] = make([]*Value, 2) 233 } 234 tuples[v.Args[0].ID][0] = v 235 case v.Op == OpSelect1: 236 if tuples[v.Args[0].ID] == nil { 237 tuples[v.Args[0].ID] = make([]*Value, 2) 238 } 239 tuples[v.Args[0].ID][1] = v 240 case v.Type.IsTuple() && tuples[v.ID] != nil: 241 if tuples[v.ID][1] != nil { 242 order = append(order, tuples[v.ID][1]) 243 } 244 if tuples[v.ID][0] != nil { 245 order = append(order, tuples[v.ID][0]) 246 } 247 delete(tuples, v.ID) 248 fallthrough 249 default: 250 order = append(order, v) 251 } 252 253 // Update use counts of arguments. 254 for _, w := range v.Args { 255 if w.Block != b { 256 continue 257 } 258 uses[w.ID]-- 259 if uses[w.ID] == 0 { 260 // All uses scheduled, w is now schedulable. 261 heap.Push(priq, w) 262 } 263 } 264 for _, w := range additionalArgs[v.ID] { 265 uses[w.ID]-- 266 if uses[w.ID] == 0 { 267 // All uses scheduled, w is now schedulable. 268 heap.Push(priq, w) 269 } 270 } 271 } 272 if len(order) != len(b.Values) { 273 f.Fatalf("schedule does not include all values") 274 } 275 for i := 0; i < len(b.Values); i++ { 276 b.Values[i] = order[len(b.Values)-1-i] 277 } 278 } 279 280 f.scheduled = true 281 }