This is the mail archive of the gcc-patches@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]
Other format: [Raw text]

[trans-mem] beginnings of eh support


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~

Attachment: d-tm-c++-4
Description: Text document


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