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  
    86  	changed := true
    87  	for changed {
    88  		changed = false
    89  
    90  		// Reset target
    91  		clear(target)
    92  
    93  		// Compute target locations (for moveable values only).
    94  		// target location = the least common ancestor of all uses in the dominator tree.
    95  		for _, b := range f.Blocks {
    96  			for _, v := range b.Values {
    97  				for i, a := range v.Args {
    98  					if !canMove[a.ID] {
    99  						continue
   100  					}
   101  					use := b
   102  					if v.Op == OpPhi {
   103  						use = b.Preds[i].b
   104  					}
   105  					if target[a.ID] == nil {
   106  						target[a.ID] = use
   107  					} else {
   108  						target[a.ID] = lca.find(target[a.ID], use)
   109  					}
   110  				}
   111  			}
   112  			for _, c := range b.ControlValues() {
   113  				if !canMove[c.ID] {
   114  					continue
   115  				}
   116  				if target[c.ID] == nil {
   117  					target[c.ID] = b
   118  				} else {
   119  					target[c.ID] = lca.find(target[c.ID], b)
   120  				}
   121  			}
   122  		}
   123  
   124  		// If the target location is inside a loop,
   125  		// move the target location up to just before the loop head.
   126  		if !loops.hasIrreducible {
   127  			// Loop info might not be correct for irreducible loops. See issue 75569.
   128  			for _, b := range f.Blocks {
   129  				origloop := loops.b2l[b.ID]
   130  				for _, v := range b.Values {
   131  					t := target[v.ID]
   132  					if t == nil {
   133  						continue
   134  					}
   135  					targetloop := loops.b2l[t.ID]
   136  					for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
   137  						t = idom[targetloop.header.ID]
   138  						target[v.ID] = t
   139  						targetloop = loops.b2l[t.ID]
   140  					}
   141  				}
   142  			}
   143  		}
   144  
   145  		// Move values to target locations.
   146  		for _, b := range f.Blocks {
   147  			for i := 0; i < len(b.Values); i++ {
   148  				v := b.Values[i]
   149  				t := target[v.ID]
   150  				if t == nil || t == b {
   151  					// v is not moveable, or is already in correct place.
   152  					continue
   153  				}
   154  				if mem := v.MemoryArg(); mem != nil {
   155  					if startMem[t.ID] != mem {
   156  						// We can't move a value with a memory arg unless the target block
   157  						// has that memory arg as its starting memory.
   158  						continue
   159  					}
   160  				}
   161  				if f.pass.debug > 0 {
   162  					b.Func.Warnl(v.Pos, "%v is moved", v.Op)
   163  				}
   164  				// Move v to the block which dominates its uses.
   165  				t.Values = append(t.Values, v)
   166  				v.Block = t
   167  				last := len(b.Values) - 1
   168  				b.Values[i] = b.Values[last]
   169  				b.Values[last] = nil
   170  				b.Values = b.Values[:last]
   171  				changed = true
   172  				i--
   173  			}
   174  		}
   175  	}
   176  }
   177  
   178  // phiTighten moves constants closer to phi users.
   179  // This pass avoids having lots of constants live for lots of the program.
   180  // See issue 16407.
   181  func phiTighten(f *Func) {
   182  	for _, b := range f.Blocks {
   183  		for _, v := range b.Values {
   184  			if v.Op != OpPhi {
   185  				continue
   186  			}
   187  			for i, a := range v.Args {
   188  				if !a.rematerializeable() {
   189  					continue // not a constant we can move around
   190  				}
   191  				if a.Block == b.Preds[i].b {
   192  					continue // already in the right place
   193  				}
   194  				// Make a copy of a, put in predecessor block.
   195  				v.SetArg(i, a.copyInto(b.Preds[i].b))
   196  			}
   197  		}
   198  	}
   199  }
   200  
   201  // memState computes the memory state at the beginning and end of each block of
   202  // the function. The memory state is represented by a value of mem type.
   203  // The returned result is stored in startMem and endMem, and endMem is nil for
   204  // blocks with no successors (Exit,Ret,RetJmp blocks). This algorithm is not
   205  // suitable for infinite loop blocks that do not contain any mem operations.
   206  // For example:
   207  // b1:
   208  //
   209  //	(some values)
   210  //
   211  // plain -> b2
   212  // b2: <- b1 b2
   213  // Plain -> b2
   214  //
   215  // Algorithm introduction:
   216  //  1. The start memory state of a block is InitMem, a Phi node of type mem or
   217  //     an incoming memory value.
   218  //  2. The start memory state of a block is consistent with the end memory state
   219  //     of its parent nodes. If the start memory state of a block is a Phi value,
   220  //     then the end memory state of its parent nodes is consistent with the
   221  //     corresponding argument value of the Phi node.
   222  //  3. The algorithm first obtains the memory state of some blocks in the tree
   223  //     in the first step. Then floods the known memory state to other nodes in
   224  //     the second step.
   225  func memState(f *Func, startMem, endMem []*Value) {
   226  	// This slice contains the set of blocks that have had their startMem set but this
   227  	// startMem value has not yet been propagated to the endMem of its predecessors
   228  	changed := make([]*Block, 0)
   229  	// First step, init the memory state of some blocks.
   230  	for _, b := range f.Blocks {
   231  		for _, v := range b.Values {
   232  			var mem *Value
   233  			if v.Op == OpPhi {
   234  				if v.Type.IsMemory() {
   235  					mem = v
   236  				}
   237  			} else if v.Op == OpInitMem {
   238  				mem = v // This is actually not needed.
   239  			} else if a := v.MemoryArg(); a != nil && a.Block != b {
   240  				// The only incoming memory value doesn't belong to this block.
   241  				mem = a
   242  			}
   243  			if mem != nil {
   244  				if old := startMem[b.ID]; old != nil {
   245  					if old == mem {
   246  						continue
   247  					}
   248  					f.Fatalf("func %s, startMem[%v] has different values, old %v, new %v", f.Name, b, old, mem)
   249  				}
   250  				startMem[b.ID] = mem
   251  				changed = append(changed, b)
   252  			}
   253  		}
   254  	}
   255  
   256  	// Second step, floods the known memory state of some blocks to others.
   257  	for len(changed) != 0 {
   258  		top := changed[0]
   259  		changed = changed[1:]
   260  		mem := startMem[top.ID]
   261  		for i, p := range top.Preds {
   262  			pb := p.b
   263  			if endMem[pb.ID] != nil {
   264  				continue
   265  			}
   266  			if mem.Op == OpPhi && mem.Block == top {
   267  				endMem[pb.ID] = mem.Args[i]
   268  			} else {
   269  				endMem[pb.ID] = mem
   270  			}
   271  			if startMem[pb.ID] == nil {
   272  				startMem[pb.ID] = endMem[pb.ID]
   273  				changed = append(changed, pb)
   274  			}
   275  		}
   276  	}
   277  }
   278  

View as plain text