github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/ssa/stackalloc.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 // TODO: live at start of block instead? 6 7 package ssa 8 9 import "fmt" 10 11 type stackAllocState struct { 12 f *Func 13 14 // live is the output of stackalloc. 15 // live[b.id] = live values at the end of block b. 16 live [][]ID 17 18 // The following slices are reused across multiple users 19 // of stackAllocState. 20 values []stackValState 21 interfere [][]ID // interfere[v.id] = values that interfere with v. 22 names []LocalSlot 23 slots []int 24 used []bool 25 } 26 27 func newStackAllocState(f *Func) *stackAllocState { 28 s := f.Config.stackAllocState 29 if s == nil { 30 return new(stackAllocState) 31 } 32 if s.f != nil { 33 f.Config.Fatalf(0, "newStackAllocState called without previous free") 34 } 35 return s 36 } 37 38 func putStackAllocState(s *stackAllocState) { 39 for i := range s.values { 40 s.values[i] = stackValState{} 41 } 42 for i := range s.interfere { 43 s.interfere[i] = nil 44 } 45 for i := range s.names { 46 s.names[i] = LocalSlot{} 47 } 48 for i := range s.slots { 49 s.slots[i] = 0 50 } 51 for i := range s.used { 52 s.used[i] = false 53 } 54 s.f.Config.stackAllocState = s 55 s.f = nil 56 s.live = nil 57 } 58 59 type stackValState struct { 60 typ Type 61 spill *Value 62 needSlot bool 63 } 64 65 // stackalloc allocates storage in the stack frame for 66 // all Values that did not get a register. 67 // Returns a map from block ID to the stack values live at the end of that block. 68 func stackalloc(f *Func, spillLive [][]ID) [][]ID { 69 if f.pass.debug > stackDebug { 70 fmt.Println("before stackalloc") 71 fmt.Println(f.String()) 72 } 73 s := newStackAllocState(f) 74 s.init(f, spillLive) 75 defer putStackAllocState(s) 76 77 s.stackalloc() 78 return s.live 79 } 80 81 func (s *stackAllocState) init(f *Func, spillLive [][]ID) { 82 s.f = f 83 84 // Initialize value information. 85 if n := f.NumValues(); cap(s.values) >= n { 86 s.values = s.values[:n] 87 } else { 88 s.values = make([]stackValState, n) 89 } 90 for _, b := range f.Blocks { 91 for _, v := range b.Values { 92 s.values[v.ID].typ = v.Type 93 s.values[v.ID].needSlot = !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && f.getHome(v.ID) == nil && !v.rematerializeable() 94 if f.pass.debug > stackDebug && s.values[v.ID].needSlot { 95 fmt.Printf("%s needs a stack slot\n", v) 96 } 97 if v.Op == OpStoreReg { 98 s.values[v.Args[0].ID].spill = v 99 } 100 } 101 } 102 103 // Compute liveness info for values needing a slot. 104 s.computeLive(spillLive) 105 106 // Build interference graph among values needing a slot. 107 s.buildInterferenceGraph() 108 } 109 110 func (s *stackAllocState) stackalloc() { 111 f := s.f 112 113 // Build map from values to their names, if any. 114 // A value may be associated with more than one name (e.g. after 115 // the assignment i=j). This step picks one name per value arbitrarily. 116 if n := f.NumValues(); cap(s.names) >= n { 117 s.names = s.names[:n] 118 } else { 119 s.names = make([]LocalSlot, n) 120 } 121 names := s.names 122 for _, name := range f.Names { 123 // Note: not "range f.NamedValues" above, because 124 // that would be nondeterministic. 125 for _, v := range f.NamedValues[name] { 126 names[v.ID] = name 127 } 128 } 129 130 // Allocate args to their assigned locations. 131 for _, v := range f.Entry.Values { 132 if v.Op != OpArg { 133 continue 134 } 135 loc := LocalSlot{v.Aux.(GCNode), v.Type, v.AuxInt} 136 if f.pass.debug > stackDebug { 137 fmt.Printf("stackalloc %s to %s\n", v, loc.Name()) 138 } 139 f.setHome(v, loc) 140 } 141 142 // For each type, we keep track of all the stack slots we 143 // have allocated for that type. 144 // TODO: share slots among equivalent types. We would need to 145 // only share among types with the same GC signature. See the 146 // type.Equal calls below for where this matters. 147 locations := map[Type][]LocalSlot{} 148 149 // Each time we assign a stack slot to a value v, we remember 150 // the slot we used via an index into locations[v.Type]. 151 slots := s.slots 152 if n := f.NumValues(); cap(slots) >= n { 153 slots = slots[:n] 154 } else { 155 slots = make([]int, n) 156 s.slots = slots 157 } 158 for i := range slots { 159 slots[i] = -1 160 } 161 162 // Pick a stack slot for each value needing one. 163 var used []bool 164 if n := f.NumValues(); cap(s.used) >= n { 165 used = s.used[:n] 166 } else { 167 used = make([]bool, n) 168 s.used = used 169 } 170 for _, b := range f.Blocks { 171 for _, v := range b.Values { 172 if !s.values[v.ID].needSlot { 173 continue 174 } 175 if v.Op == OpArg { 176 continue // already picked 177 } 178 179 // If this is a named value, try to use the name as 180 // the spill location. 181 var name LocalSlot 182 if v.Op == OpStoreReg { 183 name = names[v.Args[0].ID] 184 } else { 185 name = names[v.ID] 186 } 187 if name.N != nil && v.Type.Equal(name.Type) { 188 for _, id := range s.interfere[v.ID] { 189 h := f.getHome(id) 190 if h != nil && h.(LocalSlot).N == name.N && h.(LocalSlot).Off == name.Off { 191 // A variable can interfere with itself. 192 // It is rare, but but it can happen. 193 goto noname 194 } 195 } 196 if f.pass.debug > stackDebug { 197 fmt.Printf("stackalloc %s to %s\n", v, name.Name()) 198 } 199 f.setHome(v, name) 200 continue 201 } 202 203 noname: 204 // Set of stack slots we could reuse. 205 locs := locations[v.Type] 206 // Mark all positions in locs used by interfering values. 207 for i := 0; i < len(locs); i++ { 208 used[i] = false 209 } 210 for _, xid := range s.interfere[v.ID] { 211 slot := slots[xid] 212 if slot >= 0 { 213 used[slot] = true 214 } 215 } 216 // Find an unused stack slot. 217 var i int 218 for i = 0; i < len(locs); i++ { 219 if !used[i] { 220 break 221 } 222 } 223 // If there is no unused stack slot, allocate a new one. 224 if i == len(locs) { 225 locs = append(locs, LocalSlot{N: f.Config.fe.Auto(v.Type), Type: v.Type, Off: 0}) 226 locations[v.Type] = locs 227 } 228 // Use the stack variable at that index for v. 229 loc := locs[i] 230 if f.pass.debug > stackDebug { 231 fmt.Printf("stackalloc %s to %s\n", v, loc.Name()) 232 } 233 f.setHome(v, loc) 234 slots[v.ID] = i 235 } 236 } 237 } 238 239 // computeLive computes a map from block ID to a list of 240 // stack-slot-needing value IDs live at the end of that block. 241 // TODO: this could be quadratic if lots of variables are live across lots of 242 // basic blocks. Figure out a way to make this function (or, more precisely, the user 243 // of this function) require only linear size & time. 244 func (s *stackAllocState) computeLive(spillLive [][]ID) { 245 s.live = make([][]ID, s.f.NumBlocks()) 246 var phis []*Value 247 live := s.f.newSparseSet(s.f.NumValues()) 248 defer s.f.retSparseSet(live) 249 t := s.f.newSparseSet(s.f.NumValues()) 250 defer s.f.retSparseSet(t) 251 252 // Instead of iterating over f.Blocks, iterate over their postordering. 253 // Liveness information flows backward, so starting at the end 254 // increases the probability that we will stabilize quickly. 255 po := postorder(s.f) 256 for { 257 changed := false 258 for _, b := range po { 259 // Start with known live values at the end of the block 260 live.clear() 261 live.addAll(s.live[b.ID]) 262 263 // Propagate backwards to the start of the block 264 phis = phis[:0] 265 for i := len(b.Values) - 1; i >= 0; i-- { 266 v := b.Values[i] 267 live.remove(v.ID) 268 if v.Op == OpPhi { 269 // Save phi for later. 270 // Note: its args might need a stack slot even though 271 // the phi itself doesn't. So don't use needSlot. 272 if !v.Type.IsMemory() && !v.Type.IsVoid() { 273 phis = append(phis, v) 274 } 275 continue 276 } 277 for _, a := range v.Args { 278 if s.values[a.ID].needSlot { 279 live.add(a.ID) 280 } 281 } 282 } 283 284 // for each predecessor of b, expand its list of live-at-end values 285 // invariant: s contains the values live at the start of b (excluding phi inputs) 286 for i, p := range b.Preds { 287 t.clear() 288 t.addAll(s.live[p.ID]) 289 t.addAll(live.contents()) 290 t.addAll(spillLive[p.ID]) 291 for _, v := range phis { 292 a := v.Args[i] 293 if s.values[a.ID].needSlot { 294 t.add(a.ID) 295 } 296 if spill := s.values[a.ID].spill; spill != nil { 297 //TODO: remove? Subsumed by SpillUse? 298 t.add(spill.ID) 299 } 300 } 301 if t.size() == len(s.live[p.ID]) { 302 continue 303 } 304 // grow p's live set 305 s.live[p.ID] = append(s.live[p.ID][:0], t.contents()...) 306 changed = true 307 } 308 } 309 310 if !changed { 311 break 312 } 313 } 314 if s.f.pass.debug > stackDebug { 315 for _, b := range s.f.Blocks { 316 fmt.Printf("stacklive %s %v\n", b, s.live[b.ID]) 317 } 318 } 319 } 320 321 func (f *Func) getHome(vid ID) Location { 322 if int(vid) >= len(f.RegAlloc) { 323 return nil 324 } 325 return f.RegAlloc[vid] 326 } 327 328 func (f *Func) setHome(v *Value, loc Location) { 329 for v.ID >= ID(len(f.RegAlloc)) { 330 f.RegAlloc = append(f.RegAlloc, nil) 331 } 332 f.RegAlloc[v.ID] = loc 333 } 334 335 func (s *stackAllocState) buildInterferenceGraph() { 336 f := s.f 337 if n := f.NumValues(); cap(s.interfere) >= n { 338 s.interfere = s.interfere[:n] 339 } else { 340 s.interfere = make([][]ID, n) 341 } 342 live := f.newSparseSet(f.NumValues()) 343 defer f.retSparseSet(live) 344 for _, b := range f.Blocks { 345 // Propagate liveness backwards to the start of the block. 346 // Two values interfere if one is defined while the other is live. 347 live.clear() 348 live.addAll(s.live[b.ID]) 349 for i := len(b.Values) - 1; i >= 0; i-- { 350 v := b.Values[i] 351 if s.values[v.ID].needSlot { 352 live.remove(v.ID) 353 for _, id := range live.contents() { 354 if s.values[v.ID].typ.Equal(s.values[id].typ) { 355 s.interfere[v.ID] = append(s.interfere[v.ID], id) 356 s.interfere[id] = append(s.interfere[id], v.ID) 357 } 358 } 359 } 360 for _, a := range v.Args { 361 if s.values[a.ID].needSlot { 362 live.add(a.ID) 363 } 364 } 365 if v.Op == OpArg && s.values[v.ID].needSlot { 366 // OpArg is an input argument which is pre-spilled. 367 // We add back v.ID here because we want this value 368 // to appear live even before this point. Being live 369 // all the way to the start of the entry block prevents other 370 // values from being allocated to the same slot and clobbering 371 // the input value before we have a chance to load it. 372 live.add(v.ID) 373 } 374 } 375 } 376 if f.pass.debug > stackDebug { 377 for vid, i := range s.interfere { 378 if len(i) > 0 { 379 fmt.Printf("v%d interferes with", vid) 380 for _, x := range i { 381 fmt.Printf(" v%d", x) 382 } 383 fmt.Println() 384 } 385 } 386 } 387 }