Support __cxa_free_exception and fix exception handling.
authorTorvald Riegel <triegel@redhat.com>
Fri, 20 Nov 2015 00:10:08 +0000 (00:10 +0000)
committerTorvald Riegel <torvald@gcc.gnu.org>
Fri, 20 Nov 2015 00:10:08 +0000 (00:10 +0000)
gcc/cp/
* except.c (do_free_exception): Use transactional wrapper.

libitm/
* testsuite/libitm.c++/eh-5.C: New.
* libitm.h (_ITM_cxa_free_exception): New.
* libitm.map (_ITM_cxa_free_exception): Add it.
* libitm.texi: Update ABI docs.
* libitm_i.h (gtm_transaction_cp::cxa_unthrown): Remove.
(gtm_transaction_cp::cxa_uncaught_count): Add.
(gtm_thread::cxa_unthrown): Remove.
(gtm_thread::cxa_uncaught_count_ptr): Add.
(gtm_thread::cxa_uncaught_count): Add.
(gtm_thread::drop_references_allocations): Rename to...
(gtm_thread::discard_allocation): ... this and adapt.
(gtm_thread::init_cpp_exceptions): New.
* beginend.cc (gtm_thread::gtm_thread): Adapt EH handling.
(gtm_thread::begin_transaction): Likewise.
(gtm_transaction_cp::save): Likewise.
(gtm_thread::trycommit): Likewise.
* eh_cpp.cc: Add overview comments.
(__cxa_eh_globals, __cxa_get_globals, __cxa_free_exception): Declare.
(free_any_exception, _ITM_cxa_free_exception): New.
(gtm_thread::init_cpp_exceptions): Define.
(_ITM_cxa_allocate_exception, _ITM_cxa_throw): Adapt.
(_ITM_cxa_begin_catch, _ITM_cxa_end_catch): Likewise.
(gtm_thread::revert_cpp_exceptions): Likewise.

From-SVN: r230634

gcc/cp/ChangeLog
gcc/cp/except.c
libitm/ChangeLog
libitm/beginend.cc
libitm/eh_cpp.cc
libitm/libitm.h
libitm/libitm.map
libitm/libitm.texi
libitm/libitm_i.h
libitm/testsuite/libitm.c++/eh-5.C [new file with mode: 0644]

index c4b7614..92b1d28 100644 (file)
@@ -1,3 +1,7 @@
+2015-11-19  Torvald Riegel  <triegel@redhat.com>
+
+       * except.c (do_free_exception): Use transactional wrapper.
+
 2015-11-19  Jason Merrill  <jason@redhat.com>
 
        PR c++/68422
index 9b2450d..ad40436 100644 (file)
@@ -662,6 +662,16 @@ do_free_exception (tree ptr)
       /* Declare void __cxa_free_exception (void *) throw().  */
       fn = declare_library_fn (fn, void_type_node, ptr_type_node,
                               ECF_NOTHROW | ECF_LEAF);
+
+      if (flag_tm)
+       {
+         tree fn2 = get_identifier ("_ITM_cxa_free_exception");
+         if (!get_global_value_if_present (fn2, &fn2))
+           fn2 = declare_library_fn (fn2, void_type_node,
+                                     ptr_type_node,
+                                     ECF_NOTHROW | ECF_LEAF | ECF_TM_PURE);
+         record_tm_replacement (fn, fn2);
+       }
     }
 
   return cp_build_function_call_nary (fn, tf_warning_or_error, ptr, NULL_TREE);
