[dev.ssa] cmd/compile/internal/ssa: distinguish exit and return blocks

It is confusing to have exceptional edges jump back into
real code.  Distinguish return blocks, which execute acutal
code, and the exit block, which is a merge point for the regular
and exceptional return flow.

Prevent critical edge insertion from adding blocks on edges
into the exit block.  These added blocks serve no purpose and
add a bunch of dead jumps to the assembly output.  Furthermore,
live variable analysis is confused by these jumps.

Change-Id: Ifd69e6c00e90338ed147e7cb351b5100dc0364df
Reviewed-on: https://go-review.googlesource.com/14254
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 77c8227..f0cad90 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -562,6 +562,7 @@
 	case ORETURN:
 		s.stmtList(n.List)
 		b := s.endBlock()
+		b.Kind = ssa.BlockRet
 		b.AddEdgeTo(s.exit)
 
 	case OCONTINUE, OBREAK:
@@ -3358,6 +3359,7 @@
 			branches = append(branches, branch{p, b.Succs[0]})
 		}
 	case ssa.BlockExit:
+	case ssa.BlockRet:
 		Prog(obj.ARET)
 	case ssa.BlockCall:
 		if b.Succs[0] != next {
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index 0c2bc4c..68ba25a 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -59,6 +59,16 @@
 			if !b.Control.Type.IsMemory() {
 				f.Fatalf("exit block %s has non-memory control value %s", b, b.Control.LongString())
 			}
+		case BlockRet:
+			if len(b.Succs) != 1 {
+				f.Fatalf("ret block %s len(Succs)==%d, want 1", b, len(b.Succs))
+			}
+			if b.Control != nil {
+				f.Fatalf("ret block %s has non-nil control %s", b, b.Control.LongString())
+			}
+			if b.Succs[0].Kind != BlockExit {
+				f.Fatalf("ret block %s has successor %s, not Exit", b, b.Succs[0].Kind)
+			}
 		case BlockDead:
 			if len(b.Succs) != 0 {
 				f.Fatalf("dead block %s has successors", b)
diff --git a/src/cmd/compile/internal/ssa/critical.go b/src/cmd/compile/internal/ssa/critical.go
index ba75450..439d482 100644
--- a/src/cmd/compile/internal/ssa/critical.go
+++ b/src/cmd/compile/internal/ssa/critical.go
@@ -9,7 +9,7 @@
 // Regalloc wants a critical-edge-free CFG so it can implement phi values.
 func critical(f *Func) {
 	for _, b := range f.Blocks {
-		if len(b.Preds) <= 1 {
+		if len(b.Preds) <= 1 || b.Kind == BlockExit {
 			continue
 		}
 
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index d17f207..59b90ad 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -375,6 +375,7 @@
 	{name: "If"},    // 2 successors, if control goto Succs[0] else goto Succs[1]
 	{name: "Call"},  // 2 successors, normal return and panic
 	{name: "First"}, // 2 successors, always takes the first one (second is dead)
+	{name: "Ret"},   // 1 successor, branches to exit
 }
 
 func init() {
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index f4c74fe..a61c31a 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -28,6 +28,7 @@
 	BlockIf
 	BlockCall
 	BlockFirst
+	BlockRet
 )
 
 var blockString = [...]string{
@@ -54,6 +55,7 @@
 	BlockIf:    "If",
 	BlockCall:  "Call",
 	BlockFirst: "First",
+	BlockRet:   "Ret",
 }
 
 func (k BlockKind) String() string { return blockString[k] }