[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