github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/tighten.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  // tighten moves Values closer to the Blocks in which they are used.
     8  // This can reduce the amount of register spilling required,
     9  // if it doesn't also create more live values.
    10  // For now, it handles only the trivial case in which a
    11  // Value with one or fewer args is only used in a single Block,
    12  // and not in a phi value.
    13  // TODO: Do something smarter.
    14  // A Value can be moved to any block that
    15  // dominates all blocks in which it is used.
    16  // Figure out when that will be an improvement.
    17  func tighten(f *Func) {
    18  	// For each value, the number of blocks in which it is used.
    19  	uses := make([]int32, f.NumValues())
    20  
    21  	// For each value, whether that value is ever an arg to a phi value.
    22  	phi := make([]bool, f.NumValues())
    23  
    24  	// For each value, one block in which that value is used.
    25  	home := make([]*Block, f.NumValues())
    26  
    27  	changed := true
    28  	for changed {
    29  		changed = false
    30  
    31  		// Reset uses
    32  		for i := range uses {
    33  			uses[i] = 0
    34  		}
    35  		// No need to reset home; any relevant values will be written anew anyway.
    36  		// No need to reset phi; once used in a phi, always used in a phi.
    37  
    38  		for _, b := range f.Blocks {
    39  			for _, v := range b.Values {
    40  				for _, w := range v.Args {
    41  					if v.Op == OpPhi {
    42  						phi[w.ID] = true
    43  					}
    44  					uses[w.ID]++
    45  					home[w.ID] = b
    46  				}
    47  			}
    48  			if b.Control != nil {
    49  				uses[b.Control.ID]++
    50  				home[b.Control.ID] = b
    51  			}
    52  		}
    53  
    54  		for _, b := range f.Blocks {
    55  			for i := 0; i < len(b.Values); i++ {
    56  				v := b.Values[i]
    57  				switch v.Op {
    58  				case OpPhi, OpGetClosurePtr, OpConvert, OpArg:
    59  					// GetClosurePtr & Arg must stay in entry block.
    60  					// OpConvert must not float over call sites.
    61  					// TODO do we instead need a dependence edge of some sort for OpConvert?
    62  					// Would memory do the trick, or do we need something else that relates
    63  					// to safe point operations?
    64  					continue
    65  				default:
    66  				}
    67  				if v.Op == OpSelect0 || v.Op == OpSelect1 {
    68  					// tuple selector must stay with tuple generator
    69  					continue
    70  				}
    71  				if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() {
    72  					// We can't move values which have a memory arg - it might
    73  					// make two memory values live across a block boundary.
    74  					continue
    75  				}
    76  				if uses[v.ID] == 1 && !phi[v.ID] && home[v.ID] != b && len(v.Args) < 2 {
    77  					// v is used in exactly one block, and it is not b.
    78  					// Furthermore, it takes at most one input,
    79  					// so moving it will not increase the
    80  					// number of live values anywhere.
    81  					// Move v to that block.
    82  					c := home[v.ID]
    83  					c.Values = append(c.Values, v)
    84  					v.Block = c
    85  					last := len(b.Values) - 1
    86  					b.Values[i] = b.Values[last]
    87  					b.Values[last] = nil
    88  					b.Values = b.Values[:last]
    89  					changed = true
    90  				}
    91  			}
    92  		}
    93  	}
    94  }
    95  
    96  // phiTighten moves constants closer to phi users.
    97  // This pass avoids having lots of constants live for lots of the program.
    98  // See issue 16407.
    99  func phiTighten(f *Func) {
   100  	for _, b := range f.Blocks {
   101  		for _, v := range b.Values {
   102  			if v.Op != OpPhi {
   103  				continue
   104  			}
   105  			for i, a := range v.Args {
   106  				if !a.rematerializeable() {
   107  					continue // not a constant we can move around
   108  				}
   109  				if a.Block == b.Preds[i].b {
   110  					continue // already in the right place
   111  				}
   112  				// Make a copy of a, put in predecessor block.
   113  				v.SetArg(i, a.copyInto(b.Preds[i].b))
   114  			}
   115  		}
   116  	}
   117  }