blob: 153c2a56b716b21eaaac2f37c50bb0fb249b2902 [file] [log] [blame]
// Copyright 2021 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
// fuseBranchRedirect checks for a CFG in which the outbound branch
// of an If block can be derived from its predecessor If block, in
// some such cases, we can redirect the predecessor If block to the
// corresponding successor block directly. For example:
//
// p:
// v11 = Less64 <bool> v10 v8
// If v11 goto b else u
// b: <- p ...
// v17 = Leq64 <bool> v10 v8
// If v17 goto s else o
//
// We can redirect p to s directly.
//
// The implementation here borrows the framework of the prove pass.
//
// 1, Traverse all blocks of function f to find If blocks.
// 2, For any If block b, traverse all its predecessors to find If blocks.
// 3, For any If block predecessor p, update relationship p->b.
// 4, Traverse all successors of b.
// 5, For any successor s of b, try to update relationship b->s, if a
// contradiction is found then redirect p to another successor of b.
func fuseBranchRedirect(f *Func) bool {
ft := newFactsTable(f)
ft.checkpoint()
changed := false
for i := len(f.Blocks) - 1; i >= 0; i-- {
b := f.Blocks[i]
if b.Kind != BlockIf {
continue
}
// b is either empty or only contains the control value.
// TODO: if b contains only OpCopy or OpNot related to b.Controls,
// such as Copy(Not(Copy(Less64(v1, v2)))), perhaps it can be optimized.
bCtl := b.Controls[0]
if bCtl.Block != b && len(b.Values) != 0 || (len(b.Values) != 1 || bCtl.Uses != 1) && bCtl.Block == b {
continue
}
for k := 0; k < len(b.Preds); k++ {
pk := b.Preds[k]
p := pk.b
if p.Kind != BlockIf || p == b {
continue
}
pbranch := positive
if pk.i == 1 {
pbranch = negative
}
ft.checkpoint()
// Assume branch p->b is taken.
addBranchRestrictions(ft, p, pbranch)
// Check if any outgoing branch is unreachable based on the above condition.
parent := b
for j, bbranch := range [...]branch{positive, negative} {
ft.checkpoint()
// Try to update relationship b->child, and check if the contradiction occurs.
addBranchRestrictions(ft, parent, bbranch)
unsat := ft.unsat
ft.restore()
if !unsat {
continue
}
// This branch is impossible,so redirect p directly to another branch.
out := 1 ^ j
child := parent.Succs[out].b
if child == b {
continue
}
b.removePred(k)
p.Succs[pk.i] = Edge{child, len(child.Preds)}
// Fix up Phi value in b to have one less argument.
for _, v := range b.Values {
if v.Op != OpPhi {
continue
}
b.removePhiArg(v, k)
}
// Fix up child to have one more predecessor.
child.Preds = append(child.Preds, Edge{p, pk.i})
ai := b.Succs[out].i
for _, v := range child.Values {
if v.Op != OpPhi {
continue
}
v.AddArg(v.Args[ai])
}
if b.Func.pass.debug > 0 {
b.Func.Warnl(b.Controls[0].Pos, "Redirect %s based on %s", b.Controls[0].Op, p.Controls[0].Op)
}
changed = true
k--
break
}
ft.restore()
}
if len(b.Preds) == 0 && b != f.Entry {
// Block is now dead.
b.Kind = BlockInvalid
}
}
ft.restore()
ft.cleanup(f)
return changed
}