Mon, 10 Sep 2001 16:01:15 -0500
I certainly don't intend to argue that SSA makes the raise=>jump better. The
old block-structured code though clearly isn't as good as just being able to
set the handler. This is just like the ICFP contest problem: imperative
style is more efficient than block structured style. The `only' advantage of
block structure (and this is huge for the human) is that you can perform
local changes and know what effect it will have on the result without having
to scan the entire program.
An example of the win here is that you never need to save the initial value
of the handler stack in a function more than once.
The example I was thinking of though was simpler. If you have a fold (over
lists say) and you now want to exit the loop early, the easiest way is to
have the folder function raise an exception and handle the call to fold. In
the absence of inlining fold, this is really going to have to use the full
exception handling mechanism to pop off the frame of the folder procedure and
of the fold procedure. In MLton, if both fold and the folder procedure are
inlined (which they almost always are) then the raise in the folder just
turns into a jump out of the loop (thanks to the raise=>jump optimization).
Without inlining, this optimization would basically never trigger because no
one is going to write code to do a raise for this. In MLton it IS a win
because the raise can only be seen to be a jump after inlining.
Just to be completely explicit:
fun ('elem, 'state)
fold (lst: 'elem list,
folder: 'elem * 'state -> 'state)
: 'state =
let fun loop (lst, state) =
case lst of
 => state
| h::t => loop (t, folder (h, state))
in loop (lst, istate)
fun doMults (lst: int list): int =
let exception Zero
fun folder (e, ac) =
if e = 0
then raise Zero
else e * ac
in fold (lst, 1, folder)
handle Zero => 0
In MLton, this should generate good code.