blob: f589b7a07d3cbbe5d5ed9ff54c2af647d0c4697c [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.
ct := f.ConstBool(f.Entry.Line, f.Config.fe.TypeBool(), true)
cf := f.ConstBool(f.Entry.Line, f.Config.fe.TypeBool(), false)
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 {
p := b.Preds[i]
if p.Kind != BlockIf {
continue
}
if p.Control != a {
continue
}
if p.Succs[0] == b {
v.SetArg(i, ct)
} else {
v.SetArg(i, cf)
}
}
}
}
// Step 2: Compute which values are live across blocks.
live := make([]bool, f.NumValues())
for _, b := range f.Blocks {
for _, v := range b.Values {
for _, a := range v.Args {
if a.Block != v.Block {
live[a.ID] = true
}
}
}
if b.Control != nil && b.Control.Block != b {
live[b.Control.ID] = true
}
}
// Step 3: 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 _, b := range f.Blocks {
if b.Kind != BlockIf {
continue
}
if len(b.Values) != 1 {
continue
}
v := b.Values[0]
if v.Op != OpPhi {
continue
}
if b.Control != v {
continue
}
if live[v.ID] {
continue
}
for i := 0; i < len(v.Args); i++ {
a := v.Args[i]
if a.Op != OpConstBool {
continue
}
// The predecessor we come in from.
p := b.Preds[i]
// The successor we always go to when coming in
// from that predecessor.
t := b.Succs[1-a.AuxInt]
// Change the edge p->b to p->t.
for j, x := range p.Succs {
if x == b {
p.Succs[j] = t
break
}
}
// Fix up t to have one more predecessor.
j := predIdx(t, b)
t.Preds = append(t.Preds, p)
for _, w := range t.Values {
if w.Op != OpPhi {
continue
}
w.AddArg(w.Args[j])
}
// Fix up b to have one less predecessor.
n := len(b.Preds) - 1
b.Preds[i] = b.Preds[n]
b.Preds[n] = nil
b.Preds = b.Preds[:n]
v.Args[i].Uses--
v.Args[i] = v.Args[n]
v.Args[n] = nil
v.Args = v.Args[:n]
if n == 1 {
v.Op = OpCopy
// No longer a phi, stop optimizing here.
break
}
i--
}
}
}
// predIdx returns the index where p appears in the predecessor list of b.
// p must be in the predecessor list of b.
func predIdx(b, p *Block) int {
for i, x := range b.Preds {
if x == p {
return i
}
}
panic("predecessor not found")
}