github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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  // TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway.
    29  func fuseBlockIf(b *Block) bool {
    30  	if b.Kind != BlockIf {
    31  		return false
    32  	}
    33  
    34  	var ss0, ss1 *Block
    35  	s0 := b.Succs[0]
    36  	if s0.Kind != BlockPlain || len(s0.Preds) != 1 || len(s0.Values) != 0 {
    37  		s0, ss0 = b, s0
    38  	} else {
    39  		ss0 = s0.Succs[0]
    40  	}
    41  	s1 := b.Succs[1]
    42  	if s1.Kind != BlockPlain || len(s1.Preds) != 1 || len(s1.Values) != 0 {
    43  		s1, ss1 = b, s1
    44  	} else {
    45  		ss1 = s1.Succs[0]
    46  	}
    47  
    48  	if ss0 != ss1 {
    49  		return false
    50  	}
    51  	ss := ss0
    52  
    53  	// s0 and s1 are equal with b if the corresponding block is missing
    54  	// (2nd, 3rd and 4th case in the figure).
    55  	i0, i1 := -1, -1
    56  	for i, p := range ss.Preds {
    57  		if p == s0 {
    58  			i0 = i
    59  		}
    60  		if p == s1 {
    61  			i1 = i
    62  		}
    63  	}
    64  	if i0 == -1 || i1 == -1 {
    65  		b.Fatalf("invalid predecessors")
    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  		ss.removePred(s0)
    80  
    81  		// Replace edge b->s1->ss with b->ss.
    82  		// We need to keep a slot for Phis corresponding to b.
    83  		for i := range b.Succs {
    84  			if b.Succs[i] == s1 {
    85  				b.Succs[i] = ss
    86  			}
    87  		}
    88  		for i := range ss.Preds {
    89  			if ss.Preds[i] == s1 {
    90  				ss.Preds[i] = b
    91  			}
    92  		}
    93  	} else if s0 != b {
    94  		ss.removePred(s0)
    95  	} else if s1 != b {
    96  		ss.removePred(s1)
    97  	}
    98  	b.Kind = BlockPlain
    99  	b.SetControl(nil)
   100  	b.Succs = append(b.Succs[:0], ss)
   101  
   102  	// Trash the empty blocks s0 & s1.
   103  	if s0 != b {
   104  		s0.Kind = BlockInvalid
   105  		s0.Values = nil
   106  		s0.Succs = nil
   107  		s0.Preds = nil
   108  	}
   109  	if s1 != b {
   110  		s1.Kind = BlockInvalid
   111  		s1.Values = nil
   112  		s1.Succs = nil
   113  		s1.Preds = nil
   114  	}
   115  	return true
   116  }
   117  
   118  func fuseBlockPlain(b *Block) bool {
   119  	if b.Kind != BlockPlain {
   120  		return false
   121  	}
   122  
   123  	c := b.Succs[0]
   124  	if len(c.Preds) != 1 {
   125  		return false
   126  	}
   127  
   128  	// move all of b'c values to c.
   129  	for _, v := range b.Values {
   130  		v.Block = c
   131  		c.Values = append(c.Values, v)
   132  	}
   133  
   134  	// replace b->c edge with preds(b) -> c
   135  	c.predstorage[0] = nil
   136  	if len(b.Preds) > len(b.predstorage) {
   137  		c.Preds = b.Preds
   138  	} else {
   139  		c.Preds = append(c.predstorage[:0], b.Preds...)
   140  	}
   141  	for _, p := range c.Preds {
   142  		for i, q := range p.Succs {
   143  			if q == b {
   144  				p.Succs[i] = c
   145  			}
   146  		}
   147  	}
   148  	if f := b.Func; f.Entry == b {
   149  		f.Entry = c
   150  	}
   151  
   152  	// trash b, just in case
   153  	b.Kind = BlockInvalid
   154  	b.Values = nil
   155  	b.Preds = nil
   156  	b.Succs = nil
   157  	return true
   158  }