index 0564345..4be0561 100644 (file)
@@ -1,3 +1,29 @@
+2015-11-19  Torvald Riegel  <triegel@redhat.com>
+
+       * testsuite/libitm.c++/eh-5.C: New.
+       * libitm.h (_ITM_cxa_free_exception): New.
+       * libitm.map (_ITM_cxa_free_exception): Add it.
+       * libitm.texi: Update ABI docs.
+       * libitm_i.h (gtm_transaction_cp::cxa_unthrown): Remove.
+       (gtm_transaction_cp::cxa_uncaught_count): Add.
+       (gtm_thread::cxa_unthrown): Remove.
+       (gtm_thread::cxa_uncaught_count_ptr): Add.
+       (gtm_thread::cxa_uncaught_count): Add.
+       (gtm_thread::drop_references_allocations): Rename to...
+       (gtm_thread::discard_allocation): ... this and adapt.
+       (gtm_thread::init_cpp_exceptions): New.
+       * beginend.cc (gtm_thread::gtm_thread): Adapt EH handling.
+       (gtm_thread::begin_transaction): Likewise.
+       (gtm_transaction_cp::save): Likewise.
+       (gtm_thread::trycommit): Likewise.
+       * eh_cpp.cc: Add overview comments.
+       (__cxa_eh_globals, __cxa_get_globals, __cxa_free_exception): Declare.
+       (free_any_exception, _ITM_cxa_free_exception): New.
+       (gtm_thread::init_cpp_exceptions): Define.
+       (_ITM_cxa_allocate_exception, _ITM_cxa_throw): Adapt.
+       (_ITM_cxa_begin_catch, _ITM_cxa_end_catch): Likewise.
+       (gtm_thread::revert_cpp_exceptions): Likewise.
+
 2015-11-09  Torvald Riegel  <triegel@redhat.com>
 
        * alloc_cpp.cc (_ZdlPvX, _ZdlPvXRKSt9nothrow_t, _ZGTtdlPvX,
index c3ed11b..86f7b39 100644 (file)
@@ -132,6 +132,8 @@ GTM::gtm_thread::gtm_thread ()
   number_of_threads_changed(number_of_threads - 1, number_of_threads);
   serial_lock.write_unlock ();
 
+  init_cpp_exceptions ();
+
   if (pthread_once(&thr_release_once, thread_exit_init))
     GTM_fatal("Initializing thread release TLS key failed.");
   // Any non-null value is sufficient to trigger destruction of this
@@ -383,6 +385,11 @@ GTM::gtm_thread::begin_transaction (uint32_t prop, const gtm_jmpbuf *jb)
 #endif
     }
 
+  // Log the number of uncaught exceptions if we might have to roll back this
+  // state.
+  if (tx->cxa_uncaught_count_ptr != 0)
+    tx->cxa_uncaught_count = *tx->cxa_uncaught_count_ptr;
+
   // Run dispatch-specific restart code. Retry until we succeed.
   GTM::gtm_restart_reason rr;
   while ((rr = disp->begin_or_restart()) != NO_RESTART)
@@ -411,7 +418,7 @@ GTM::gtm_transaction_cp::save(gtm_thread* tx)
   id = tx->id;
   prop = tx->prop;
   cxa_catch_count = tx->cxa_catch_count;
-  cxa_unthrown = tx->cxa_unthrown;
+  cxa_uncaught_count = tx->cxa_uncaught_count;
   disp = abi_disp();
   nesting = tx->nesting;
 }
@@ -583,7 +590,6 @@ GTM::gtm_thread::trycommit ()
       undolog.commit ();
       // Reset further transaction state.
       cxa_catch_count = 0;
-      cxa_unthrown = NULL;
       restart_total = 0;
 
       // Ensure privatization safety, if necessary.
index a86dbf1..1fe1c90 100644 (file)
 
 using namespace GTM;
 
+/* Exceptions can exist in three phases: (1) after having been allocated by
+   __cxa_allocate_exception but before being handed off to __cxa_throw,
+   (2) when they are in flight, so between __cxa_throw and __cxa_begin_catch,
+   and (3) when they are being handled (between __cxa_begin_catch and
+   __cxa_end_catch).  Note that when an exception is re-thrown in (3), it is
+   not moving back to (2) but handled as a special case of (3) by the EH
+   runtime.
+
+   We can get aborts in all three phases, for example in (1) during
+   construction of the exception object, or in (2) in destructors called
+   while unwinding the stack.  The transaction that created an exception
+   object can only commit in phase (3) by re-throwing the exception; it cannot
+   commit in other phases because throw expressions and catch clauses are
+   properly nested wrt transactions and because the compiler wraps
+   transaction bodies in a try/catch-all construct.
+
+   We handle phase (1) by dealing with exception objects similar to how we
+   deal with other (de)allocations, which also ensures that we can have more
+   than one exception object allocated at the same time (e.g., if the
+   throw expression itself throws an exception and thus calls
+   __cxa_allocate_exception).  However, on the call to __cxa_begin_catch
+   we hand off the exception to the special handling of phase (3) and
+   remove the undo log entry of the allocation.  Note that if the allocation
+   happened outside of this transaction, we do not need to do anything.
+
+   When an exception reaches phase (2) due to a call to __cxa_throw, the count
+   of uncaught exceptions is incremented.  We roll back this effect by saving
+   and restoring this number in the structure returned from __cxa_get_globals.
+   This also takes care of increments of this count when re-throwing an
+   exception.
+
+   For phase (3), we keep track of the number of times __cxa_begin_catch
+   has been called without a matching call to __cxa_end_catch.  This count
+   is then used by __cxa_tm_cleanup to roll back the exception handling state
+   by calling __cxa_end_catch for the exceptions that have not been finished
+   yet (without running destructors though because we roll back the memory
+   anyway).
+   Once an exception that was allocated in this transaction enters phase (3),
+   it does not need to be deallocated on abort anymore because the calls to
+   __cxa_end_catch will take care of that.
+
+   We require all code executed by the transaction to be transaction_safe (or
+   transaction_pure, or to have wrappers) if the transaction is to be rolled
+   back.  However, we take care to not require this for transactions that
+   just commit; this way, transactions that enter serial mode and then call
+   uninstrumented code continue to work.
+   */
+
 /* Everything from libstdc++ is weak, to avoid requiring that library
    to be linked into plain C applications using libitm.so.  */
 
