shrinker checked in

Stephen Weeks MLton@sourcelight.com
Wed, 14 Nov 2001 09:59:46 -0800


> How does the shrinker deal with blocks that do nothing but goto another
> block?  The way the SSA shrinker works, it is completely nullifying the
> effect of commonBlock.  I can also find a number of instances of
> gotos-to-gotos that aren't eliminated.

Gotos-to-gotos, and in particular "eta" gotos like f (x) = g (x), are
not handled as well as in the CPS shrinker.  I think they could be,
but I wanted to go for a slightly simpler shrinker first just to get
it working.

> >   - Added mayRaise: bool to functions to record whether or not a function may
> >     raise.
> 
> Would it be worth replacing mayRaise with raises : Type.t vector option to
> match returns?  I know that this vector should be identical in all
> functions, but it would make things a little more uniform.

No objection here.  I can even imagine an optimization that represents
exceptions differently in different parts of the program.

> >   - Changed returns to the following datatype
> > 
> > 	    datatype t =
> > 	       Dead
> > 	     | HandleOnly
> > 	     | NonTail of {cont: Label.t, handler: Handler.t}
> > 	     | Tail
> 
> Seems a little complicated to me, but if it works.
> 
> I still don't quite understand why Caller/None is disallowed.  If the
> callee doesn't raise, won't the exception stack be exactly the same as it
> was at the call when the callee returns -- even if the callee installs and
> uninstalls handlers?  Maybe I'm missing some aspect of what a callee is
> allowed to do when it is called with a continuation or handler of None.

It's not what the callee can do.  I agree that our current convention
is that the callee preserves the exception stack, essentially treating
the global (per thread) exception stack pointer as a callee save
register.  The problem is that None means that the *caller* can leave
the exception stack in *any* state that it likes.  But that means that
when the callee returns (as it is allowed to do with a Caller cont),
the exception stack will be in that same *any* state.  But the
continuation may refer to the exception stack.  Hence, Caller/None
doesn't make sense -- it only makes sense to have Caller/Caller,
i.e. Tail.

Thinking of it from a continuation-passing, exception-stack passing
style with callee-save registers, the continuation expects to be
passed the exception stack pointer, so it must be set to whatever the
continuation needs before calling the callee.

> was thinking that while the callee won't return to either the continuation
> or exception stack, it should preserve both stacks.  For example, we could
> conceivable implement a None/None (Dead) call by a "tail call" that
> adjusts the stack all the way to stackBottom; i.e., trash the entire stack
> (continuation and exception) because we can't possibly return to them.

I would like to do this.  In fact, I bet Henry would argue we are
required to.

> Similarly, None/Caller (HandleOnly) is implemented as a normal tail
> call.  This makes sense, because the handler stack is intertwined in the
> continuation stack, so we can't really trash the continuation stack; 
> although, again, conceivably we could implement it as a tail call that
> trashes the stack down to the frame that has the top handler. 

I would like to do this too.