github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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.Cache.stackAllocState
    39  	if s == nil {
    40  		return new(stackAllocState)
    41  	}
    42  	if s.f != nil {
    43  		f.fe.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.Cache.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.fe.Auto(v.Pos, 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  }