github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/compile/internal/ssa/branchelim.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  // branchelim tries to elminiate branches by
     8  // generating CondSelect instructions.
     9  //
    10  // Search for basic blocks that look like
    11  //
    12  // bb0            bb0
    13  //  | \          /   \
    14  //  | bb1  or  bb1   bb2    <- trivial if/else blocks
    15  //  | /          \   /
    16  // bb2            bb3
    17  //
    18  // where the intermediate blocks are mostly empty (with no side-effects);
    19  // rewrite Phis in the postdominator as CondSelects.
    20  func branchelim(f *Func) {
    21  	// FIXME: add support for lowering CondSelects on more architectures
    22  	if f.Config.arch != "arm64" {
    23  		return
    24  	}
    25  
    26  	change := true
    27  	for change {
    28  		change = false
    29  		for _, b := range f.Blocks {
    30  			change = elimIf(f, b) || elimIfElse(f, b) || change
    31  		}
    32  	}
    33  }
    34  
    35  func canCondSelect(v *Value) bool {
    36  	// For now, stick to simple scalars that fit in registers
    37  	sz := v.Type.Size()
    38  	return sz <= v.Block.Func.Config.RegSize && (v.Type.IsInteger() || v.Type.IsPtrShaped())
    39  }
    40  
    41  func elimIf(f *Func, dom *Block) bool {
    42  	// See if dom is an If with one arm that
    43  	// is trivial and succeeded by the other
    44  	// successor of dom.
    45  	if dom.Kind != BlockIf || dom.Likely != BranchUnknown {
    46  		return false
    47  	}
    48  	var simple, post *Block
    49  	for i := range dom.Succs {
    50  		bb, other := dom.Succs[i].Block(), dom.Succs[i^1].Block()
    51  		if isLeafPlain(bb) && bb.Succs[0].Block() == other {
    52  			simple = bb
    53  			post = other
    54  			break
    55  		}
    56  	}
    57  	if simple == nil || len(post.Preds) != 2 || post == dom {
    58  		return false
    59  	}
    60  
    61  	// We've found our diamond CFG of blocks.
    62  	// Now decide if fusing 'simple' into dom+post
    63  	// looks profitable.
    64  
    65  	// Check that there are Phis, and that all of them
    66  	// can be safely rewritten to CondSelect.
    67  	hasphis := false
    68  	for _, v := range post.Values {
    69  		if v.Op == OpPhi {
    70  			hasphis = true
    71  			if !canCondSelect(v) {
    72  				return false
    73  			}
    74  		}
    75  	}
    76  	if !hasphis {
    77  		return false
    78  	}
    79  
    80  	// Pick some upper bound for the number of instructions
    81  	// we'd be willing to execute just to generate a dead
    82  	// argument to CondSelect. In the worst case, this is
    83  	// the number of useless instructions executed.
    84  	const maxfuseinsts = 2
    85  
    86  	if len(simple.Values) > maxfuseinsts || !allTrivial(simple) {
    87  		return false
    88  	}
    89  
    90  	// Replace Phi instructions in b with CondSelect instructions
    91  	swap := (post.Preds[0].Block() == dom) != (dom.Succs[0].Block() == post)
    92  	for _, v := range post.Values {
    93  		if v.Op != OpPhi {
    94  			continue
    95  		}
    96  		v.Op = OpCondSelect
    97  		if swap {
    98  			v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
    99  		}
   100  		v.AddArg(dom.Control)
   101  	}
   102  
   103  	// Put all of the instructions into 'dom'
   104  	// and update the CFG appropriately.
   105  	dom.Kind = post.Kind
   106  	dom.SetControl(post.Control)
   107  	dom.Aux = post.Aux
   108  	dom.Succs = append(dom.Succs[:0], post.Succs...)
   109  	for i := range dom.Succs {
   110  		e := dom.Succs[i]
   111  		e.b.Preds[e.i].b = dom
   112  	}
   113  
   114  	for i := range simple.Values {
   115  		simple.Values[i].Block = dom
   116  	}
   117  	for i := range post.Values {
   118  		post.Values[i].Block = dom
   119  	}
   120  	dom.Values = append(dom.Values, simple.Values...)
   121  	dom.Values = append(dom.Values, post.Values...)
   122  
   123  	// Trash 'post' and 'simple'
   124  	clobberBlock(post)
   125  	clobberBlock(simple)
   126  
   127  	f.invalidateCFG()
   128  	return true
   129  }
   130  
   131  // is this a BlockPlain with one predecessor?
   132  func isLeafPlain(b *Block) bool {
   133  	return b.Kind == BlockPlain && len(b.Preds) == 1
   134  }
   135  
   136  func clobberBlock(b *Block) {
   137  	b.Values = nil
   138  	b.Preds = nil
   139  	b.Succs = nil
   140  	b.Aux = nil
   141  	b.SetControl(nil)
   142  	b.Kind = BlockInvalid
   143  }
   144  
   145  func elimIfElse(f *Func, b *Block) bool {
   146  	// See if 'b' ends in an if/else: it should
   147  	// have two successors, both of which are BlockPlain
   148  	// and succeeded by the same block.
   149  	if b.Kind != BlockIf || b.Likely != BranchUnknown {
   150  		return false
   151  	}
   152  	yes, no := b.Succs[0].Block(), b.Succs[1].Block()
   153  	if !isLeafPlain(yes) || len(yes.Values) > 1 || !allTrivial(yes) {
   154  		return false
   155  	}
   156  	if !isLeafPlain(no) || len(no.Values) > 1 || !allTrivial(no) {
   157  		return false
   158  	}
   159  	if b.Succs[0].Block().Succs[0].Block() != b.Succs[1].Block().Succs[0].Block() {
   160  		return false
   161  	}
   162  	// block that postdominates the if/else
   163  	post := b.Succs[0].Block().Succs[0].Block()
   164  	if len(post.Preds) != 2 || post == b {
   165  		return false
   166  	}
   167  	hasphis := false
   168  	for _, v := range post.Values {
   169  		if v.Op == OpPhi {
   170  			hasphis = true
   171  			if !canCondSelect(v) {
   172  				return false
   173  			}
   174  		}
   175  	}
   176  	if !hasphis {
   177  		return false
   178  	}
   179  
   180  	// now we're committed: rewrite each Phi as a CondSelect
   181  	swap := post.Preds[0].Block() != b.Succs[0].Block()
   182  	for _, v := range post.Values {
   183  		if v.Op != OpPhi {
   184  			continue
   185  		}
   186  		v.Op = OpCondSelect
   187  		if swap {
   188  			v.Args[0], v.Args[1] = v.Args[1], v.Args[0]
   189  		}
   190  		v.AddArg(b.Control)
   191  	}
   192  
   193  	// Move the contents of all of these
   194  	// blocks into 'b' and update CFG edges accordingly
   195  	b.Kind = post.Kind
   196  	b.SetControl(post.Control)
   197  	b.Aux = post.Aux
   198  	b.Succs = append(b.Succs[:0], post.Succs...)
   199  	for i := range b.Succs {
   200  		e := b.Succs[i]
   201  		e.b.Preds[e.i].b = b
   202  	}
   203  	for i := range post.Values {
   204  		post.Values[i].Block = b
   205  	}
   206  	for i := range yes.Values {
   207  		yes.Values[i].Block = b
   208  	}
   209  	for i := range no.Values {
   210  		no.Values[i].Block = b
   211  	}
   212  	b.Values = append(b.Values, yes.Values...)
   213  	b.Values = append(b.Values, no.Values...)
   214  	b.Values = append(b.Values, post.Values...)
   215  
   216  	// trash post, yes, and no
   217  	clobberBlock(yes)
   218  	clobberBlock(no)
   219  	clobberBlock(post)
   220  
   221  	f.invalidateCFG()
   222  	return true
   223  }
   224  
   225  func allTrivial(b *Block) bool {
   226  	// don't fuse memory ops, Phi ops, divides (can panic),
   227  	// or anything else with side-effects
   228  	for _, v := range b.Values {
   229  		if v.Op == OpPhi || isDivMod(v.Op) || v.Type.IsMemory() ||
   230  			v.MemoryArg() != nil || opcodeTable[v.Op].hasSideEffects {
   231  			return false
   232  		}
   233  	}
   234  	return true
   235  }
   236  
   237  func isDivMod(op Op) bool {
   238  	switch op {
   239  	case OpDiv8, OpDiv8u, OpDiv16, OpDiv16u,
   240  		OpDiv32, OpDiv32u, OpDiv64, OpDiv64u, OpDiv128u,
   241  		OpDiv32F, OpDiv64F,
   242  		OpMod8, OpMod8u, OpMod16, OpMod16u,
   243  		OpMod32, OpMod32u, OpMod64, OpMod64u:
   244  		return true
   245  	default:
   246  		return false
   247  	}
   248  }