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

     1  // Copyright 2026 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  // maybeRewriteLoopToDownwardCountingLoop tries to rewrite the loop to a
     8  // downward counting loop checking against start if the loop body does
     9  // not depend on ind or nxt and end is known before the loop.
    10  // That means this code:
    11  //
    12  //	loop:
    13  //		ind = (Phi (Const [x]) nxt),
    14  //		if ind < end
    15  //		then goto enter_loop
    16  //		else goto exit_loop
    17  //
    18  //	enter_loop:
    19  //		do something without using ind nor nxt
    20  //		nxt = inc + ind
    21  //		goto loop
    22  //
    23  //	exit_loop:
    24  //
    25  // is rewritten to:
    26  //
    27  //	loop:
    28  //		ind = (Phi end nxt)
    29  //		if (Const [x]) < ind
    30  //		then goto enter_loop
    31  //		else goto exit_loop
    32  //
    33  //	enter_loop:
    34  //		do something without using ind nor nxt
    35  //		nxt = ind - inc
    36  //		goto loop
    37  //
    38  //	exit_loop:
    39  //
    40  // This is better because it only requires to keep ind then nxt alive while looping,
    41  // while the original form keeps ind then nxt and end alive.
    42  //
    43  // If the loop could not be rewritten, it is left unchanged.
    44  func maybeRewriteLoopToDownwardCountingLoop(f *Func, v indVar) {
    45  	ind := v.ind
    46  	nxt := v.nxt
    47  	if !(ind.Uses == 2 && // 2 used by comparison and next
    48  		nxt.Uses == 1) { // 1 used by induction
    49  		return
    50  	}
    51  
    52  	start, end := v.min, v.max
    53  	if v.flags&indVarCountDown != 0 {
    54  		start, end = end, start
    55  	}
    56  
    57  	if !start.isGenericIntConst() {
    58  		// if start is not a constant we would be winning nothing from inverting the loop
    59  		return
    60  	}
    61  	if end.isGenericIntConst() {
    62  		// TODO: if both start and end are constants we should rewrite such that the comparison
    63  		// is against zero and nxt is ++ or -- operation
    64  		// That means:
    65  		//	for i := 2; i < 11; i += 2 {
    66  		// should be rewritten to:
    67  		//	for i := 5; 0 < i; i-- {
    68  		return
    69  	}
    70  
    71  	if end.Block == ind.Block {
    72  		// we can't rewrite loops where the condition depends on the loop body
    73  		// this simple check is forced to work because if this is true a Phi in ind.Block must exist
    74  		return
    75  	}
    76  
    77  	check := v.entry.Preds[0].b.Controls[0]
    78  
    79  	idxEnd, idxStart := -1, -1
    80  	for i, v := range check.Args {
    81  		if v == end {
    82  			idxEnd = i
    83  			break
    84  		}
    85  	}
    86  	for i, v := range ind.Args {
    87  		if v == start {
    88  			idxStart = i
    89  			break
    90  		}
    91  	}
    92  	if idxEnd < 0 || idxStart < 0 {
    93  		return
    94  	}
    95  
    96  	sdom := f.Sdom()
    97  	// the end may not dominate the ind after rewrite, check it first
    98  	if !sdom.IsAncestorEq(end.Block, ind.Block) {
    99  		return
   100  	}
   101  
   102  	// swap start and end in the loop
   103  	check.SetArg(idxEnd, start)
   104  	ind.SetArg(idxStart, end)
   105  
   106  	// invert the check
   107  	check.Args[0], check.Args[1] = check.Args[1], check.Args[0]
   108  
   109  	if nxt.Args[0] != ind {
   110  		// unlike additions subtractions are not commutative so be sure we get it right
   111  		nxt.Args[0], nxt.Args[1] = nxt.Args[1], nxt.Args[0]
   112  	}
   113  
   114  	switch nxt.Op {
   115  	case OpAdd8:
   116  		nxt.Op = OpSub8
   117  	case OpAdd16:
   118  		nxt.Op = OpSub16
   119  	case OpAdd32:
   120  		nxt.Op = OpSub32
   121  	case OpAdd64:
   122  		nxt.Op = OpSub64
   123  	case OpSub8:
   124  		nxt.Op = OpAdd8
   125  	case OpSub16:
   126  		nxt.Op = OpAdd16
   127  	case OpSub32:
   128  		nxt.Op = OpAdd32
   129  	case OpSub64:
   130  		nxt.Op = OpAdd64
   131  	default:
   132  		panic("unreachable")
   133  	}
   134  
   135  	if f.pass.debug > 0 {
   136  		f.Warnl(ind.Pos, "Inverted loop iteration")
   137  	}
   138  }
   139  

View as plain text