github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/cmd/compile/internal/ssa/loopbce.go (about)

     1  package ssa
     2  
     3  type indVar struct {
     4  	ind   *Value // induction variable
     5  	inc   *Value // increment, a constant
     6  	nxt   *Value // ind+inc variable
     7  	min   *Value // minimum value. inclusive,
     8  	max   *Value // maximum value. exclusive.
     9  	entry *Block // entry block in the loop.
    10  	// Invariants: for all blocks dominated by entry:
    11  	//	min <= ind < max
    12  	//	min <= nxt <= max
    13  }
    14  
    15  // findIndVar finds induction variables in a function.
    16  //
    17  // Look for variables and blocks that satisfy the following
    18  //
    19  // loop:
    20  //   ind = (Phi min nxt),
    21  //   if ind < max
    22  //     then goto enter_loop
    23  //     else goto exit_loop
    24  //
    25  //   enter_loop:
    26  //	do something
    27  //      nxt = inc + ind
    28  //	goto loop
    29  //
    30  // exit_loop:
    31  //
    32  //
    33  // TODO: handle 32 bit operations
    34  func findIndVar(f *Func) []indVar {
    35  	var iv []indVar
    36  	sdom := f.sdom()
    37  
    38  nextb:
    39  	for _, b := range f.Blocks {
    40  		if b.Kind != BlockIf || len(b.Preds) != 2 {
    41  			continue
    42  		}
    43  
    44  		var ind, max *Value // induction, and maximum
    45  		entry := -1         // which successor of b enters the loop
    46  
    47  		// Check thet the control if it either ind < max or max > ind.
    48  		// TODO: Handle Leq64, Geq64.
    49  		switch b.Control.Op {
    50  		case OpLess64:
    51  			entry = 0
    52  			ind, max = b.Control.Args[0], b.Control.Args[1]
    53  		case OpGreater64:
    54  			entry = 0
    55  			ind, max = b.Control.Args[1], b.Control.Args[0]
    56  		default:
    57  			continue nextb
    58  		}
    59  
    60  		// Check that the induction variable is a phi that depends on itself.
    61  		if ind.Op != OpPhi {
    62  			continue
    63  		}
    64  
    65  		// Extract min and nxt knowing that nxt is an addition (e.g. Add64).
    66  		var min, nxt *Value // minimum, and next value
    67  		if n := ind.Args[0]; n.Op == OpAdd64 && (n.Args[0] == ind || n.Args[1] == ind) {
    68  			min, nxt = ind.Args[1], n
    69  		} else if n := ind.Args[1]; n.Op == OpAdd64 && (n.Args[0] == ind || n.Args[1] == ind) {
    70  			min, nxt = ind.Args[0], n
    71  		} else {
    72  			// Not a recognized induction variable.
    73  			continue
    74  		}
    75  
    76  		var inc *Value
    77  		if nxt.Args[0] == ind { // nxt = ind + inc
    78  			inc = nxt.Args[1]
    79  		} else if nxt.Args[1] == ind { // nxt = inc + ind
    80  			inc = nxt.Args[0]
    81  		} else {
    82  			panic("unreachable") // one of the cases must be true from the above.
    83  		}
    84  
    85  		// Expect the increment to be a positive constant.
    86  		// TODO: handle negative increment.
    87  		if inc.Op != OpConst64 || inc.AuxInt <= 0 {
    88  			continue
    89  		}
    90  
    91  		// Up to now we extracted the induction variable (ind),
    92  		// the increment delta (inc), the temporary sum (nxt),
    93  		// the mininum value (min) and the maximum value (max).
    94  		//
    95  		// We also know that ind has the form (Phi min nxt) where
    96  		// nxt is (Add inc nxt) which means: 1) inc dominates nxt
    97  		// and 2) there is a loop starting at inc and containing nxt.
    98  		//
    99  		// We need to prove that the induction variable is incremented
   100  		// only when it's smaller than the maximum value.
   101  		// Two conditions must happen listed below to accept ind
   102  		// as an induction variable.
   103  
   104  		// First condition: loop entry has a single predecessor, which
   105  		// is the header block.  This implies that b.Succs[entry] is
   106  		// reached iff ind < max.
   107  		if len(b.Succs[entry].b.Preds) != 1 {
   108  			// b.Succs[1-entry] must exit the loop.
   109  			continue
   110  		}
   111  
   112  		// Second condition: b.Succs[entry] dominates nxt so that
   113  		// nxt is computed when inc < max, meaning nxt <= max.
   114  		if !sdom.isAncestorEq(b.Succs[entry].b, nxt.Block) {
   115  			// inc+ind can only be reached through the branch that enters the loop.
   116  			continue
   117  		}
   118  
   119  		// If max is c + SliceLen with c <= 0 then we drop c.
   120  		// Makes sure c + SliceLen doesn't overflow when SliceLen == 0.
   121  		// TODO: save c as an offset from max.
   122  		if w, c := dropAdd64(max); (w.Op == OpStringLen || w.Op == OpSliceLen) && 0 >= c && -c >= 0 {
   123  			max = w
   124  		}
   125  
   126  		// We can only guarantee that the loops runs within limits of induction variable
   127  		// if the increment is 1 or when the limits are constants.
   128  		if inc.AuxInt != 1 {
   129  			ok := false
   130  			if min.Op == OpConst64 && max.Op == OpConst64 {
   131  				if max.AuxInt > min.AuxInt && max.AuxInt%inc.AuxInt == min.AuxInt%inc.AuxInt { // handle overflow
   132  					ok = true
   133  				}
   134  			}
   135  			if !ok {
   136  				continue
   137  			}
   138  		}
   139  
   140  		if f.pass.debug > 1 {
   141  			if min.Op == OpConst64 {
   142  				b.Func.Warnl(b.Pos, "Induction variable with minimum %d and increment %d", min.AuxInt, inc.AuxInt)
   143  			} else {
   144  				b.Func.Warnl(b.Pos, "Induction variable with non-const minimum and increment %d", inc.AuxInt)
   145  			}
   146  		}
   147  
   148  		iv = append(iv, indVar{
   149  			ind:   ind,
   150  			inc:   inc,
   151  			nxt:   nxt,
   152  			min:   min,
   153  			max:   max,
   154  			entry: b.Succs[entry].b,
   155  		})
   156  		b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)
   157  	}
   158  
   159  	return iv
   160  }
   161  
   162  // loopbce performs loop based bounds check elimination.
   163  func loopbce(f *Func) {
   164  	ivList := findIndVar(f)
   165  
   166  	m := make(map[*Value]indVar)
   167  	for _, iv := range ivList {
   168  		m[iv.ind] = iv
   169  	}
   170  
   171  	removeBoundsChecks(f, m)
   172  }
   173  
   174  // removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables.
   175  func removeBoundsChecks(f *Func, m map[*Value]indVar) {
   176  	sdom := f.sdom()
   177  	for _, b := range f.Blocks {
   178  		if b.Kind != BlockIf {
   179  			continue
   180  		}
   181  
   182  		v := b.Control
   183  
   184  		// Simplify:
   185  		// (IsInBounds ind max) where 0 <= const == min <= ind < max.
   186  		// (IsSliceInBounds ind max) where 0 <= const == min <= ind < max.
   187  		// Found in:
   188  		//	for i := range a {
   189  		//		use a[i]
   190  		//		use a[i:]
   191  		//		use a[:i]
   192  		//	}
   193  		if v.Op == OpIsInBounds || v.Op == OpIsSliceInBounds {
   194  			ind, add := dropAdd64(v.Args[0])
   195  			if ind.Op != OpPhi {
   196  				goto skip1
   197  			}
   198  			if v.Op == OpIsInBounds && add != 0 {
   199  				goto skip1
   200  			}
   201  			if v.Op == OpIsSliceInBounds && (0 > add || add > 1) {
   202  				goto skip1
   203  			}
   204  
   205  			if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
   206  				if v.Args[1] == iv.max {
   207  					if f.pass.debug > 0 {
   208  						f.Warnl(b.Pos, "Found redundant %s", v.Op)
   209  					}
   210  					goto simplify
   211  				}
   212  			}
   213  		}
   214  	skip1:
   215  
   216  		// Simplify:
   217  		// (IsSliceInBounds ind (SliceCap a)) where 0 <= min <= ind < max == (SliceLen a)
   218  		// Found in:
   219  		//	for i := range a {
   220  		//		use a[:i]
   221  		//		use a[:i+1]
   222  		//	}
   223  		if v.Op == OpIsSliceInBounds {
   224  			ind, add := dropAdd64(v.Args[0])
   225  			if ind.Op != OpPhi {
   226  				goto skip2
   227  			}
   228  			if 0 > add || add > 1 {
   229  				goto skip2
   230  			}
   231  
   232  			if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
   233  				if v.Args[1].Op == OpSliceCap && iv.max.Op == OpSliceLen && v.Args[1].Args[0] == iv.max.Args[0] {
   234  					if f.pass.debug > 0 {
   235  						f.Warnl(b.Pos, "Found redundant %s (len promoted to cap)", v.Op)
   236  					}
   237  					goto simplify
   238  				}
   239  			}
   240  		}
   241  	skip2:
   242  
   243  		// Simplify
   244  		// (IsInBounds (Add64 ind) (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c])
   245  		// (IsSliceInBounds ind (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c])
   246  		if v.Op == OpIsInBounds || v.Op == OpIsSliceInBounds {
   247  			ind, add := dropAdd64(v.Args[0])
   248  			if ind.Op != OpPhi {
   249  				goto skip3
   250  			}
   251  
   252  			// ind + add >= 0 <-> min + add >= 0 <-> min >= -add
   253  			if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) {
   254  				if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() {
   255  					goto skip3
   256  				}
   257  
   258  				limit := v.Args[1].AuxInt
   259  				if v.Op == OpIsSliceInBounds {
   260  					// If limit++ overflows signed integer then 0 <= max && max <= limit will be false.
   261  					limit++
   262  				}
   263  
   264  				if max := iv.max.AuxInt + add; 0 <= max && max <= limit { // handle overflow
   265  					if f.pass.debug > 0 {
   266  						f.Warnl(b.Pos, "Found redundant (%s ind %d), ind < %d", v.Op, v.Args[1].AuxInt, iv.max.AuxInt+add)
   267  					}
   268  					goto simplify
   269  				}
   270  			}
   271  		}
   272  	skip3:
   273  
   274  		continue
   275  
   276  	simplify:
   277  		f.Logf("removing bounds check %v at %v in %s\n", b.Control, b, f.Name)
   278  		b.Kind = BlockFirst
   279  		b.SetControl(nil)
   280  	}
   281  }
   282  
   283  func dropAdd64(v *Value) (*Value, int64) {
   284  	if v.Op == OpAdd64 && v.Args[0].Op == OpConst64 {
   285  		return v.Args[1], v.Args[0].AuxInt
   286  	}
   287  	if v.Op == OpAdd64 && v.Args[1].Op == OpConst64 {
   288  		return v.Args[0], v.Args[1].AuxInt
   289  	}
   290  	return v, 0
   291  }
   292  
   293  func isGreaterOrEqualThan(v *Value, c int64) bool {
   294  	if c == 0 {
   295  		return isNonNegative(v)
   296  	}
   297  	if v.isGenericIntConst() && v.AuxInt >= c {
   298  		return true
   299  	}
   300  	return false
   301  }