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