github.com/bir3/gocompiler@v0.3.205/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  // A Value can be moved to any block that
    11  // dominates all blocks in which it is used.
    12  func tighten(f *Func) {
    13  	canMove := f.Cache.allocBoolSlice(f.NumValues())
    14  	defer f.Cache.freeBoolSlice(canMove)
    15  	for _, b := range f.Blocks {
    16  		for _, v := range b.Values {
    17  			if v.Op.isLoweredGetClosurePtr() {
    18  				// Must stay in the entry block.
    19  				continue
    20  			}
    21  			switch v.Op {
    22  			case OpPhi, OpArg, OpArgIntReg, OpArgFloatReg, OpSelect0, OpSelect1, OpSelectN:
    23  				// Phis need to stay in their block.
    24  				// Arg must stay in the entry block.
    25  				// Tuple selectors must stay with the tuple generator.
    26  				// SelectN is typically, ultimately, a register.
    27  				continue
    28  			}
    29  			if v.MemoryArg() != nil {
    30  				// We can't move values which have a memory arg - it might
    31  				// make two memory values live across a block boundary.
    32  				continue
    33  			}
    34  			// Count arguments which will need a register.
    35  			narg := 0
    36  			for _, a := range v.Args {
    37  				if !a.rematerializeable() {
    38  					narg++
    39  				}
    40  			}
    41  			if narg >= 2 && !v.Type.IsFlags() {
    42  				// Don't move values with more than one input, as that may
    43  				// increase register pressure.
    44  				// We make an exception for flags, as we want flag generators
    45  				// moved next to uses (because we only have 1 flag register).
    46  				continue
    47  			}
    48  			canMove[v.ID] = true
    49  		}
    50  	}
    51  
    52  	// Build data structure for fast least-common-ancestor queries.
    53  	lca := makeLCArange(f)
    54  
    55  	// For each moveable value, record the block that dominates all uses found so far.
    56  	target := f.Cache.allocBlockSlice(f.NumValues())
    57  	defer f.Cache.freeBlockSlice(target)
    58  
    59  	// Grab loop information.
    60  	// We use this to make sure we don't tighten a value into a (deeper) loop.
    61  	idom := f.Idom()
    62  	loops := f.loopnest()
    63  	loops.calculateDepths()
    64  
    65  	changed := true
    66  	for changed {
    67  		changed = false
    68  
    69  		// Reset target
    70  		for i := range target {
    71  			target[i] = nil
    72  		}
    73  
    74  		// Compute target locations (for moveable values only).
    75  		// target location = the least common ancestor of all uses in the dominator tree.
    76  		for _, b := range f.Blocks {
    77  			for _, v := range b.Values {
    78  				for i, a := range v.Args {
    79  					if !canMove[a.ID] {
    80  						continue
    81  					}
    82  					use := b
    83  					if v.Op == OpPhi {
    84  						use = b.Preds[i].b
    85  					}
    86  					if target[a.ID] == nil {
    87  						target[a.ID] = use
    88  					} else {
    89  						target[a.ID] = lca.find(target[a.ID], use)
    90  					}
    91  				}
    92  			}
    93  			for _, c := range b.ControlValues() {
    94  				if !canMove[c.ID] {
    95  					continue
    96  				}
    97  				if target[c.ID] == nil {
    98  					target[c.ID] = b
    99  				} else {
   100  					target[c.ID] = lca.find(target[c.ID], b)
   101  				}
   102  			}
   103  		}
   104  
   105  		// If the target location is inside a loop,
   106  		// move the target location up to just before the loop head.
   107  		for _, b := range f.Blocks {
   108  			origloop := loops.b2l[b.ID]
   109  			for _, v := range b.Values {
   110  				t := target[v.ID]
   111  				if t == nil {
   112  					continue
   113  				}
   114  				targetloop := loops.b2l[t.ID]
   115  				for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
   116  					t = idom[targetloop.header.ID]
   117  					target[v.ID] = t
   118  					targetloop = loops.b2l[t.ID]
   119  				}
   120  			}
   121  		}
   122  
   123  		// Move values to target locations.
   124  		for _, b := range f.Blocks {
   125  			for i := 0; i < len(b.Values); i++ {
   126  				v := b.Values[i]
   127  				t := target[v.ID]
   128  				if t == nil || t == b {
   129  					// v is not moveable, or is already in correct place.
   130  					continue
   131  				}
   132  				// Move v to the block which dominates its uses.
   133  				t.Values = append(t.Values, v)
   134  				v.Block = t
   135  				last := len(b.Values) - 1
   136  				b.Values[i] = b.Values[last]
   137  				b.Values[last] = nil
   138  				b.Values = b.Values[:last]
   139  				changed = true
   140  				i--
   141  			}
   142  		}
   143  	}
   144  }
   145  
   146  // phiTighten moves constants closer to phi users.
   147  // This pass avoids having lots of constants live for lots of the program.
   148  // See issue 16407.
   149  func phiTighten(f *Func) {
   150  	for _, b := range f.Blocks {
   151  		for _, v := range b.Values {
   152  			if v.Op != OpPhi {
   153  				continue
   154  			}
   155  			for i, a := range v.Args {
   156  				if !a.rematerializeable() {
   157  					continue // not a constant we can move around
   158  				}
   159  				if a.Block == b.Preds[i].b {
   160  					continue // already in the right place
   161  				}
   162  				// Make a copy of a, put in predecessor block.
   163  				v.SetArg(i, a.copyInto(b.Preds[i].b))
   164  			}
   165  		}
   166  	}
   167  }