//===- c++-exception.cpp - Exception handling support for C++ exceptions --===// // // This file defines the methods used to implement C++ exception handling in // terms of the invoke and %llvm.unwind intrinsic. These primitives implement // an exception handling ABI similar (but simpler and more efficient than) the // Itanium C++ ABI exception handling standard. // //===----------------------------------------------------------------------===// #include "c++-exception.h" #include #include //===----------------------------------------------------------------------===// // Generic exception support // // Thread local state for exception handling. // FIXME: This should really be made thread-local! // static llvm_exception *CaughtExceptionStack = 0; // UncaughtExceptionStack - The stack of exceptions currently being thrown. static llvm_exception *UncaughtExceptionStack = 0; // __llvm_eh_has_uncaught_exception - This is used to implement // std::uncaught_exception. // bool __llvm_eh_has_uncaught_exception(void) { return UncaughtExceptionStack != 0; } // __llvm_eh_current_uncaught_exception - This function checks to see if the // current uncaught exception is of the specified language type. If so, it // returns a pointer to the exception area data. // void *__llvm_eh_current_uncaught_exception_type(unsigned HandlerType) { assert(UncaughtExceptionStack && "No uncaught exception!"); if (UncaughtExceptionStack->ExceptionType == HandlerType) return UncaughtExceptionStack+1; return 0; } /*===----------------------------------------------------------------------===** * C++ Specific exception handling support... */ // __llvm_cxxeh_allocate_exception - This function allocates space for the // specified number of bytes, plus a C++ exception object header. // void *__llvm_cxxeh_allocate_exception(unsigned NumBytes) { // FIXME: This should eventually have back-up buffers for out-of-memory // situations. // llvm_cxx_exception *E = (llvm_cxx_exception *)malloc(NumBytes+sizeof(llvm_cxx_exception)); E->BaseException.ExceptionType = 0; // intialize to invalid return E+1; // return the pointer after the header } // __llvm_cxxeh_free_exception - Low-level function to free an exception. This // is called directly from generated C++ code if evaluating the exception value // into the exception location throws. Otherwise it is called from the C++ // exception object destructor. // void __llvm_cxxeh_free_exception(void *ObjectPtr) { llvm_cxx_exception *E = (llvm_cxx_exception *)ObjectPtr - 1; free(E); } // cxx_destructor - This function is called through the generic // exception->ExceptionDestructor function pointer to destroy a caught // exception. // static void cxx_destructor(llvm_exception *LE) { void *ObjectPtr = LE+1; llvm_cxx_exception *E = (llvm_cxx_exception *)ObjectPtr - 1; // The exception is no longer caught. assert(CaughtExceptionStack == LE && "Destroying an exception which is not the current caught exception?"); CaughtExceptionStack = LE->Next; struct ExceptionFreer { void *Ptr; ExceptionFreer(void *P) : Ptr(P) {} ~ExceptionFreer() { // Free the memory for the exception, when the function is left, even if // the exception object dtor throws its own exception! __llvm_cxxeh_free_exception(Ptr); } } EF(E+1); // Run the exception object dtor if it exists. */ if (E->ExceptionObjectDestructor) E->ExceptionObjectDestructor(E); } // __llvm_cxxeh_throw - Given a pointer to memory which has an exception object // evaluated into it, this sets up all of the fields of the exception allowing // it to be thrown. After calling this, the code should call %llvm.unwind // void __llvm_cxxeh_throw(void *ObjectPtr, const std::type_info *TypeInfoPtr, void (*DtorPtr)(void*)) { llvm_cxx_exception *E = (llvm_cxx_exception *)ObjectPtr - 1; E->BaseException.ExceptionDestructor = cxx_destructor; E->BaseException.ExceptionType = CXXException; E->BaseException.Next = UncaughtExceptionStack; UncaughtExceptionStack = &E->BaseException; E->BaseException.HandlerCount = 0; E->TypeInfo = TypeInfoPtr; E->ExceptionObjectDestructor = DtorPtr; E->UnexpectedHandler = 0; /* FIXME */ E->TerminateHandler = 0; /* FIXME */ } // __llvm_cxxeh_current_uncaught_exception_isa - This function checks to see if // the current uncaught exception is a C++ exception, and if it is of the // specified type id. If so, it returns a pointer to the object adjusted as // appropriate, otherwise it returns null. // void *__llvm_cxxeh_current_uncaught_exception_isa( const std::type_info *CatchType) { assert(UncaughtExceptionStack && "No uncaught exception!"); if (UncaughtExceptionStack->ExceptionType != CXXException) return 0; /* If it's not a c++ exception, it doesn't match! */ // If it is a C++ exception, use the type info object stored in the exception // to see if TypeID matches and, if so, to adjust the exception object // pointer. // llvm_cxx_exception *E = (llvm_cxx_exception*)UncaughtExceptionStack; // ThrownPtr is a pointer to the object being thrown... void *ThrownPtr = E+1; const std::type_info *ThrownType = E->TypeInfo; // FIXME: this code exists in the GCC exception handling library: I haven't // thought about this yet, so it should be verified at some point! #if 1 // Pointer types need to adjust the actual pointer, not // the pointer to pointer that is the exception object. // This also has the effect of passing pointer types // "by value" through the __cxa_begin_catch return value. if (ThrownType->__is_pointer_p()) ThrownPtr = *(void **)ThrownPtr; #endif if (CatchType->__do_catch(ThrownType, &ThrownPtr, 1)) return ThrownPtr; return 0; } /* __llvm_cxxeh_begin_catch - This function is called by "exception handlers", * which transition an exception from being uncaught to being caught. It * returns a pointer to the exception object portion of the exception. This * function must work with foreign exceptions. */ void *__llvm_cxxeh_begin_catch(void) { llvm_exception *E = UncaughtExceptionStack; assert(UncaughtExceptionStack && "There are no uncaught exceptions!?!?"); /* The exception is now no longer uncaught... */ UncaughtExceptionStack = E->Next; /* The exception is now caught... */ E->Next = CaughtExceptionStack; CaughtExceptionStack = E->Next; /* Increment the handler count for this exception. */ E->HandlerCount++; /* Return a pointer to the exception object */ return E+1; } /* __llvm_cxxeh_end_catch - This function decrements the HandlerCount of the * top-level caught exception, destroying it if this is the last handler for the * exception. */ void __llvm_cxxeh_end_catch(void) { llvm_exception *E = CaughtExceptionStack; assert(E && "There are no caught exceptions!"); /* If this is the last handler using the exception, destroy it now! */ if (--E->HandlerCount == 0) { CaughtExceptionStack = E->Next; /* Unlink from the stack */ E->ExceptionDestructor(E); /* Release memory for the exception */ } } /* __llvm_cxxeh_rethrow - This function turns the top-level caught exception * into an uncaught exception, in preparation for an llvm.unwind, which should * follow immediately after the call to this function. This function must be * prepared to deal with foreign exceptions. */ void __llvm_cxxeh_rethrow(void) { llvm_exception *E = CaughtExceptionStack; if (E == 0) { /* 15.1.8 - If there are no uncaught exceptions being thrown, 'throw;' * should call terminate. */ /* FIXME */assert(0 && "FIXME: this should call E->Terminate!"); } /* Otherwise we have an exception to rethrow. Move it back to the uncaught * stack. */ CaughtExceptionStack = E->Next; E->Next = UncaughtExceptionStack; UncaughtExceptionStack = E; /* Decrement the number of handlers which are using the exception. */ --E->HandlerCount; /* Return to the caller, which should perform the unwind now. */ }