github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/cmd/compile/internal/ssa/looprotate.go (about)

     1  // Copyright 2017 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  // loopRotate converts loops with a check-loop-condition-at-beginning
     8  // to loops with a check-loop-condition-at-end.
     9  // This helps loops avoid extra unnecessary jumps.
    10  //
    11  //   loop:
    12  //     CMPQ ...
    13  //     JGE exit
    14  //     ...
    15  //     JMP loop
    16  //   exit:
    17  //
    18  //    JMP entry
    19  //  loop:
    20  //    ...
    21  //  entry:
    22  //    CMPQ ...
    23  //    JLT loop
    24  func loopRotate(f *Func) {
    25  	loopnest := f.loopnest()
    26  	if len(loopnest.loops) == 0 {
    27  		return
    28  	}
    29  
    30  	// Set of blocks we're moving, by ID.
    31  	move := map[ID]struct{}{}
    32  
    33  	// Map from block ID to the moving block that should
    34  	// come right after it.
    35  	after := map[ID]*Block{}
    36  
    37  	// Check each loop header and decide if we want to move it.
    38  	for _, loop := range loopnest.loops {
    39  		b := loop.header
    40  		var p *Block // b's in-loop predecessor
    41  		for _, e := range b.Preds {
    42  			if e.b.Kind != BlockPlain {
    43  				continue
    44  			}
    45  			if loopnest.b2l[e.b.ID] != loop {
    46  				continue
    47  			}
    48  			p = e.b
    49  		}
    50  		if p == nil || p == b {
    51  			continue
    52  		}
    53  
    54  		// Place b after p.
    55  		move[b.ID] = struct{}{}
    56  		after[p.ID] = b
    57  	}
    58  
    59  	// Move blocks to their destinations in a single pass.
    60  	// We rely here on the fact that loop headers must come
    61  	// before the rest of the loop.  And that relies on the
    62  	// fact that we only identify reducible loops.
    63  	j := 0
    64  	for i, b := range f.Blocks {
    65  		if _, ok := move[b.ID]; ok {
    66  			continue
    67  		}
    68  		f.Blocks[j] = b
    69  		j++
    70  		if a := after[b.ID]; a != nil {
    71  			if j > i {
    72  				f.Fatalf("head before tail in loop %s", b)
    73  			}
    74  			f.Blocks[j] = a
    75  			j++
    76  		}
    77  	}
    78  	if j != len(f.Blocks) {
    79  		f.Fatalf("bad reordering in looprotate")
    80  	}
    81  }