github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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  				if v.Op == OpPhi || v.Op == OpGetClosurePtr || v.Op == OpConvert || v.Op == OpArg {
    58  					// GetClosurePtr & Arg must stay in entry block.
    59  					// OpConvert must not float over call sites.
    60  					// TODO do we instead need a dependence edge of some sort for OpConvert?
    61  					// Would memory do the trick, or do we need something else that relates
    62  					// to safe point operations?
    63  					continue
    64  				}
    65  				if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() {
    66  					// We can't move values which have a memory arg - it might
    67  					// make two memory values live across a block boundary.
    68  					continue
    69  				}
    70  				if uses[v.ID] == 1 && !phi[v.ID] && home[v.ID] != b && len(v.Args) < 2 {
    71  					// v is used in exactly one block, and it is not b.
    72  					// Furthermore, it takes at most one input,
    73  					// so moving it will not increase the
    74  					// number of live values anywhere.
    75  					// Move v to that block.
    76  					c := home[v.ID]
    77  					c.Values = append(c.Values, v)
    78  					v.Block = c
    79  					last := len(b.Values) - 1
    80  					b.Values[i] = b.Values[last]
    81  					b.Values[last] = nil
    82  					b.Values = b.Values[:last]
    83  					changed = true
    84  				}
    85  			}
    86  		}
    87  	}
    88  }