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