@@ -33,85 +81,139 @@ using namespace GTM;
 
 extern "C" {
 
+struct __cxa_eh_globals
+{
+  void *       caughtExceptions;
+  unsigned int uncaughtExceptions;
+};
+
 extern void *__cxa_allocate_exception (size_t) WEAK;
+extern void __cxa_free_exception (void *) WEAK;
 extern void __cxa_throw (void *, void *, void *) WEAK;
 extern void *__cxa_begin_catch (void *) WEAK;
 extern void __cxa_end_catch (void) WEAK;
 extern void __cxa_tm_cleanup (void *, void *, unsigned int) WEAK;
+extern __cxa_eh_globals *__cxa_get_globals (void) WEAK;
 
 #if !defined (HAVE_ELF_STYLE_WEAKREF) 
 void *__cxa_allocate_exception (size_t) { return NULL; }
+void __cxa_free_exception (void *) { return; }
 void __cxa_throw (void *, void *, void *) { return; }
 void *__cxa_begin_catch (void *) { return NULL; }
 void __cxa_end_catch (void) { return; }
 void __cxa_tm_cleanup (void *, void *, unsigned int) { return; }
 void _Unwind_DeleteException (_Unwind_Exception *) { return; }
+__cxa_eh_globals *__cxa_get_globals (void) { return NULL; }
 #endif /* HAVE_ELF_STYLE_WEAKREF */
 
 }
 
+static void
+free_any_exception (void *exc_ptr)
+{
+  // The exception could be in phase (2) and thus calling just
+  // _cxa_free_exception might not be sufficient.
+  __cxa_tm_cleanup (NULL, exc_ptr, 0);
+}
 
 void *
 _ITM_cxa_allocate_exception (size_t size)
 {
   void *r = __cxa_allocate_exception (size);
-  gtm_thr()->cxa_unthrown = r;
+  gtm_thr()->record_allocation (r, free_any_exception);
   return r;
 }
 
 void
+_ITM_cxa_free_exception (void *exc_ptr)
+{
+  // __cxa_free_exception can be called from user code directly if
+  // construction of an exception object throws another exception, in which
+  // case we need to roll back the initial exception.  We handle this similar
+  // to dead allocations in that we deallocate the exception on both commit
+  // and abort of an outermost transaction.
+  gtm_thr()->forget_allocation (exc_ptr, free_any_exception);
+}
+
+void
 _ITM_cxa_throw (void *obj, void *tinfo, void *dest)
 {
-  gtm_thr()->cxa_unthrown = NULL;
+  // This used to be instrumented, but does not need to be anymore.
   __cxa_throw (obj, tinfo, dest);
 }
 
 void *
 _ITM_cxa_begin_catch (void *exc_ptr)
 {
-  gtm_thr()->cxa_catch_count++;
+  // If this exception object has been allocated by this transaction, we
+  // discard the undo log entry for the allocation; we are entering phase (3)
+  // now and will handle this exception specially.
+  // Note that this exception cannot have been allocated in a parent
+  // transaction or enclosing nontransactional block because an atomic block
+  // cannot contain just a catch clause but not the associated try clause.
+  // The exception can have been allocated in a nested transaction, in which
+  // case the commit of the nested transaction will have inserted the undo
+  // log entry of the allocation in our undo log.
+  // The exception can also have been allocated in a nested nontransactional
+  // block, but then this transaction cannot abort anymore; functions that
+  // are marked transaction_pure, for example, must not side-step the
+  // transactional exception handling we implement here.
+  gtm_thread *t = gtm_thr ();
+  t->discard_allocation (exc_ptr);
+  // Keep track of the number of unfinished catch handlers.
+  t->cxa_catch_count++;
   return __cxa_begin_catch (exc_ptr);
 }
 
 void
 _ITM_cxa_end_catch (void)
 {
+  // Keep track of the number of unfinished catch handlers.
   gtm_thr()->cxa_catch_count--;
   __cxa_end_catch ();
 }
 
 void
