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

View as plain text