[dev.ssa] cmd/compile: better ANDAND and OROR in IF and FOR

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>
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 521e6d7..6210c7a 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -645,23 +645,14 @@
 		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 @@
 
 		// 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 @@
 	}
 }
 
+// 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