+GTM::gtm_thread::init_cpp_exceptions ()
+{
+  // Only save and restore the number of uncaught exceptions if this is
+  // actually used in the program.
+  if (__cxa_get_globals != NULL && __cxa_get_globals () != 0)
+    cxa_uncaught_count_ptr = &__cxa_get_globals ()->uncaughtExceptions;
+  else
+    cxa_uncaught_count_ptr = 0;
+}
+
+void
 GTM::gtm_thread::revert_cpp_exceptions (gtm_transaction_cp *cp)
 {
   if (cp)
     {
-      // If rolling back a nested transaction, only clean up unthrown
-      // exceptions since the last checkpoint. Always reset eh_in_flight
-      // because it just contains the argument provided to
-      // _ITM_commitTransactionEH
-      void *unthrown =
-         (cxa_unthrown != cp->cxa_unthrown ? cxa_unthrown : NULL);
+      // If rolling back a nested transaction, only clean up incompletely
+      // caught exceptions since the last checkpoint.
       assert (cxa_catch_count >= cp->cxa_catch_count);
       uint32_t catch_count = cxa_catch_count - cp->cxa_catch_count;
-      if (unthrown || catch_count)
+      if (catch_count)
        {
-         __cxa_tm_cleanup (unthrown, this->eh_in_flight, catch_count);
+         __cxa_tm_cleanup (NULL, NULL, catch_count);
          cxa_catch_count = cp->cxa_catch_count;
-         cxa_unthrown = cp->cxa_unthrown;
-         this->eh_in_flight = NULL;
        }
     }
   else
     {
       // Both cxa_catch_count and cxa_unthrown are maximal because EH regions
       // and transactions are properly nested.
-      if (this->cxa_unthrown || this->cxa_catch_count)
+      if (cxa_catch_count)
        {
-         __cxa_tm_cleanup (this->cxa_unthrown, this->eh_in_flight,
-             this->cxa_catch_count);
-         this->cxa_catch_count = 0;
-         this->cxa_unthrown = NULL;
-         this->eh_in_flight = NULL;
+         __cxa_tm_cleanup (NULL, NULL, cxa_catch_count);
+         cxa_catch_count = 0;
        }
     }
+  // Reset the number of uncaught exceptions.  Any allocations for these
+  // exceptions have been rolled back already, if necessary.
+  if (cxa_uncaught_count_ptr != 0)
+    *cxa_uncaught_count_ptr = cxa_uncaught_count;
+  // Always reset eh_in_flight because it just contains the argument provided
+  // to _ITM_commitTransactionEH.
+  eh_in_flight = NULL;
 }
index 651896b..900c444 100644 (file)
@@ -283,6 +283,7 @@ extern void _ITM_registerTMCloneTable (void *, size_t);
 extern void _ITM_deregisterTMCloneTable (void *);
 
 extern void *_ITM_cxa_allocate_exception (size_t);
+extern void _ITM_cxa_free_exception (void *exc_ptr);
 extern void _ITM_cxa_throw (void *obj, void *tinfo, void *dest);
 extern void *_ITM_cxa_begin_catch (void *exc_ptr);
 extern void _ITM_cxa_end_catch (void);
index ac371de..b2e1c2d 100644 (file)
@@ -186,4 +186,5 @@ LIBITM_1.1 {
   global:
        _ZGTtdlPv?;
        _ZGTtdlPv?RKSt9nothrow_t;
+       _ITM_cxa_free_exception;
 } LIBITM_1.0;
index d3678c5..eb57fda 100644 (file)
@@ -268,17 +268,26 @@ transactions.
 @example
 void _ITM_commitTransactionEH(void *exc_ptr) ITM_REGPARM;
 void *_ITM_cxa_allocate_exception (size_t);
+void _ITM_cxa_free_exception (void *exc_ptr);
 void _ITM_cxa_throw (void *obj, void *tinfo, void *dest);
 void *_ITM_cxa_begin_catch (void *exc_ptr);
 void _ITM_cxa_end_catch (void);
 @end example
 
-@code{_ITM_commitTransactionEH} must be called to commit a transaction if an
-exception could be in flight at this position in the code. @code{exc_ptr} is
-the current exception or zero if there is no current exception.
+The EH scheme changed in version 6 of GCC.  Previously, the compiler
+added a call to @code{_ITM_commitTransactionEH} to commit a transaction if
+an exception could be in flight at this position in the code; @code{exc_ptr} is
+the address of the current exception and must be non-zero.  Now, the
+compiler must catch all exceptions that are about to be thrown out of a
+transaction and call @code{_ITM_commitTransactionEH} from the catch clause,
+with @code{exc_ptr} being zero.
+
+Note that the old EH scheme never worked completely in GCC's implementation;
+libitm currently does not try to be compatible with the old scheme.
+
 The @code{_ITM_cxa...} functions are transactional wrappers for the respective
 @code{__cxa...} functions and must be called instead of these in transactional
