From: Keith Randall Date: Tue, 3 Nov 2015 00:56:53 +0000 (-0800) Subject: [dev.ssa] cmd/compile: better ANDAND and OROR in IF and FOR X-Git-Tag: go1.7beta1~1623^2^2~114 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=991873116e316f334f41343bb9bbd97720b0ee29;p=gostls13.git [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 Reviewed-by: David Chase --- diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 521e6d7ffa..6210c7a691 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -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