conts, handlers, and liveness

Matthew Fluet mfluet@intertrust.com
Thu, 19 Jul 2001 09:37:59 -0700 (PDT)


> liveOffsets in the contInfo makes sense to me.  After all, it is only defined if
> the jump is a cont.  Maybe a better way to go would be to have a single
> property, jumpInfo, that has an option for the handler and an option for the
> cont?

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.

      structure Info:
	 sig
	    datatype t =
	       T of {
		     limitCheck: Machine.LimitCheck.t,
		     (* Live operands at the beginning of the block.
		      *)
		     live: Machine.Operand.t list,
		     (* Live operands at the beginning of the block,
		      * excepting its formals
		      *)
		     liveNoFormals: Machine.Operand.t list,
		     (* Live variables at the frame corresponding to the block
		      *)
		     liveFrame: Machine.Operand.t list,
		     (* Number of bytes in frame including return address.
		      *)
		     cont: {size: int} option,
		     (* Amount to subtract to get to the next frame.
		      *)
		     handler: {size: int} option
		     }
	 end

I've run through all of the regression and benchmarks and after the
initial translation from MachineIL to pseudo-x86, I find no differences
between the liveness passed down from the MachineIL and the liveness I
compute.  (I don't think this guarantees that the MachineIL's liveness
info is correct (as in lfp of the liveness equations) -- just that it is a
fixed point, because I'm just verifying that for each block, given what's
stated as being live into the destinations of the transfers and what's
stated as being live into the block are consistent with what's used and
defed in the block.)  After doing some simplifications and dead-code
elimination, there are just a few patches necessary to the liveness
information.  (Again, I'm not doing this to a fixed point, so there might
be some additional dead code that I'm missing.)

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

The second reason I think I can fix in the backend.  Essentially, there
are occasionally extraneous switch statements:

t_0 = Default_0 | Ignore_0 | Handler_0 of (lambdas_2)

   fun L_43 () =
      let
	 val childReady_0 = Ref_ref (global_19)
	 val x_387 = Env_9 (childReady_0)
	 val x_386 = Handler_0 (x_387)
	 val x_384 = Array_sub (x_58, global_38)
	 val x_385 = Array_update (x_58, global_38, x_386)
      in
	 case x_384 of
	   Handler_0 => L_255 | Ignore_0 => x_148 | Default_0 => x_148
      end

So, the switch is first a switch on IntOrPointer and the int branch does a
further switch (and backend adds the test to the live in variables), but
both cases jump to L_148.  The function that builds the switch should
check to see if all branches and the default have the same label, and if
so turn it into a jump and not add the test to the live in variables.