Source file src/cmd/compile/internal/ssa/phiopt.go

Documentation: cmd/compile/internal/ssa

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

View as plain text