conts, handlers, and liveness

Stephen Weeks MLton@sourcelight.com
Thu, 19 Jul 2001 12:26:32 -0700


> x_3673 doesn't appear in any live list.  What happens is something like
> the following:
> 
> fun L_1 () = L_3(global_1)
> fun L_2 () = L_3(x_1)
> fun L_3 (x_2) = let
>                    val _ = HandlerPop
>                 in
>                    ...
>                 end
> fun L_4 () = let
>                 val x_3 = Int_add (x_7, x_8)
>                 val x_4 = Int_eq (x_3, 0)
>              in
>                 Switch(x_3, (0, L_1), (1, L_2))
>              end
> (presumably somewhere else is a nontail call with L_3 as the handler)
...
> I think I see what's going on.  At CPS, you don't have near handlers and
> far handlers -- just handlers.  But, in MachineIL you have far handlers
> (i.e., the code pointer you put on the stack) which adjust the stack top
> on a return, moves the raise global to the near handler actual, and then
> jumps to the near handler, and also near handlers which is where raise to
> jumps jump and do the actual handle code.  And it's the raise-to-jumps
> that are getting the short end -- they can't tell that the exception
> argument that they are raising won't get used and can't do the appropriate
> dead code elimination.

A simple CPS optimization could introduce near/far handlers in cases where the
handler argument is unused, and rewrite the above as follows.

fun L_1 () = L_3(global_1)
fun L_2 () = L_3(x_1)
fun L_3' () = let
                 val _ = HandlerPop
              in
                 ...
              end
fun L_3 (x_2) = L_3'()
fun L_4 () = let
                val x_3 = Int_add (x_7, x_8)
                val x_4 = Int_eq (x_3, 0)
             in
                Switch(x_3, (0, L_1), (1, L_2))
             end

I think the shrinker will then do the right thing.

It probably makes sense to do this as part of raise-to-jump.  Any interest?  The
CVS tree is there for the taking. :-)