github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/cmd/compile/internal/ssa/fuse.go (about)

     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  // fuse simplifies control flow by joining basic blocks.
     8  func fuse(f *Func) {
     9  	for changed := true; changed; {
    10  		changed = false
    11  		// Fuse from end to beginning, to avoid quadratic behavior in fuseBlockPlain. See issue 13554.
    12  		for i := len(f.Blocks) - 1; i >= 0; i-- {
    13  			b := f.Blocks[i]
    14  			changed = fuseBlockIf(b) || changed
    15  			changed = fuseBlockPlain(b) || changed
    16  		}
    17  	}
    18  }
    19  
    20  // fuseBlockIf handles the following cases where s0 and s1 are empty blocks.
    21  //
    22  //   b        b        b      b
    23  //  / \      | \      / |    | |
    24  // s0  s1    |  s1   s0 |    | |
    25  //  \ /      | /      \ |    | |
    26  //   ss      ss        ss     ss
    27  //
    28  // If all Phi ops in ss have identical variables for slots corresponding to
    29  // s0, s1 and b then the branch can be dropped.
    30  // This optimization often comes up in switch statements with multiple
    31  // expressions in a case clause:
    32  //   switch n {
    33  //     case 1,2,3: return 4
    34  //   }
    35  // TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway.
    36  func fuseBlockIf(b *Block) bool {
    37  	if b.Kind != BlockIf {
    38  		return false
    39  	}
    40  
    41  	var ss0, ss1 *Block
    42  	s0 := b.Succs[0].b
    43  	i0 := b.Succs[0].i
    44  	if s0.Kind != BlockPlain || len(s0.Preds) != 1 || len(s0.Values) != 0 {
    45  		s0, ss0 = b, s0
    46  	} else {
    47  		ss0 = s0.Succs[0].b
    48  		i0 = s0.Succs[0].i
    49  	}
    50  	s1 := b.Succs[1].b
    51  	i1 := b.Succs[1].i
    52  	if s1.Kind != BlockPlain || len(s1.Preds) != 1 || len(s1.Values) != 0 {
    53  		s1, ss1 = b, s1
    54  	} else {
    55  		ss1 = s1.Succs[0].b
    56  		i1 = s1.Succs[0].i
    57  	}
    58  
    59  	if ss0 != ss1 {
    60  		return false
    61  	}
    62  	ss := ss0
    63  
    64  	// s0 and s1 are equal with b if the corresponding block is missing
    65  	// (2nd, 3rd and 4th case in the figure).
    66  
    67  	for _, v := range ss.Values {
    68  		if v.Op == OpPhi && v.Uses > 0 && v.Args[i0] != v.Args[i1] {
    69  			return false
    70  		}
    71  	}
    72  
    73  	// Now we have two of following b->ss, b->s0->ss and b->s1->ss,
    74  	// with s0 and s1 empty if exist.
    75  	// We can replace it with b->ss without if all OpPhis in ss
    76  	// have identical predecessors (verified above).
    77  	// No critical edge is introduced because b will have one successor.
    78  	if s0 != b && s1 != b {
    79  		// Replace edge b->s0->ss with b->ss.
    80  		// We need to keep a slot for Phis corresponding to b.
    81  		b.Succs[0] = Edge{ss, i0}
    82  		ss.Preds[i0] = Edge{b, 0}
    83  		b.removeEdge(1)
    84  		s1.removeEdge(0)
    85  	} else if s0 != b {
    86  		b.removeEdge(0)
    87  		s0.removeEdge(0)
    88  	} else if s1 != b {
    89  		b.removeEdge(1)
    90  		s1.removeEdge(0)
    91  	} else {
    92  		b.removeEdge(1)
    93  	}
    94  	b.Kind = BlockPlain
    95  	b.SetControl(nil)
    96  
    97  	// Trash the empty blocks s0 & s1.
    98  	if s0 != b {
    99  		s0.Kind = BlockInvalid
   100  		s0.Values = nil
   101  		s0.Succs = nil
   102  		s0.Preds = nil
   103  	}
   104  	if s1 != b {
   105  		s1.Kind = BlockInvalid
   106  		s1.Values = nil
   107  		s1.Succs = nil
   108  		s1.Preds = nil
   109  	}
   110  	return true
   111  }
   112  
   113  func fuseBlockPlain(b *Block) bool {
   114  	if b.Kind != BlockPlain {
   115  		return false
   116  	}
   117  
   118  	c := b.Succs[0].b
   119  	if len(c.Preds) != 1 {
   120  		return false
   121  	}
   122  
   123  	// move all of b's values to c.
   124  	for _, v := range b.Values {
   125  		v.Block = c
   126  	}
   127  	// Use whichever value slice is larger, in the hopes of avoiding growth.
   128  	// However, take care to avoid c.Values pointing to b.valstorage.
   129  	// See golang.org/issue/18602.
   130  	if cap(c.Values) >= cap(b.Values) || len(b.Values) <= len(b.valstorage) {
   131  		c.Values = append(c.Values, b.Values...)
   132  	} else {
   133  		c.Values = append(b.Values, c.Values...)
   134  	}
   135  
   136  	// replace b->c edge with preds(b) -> c
   137  	c.predstorage[0] = Edge{}
   138  	if len(b.Preds) > len(b.predstorage) {
   139  		c.Preds = b.Preds
   140  	} else {
   141  		c.Preds = append(c.predstorage[:0], b.Preds...)
   142  	}
   143  	for i, e := range c.Preds {
   144  		p := e.b
   145  		p.Succs[e.i] = Edge{c, i}
   146  	}
   147  	f := b.Func
   148  	if f.Entry == b {
   149  		f.Entry = c
   150  	}
   151  	f.invalidateCFG()
   152  
   153  	// trash b, just in case
   154  	b.Kind = BlockInvalid
   155  	b.Values = nil
   156  	b.Preds = nil
   157  	b.Succs = nil
   158  	return true
   159  }