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