blob: cc23701c3959440d4a430219560a1a274754c389 [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// Shortcircuit finds situations where branch directions
// are always correlated and rewrites the CFG to take
// advantage of that fact.
// This optimization is useful for compiling && and || expressions.
func shortcircuit(f *Func) {
// Step 1: Replace a phi arg with a constant if that arg
// is the control value of a preceding If block.
// b1:
// If a goto b2 else b3
// b2: <- b1 ...
// x = phi(a, ...)
//
// We can replace the "a" in the phi with the constant true.
var ct, cf *Value
for _, b := range f.Blocks {
for _, v := range b.Values {
if v.Op != OpPhi {
continue
}
if !v.Type.IsBoolean() {
continue
}
for i, a := range v.Args {
e := b.Preds[i]
p := e.b
if p.Kind != BlockIf {
continue
}
if p.Controls[0] != a {
continue
}
if e.i == 0 {
if ct == nil {
ct = f.ConstBool(f.Config.Types.Bool, true)
}
v.SetArg(i, ct)
} else {
if cf == nil {
cf = f.ConstBool(f.Config.Types.Bool, false)
}
v.SetArg(i, cf)
}
}
}
}
// Step 2: Redirect control flow around known branches.
// p:
// ... goto b ...
// b: <- p ...
// v = phi(true, ...)
// if v goto t else u
// We can redirect p to go directly to t instead of b.
// (If v is not live after b).
for changed := true; changed; {
changed = false
for i := len(f.Blocks) - 1; i >= 0; i-- {
b := f.Blocks[i]
if fuseBlockPlain(b) {
changed = true
continue
}
changed = shortcircuitBlock(b) || changed
}
if changed {
f.invalidateCFG()
}
}
}
// shortcircuitBlock checks for a CFG of the form
//
// p other pred(s)
// \ /
// b
// / \
// s other succ
//
// in which b is an If block containing a single phi value with a single use,
// which has a ConstBool arg.
// The only use of the phi value must be the control value of b.
// p is the predecessor determined by the argument slot in which the ConstBool is found.
//
// It rewrites this into
//
// p other pred(s)
// | /
// | b
// |/ \
// s other succ
//
// and removes the appropriate phi arg(s).
func shortcircuitBlock(b *Block) bool {
if b.Kind != BlockIf {
return false
}
// Look for control values of the form Copy(Not(Copy(Phi(const, ...)))).
// Those must be the only values in the b, and they each must be used only by b.
// Track the negations so that we can swap successors as needed later.
ctl := b.Controls[0]
nval := 1 // the control value
var swap int64
for ctl.Uses == 1 && ctl.Block == b && (ctl.Op == OpCopy || ctl.Op == OpNot) {
if ctl.Op == OpNot {
swap = 1 ^ swap
}
ctl = ctl.Args[0]
nval++ // wrapper around control value
}
if len(b.Values) != nval || ctl.Op != OpPhi || ctl.Block != b || ctl.Uses != 1 {
return false
}
// Locate index of first const phi arg.
cidx := -1
for i, a := range ctl.Args {
if a.Op == OpConstBool {
cidx = i
break
}
}
if cidx == -1 {
return false
}
// p is the predecessor corresponding to cidx.
pe := b.Preds[cidx]
p := pe.b
pi := pe.i
// t is the "taken" branch: the successor we always go to when coming in from p.
ti := 1 ^ ctl.Args[cidx].AuxInt ^ swap
te := b.Succs[ti]
t := te.b
if p == b || t == b {
// This is an infinite loop; we can't remove it. See issue 33903.
return false
}
// We're committed. Update CFG and Phis.
// Remove b's incoming edge from p.
b.removePred(cidx)
n := len(b.Preds)
ctl.Args[cidx].Uses--
ctl.Args[cidx] = ctl.Args[n]
ctl.Args[n] = nil
ctl.Args = ctl.Args[:n]
// Redirect p's outgoing edge to t.
p.Succs[pi] = Edge{t, len(t.Preds)}
// Fix up t to have one more predecessor.
t.Preds = append(t.Preds, Edge{p, pi})
for _, v := range t.Values {
if v.Op != OpPhi {
continue
}
v.AddArg(v.Args[te.i])
}
if len(b.Preds) == 0 {
// Block is now dead.
b.Kind = BlockInvalid
}
phielimValue(ctl)
return true
}