src/cmd/compile/internal/ssacompile/phiopt.go GO 359 lines View on github.com → Search inside
1// Copyright 2016 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package ssacompile67import (8	"cmd/compile/internal/ssa"9	"cmd/compile/internal/ssa/block"10	"cmd/compile/internal/ssa/ssaop"11)1213// phiopt eliminates boolean Phis based on the previous if.14//15// Main use case is to transform:16//17//	x := false18//	if b {19//	  x = true20//	}21//22// into x = b.23//24// In SSA code this appears as25//26//	b027//	  If b -> b1 b228//	b129//	  Plain -> b230//	b231//	  x = (OpPhi (ConstBool [true]) (ConstBool [false]))32//33// In this case we can replace x with a copy of b.34func phiopt(f *ssa.Func) {35	sdom := f.Sdom()36	for _, b := range f.Blocks {37		if len(b.Preds) != 2 || len(b.Values) == 0 {38			// TODO: handle more than 2 predecessors, e.g. a || b || c.39			continue40		}4142		pb0, b0 := b, b.Preds[0].B43		for len(b0.Succs) == 1 && len(b0.Preds) == 1 {44			pb0, b0 = b0, b0.Preds[0].B45		}46		if b0.Kind != block.BlockIf {47			continue48		}49		pb1, b1 := b, b.Preds[1].B50		for len(b1.Succs) == 1 && len(b1.Preds) == 1 {51			pb1, b1 = b1, b1.Preds[0].B52		}53		if b1 != b0 {54			continue55		}56		// b0 is the if block giving the boolean value.57		// reverse is the predecessor from which the truth value comes.58		var reverse int59		if b0.Succs[0].B == pb0 && b0.Succs[1].B == pb1 {60			reverse = 061		} else if b0.Succs[0].B == pb1 && b0.Succs[1].B == pb0 {62			reverse = 163		} else {64			b.Fatalf("invalid predecessors\n")65		}6667		for _, v := range b.Values {68			if v.Op != ssaop.OpPhi {69				continue70			}7172			// Look for conversions from bool to 0/1.73			if v.Type.IsInteger() {74				phioptint(v, b0, reverse)75			}7677			if !v.Type.IsBoolean() {78				continue79			}8081			// Replaces82			//   if a { x = true } else { x = false } with x = a83			// and84			//   if a { x = false } else { x = true } with x = !a85			if v.Args[0].Op == ssaop.OpConstBool && v.Args[1].Op == ssaop.OpConstBool {86				if v.Args[reverse].AuxInt != v.Args[1-reverse].AuxInt {87					ops := [2]ssaop.Op{ssaop.OpNot, ssaop.OpCopy}88					v.Reset(ops[v.Args[reverse].AuxInt])89					v.AddArg(b0.Controls[0])90					if f.Pass.Debug > 0 {91						f.Warnl(b.Pos, "converted OpPhi to %v", v.Op)92					}93					continue94				}95			}9697			// Replaces98			//   if a { x = true } else { x = value } with x = a || value.99			// Requires that value dominates x, meaning that regardless of a,100			// value is always computed. This guarantees that the side effects101			// of value are not seen if a is false.102			if v.Args[reverse].Op == ssaop.OpConstBool && v.Args[reverse].AuxInt == 1 {103				if tmp := v.Args[1-reverse]; sdom.IsAncestorEq(tmp.Block, b) {104					v.Reset(ssaop.OpOrB)105					v.SetArgs2(b0.Controls[0], tmp)106					if f.Pass.Debug > 0 {107						f.Warnl(b.Pos, "converted OpPhi to %v", v.Op)108					}109					continue110				}111			}112113			// Replaces114			//   if a { x = value } else { x = false } with x = a && value.115			// Requires that value dominates x, meaning that regardless of a,116			// value is always computed. This guarantees that the side effects117			// of value are not seen if a is false.118			if v.Args[1-reverse].Op == ssaop.OpConstBool && v.Args[1-reverse].AuxInt == 0 {119				if tmp := v.Args[reverse]; sdom.IsAncestorEq(tmp.Block, b) {120					v.Reset(ssaop.OpAndB)121					v.SetArgs2(b0.Controls[0], tmp)122					if f.Pass.Debug > 0 {123						f.Warnl(b.Pos, "converted OpPhi to %v", v.Op)124					}125					continue126				}127			}128			// Replaces129			//   if a { x = value } else { x = a } with x = a && value.130			// Requires that value dominates x.131			if v.Args[1-reverse] == b0.Controls[0] {132				if tmp := v.Args[reverse]; sdom.IsAncestorEq(tmp.Block, b) {133					v.Reset(ssaop.OpAndB)134					v.SetArgs2(b0.Controls[0], tmp)135					if f.Pass.Debug > 0 {136						f.Warnl(b.Pos, "converted OpPhi to %v", v.Op)137					}138					continue139				}140			}141142			// Replaces143			//   if a { x = a } else { x = value } with x = a || value.144			// Requires that value dominates x.145			if v.Args[reverse] == b0.Controls[0] {146				if tmp := v.Args[1-reverse]; sdom.IsAncestorEq(tmp.Block, b) {147					v.Reset(ssaop.OpOrB)148					v.SetArgs2(b0.Controls[0], tmp)149					if f.Pass.Debug > 0 {150						f.Warnl(b.Pos, "converted OpPhi to %v", v.Op)151					}152					continue153				}154			}155		}156	}157	// strengthen phi optimization.158	// Main use case is to transform:159	//   x := false160	//   if c {161	//     x = true162	//     ...163	//   }164	// into165	//   x := c166	//   if x { ... }167	//168	// For example, in SSA code a case appears as169	// b0170	//   If c -> b, sb0171	// sb0172	//   If d -> sd0, sd1173	// sd1174	//   ...175	// sd0176	//   Plain -> b177	// b178	//   x = (OpPhi (ConstBool [true]) (ConstBool [false]))179	//180	// In this case we can also replace x with a copy of c.181	//182	// The optimization idea:183	// 1. block b has a phi value x, x = OpPhi (ConstBool [true]) (ConstBool [false]),184	//    and len(b.Preds) is equal to 2.185	// 2. find the common dominator(b0) of the predecessors(pb0, pb1) of block b, and the186	//    dominator(b0) is a If block.187	//    Special case: one of the predecessors(pb0 or pb1) is the dominator(b0).188	// 3. the successors(sb0, sb1) of the dominator need to dominate the predecessors(pb0, pb1)189	//    of block b respectively.190	// 4. replace this boolean Phi based on dominator block.191	//192	//     b0(pb0)            b0(pb1)          b0193	//    |  \               /  |             /  \194	//    |  sb1           sb0  |           sb0  sb1195	//    |  ...           ...  |           ...   ...196	//    |  pb1           pb0  |           pb0  pb1197	//    |  /               \  |            \   /198	//     b                   b               b199	//200	var lca *lcaRange201	for _, b := range f.Blocks {202		if len(b.Preds) != 2 || len(b.Values) == 0 {203			// TODO: handle more than 2 predecessors, e.g. a || b || c.204			continue205		}206207		for _, v := range b.Values {208			// find a phi value v = OpPhi (ConstBool [true]) (ConstBool [false]).209			// TODO: v = OpPhi (ConstBool [true]) (Arg <bool> {value})210			if v.Op != ssaop.OpPhi {211				continue212			}213			if v.Args[0].Op != ssaop.OpConstBool || v.Args[1].Op != ssaop.OpConstBool {214				continue215			}216			if v.Args[0].AuxInt == v.Args[1].AuxInt {217				continue218			}219220			pb0 := b.Preds[0].B221			pb1 := b.Preds[1].B222			if pb0.Kind == block.BlockIf && pb0 == sdom.Parent(b) {223				// special case: pb0 is the dominator block b0.224				//     b0(pb0)225				//    |  \226				//    |  sb1227				//    |  ...228				//    |  pb1229				//    |  /230				//     b231				// if another successor sb1 of b0(pb0) dominates pb1, do replace.232				ei := b.Preds[0].I233				sb1 := pb0.Succs[1-ei].B234				if sdom.IsAncestorEq(sb1, pb1) {235					convertPhi(pb0, v, ei)236					break237				}238			} else if pb1.Kind == block.BlockIf && pb1 == sdom.Parent(b) {239				// special case: pb1 is the dominator block b0.240				//       b0(pb1)241				//     /   |242				//    sb0  |243				//    ...  |244				//    pb0  |245				//      \  |246				//        b247				// if another successor sb0 of b0(pb0) dominates pb0, do replace.248				ei := b.Preds[1].I249				sb0 := pb1.Succs[1-ei].B250				if sdom.IsAncestorEq(sb0, pb0) {251					convertPhi(pb1, v, 1-ei)252					break253				}254			} else {255				//      b0256				//     /   \257				//    sb0  sb1258				//    ...  ...259				//    pb0  pb1260				//      \   /261				//        b262				//263				// Build data structure for fast least-common-ancestor queries.264				if lca == nil {265					lca = makeLCArange(f)266				}267				b0 := lca.find(pb0, pb1)268				if b0.Kind != block.BlockIf {269					break270				}271				sb0 := b0.Succs[0].B272				sb1 := b0.Succs[1].B273				var reverse int274				if sdom.IsAncestorEq(sb0, pb0) && sdom.IsAncestorEq(sb1, pb1) {275					reverse = 0276				} else if sdom.IsAncestorEq(sb1, pb0) && sdom.IsAncestorEq(sb0, pb1) {277					reverse = 1278				} else {279					break280				}281				if len(sb0.Preds) != 1 || len(sb1.Preds) != 1 {282					// we can not replace phi value x in the following case.283					//   if gp == nil || sp < lo { x = true}284					//   if a || b { x = true }285					// so the if statement can only have one condition.286					break287				}288				convertPhi(b0, v, reverse)289			}290		}291	}292}293294func phioptint(v *ssa.Value, b0 *ssa.Block, reverse int) {295	a0 := v.Args[0]296	a1 := v.Args[1]297	if a0.Op != a1.Op {298		return299	}300301	switch a0.Op {302	case ssaop.OpConst8, ssaop.OpConst16, ssaop.OpConst32, ssaop.OpConst64:303	default:304		return305	}306307	negate := false308	switch {309	case a0.AuxInt == 0 && a1.AuxInt == 1:310		negate = true311	case a0.AuxInt == 1 && a1.AuxInt == 0:312	default:313		return314	}315316	if reverse == 1 {317		negate = !negate318	}319320	a := b0.Controls[0]321	if negate {322		a = v.Block.NewValue1(v.Pos, ssaop.OpNot, a.Type, a)323	}324	v.AddArg(a)325326	cvt := v.Block.NewValue1(v.Pos, ssaop.OpCvtBoolToUint8, v.Block.Func.Config.Types.UInt8, a)327	switch v.Type.Size() {328	case 1:329		v.Reset(ssaop.OpCopy)330	case 2:331		v.Reset(ssaop.OpZeroExt8to16)332	case 4:333		v.Reset(ssaop.OpZeroExt8to32)334	case 8:335		v.Reset(ssaop.OpZeroExt8to64)336	default:337		v.Fatalf("bad int size %d", v.Type.Size())338	}339	v.AddArg(cvt)340341	f := b0.Func342	if f.Pass.Debug > 0 {343		f.Warnl(v.Block.Pos, "converted OpPhi bool -> int%d", v.Type.Size()*8)344	}345}346347// b is the If block giving the boolean value.348// v is the phi value v = (OpPhi (ConstBool [true]) (ConstBool [false])).349// reverse is the predecessor from which the truth value comes.350func convertPhi(b *ssa.Block, v *ssa.Value, reverse int) {351	f := b.Func352	ops := [2]ssaop.Op{ssaop.OpNot, ssaop.OpCopy}353	v.Reset(ops[v.Args[reverse].AuxInt])354	v.AddArg(b.Controls[0])355	if f.Pass.Debug > 0 {356		f.Warnl(b.Pos, "converted OpPhi to %v", v.Op)357	}358}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.