summaryrefslogtreecommitdiff
path: root/runtime/GCCLibraries/crtend/C++-Exception.cpp
blob: 7ed78688eaa0baee9ef6d45529afb89ecd515465 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//===- 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 <cstdlib>
#include <cassert>

//===----------------------------------------------------------------------===//
// 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. */
}