github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/fuse_branchredirect.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssa
     6  
     7  // fuseBranchRedirect checks for a CFG in which the outbound branch
     8  // of an If block can be derived from its predecessor If block, in
     9  // some such cases, we can redirect the predecessor If block to the
    10  // corresponding successor block directly. For example:
    11  //
    12  //	p:
    13  //	  v11 = Less64 <bool> v10 v8
    14  //	  If v11 goto b else u
    15  //	b: <- p ...
    16  //	  v17 = Leq64 <bool> v10 v8
    17  //	  If v17 goto s else o
    18  //
    19  // We can redirect p to s directly.
    20  //
    21  // The implementation here borrows the framework of the prove pass.
    22  //
    23  //	1, Traverse all blocks of function f to find If blocks.
    24  //	2,   For any If block b, traverse all its predecessors to find If blocks.
    25  //	3,     For any If block predecessor p, update relationship p->b.
    26  //	4,     Traverse all successors of b.
    27  //	5,       For any successor s of b, try to update relationship b->s, if a
    28  //	         contradiction is found then redirect p to another successor of b.
    29  func fuseBranchRedirect(f *Func) bool {
    30  	ft := newFactsTable(f)
    31  	ft.checkpoint()
    32  
    33  	changed := false
    34  	for i := len(f.Blocks) - 1; i >= 0; i-- {
    35  		b := f.Blocks[i]
    36  		if b.Kind != BlockIf {
    37  			continue
    38  		}
    39  		// b is either empty or only contains the control value.
    40  		// TODO: if b contains only OpCopy or OpNot related to b.Controls,
    41  		// such as Copy(Not(Copy(Less64(v1, v2)))), perhaps it can be optimized.
    42  		bCtl := b.Controls[0]
    43  		if bCtl.Block != b && len(b.Values) != 0 || (len(b.Values) != 1 || bCtl.Uses != 1) && bCtl.Block == b {
    44  			continue
    45  		}
    46  
    47  		for k := 0; k < len(b.Preds); k++ {
    48  			pk := b.Preds[k]
    49  			p := pk.b
    50  			if p.Kind != BlockIf || p == b {
    51  				continue
    52  			}
    53  			pbranch := positive
    54  			if pk.i == 1 {
    55  				pbranch = negative
    56  			}
    57  			ft.checkpoint()
    58  			// Assume branch p->b is taken.
    59  			addBranchRestrictions(ft, p, pbranch)
    60  			// Check if any outgoing branch is unreachable based on the above condition.
    61  			parent := b
    62  			for j, bbranch := range [...]branch{positive, negative} {
    63  				ft.checkpoint()
    64  				// Try to update relationship b->child, and check if the contradiction occurs.
    65  				addBranchRestrictions(ft, parent, bbranch)
    66  				unsat := ft.unsat
    67  				ft.restore()
    68  				if !unsat {
    69  					continue
    70  				}
    71  				// This branch is impossible,so redirect p directly to another branch.
    72  				out := 1 ^ j
    73  				child := parent.Succs[out].b
    74  				if child == b {
    75  					continue
    76  				}
    77  				b.removePred(k)
    78  				p.Succs[pk.i] = Edge{child, len(child.Preds)}
    79  				// Fix up Phi value in b to have one less argument.
    80  				for _, v := range b.Values {
    81  					if v.Op != OpPhi {
    82  						continue
    83  					}
    84  					b.removePhiArg(v, k)
    85  				}
    86  				// Fix up child to have one more predecessor.
    87  				child.Preds = append(child.Preds, Edge{p, pk.i})
    88  				ai := b.Succs[out].i
    89  				for _, v := range child.Values {
    90  					if v.Op != OpPhi {
    91  						continue
    92  					}
    93  					v.AddArg(v.Args[ai])
    94  				}
    95  				if b.Func.pass.debug > 0 {
    96  					b.Func.Warnl(b.Controls[0].Pos, "Redirect %s based on %s", b.Controls[0].Op, p.Controls[0].Op)
    97  				}
    98  				changed = true
    99  				k--
   100  				break
   101  			}
   102  			ft.restore()
   103  		}
   104  		if len(b.Preds) == 0 && b != f.Entry {
   105  			// Block is now dead.
   106  			b.Kind = BlockInvalid
   107  		}
   108  	}
   109  	ft.restore()
   110  	ft.cleanup(f)
   111  	return changed
   112  }