-code.
+code.  @code{_ITM_cxa_free_exception} is new in GCC 6.
 
 To support this EH scheme, libstdc++ needs to provide one additional function
 (@code{_cxa_tm_cleanup}), which is used by the TM to clean up the exception
@@ -289,7 +298,8 @@ void __cxa_tm_cleanup (void *unthrown_obj, void *cleanup_exc,
                        unsigned int caught_count);
 @end example
 
-@code{unthrown_obj} is non-null if the program called
+Since GCC 6, @code{unthrown_obj} is not used anymore and always null;
+prior to that, @code{unthrown_obj} is non-null if the program called
 @code{__cxa_allocate_exception} for this exception but did not yet called
 @code{__cxa_throw} for it. @code{cleanup_exc} is non-null if the program is
 currently processing a cleanup along an exception path but has not caught this
@@ -406,6 +416,10 @@ These functions are essentially transactional wrappers for @code{malloc},
 @code{calloc}, and @code{free}. Within transactions, the compiler should
 replace calls to the original functions with calls to the wrapper functions.
 
+libitm also provides transactional clones of C++ memory management functions
+such as global operator new and delete.  They are part of libitm for historic
+reasons but do not need to be part of this ABI.
+
 
 @section [No changes] Future Enhancements to the ABI
 
index bf8d4d1..f01a1ab 100644 (file)
@@ -132,7 +132,7 @@ struct gtm_transaction_cp
   _ITM_transactionId_t id;
   uint32_t prop;
   uint32_t cxa_catch_count;
-  void *cxa_unthrown;
+  unsigned int cxa_uncaught_count;
   // We might want to use a different but compatible dispatch method for
   // a nested transaction.
   abi_dispatch *disp;
@@ -242,7 +242,9 @@ struct gtm_thread
 
   // Data used by eh_cpp.c for managing exceptions within the transaction.
   uint32_t cxa_catch_count;
-  void *cxa_unthrown;
+  // If cxa_uncaught_count_ptr is 0, we don't need to roll back exceptions.
+  unsigned int *cxa_uncaught_count_ptr;
+  unsigned int cxa_uncaught_count;
   void *eh_in_flight;
 
   // Checkpoints for closed nesting.
@@ -284,9 +286,9 @@ struct gtm_thread
   void record_allocation (void *, void (*)(void *));
   void forget_allocation (void *, void (*)(void *));
   void forget_allocation (void *, size_t, void (*)(void *, size_t));
-  void drop_references_allocations (const void *ptr)
+  void discard_allocation (const void *ptr)
   {
-    this->alloc_actions.erase((uintptr_t) ptr);
+    alloc_actions.erase((uintptr_t) ptr);
   }
 
   // In beginend.cc
@@ -306,6 +308,7 @@ struct gtm_thread
   static uint32_t begin_transaction(uint32_t, const gtm_jmpbuf *)
        __asm__(UPFX "GTM_begin_transaction") ITM_REGPARM;
   // In eh_cpp.cc
+  void init_cpp_exceptions ();
   void revert_cpp_exceptions (gtm_transaction_cp *cp = 0);
 
   // In retry.cc
diff --git a/libitm/testsuite/libitm.c++/eh-5.C b/libitm/testsuite/libitm.c++/eh-5.C
new file mode 100644 (file)
index 0000000..ae38bba
--- /dev/null
@@ -0,0 +1,46 @@
+// Test throwing an exception whose constructor might throw.  This tests that
+// _cxa_free_exception is instrumented.
+
+// { dg-do run }
+// { dg-options "-fgnu-tm" }
+
+void __attribute__ ((transaction_pure,noinline)) dontoptimize (int *i)
+{ }
+
+struct test
+{
+  int* data;
+  test (int i)
+  {
+    // new may throw
+    data = new int[1];
+    data[0] = i;
+    dontoptimize (data);
+  }
+  test (const test& t) : test (t.data[0])
+  { }
+  ~test ()
+  {
+    delete data;
+  }
+  bool operator !=(const test& other)
+  {
+    return data[0] != other.data[0];
+  }
+};
+
+int main()
+{
+  try
+    {
+      atomic_commit
+      {
+       throw test(23);
+      }
+    }
+  catch (test ex)
+    {
+      if (ex.data[0] != 23) __builtin_abort ();
+    }
+  return 0;
+}