]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: better ANDAND and OROR in IF and FOR
authorKeith Randall <khr@golang.org>
Tue, 3 Nov 2015 00:56:53 +0000 (16:56 -0800)
committerKeith Randall <khr@golang.org>
Tue, 3 Nov 2015 17:22:27 +0000 (17:22 +0000)
For the statement

    if a && b { target }

the old code allocated a new variable v and did:

    v = a
    if a {
       v = b
    }
    if v { goto target }

The new code does:

    if a {
      if b { goto target }
    }

The new arrangement tends to generate much more efficient code.  In
particular, there is no temporary variable and there is only one join
point instead of two.

The old code is still used for ANDAND and OROR which are not
direct descendents of IF or FOR statements.

Change-Id: I082f246d27c823c6f32d1287300e4b0911607507
Reviewed-on: https://go-review.googlesource.com/16584
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/gc/ssa.go

index 521e6d7ffa31eefc8ffa11a74752768c2c0ff53e..6210c7a6915999a62ed570a35f7472fbe9662adf 100644 (file)
@@ -645,23 +645,14 @@ func (s *state) stmt(n *Node) {
                s.assign(n.Left, r, n.Op == OASWB)
 
        case OIF:
-               cond := s.expr(n.Left)
-               b := s.endBlock()
-               b.Kind = ssa.BlockIf
-               b.Control = cond
-               b.Likely = ssa.BranchPrediction(n.Likely) // gc and ssa both use -1/0/+1 for likeliness
-
                bThen := s.f.NewBlock(ssa.BlockPlain)
                bEnd := s.f.NewBlock(ssa.BlockPlain)
                var bElse *ssa.Block
-
-               if n.Rlist == nil {
-                       b.AddEdgeTo(bThen)
-                       b.AddEdgeTo(bEnd)
-               } else {
+               if n.Rlist != nil {
                        bElse = s.f.NewBlock(ssa.BlockPlain)
-                       b.AddEdgeTo(bThen)
-                       b.AddEdgeTo(bElse)
+                       s.condBranch(n.Left, bThen, bElse, n.Likely)
+               } else {
+                       s.condBranch(n.Left, bThen, bEnd, n.Likely)
                }
 
                s.startBlock(bThen)
@@ -760,18 +751,13 @@ func (s *state) stmt(n *Node) {
 
                // generate code to test condition
                s.startBlock(bCond)
-               var cond *ssa.Value
                if n.Left != nil {
-                       cond = s.expr(n.Left)
+                       s.condBranch(n.Left, bBody, bEnd, 1)
                } else {
-                       cond = s.constBool(true)
+                       b := s.endBlock()
+                       b.Kind = ssa.BlockPlain
+                       b.AddEdgeTo(bBody)
                }
-               b = s.endBlock()
-               b.Kind = ssa.BlockIf
-               b.Control = cond
-               b.Likely = ssa.BranchLikely
-               b.AddEdgeTo(bBody)
-               b.AddEdgeTo(bEnd)
 
                // set up for continue/break in body
                prevContinue := s.continueTo
@@ -2016,6 +2002,45 @@ func (s *state) expr(n *Node) *ssa.Value {
        }
 }
 
+// condBranch evaluates the boolean expression cond and branches to yes
+// if cond is true and no if cond is false.
+// This function is intended to handle && and || better than just calling
+// s.expr(cond) and branching on the result.
+func (s *state) condBranch(cond *Node, yes, no *ssa.Block, likely int8) {
+       if cond.Op == OANDAND {
+               mid := s.f.NewBlock(ssa.BlockPlain)
+               s.stmtList(cond.Ninit)
+               s.condBranch(cond.Left, mid, no, max8(likely, 0))
+               s.startBlock(mid)
+               s.condBranch(cond.Right, yes, no, likely)
+               return
+               // Note: if likely==1, then both recursive calls pass 1.
+               // If likely==-1, then we don't have enough information to decide
+               // whether the first branch is likely or not.  So we pass 0 for
+               // the likeliness of the first branch.
+               // TODO: have the frontend give us branch prediction hints for
+               // OANDAND and OOROR nodes (if it ever has such info).
+       }
+       if cond.Op == OOROR {
+               mid := s.f.NewBlock(ssa.BlockPlain)
+               s.stmtList(cond.Ninit)
+               s.condBranch(cond.Left, yes, mid, min8(likely, 0))
+               s.startBlock(mid)
+               s.condBranch(cond.Right, yes, no, likely)
+               return
+               // Note: if likely==-1, then both recursive calls pass -1.
+               // If likely==1, then we don't have enough info to decide
+               // the likelihood of the first branch.
+       }
+       c := s.expr(cond)
+       b := s.endBlock()
+       b.Kind = ssa.BlockIf
+       b.Control = c
+       b.Likely = ssa.BranchPrediction(likely) // gc and ssa both use -1/0/+1 for likeliness
+       b.AddEdgeTo(yes)
+       b.AddEdgeTo(no)
+}
+
 func (s *state) assign(left *Node, right *ssa.Value, wb bool) {
        if left.Op == ONAME && isblank(left) {
                return