[trans-mem] beginnings of eh support

Richard Henderson rth@redhat.com
Sat Jul 11 01:00:00 GMT 2009


Logically this patch ought to have been split into separate pieces, but 
I'm lazy today.  Also, I've not yet bothered to implement any of the 
library functions that would go along with this patch.

My approach to exception handling is a bit different from what is 
described in the latest Intel TM ABI document; I hope this version will 
either make the next version, or someone will poke holes in my scheme 
and I'll go back to the drawing board.

Let's consider first the case where we're not catching an exception, but 
merely cleaning up as the exception unwinds past the atomic block:

   void f() __attribute__((tm_callable)); // can throw
   void g() { __tm_atomic { f(); }

The ABI document has an expanded example of this in section 7.4.1. 
Instead of the catch/tryCommit/rethrow/abort sequence, I invent a new 
library entry point

   void _ITM_commitTransactionEH(void *exc_ptr)
   {
     if (!_ITM_tryCommitTransaction ())
       {
         _Unwind_DeleteException (exc_ptr);
         _ITM_abortTransaction (TMConflict);
       }
   }

If the commit succeeds, we return and we continue unharmed.  If the 
commit fails, the call to _Unwind_DeleteException deletes the exception 
object and removes the exception state from the runtime library.  (Or at 
least it is supposed to -- I doubt the foreign function exception catch 
paths are well tested in libstdc++, but those are just bugs to be 
fixed.)  Once the exception is gone, we can restart the transaction like 
usual.

So the Idea is, to generate

   actions = _ITM_beginTransaciton (...);
   // normal _ITM_actions goop
   f();

  fallthru_lab:
   _ITM_commitTransaction ();
   goto over_lab;

  exception_lab:
   _ITM_commitTransactionEH (exception_pointer);
   _Unwind_Resume (exception_pointer);

  over_lab:

Which, if I'm not greatly mistaken, will work.  =)

Second, let's consider the case where there's an actual catch involved:

   extern void f1() __attribute__((tm_callable));
   extern void f2(int) __attribute__((tm_callable));

   void h()
   {
     __tm_atomic {
       try {
         f1();
       } catch (int x) {
         f2(x);
       }
     }
   }

The ABI document has an example of this as well, section 7.4.3.  It's 
really quite amazing the lengths to which they are going.  However, this 
is all generic stuff and is completely ignorant of what C++ is doing 
with EH under the covers.  Pull the sheet back and it's pretty simple.

We have a pair of function calls __cxa_begin_catch/__cxa_end_catch that 
update the exception state within libstdc++ with respect to number of 
exceptions uncaught, etc etc.  Really all we need to do when restarting 
a transaction is to keep those calls properly nested.  So I invent:

   void *_ITM_cxa_begin_catch (void *exc_ptr)
   {
     current_transaction.cxa_catch_count++;
     return __cxa_begin_catch (exc_ptr);
   }

   void _ITM_cxa_end_catch ()
   {
     current_transaction.cxa_catch_count--;
     __cxa_end_catch ();
   }

And then somewhere within _ITM_abortTransaction we add

  for (i = current_transaction.cxa_catch_count; i > 0; --i)
    __cxa_end_catch ();
  current_transaction.cxa_catch_count = 0;

which all go together to make sure that we end all the catches that we 
started before aborting the transaction.

Now, in order to implement the commitTransactionEH part, I need to find 
a way to allow a try-finally construct to execute different bits of code 
depending on whether the try is being exited via an exception, or a 
normal goto sort of control path.

I have added the GIMPLE_EH_ELSE construct to do exactly that.  It 
consists of two code sequences, one of which is run for normal exits, 
and the other is run for exception exits from the try block.  This new 
gimple object may only appear as the sole member of the cleanup block 
for a GIMPLE_TRY_FINALLY.

Going back to our example, the code immediately before EH lowering looks 
like

void g() ()
{
   __tm_atomic [SUBCODE=6,LABEL=]
     {
       try
         {
           f1 (); [in atomic]
         }
       finally
         {
           IF_NORMAL_EXIT
             {
               __builtin__ITM_commitTransaction ();
             }
           ELSE_EH_EXIT
             {
               __builtin__ITM_commitTransactionEH (<<exc_ptr>>);
             }
         }
     }
}

and after EH lowering and CFG cleanup:

<bb 2>:
   // This will become beginTransaction plus action goop later.
   __tm_atomic [SUBCODE=6,LABEL=]

<bb 3>:
   f1 (); [in atomic]
   goto <bb 6>;

<L0>:    // Exception landing pad
   __builtin__ITM_commitTransactionEH (<<exc_ptr>>);
   resx 2 // Resume exception

<bb 6>:
   __builtin__ITM_commitTransaction ();
   return;

Going back to the throw example, we get

<bb 2>:
   __tm_atomic [SUBCODE=6,LABEL=]

<bb 3>:
   f1 (); [in atomic]
   goto <bb 9>;

<L0>: // Exception landing pad from f1(), i.e. the catch.
   D.2117_1 = _ITM_cxa_begin_catch (<<exc_ptr>>); [in atomic]
   D.2115_2 = (int *) D.2117_1;
   x_3 = *D.2115_2;
   f2 (x_3); [in atomic]
   _ITM_cxa_end_catch (); [in atomic]
   goto <bb 9>;

<L1>: // Exception landing pad from f2(x).
   _ITM_cxa_end_catch (); [in atomic]
   __builtin__ITM_commitTransactionEH (<<exc_ptr>>);
   resx 2

<bb 9>:
   __builtin__ITM_commitTransaction ();
   return;

All of which looks very acceptable in terms of overhead.

Comments?


r~
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: d-tm-c++-4
URL: <http://gcc.gnu.org/pipermail/gcc-patches/attachments/20090711/e11a46df/attachment.ksh>


More information about the Gcc-patches mailing list