conts, handlers, and liveness

Matthew Fluet mfluet@intertrust.com
Thu, 19 Jul 2001 12:13:13 -0700 (PDT)


> > allocate-registers.sig now uses the following Info.t type and just returns
> > jumpInfo and funcInfo functions that return Info.t's (and handlerOffset
> > for funcInfo).  All in all, a little bit cleaner now.
> ...
>
> Why not the following?

No real reason.  In live.fun, the three live lists are built together,
so I just found it easiest to keep them grouped.  But, you're right that
only conts ever need the live at frame list.  It wouldn't be hard to
change it.

> > Looking through all the examples, there are two reasons why I need to
> > patch.  One is the unused argument example that I sent out earlier;
> > something that might be of interest, in all of these cases, the jump that
> > has the unused argument is a block of the form:
> >
> >    fun L_2226 (x_3673) =
> >       let
> > 	 val _ = HandlerPop
> >       in
> > 	 (* some transfer, often a raise, but not always *)
> >       end
>
> L_2226 is a handler, and so must follow the handler calling convention, which
> means it takes one argument, the exception.  That unused argument is
> unavoidable.  But, x_3673 should not appear in any live list.

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)

x_2 is never used, so L_1 and L_2 turn into blocks that just jump to L_3.
So, I rewrite jumps to L_1 and L_2 to jumps to L_3.  This means that all
the targets of L_4 are the same, so I rewrite the switch to a jump to L_3.
x_4 is dead code, as is x_3, and that now means that x_7 and x_8 are no
longer live into L_4.

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.

> But somehow CPS doesn't feel like the right place for this.  It really belongs
> an a lower level IL.  And since that doesn't exist right now :-), I guess
> backend is the place to do it.

Agreed. The backend change I made caught the couple I was seeing in the
regressions and benchmarks.