github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/phiopt.go (about)

     1  // Copyright 2016 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  // phiopt eliminates boolean Phis based on the previous if.
     8  //
     9  // Main use case is to transform:
    10  //   x := false
    11  //   if b {
    12  //     x = true
    13  //   }
    14  // into x = b.
    15  //
    16  // In SSA code this appears as
    17  //
    18  // b0
    19  //   If b -> b1 b2
    20  // b1
    21  //   Plain -> b2
    22  // b2
    23  //   x = (OpPhi (ConstBool [true]) (ConstBool [false]))
    24  //
    25  // In this case we can replace x with a copy of b.
    26  func phiopt(f *Func) {
    27  	for _, b := range f.Blocks {
    28  		if len(b.Preds) != 2 || len(b.Values) == 0 {
    29  			// TODO: handle more than 2 predecessors, e.g. a || b || c.
    30  			continue
    31  		}
    32  
    33  		pb0, b0 := b, b.Preds[0].b
    34  		for len(b0.Succs) == 1 && len(b0.Preds) == 1 {
    35  			pb0, b0 = b0, b0.Preds[0].b
    36  		}
    37  		if b0.Kind != BlockIf {
    38  			continue
    39  		}
    40  		pb1, b1 := b, b.Preds[1].b
    41  		for len(b1.Succs) == 1 && len(b1.Preds) == 1 {
    42  			pb1, b1 = b1, b1.Preds[0].b
    43  		}
    44  		if b1 != b0 {
    45  			continue
    46  		}
    47  		// b0 is the if block giving the boolean value.
    48  
    49  		// reverse is the predecessor from which the truth value comes.
    50  		var reverse int
    51  		if b0.Succs[0].b == pb0 && b0.Succs[1].b == pb1 {
    52  			reverse = 0
    53  		} else if b0.Succs[0].b == pb1 && b0.Succs[1].b == pb0 {
    54  			reverse = 1
    55  		} else {
    56  			b.Fatalf("invalid predecessors\n")
    57  		}
    58  
    59  		for _, v := range b.Values {
    60  			if v.Op != OpPhi {
    61  				continue
    62  			}
    63  
    64  			// Look for conversions from bool to 0/1.
    65  			if v.Type.IsInteger() {
    66  				phioptint(v, b0, reverse)
    67  			}
    68  
    69  			if !v.Type.IsBoolean() {
    70  				continue
    71  			}
    72  
    73  			// Replaces
    74  			//   if a { x = true } else { x = false } with x = a
    75  			// and
    76  			//   if a { x = false } else { x = true } with x = !a
    77  			if v.Args[0].Op == OpConstBool && v.Args[1].Op == OpConstBool {
    78  				if v.Args[reverse].AuxInt != v.Args[1-reverse].AuxInt {
    79  					ops := [2]Op{OpNot, OpCopy}
    80  					v.reset(ops[v.Args[reverse].AuxInt])
    81  					v.AddArg(b0.Control)
    82  					if f.pass.debug > 0 {
    83  						f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
    84  					}
    85  					continue
    86  				}
    87  			}
    88  
    89  			// Replaces
    90  			//   if a { x = true } else { x = value } with x = a || value.
    91  			// Requires that value dominates x, meaning that regardless of a,
    92  			// value is always computed. This guarantees that the side effects
    93  			// of value are not seen if a is false.
    94  			if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 {
    95  				if tmp := v.Args[1-reverse]; f.sdom.isAncestorEq(tmp.Block, b) {
    96  					v.reset(OpOrB)
    97  					v.SetArgs2(b0.Control, tmp)
    98  					if f.pass.debug > 0 {
    99  						f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
   100  					}
   101  					continue
   102  				}
   103  			}
   104  
   105  			// Replaces
   106  			//   if a { x = value } else { x = false } with x = a && value.
   107  			// Requires that value dominates x, meaning that regardless of a,
   108  			// value is always computed. This guarantees that the side effects
   109  			// of value are not seen if a is false.
   110  			if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 {
   111  				if tmp := v.Args[reverse]; f.sdom.isAncestorEq(tmp.Block, b) {
   112  					v.reset(OpAndB)
   113  					v.SetArgs2(b0.Control, tmp)
   114  					if f.pass.debug > 0 {
   115  						f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
   116  					}
   117  					continue
   118  				}
   119  			}
   120  		}
   121  	}
   122  }
   123  
   124  func phioptint(v *Value, b0 *Block, reverse int) {
   125  	a0 := v.Args[0]
   126  	a1 := v.Args[1]
   127  	if a0.Op != a1.Op {
   128  		return
   129  	}
   130  
   131  	switch a0.Op {
   132  	case OpConst8, OpConst16, OpConst32, OpConst64:
   133  	default:
   134  		return
   135  	}
   136  
   137  	negate := false
   138  	switch {
   139  	case a0.AuxInt == 0 && a1.AuxInt == 1:
   140  		negate = true
   141  	case a0.AuxInt == 1 && a1.AuxInt == 0:
   142  	default:
   143  		return
   144  	}
   145  
   146  	if reverse == 1 {
   147  		negate = !negate
   148  	}
   149  
   150  	switch v.Type.Size() {
   151  	case 1:
   152  		v.reset(OpCopy)
   153  	case 2:
   154  		v.reset(OpZeroExt8to16)
   155  	case 4:
   156  		v.reset(OpZeroExt8to32)
   157  	case 8:
   158  		v.reset(OpZeroExt8to64)
   159  	default:
   160  		v.Fatalf("bad int size %d", v.Type.Size())
   161  	}
   162  
   163  	a := b0.Control
   164  	if negate {
   165  		a = v.Block.NewValue1(v.Line, OpNot, a.Type, a)
   166  	}
   167  	v.AddArg(a)
   168  
   169  	f := b0.Func
   170  	if f.pass.debug > 0 {
   171  		f.Config.Warnl(v.Block.Line, "converted OpPhi bool -> int%d", v.Type.Size()*8)
   172  	}
   173  }