This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
extending libgcc's exception delivery path
- To: gcc at gcc dot gnu dot org
- Subject: extending libgcc's exception delivery path
- From: Godmar Back <gback at cs dot utah dot edu>
- Date: Wed, 1 Dec 1999 01:48:36 -0700 (MST)
- Cc: tim at transvirtual dot com, gback at cs dot utah dot edu
Hi,
In order to be able to use gcc's __throw exception handling mechanism
with a dynamic code generation system (the Kaffe just-in-time compiler,
see www.kaffe.org), I needed to make some changes to it.
In this mail, I'll discuss the changes I made and explain why I believe
that these changes could be beneficial to a larger community and should
therefore be considered for inclusion in the gcc tree.
In short, what I have done is to replace the direct invocation of
frame.c:__frame_state_for() in libgcc2.c:next_stack_level() with an
indirect invocation via a function pointer. This function pointer
is exported as a global symbol. By doing so, cooperating applications
can override that function. An overridden function can handle stack
frames for which there is no dwarf2 exception handling information
specially.
Here are the pros and cons as I see it.
Cons:
- One minor con is that the exception delivery path slows down by
a slight amount. For each stack frame that is to be unwound,
two direct calls are replaced with two indirect calls.
I do not believe that this is a problem, especially since the slowdown
lies in the exception delivery path, which is generally assumed to be
outside the critical path of a program.
- A bigger con is that gcc would open a backdoor that it may not want
to support. Applications that came to rely on the backdoor may break
as the libgcc2.c/frame.c implementation changes. In particular, clients
of that backdoor will be required to include "frame.h", which contains
frame_state, which is essential an internal data structure. This creates
another dependency. To make matters even more complicated, clients will
depend on machine-dependent information, such as how registers are
numbered etc.
I believe this dependency is unavoidable, but it is tolerable. I believe
that any system that makes use of this backdoor will have to evolve in
close synchronization with gcc *anyway*. As with other backdoors in gcc,
The GCC Project does not have to make any backward compatibility guarantees,
just a best effort to not prevent clients from updating their code as
gcc evolves.
Pros:
I think there is really only one pro, but it is a big one:
- Dynamic code generation systems can be integrated with the exception
mechanism seamlessly. In a Java system such as Kaffe, exceptions thrown
in precompiled code can be caught in just-in-time compiled code and
vice versa. Precompiled code can call into just-in-time compiled code
without having to needlessly spill callee-saved registers that may be
live upon exception handler entry.
I've been looking at kaffe, but Cygnus's libgcj run-time system
is another big potential client I would like to mention. While it
currently does not provide just-in-time compilation of dynamically
loaded classes, such support would certainly be desirable and may appear
in the future.
Kresten Thorup wrote an interpreter for libgcj: I believe that his
interpreter could be sped up by careful use of this mechanism as well.
(I have not implemented this, however.)
Alternative Solutions:
The most obvious alternative solution for me is to keep a copy
of libgcc2.c (or specifically, the ifdef'd L_eh part) in my tree. :-(
Another alternative is to require dynamic code generation systems to
create dwarf2 compatible information on the fly and invoke the
register_frame_info() function to register it, just like a normally loaded
object module would do. One possible advantage of this approach is
that it could provide full gdb support for such dynamically generated
code.
However, I submit that this alternative is too burdensome in general.
First, dcg systems such as kaffe generally translate methods lazily
as they are invoked, which I believe is at odds with the assumptions made
by register_frame_info. For instance, it's not clear whether each function
should become its own module, which would lead to a long list of objects.
Otherwise, one would have to unregister a module and re-register it
afterwards.
Secondly, having to generate dwarf2 frame information is frankly a big
hassle to do on the fly.
For these reasons, I do not believe that this alternative is desirable.
I would appreciate your feedback, or maybe suggestions for how I can
accomplish my goal w/o interfering with the details of gcc's run-time.
Thanks,
- Godmar (gback@cs.utah.edu)
Below is a sketch of the patch I am proposing.
Note that I am passing the current frame's udata along so that
any function that may be plugged in can get ahold of the current
frame's CFA.
This is necessary if the exception has to be delivered directly to
that frame, which is something I also need to support.
I have implemented an alternative __frame_state_for_func for the
x86, and because of the eager strategy with which callee-saved registers
in kaffe's code generator are spilled, it is actually quite short
and simple.
@@ -658,13 +681,24 @@
put_reg (udata->retaddr_column, val, udata);
}
+static frame_state *
+/* ARGSUSED */
+default_frame_state_for(void *pc, frame_state *udata, frame_state *caller_udata)
+{
+ return __frame_state_for(pc, caller_udata);
+}
+
+frame_state * (*__frame_state_for_func)(void *, frame_state *, frame_state *) =
+ default_frame_state_for;
+
/* Given the current frame UDATA and its return address PC, return the
information about the calling frame in CALLER_UDATA. */
static void *
next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata)
{
- caller_udata = __frame_state_for (pc, caller_udata);
+ caller_udata = (*__frame_state_for_func) (pc, udata, caller_udata);
+
if (! caller_udata)
return 0;