// 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 v10 v8 // If v11 goto b else u // b: <- p ... // v17 = Leq64 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 }