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  }