This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

extending libgcc's exception delivery path



 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;
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]