...

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

     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