[cfe-commits] [libcxxabi] r137118 - /libcxxabi/trunk/src/cxa_exception.cpp

Marshall Clow mclow at qualcomm.com
Tue Aug 9 08:09:41 PDT 2011


Author: marshall
Date: Tue Aug  9 10:09:41 2011
New Revision: 137118

URL: http://llvm.org/viewvc/llvm-project?rev=137118&view=rev
Log:
First cut at exception handling; missing dependent exceptions. Next step: tests

Modified:
    libcxxabi/trunk/src/cxa_exception.cpp

Modified: libcxxabi/trunk/src/cxa_exception.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp?rev=137118&r1=137117&r2=137118&view=diff
==============================================================================
--- libcxxabi/trunk/src/cxa_exception.cpp (original)
+++ libcxxabi/trunk/src/cxa_exception.cpp Tue Aug  9 10:09:41 2011
@@ -21,20 +21,49 @@
 #include "cxa_exception.hpp"
 
 namespace __cxxabiv1 {
-
+static const uint64_t kOurExceptionClass          = 0x434C4E47432B2B00; // CLNGC++\0
+static const uint64_t kOurDependentExceptionClass = 0x434C4E47432B2B01; // CLNGC++\1
+                                                    
 //  Utility routines
-static __cxa_exception *exception_from_object ( void *p ) {
+static __cxa_exception *exception_from_thrown_object ( void *p ) throw () {
     return ((__cxa_exception *) p ) - 1;
     }
     
-void * object_from_exception ( void *p ) {
+static void * thrown_object_from_exception ( void *p ) throw () {
     return (void *) (((__cxa_exception *) p ) + 1 );
     }
 
-static size_t object_size_from_exception_size ( size_t size ) {
+static size_t object_size_from_exception_size ( size_t size ) throw () {
     return size + sizeof (__cxa_exception);
     }
 
+//  Get the exception object from the unwind pointer.
+//  Relies on the structure layout, where the unwind pointer is right in
+//  front of the user's exception object
+static __cxa_exception *
+exception_from_exception_object ( void *ptr ) throw () {
+    _Unwind_Exception *p = reinterpret_cast<_Unwind_Exception *> ( ptr );
+    return exception_from_thrown_object ( p + 1 );
+    }
+
+static void setExceptionClass ( _Unwind_Exception *unwind ) throw () {
+    unwind->exception_class = kOurExceptionClass;
+    }
+
+static void setDependentExceptionClass ( _Unwind_Exception *unwind ) throw () {
+    unwind->exception_class = kOurDependentExceptionClass;
+    }
+
+//  Is it one of ours?
+static bool isOurExceptionClass ( _Unwind_Exception *unwind ) throw () {
+    return ( unwind->exception_class == kOurExceptionClass ) ||
+                ( unwind->exception_class == kOurDependentExceptionClass );
+    }
+
+static bool isDependentException ( _Unwind_Exception *unwind ) throw () {
+    return ( unwind->exception_class & 0xFF ) == 0x01;
+    }
+
 #include "fallback_malloc.cpp"
 
 //  Allocate some memory from _somewhere_
@@ -51,7 +80,33 @@
     return is_fallback_ptr ( ptr ) ? fallback_free ( ptr ) : std::free ( ptr );
     }
 
-// pthread_once_t __globals::flag_ = PTHREAD_ONCE_INIT;
+/*  Howard says:
+    If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler
+    stored in exc is called.  Otherwise the exceptionDestructor stored in 
+    exc is called, and then the memory for the exception is deallocated.
+*/
+static void exception_cleanup_func ( _Unwind_Reason_Code reason, struct _Unwind_Exception* exc ) {
+    __cxa_exception *exception = exception_from_exception_object ( exc );
+    if ( _URC_FOREIGN_EXCEPTION_CAUGHT != reason )
+        exception->terminateHandler ();
+        
+    void * thrown_object = thrown_object_from_exception ( exception );
+    if ( NULL != exception->exceptionDestructor )
+        exception->exceptionDestructor ( thrown_object );
+    __cxa_free_exception( thrown_object );
+    }
+
+static LIBCXXABI_NORETURN void failed_throw ( __cxa_exception *exception ) throw () {
+//  Section 2.5.3 says:
+//      * For purposes of this ABI, several things are considered exception handlers:
+//      ** A terminate() call due to a throw.
+//  and
+//      * Upon entry, Following initialization of the catch parameter, 
+//          a handler must call:
+//      * void *__cxa_begin_catch ( void *exceptionObject );
+    (void) __cxa_begin_catch ( &exception->unwindHeader );
+    std::terminate ();
+    }
 
 extern "C" {
 
@@ -66,13 +121,13 @@
     if ( NULL == ptr )
         std::terminate ();
     std::memset ( ptr, 0, actual_size );
-    return object_from_exception ( ptr );
+    return thrown_object_from_exception ( ptr );
     }
 
 
 //  Free a __cxa_exception object allocated with __cxa_allocate_exception.
 void __cxa_free_exception (void * thrown_exception) throw() {
-    do_free ( exception_from_object ( thrown_exception ));
+    do_free ( exception_from_thrown_object ( thrown_exception ));
     }
 
 
@@ -97,6 +152,168 @@
     do_free ( dependent_exception );
     }
 
+
+// 2.4.3 Throwing the Exception Object
+/*
+After constructing the exception object with the throw argument value,
+the generated code calls the __cxa_throw runtime library routine. This
+routine never returns.
+
+The __cxa_throw routine will do the following:
+
+* Obtain the __cxa_exception header from the thrown exception object address,
+which can be computed as follows: 
+ __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); 
+* Save the current unexpected_handler and terminate_handler in the __cxa_exception header.
+* Save the tinfo and dest arguments in the __cxa_exception header. 
+* Set the exception_class field in the unwind header. This is a 64-bit value
+representing the ASCII string "XXXXC++\0", where "XXXX" is a
+vendor-dependent string. That is, for implementations conforming to this
+ABI, the low-order 4 bytes of this 64-bit value will be "C++\0".
+* Increment the uncaught_exception flag. 
+* Call _Unwind_RaiseException in the system unwind library, Its argument is the
+pointer to the thrown exception, which __cxa_throw itself received as an argument.
+__Unwind_RaiseException begins the process of stack unwinding, described
+in Section 2.5. In special cases, such as an inability to find a
+handler, _Unwind_RaiseException may return. In that case, __cxa_throw
+will call terminate, assuming that there was no handler for the
+exception.
+*/
+LIBCXXABI_NORETURN void 
+__cxa_throw(void * thrown_exception, std::type_info * tinfo, void (*dest)(void *)) {
+    __cxa_eh_globals *globals = __cxa_get_globals ();
+    __cxa_exception *exception = exception_from_thrown_object ( thrown_exception );
+    
+    exception->unexpectedHandler = __cxxabiapple::__cxa_unexpected_handler;
+    exception->terminateHandler  = __cxxabiapple::__cxa_terminate_handler;
+    exception->exceptionType = tinfo;
+    exception->exceptionDestructor = dest;
+    setExceptionClass ( &exception->unwindHeader );
+    exception->referenceCount = 1;  // This is a newly allocated exception, no need for thread safety.
+    globals->uncaughtExceptions += 1;   // Not atomically, since globals are thread-local
+
+    exception->unwindHeader.exception_cleanup = exception_cleanup_func;
+    _Unwind_RaiseException ( &exception->unwindHeader );
+    
+//  If we get here, some kind of unwinding error has occurred.
+    failed_throw ( exception );
+    }
+
+
+// 2.5.3 Exception Handlers
+extern void * __cxa_get_exception_ptr(void * exceptionObject) throw() {
+    return exception_from_exception_object ( exceptionObject );
+    }
+    
+
+/*
+This routine:
+* Increment's the exception's handler count.
+* Places the exception on the stack of currently-caught exceptions if it is not 
+  already there, linking the exception to the previous top of the stack.
+* Decrements the uncaught_exception count.
+* Returns the adjusted pointer to the exception object.
+*/
+void * __cxa_begin_catch(void * exceptionObject) throw() {
+    __cxa_eh_globals *globals = __cxa_get_globals ();
+    __cxa_exception *exception = exception_from_exception_object ( exceptionObject );
+
+//  TODO add stuff for dependent exceptions.
+
+//  TODO - should this be atomic?
+//  Increment the handler count, removing the flag about being rethrown
+//  assert ( exception->handlerCount != 0 );
+    exception->handlerCount = exception->handlerCount < 0 ?
+        -exception->handlerCount + 1 : exception->handlerCount + 1;
+
+//  place the exception on the top of the stack if it's not there.
+    if ( exception != globals->caughtExceptions ) {
+        exception->nextException = globals->caughtExceptions;
+        globals->caughtExceptions = exception;
+        }
+        
+    globals->uncaughtExceptions -= 1;   // Not atomically, since globals are thread-local
+    return thrown_object_from_exception ( exception );
+    }
+
+
+/*
+Upon exit for any reason, a handler must call:
+    void __cxa_end_catch ();
+
+This routine:
+* Locates the most recently caught exception and decrements its handler count.
+* Removes the exception from the caught exception stack, if the handler count goes to zero.
+* Destroys the exception if the handler count goes to zero, and the exception was not re-thrown by throw.
+*/
+void __cxa_end_catch() {
+    __cxa_eh_globals *globals = __cxa_get_globals ();
+    __cxa_exception *current_exception = globals->caughtExceptions;
+    
+    if ( NULL != current_exception ) {
+        if ( current_exception->handlerCount < 0 ) {
+        //  The exception has been rethrown
+            current_exception->handlerCount += 1;       // TODO: should be atomic?
+            if ( 0 == current_exception->handlerCount )
+                globals->caughtExceptions = current_exception->nextException;
+            //	Howard says: If the exception has been rethrown, don't destroy.
+            }
+        else {
+            current_exception->handlerCount -= 1;       // TODO: should be atomic?
+            if ( 0 == current_exception->handlerCount ) {
+            //  Remove from the chain of uncaught exceptions
+                globals->caughtExceptions = current_exception->nextException;
+                if ( !isDependentException ( &current_exception->unwindHeader ))
+                    _Unwind_DeleteException ( &current_exception->unwindHeader );
+                else {
+                //  TODO: deal with a dependent exception
+                    }
+                }
+            }       
+        }
+    }
+
+
+std::type_info * __cxa_current_exception_type() {
+//  get the current exception
+    __cxa_eh_globals *globals = __cxa_get_globals ();
+    __cxa_exception *current_exception = globals->caughtExceptions;
+    if ( NULL == current_exception )
+        return NULL;        //  No current exception
+//  TODO add stuff for dependent exceptions.
+    return current_exception->exceptionType;
+    }
+
+// 2.5.4 Rethrowing Exceptions
+/*  This routine 
+* marks the exception object on top of the caughtExceptions stack 
+  (in an implementation-defined way) as being rethrown. 
+* If the caughtExceptions stack is empty, it calls terminate() 
+  (see [C++FDIS] [except.throw], 15.1.8). 
+* It then returns to the handler that called it, which must call 
+  __cxa_end_catch(), perform any necessary cleanup, and finally 
+  call _Unwind_Resume() to continue unwinding.
+*/
+extern LIBCXXABI_NORETURN void __cxa_rethrow() {
+    __cxa_eh_globals *globals = __cxa_get_globals ();
+    __cxa_exception *exception = exception_from_exception_object ( globals->caughtExceptions );
+
+    if ( NULL == exception )    // there's no current exception!
+        std::terminate ();
+
+//  Mark the exception as being rethrown
+    exception->handlerCount = -exception->handlerCount ;
+    
+#if __arm__
+    (void) _Unwind_SjLj_Resume_or_Rethrow ( &exception->unwindHeader );
+#else
+    (void) _Unwind_Resume_or_Rethrow      ( &exception->unwindHeader );
+#endif
+
+//  If we get here, some kind of unwinding error has occurred.
+    failed_throw ( exception );
+    }
+
 }  // extern "C"
 
 }  // abi





More information about the cfe-commits mailing list