extending libgcc's exception delivery path

Mike Stump mrs@windriver.com
Tue Dec 7 16:43:00 GMT 1999


> From: Godmar Back <gback@cs.utah.edu>
> Date: Tue, 7 Dec 1999 14:18:29 -0700 (MST)

> Since you've never read it yourself, I assume you didn't use that 
> library in gcc.

Right, we did not.

> (Any reason why not?  Did someone investigate its use when gcc's
> implementation was done?)

Probably not.  Not sure about the timing and I didn't want to go
rooting around rcs and cvs files to find the answer...  but I suspect
that we had ours (dwarf) before it existed...  and that our dwarf2 is
loosely based upon the dwarf writter.  I don't even know it it would
be a win to use it, if someone were implementing it from scratch
today, this area of the compiler is beyond my usual stomping ground.

> > I generally do this type of work using the native EH constructs in the
> > source language (or specifically, the midlevel end that the frontend
> > uses to generate code).  Why go any lower, I don't see any advantage?

> I believe it's certainly faster than wrapping the function in 
> try/finally->rethrow, as gcj currently does.

What is the cost of violating the natural layering of the software?
Knowing dwarf.  What is the gain?  0.02%?  or 40%?  If the gain is
large, can that gain be had by fixing the optimizer of such code, or
by changing the codegen strategy?  What is the real savings of this
optimization in the longer run?

Bear in mind that the stiffness of the resulting code is less able to
be optimized than if you preserve the flexibility of the code by using
the midend.  The best optimization happens when you choose a better
algorithm, or a better datastructure, or a new philosophy.  By
prewiring this too early, you loose that ability to improve the code.
Leading to less optimal code in the mid and longer term.

> I honestly don't know all the trade-offs involved in that particular
> design decision, but I don't think the issue is clearcut at this
> point.  Hence, my gut feeling would be to allow for multiple ways of
> doing it.

Or, try multiple ways of doing it, see what the results are what the
performance is, and then go with the `best one'.

> > > - check whether the frame is inside some special assembly code for which
> > >   I don't have exception handling information and if so skip it.
> > 
> > :-) Oh, the simplicity of that statement.  Sorry, this generally is
> > really hard to do.

> Maybe I didn't express well what I wanted to say.

I thought you expressed yourself perfectly.

> Let me show you some code.  Suppose I have x86 assembly code like this:

> trampoline_1:
> 	call 		trampoline_2		# returns address in %eax
> 	jmp		*%eax

> A frame that has return address "trampoline_1+5"

This condition is unknowable, in general.  Because you can't know it,
what you say next can't happen.

> needs to be skipped if "trampoline_2" or a function called by it
> causes an exception.

Again, unknowable.

> I can do this easily by overriding the frame_state_for function.

You're lucky, or you just solve a subset problem.  Getting it to kinda
work isn't too hard.  Can you solve the problem when gcc optimizes the
prologue and epilogue to death and saves registers in the mmx
registers because someone found it produced faster code and eliminates
the frame pointer?  No.

> In your scheme, how would I communicate callee-saved registers to a
> precompiled caller?

Your question isn't this, it is, what if in my precompiled code I do
X, and X must be undone in EH situations.  The answer is you place a
region that starts after you have done X, extending until just before
you undo X, that has a handler that undoes X and then rethrows.

For example, the X can be saving a callee-saved register in your JIT
code.  Undoing it would be to move the register from the save location
on the stack back into the register.  For regions that are the same
(same begin and same end), you collapse the actions together.

> Suppose I have a precompiled caller invoke a translated method,
> which saves some callee-saved register upon which the catch clause
> in the precompiled handler relies.

> For example:
> just_in_time_compiled_f:
> 		...
> 		push %edi
LEHB1:
> 		...
> 		call	precompiled_f_2
> 		...
LEHE1:
		pop %edi
		...

L1:		pop %edi
		call __rethrow in the LEHE1 context

> precompiled_f_2:
> 		...
> 		call	__throw

> Could you outline how I'd communicate the saved value of %edi to
> precompiled_f when precompiled_f_2 throws an exception caught by
> "EH"?

After you do all of this, you will notice the large number of these
things that you have to generate, the space they take, the way they
kill the instruction cache, and you will say, if only I put them out
into the .data segment, I could improve this...  then you would
implement the dwarf generation, as it handles this with the advantages
describe above.  It is just a quality of implementation detail.  The
first implementation can be less efficient.  Remember, you can't have
a second implementation until after you have a first.  Anyway, just
trying to forshadow a little.


More information about the Gcc mailing list