Source file src/cmd/compile/internal/ssa/tighten.go

     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 "cmd/compile/internal/base"
     8  
     9  // tighten moves Values closer to the Blocks in which they are used.
    10  // This can reduce the amount of register spilling required,
    11  // if it doesn't also create more live values.
    12  // A Value can be moved to any block that
    13  // dominates all blocks in which it is used.
    14  func tighten(f *Func) {
    15  	if base.Flag.N != 0 && len(f.Blocks) < 10000 {
    16  		// Skip the optimization in -N mode, except for huge functions.
    17  		// Too many values live across blocks can cause pathological
    18  		// behavior in the register allocator (see issue 52180).
    19  		return
    20  	}
    21  
    22  	canMove := f.Cache.allocBoolSlice(f.NumValues())
    23  	defer f.Cache.freeBoolSlice(canMove)
    24  
    25  	// Compute the memory states of each block.
    26  	startMem := f.Cache.allocValueSlice(f.NumBlocks())
    27  	defer f.Cache.freeValueSlice(startMem)
    28  	endMem := f.Cache.allocValueSlice(f.NumBlocks())
    29  	defer f.Cache.freeValueSlice(endMem)
    30  	memState(f, startMem, endMem)
    31  
    32  	for _, b := range f.Blocks {
    33  		for _, v := range b.Values {
    34  			if v.Op.isLoweredGetClosurePtr() {
    35  				// Must stay in the entry block.
    36  				continue
    37  			}
    38  			switch v.Op {
    39  			case OpPhi, OpArg, OpArgIntReg, OpArgFloatReg, OpSelect0, OpSelect1, OpSelectN:
    40  				// Phis need to stay in their block.
    41  				// Arg must stay in the entry block.
    42  				// Tuple selectors must stay with the tuple generator.
    43  				// SelectN is typically, ultimately, a register.
    44  				continue
    45  			}
    46  			if opcodeTable[v.Op].nilCheck {
    47  				// Nil checks need to stay in their block. See issue 72860.
    48  				continue
    49  			}
    50  			// Count arguments which will need a register.
    51  			narg := 0
    52  			for _, a := range v.Args {
    53  				// SP and SB are special registers and have no effect on
    54  				// the allocation of general-purpose registers.
    55  				if a.needRegister() && a.Op != OpSB && a.Op != OpSP {
    56  					narg++
    57  				}
    58  			}
    59  			if narg >= 2 && !v.Type.IsFlags() {
    60  				// Don't move values with more than one input, as that may
    61  				// increase register pressure.
    62  				// We make an exception for flags, as we want flag generators
    63  				// moved next to uses (because we only have 1 flag register).
    64  				continue
    65  			}
    66  			canMove[v.ID] = true
    67  		}
    68  	}
    69  
    70  	// Build data structure for fast least-common-ancestor queries.
    71  	lca := makeLCArange(f)
    72  
    73  	// For each moveable value, record the block that dominates all uses found so far.
    74  	target := f.Cache.allocBlockSlice(f.NumValues())
    75  	defer f.Cache.freeBlockSlice(target)
    76  
    77  	// Grab loop information.
    78  	// We use this to make sure we don't tighten a value into a (deeper) loop.
    79  	idom := f.Idom()
    80  	loops := f.loopnest()
    81  	loops.calculateDepths()
    82  
    83  	changed := true
    84  	for changed {
    85  		changed = false
    86  
    87  		// Reset target
    88  		for i := range target {
    89  			target[i] = nil
    90  		}
    91  
    92  		// Compute target locations (for moveable values only).
    93  		// target location = the least common ancestor of all uses in the dominator tree.
    94  		for _, b := range f.Blocks {
    95  			for _, v := range b.Values {
    96  				for i, a := range v.Args {
    97  					if !canMove[a.ID] {
    98  						continue
    99  					}
   100  					use := b
   101  					if v.Op == OpPhi {
   102  						use = b.Preds[i].b
   103  					}
   104  					if target[a.ID] == nil {
   105  						target[a.ID] = use
   106  					} else {
   107  						target[a.ID] = lca.find(target[a.ID], use)
   108  					}
   109  				}
   110  			}
   111  			for _, c := range b.ControlValues() {
   112  				if !canMove[c.ID] {
   113  					continue
   114  				}
   115  				if target[c.ID] == nil {
   116  					target[c.ID] = b
   117  				} else {
   118  					target[c.ID] = lca.find(target[c.ID], b)
   119  				}
   120  			}
   121  		}
   122  
   123  		// If the target location is inside a loop,
   124  		// move the target location up to just before the loop head.
   125  		for _, b := range f.Blocks {
   126  			origloop := loops.b2l[b.ID]
   127  			for _, v := range b.Values {
   128  				t := target[v.ID]
   129  				if t == nil {
   130  					continue
   131  				}
   132  				targetloop := loops.b2l[t.ID]
   133  				for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
   134  					t = idom[targetloop.header.ID]
   135  					target[v.ID] = t
   136  					targetloop = loops.b2l[t.ID]
   137  				}
   138  			}
   139  		}
   140  
   141  		// Move values to target locations.
   142  		for _, b := range f.Blocks {
   143  			for i := 0; i < len(b.Values); i++ {
   144  				v := b.Values[i]
   145  				t := target[v.ID]
   146  				if t == nil || t == b {
   147  					// v is not moveable, or is already in correct place.
   148  					continue
   149  				}
   150  				if mem := v.MemoryArg(); mem != nil {
   151  					if startMem[t.ID] != mem {
   152  						// We can't move a value with a memory arg unless the target block
   153  						// has that memory arg as its starting memory.
   154  						continue
   155  					}
   156  				}
   157  				if f.pass.debug > 0 {
   158  					b.Func.Warnl(v.Pos, "%v is moved", v.Op)
   159  				}
   160  				// Move v to the block which dominates its uses.
   161  				t.Values = append(t.Values, v)
   162  				v.Block = t
   163  				last := len(b.Values) - 1
   164  				b.Values[i] = b.Values[last]
   165  				b.Values[last] = nil
   166  				b.Values = b.Values[:last]
   167  				changed = true
   168  				i--
   169  			}
   170  		}
   171  	}
   172  }
   173  
   174  // phiTighten moves constants closer to phi users.
   175  // This pass avoids having lots of constants live for lots of the program.
   176  // See issue 16407.
   177  func phiTighten(f *Func) {
   178  	for _, b := range f.Blocks {
   179  		for _, v := range b.Values {
   180  			if v.Op != OpPhi {
   181  				continue
   182  			}
   183  			for i, a := range v.Args {
   184  				if !a.rematerializeable() {
   185  					continue // not a constant we can move around
   186  				}
   187  				if a.Block == b.Preds[i].b {
   188  					continue // already in the right place
   189  				}
   190  				// Make a copy of a, put in predecessor block.
   191  				v.SetArg(i, a.copyInto(b.Preds[i].b))
   192  			}
   193  		}
   194  	}
   195  }
   196  
   197  // memState computes the memory state at the beginning and end of each block of
   198  // the function. The memory state is represented by a value of mem type.
   199  // The returned result is stored in startMem and endMem, and endMem is nil for
   200  // blocks with no successors (Exit,Ret,RetJmp blocks). This algorithm is not
   201  // suitable for infinite loop blocks that do not contain any mem operations.
   202  // For example:
   203  // b1:
   204  //
   205  //	(some values)
   206  //
   207  // plain -> b2
   208  // b2: <- b1 b2
   209  // Plain -> b2
   210  //
   211  // Algorithm introduction:
   212  //  1. The start memory state of a block is InitMem, a Phi node of type mem or
   213  //     an incoming memory value.
   214  //  2. The start memory state of a block is consistent with the end memory state
   215  //     of its parent nodes. If the start memory state of a block is a Phi value,
   216  //     then the end memory state of its parent nodes is consistent with the
   217  //     corresponding argument value of the Phi node.
   218  //  3. The algorithm first obtains the memory state of some blocks in the tree
   219  //     in the first step. Then floods the known memory state to other nodes in
   220  //     the second step.
   221  func memState(f *Func, startMem, endMem []*Value) {
   222  	// This slice contains the set of blocks that have had their startMem set but this
   223  	// startMem value has not yet been propagated to the endMem of its predecessors
   224  	changed := make([]*Block, 0)
   225  	// First step, init the memory state of some blocks.
   226  	for _, b := range f.Blocks {
   227  		for _, v := range b.Values {
   228  			var mem *Value
   229  			if v.Op == OpPhi {
   230  				if v.Type.IsMemory() {
   231  					mem = v
   232  				}
   233  			} else if v.Op == OpInitMem {
   234  				mem = v // This is actually not needed.
   235  			} else if a := v.MemoryArg(); a != nil && a.Block != b {
   236  				// The only incoming memory value doesn't belong to this block.
   237  				mem = a
   238  			}
   239  			if mem != nil {
   240  				if old := startMem[b.ID]; old != nil {
   241  					if old == mem {
   242  						continue
   243  					}
   244  					f.Fatalf("func %s, startMem[%v] has different values, old %v, new %v", f.Name, b, old, mem)
   245  				}
   246  				startMem[b.ID] = mem
   247  				changed = append(changed, b)
   248  			}
   249  		}
   250  	}
   251  
   252  	// Second step, floods the known memory state of some blocks to others.
   253  	for len(changed) != 0 {
   254  		top := changed[0]
   255  		changed = changed[1:]
   256  		mem := startMem[top.ID]
   257  		for i, p := range top.Preds {
   258  			pb := p.b
   259  			if endMem[pb.ID] != nil {
   260  				continue
   261  			}
   262  			if mem.Op == OpPhi && mem.Block == top {
   263  				endMem[pb.ID] = mem.Args[i]
   264  			} else {
   265  				endMem[pb.ID] = mem
   266  			}
   267  			if startMem[pb.ID] == nil {
   268  				startMem[pb.ID] = endMem[pb.ID]
   269  				changed = append(changed, pb)
   270  			}
   271  		}
   272  	}
   273  }
   274  

View as plain text