[dev.ssa] cmd/compile/internal/ssa: simplify how exit blocks are used
Move to implicit (mostly) instead of explicit exit blocks.
RET and RETJMP have no outgoing edges - they implicitly exit.
CALL only has one outgoing edge, as its exception edge is
implicit as well.
Exit blocks are only used for unconditionally panicking code,
like the failed branches of nil and bounds checks.
There may now be more than one exit block. No merges happen
at exit blocks.
The only downside is it is harder to find all the places code
can exit the method. See the reverse dominator code for an
example.
Change-Id: I42e2fd809a4bf81301ab993e29ad9f203ce48eb0
Reviewed-on: https://go-review.googlesource.com/14462
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index 1c26946..a949764 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -366,24 +366,27 @@
{name: "VarKill"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
}
-// kind control successors
-// ------------------------------------------
-// Exit return mem []
-// Ret return mem [exit]
+// kind control successors implicit exit
+// ----------------------------------------------------------
+// Exit return mem [] yes
+// Ret return mem [] yes
+// RetJmp return mem [] yes
// Plain nil [next]
// If a boolean Value [then, else]
-// Call mem [nopanic, exit] (control opcode should be OpCall or OpStaticCall)
+// Call mem [next] yes (control opcode should be OpCall or OpStaticCall)
// First nil [always,never]
var genericBlocks = []blockData{
- {name: "Exit"}, // no successors. There should only be 1 of these.
- {name: "Dead"}, // no successors; determined to be dead but not yet removed
{name: "Plain"}, // a single successor
{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
- {name: "RetJmp"}, // 1 successor, branches to exit. Jumps to b.Aux.(*gc.Sym)
+ {name: "Call"}, // 1 successor, control is call op (of memory type)
+ {name: "Ret"}, // no successors, control value is memory result
+ {name: "RetJmp"}, // no successors, jumps to b.Aux.(*gc.Sym)
+ {name: "Exit"}, // no successors, control value generates a panic
+
+ // transient block states used for dead code removal
+ {name: "First"}, // 2 successors, always takes the first one (second is dead)
+ {name: "Dead"}, // no successors; determined to be dead but not yet removed
}
func init() {