Source file src/cmd/compile/internal/ssa/copyelim.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  // combine copyelim and phielim into a single pass.
     8  // copyelim removes all uses of OpCopy values from f.
     9  // A subsequent deadcode pass is needed to actually remove the copies.
    10  func copyelim(f *Func) {
    11  	phielim(f)
    12  
    13  	// loop of copyelimValue(v) process has been done in phielim() pass.
    14  	// Update block control values.
    15  	for _, b := range f.Blocks {
    16  		for i, v := range b.ControlValues() {
    17  			if v.Op == OpCopy {
    18  				b.ReplaceControl(i, v.Args[0])
    19  			}
    20  		}
    21  	}
    22  
    23  	// Update named values.
    24  	for _, name := range f.Names {
    25  		values := f.NamedValues[*name]
    26  		for i, v := range values {
    27  			if v.Op == OpCopy {
    28  				values[i] = v.Args[0]
    29  			}
    30  		}
    31  	}
    32  }
    33  
    34  // copySource returns the (non-copy) op which is the
    35  // ultimate source of v.  v must be a copy op.
    36  func copySource(v *Value) *Value {
    37  	w := v.Args[0]
    38  
    39  	// This loop is just:
    40  	// for w.Op == OpCopy {
    41  	//     w = w.Args[0]
    42  	// }
    43  	// but we take some extra care to make sure we
    44  	// don't get stuck in an infinite loop.
    45  	// Infinite copy loops may happen in unreachable code.
    46  	// (TODO: or can they? Needs a test.)
    47  	slow := w
    48  	var advance bool
    49  	for w.Op == OpCopy {
    50  		w = w.Args[0]
    51  		if w == slow {
    52  			w.reset(OpUnknown)
    53  			break
    54  		}
    55  		if advance {
    56  			slow = slow.Args[0]
    57  		}
    58  		advance = !advance
    59  	}
    60  
    61  	// The answer is w.  Update all the copies we saw
    62  	// to point directly to w.  Doing this update makes
    63  	// sure that we don't end up doing O(n^2) work
    64  	// for a chain of n copies.
    65  	for v != w {
    66  		x := v.Args[0]
    67  		v.SetArg(0, w)
    68  		v = x
    69  	}
    70  	return w
    71  }
    72  
    73  // copyelimValue ensures that no args of v are copies.
    74  func copyelimValue(v *Value) {
    75  	for i, a := range v.Args {
    76  		if a.Op == OpCopy {
    77  			v.SetArg(i, copySource(a))
    78  		}
    79  	}
    80  }
    81  
    82  // phielim eliminates redundant phi values from f.
    83  // A phi is redundant if its arguments are all equal. For
    84  // purposes of counting, ignore the phi itself. Both of
    85  // these phis are redundant:
    86  //
    87  //	v = phi(x,x,x)
    88  //	v = phi(x,v,x,v)
    89  //
    90  // We repeat this process to also catch situations like:
    91  //
    92  //	v = phi(x, phi(x, x), phi(x, v))
    93  //
    94  // TODO: Can we also simplify cases like:
    95  //
    96  //	v = phi(v, w, x)
    97  //	w = phi(v, w, x)
    98  //
    99  // and would that be useful?
   100  func phielim(f *Func) {
   101  	for {
   102  		change := false
   103  		for _, b := range f.Blocks {
   104  			for _, v := range b.Values {
   105  				// This is an early place in SSA where all values are examined.
   106  				// Rewrite all 0-sized Go values to remove accessors, dereferences, loads, etc.
   107  				if t := v.Type; (t.IsStruct() || t.IsArray()) && t.Size() == 0 {
   108  					if t.IsStruct() {
   109  						v.reset(OpStructMake0)
   110  					} else {
   111  						v.reset(OpArrayMake0)
   112  					}
   113  				}
   114  				// Modify all values so no arg (including args
   115  				// of OpCopy) is a copy.
   116  				copyelimValue(v)
   117  				change = phielimValue(v) || change
   118  			}
   119  		}
   120  		if !change {
   121  			break
   122  		}
   123  	}
   124  }
   125  
   126  // phielimValue tries to convert the phi v to a copy.
   127  func phielimValue(v *Value) bool {
   128  	if v.Op != OpPhi {
   129  		return false
   130  	}
   131  
   132  	// If there are two distinct args of v which
   133  	// are not v itself, then the phi must remain.
   134  	// Otherwise, we can replace it with a copy.
   135  	var w *Value
   136  	for _, x := range v.Args {
   137  		if x == v {
   138  			continue
   139  		}
   140  		if x == w {
   141  			continue
   142  		}
   143  		if w != nil {
   144  			return false
   145  		}
   146  		w = x
   147  	}
   148  
   149  	if w == nil {
   150  		// v references only itself. It must be in
   151  		// a dead code loop. Don't bother modifying it.
   152  		return false
   153  	}
   154  	v.Op = OpCopy
   155  	v.SetArgs1(w)
   156  	f := v.Block.Func
   157  	if f.pass.debug > 0 {
   158  		f.Warnl(v.Pos, "eliminated phi")
   159  	}
   160  	return true
   161  }
   162  

View as plain text