1 /* except-gcc.cpp -*-C++-*-
2 *
3 *************************************************************************
4 *
5 * @copyright
6 * Copyright (C) 2009-2013, Intel Corporation
7 * All rights reserved.
8 *
9 * @copyright
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * * Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * * Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * * Neither the name of Intel Corporation nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * @copyright
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
32 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
35 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 **************************************************************************/
38
39 #include "except-gcc.h"
40 #include "except.h"
41 #include "sysdep.h"
42 #include "bug.h"
43 #include "local_state.h"
44 #include "full_frame.h"
45 #include "scheduler.h"
46 #include "frame_malloc.h"
47 #include "pedigrees.h"
48
49 #include <stdint.h>
50 #include <typeinfo>
51
52 #define DEBUG_EXCEPTIONS 0
53
54 struct pending_exception_info
55 {
56 void make(__cxa_eh_globals *, _Unwind_Exception *, bool);
57 void destruct();
58 bool empty() const;
59 void check() const;
60 /* Active exception at time of suspend. */
61 _Unwind_Exception *active;
62 /* If true the most recently caught exception is to be rethrown
63 on resume. This handling is technically incorrect but allows
64 running without compiler support; the proper standards-compliant
65 method is to save the exception in the previous field. */
66 bool rethrow;
67 struct __cxa_eh_globals runtime_state;
68 };
69
check() const70 void pending_exception_info::check() const
71 {
72 if (active)
73 CILK_ASSERT((int)runtime_state.uncaughtExceptions > 0);
74 }
75
make(__cxa_eh_globals * state_in,_Unwind_Exception * exc_in,bool rethrow_in)76 void pending_exception_info::make(__cxa_eh_globals *state_in,
77 _Unwind_Exception *exc_in, bool rethrow_in)
78 {
79 active = exc_in;
80 rethrow = rethrow_in;
81 runtime_state = *state_in;
82 /* Read and clear C++ runtime state. */
83 state_in->caughtExceptions = 0;
84 state_in->uncaughtExceptions = 0;
85 #if CILK_LIB_DEBUG
86 check();
87 #endif
88 }
89
90 bool
empty() const91 pending_exception_info::empty() const
92 {
93 return !active && !rethrow && !runtime_state.caughtExceptions &&
94 !runtime_state.uncaughtExceptions;
95 }
96
97 #if DEBUG_EXCEPTIONS
98 #include <stdio.h>
99 static void
decode_exceptions(char * out,size_t len,struct pending_exception_info * info)100 decode_exceptions(char *out, size_t len, struct pending_exception_info *info)
101 {
102 if (info->empty())
103 snprintf(out, len, "[empty]");
104 else if (info->rethrow)
105 snprintf(out, len, "[rethrow %p]",
106 info->runtime_state.caughtExceptions);
107 else
108 snprintf(out, len, "[throw %p]", (void *)info->active);
109 }
110 #endif
111
112 static void
save_exception_info(__cilkrts_worker * w,__cxa_eh_globals * state,_Unwind_Exception * exc,bool rethrow,const char * why)113 save_exception_info(__cilkrts_worker *w,
114 __cxa_eh_globals *state,
115 _Unwind_Exception *exc,
116 bool rethrow,
117 const char *why)
118 {
119 struct pending_exception_info *info =
120 (struct pending_exception_info *)__cilkrts_frame_malloc(w, sizeof (struct pending_exception_info));
121 CILK_ASSERT(info);
122 info->make(state, exc, rethrow);
123
124 #if DEBUG_EXCEPTIONS
125 {
126 char buf[40];
127 decode_exceptions(buf, sizeof buf, info);
128 fprintf(stderr, "make exception info W%u %p %s (%s)\n",
129 w->self, info, buf, why);
130 }
131 #endif
132
133 CILK_ASSERT(w->l->pending_exception == 0);
134 w->l->pending_exception = info;
135 }
136
137 #if DEBUG_EXCEPTIONS
138 #include <stdio.h> /* DEBUG */
139
decode_flags(int flags,char out[9])140 static void decode_flags(int flags, char out[9])
141 {
142 out[0] = (flags & CILK_FRAME_STOLEN) ? 'S' : '_';
143 out[1] = (flags & CILK_FRAME_UNSYNCHED) ? 'U' : '_';
144 out[2] = (flags & CILK_FRAME_DETACHED) ? 'D' : '_';
145 out[3] = (flags & CILK_FRAME_EXCEPTING) ? 'X' : '_';
146 out[4] = '\0';
147 }
148 #endif
149
150 /* __cilkrts_save_except is called from the runtime epilogue
151 when a function is returning with an exception pending.
152
153 If the function has a parent to which it could return normally,
154 return and have the caller call _Unwind_Resume, the same as if
155 an exception filter had not matched.
156
157 Otherwise save the exception in the worker.
158
159 If this is a return from a ordinary call that must go through
160 the runtime, the assembly epilogue must have saved the call-saved
161 register state in the parent frame. */
162
163 extern "C"
164 CILK_ABI_THROWS_VOID
__cilkrts_return_exception(__cilkrts_stack_frame * sf)165 __cilkrts_return_exception(__cilkrts_stack_frame *sf)
166 {
167 __cilkrts_worker *w = sf->worker;
168 _Unwind_Exception *exc = (_Unwind_Exception *)sf->except_data;
169
170 CILK_ASSERT(sf->flags & CILK_FRAME_DETACHED);
171 sf->flags &= ~CILK_FRAME_DETACHED;
172
173 /*
174 * If we are in replay mode, and a steal occurred during the recording
175 * phase, stall till a steal actually occurs.
176 */
177 replay_wait_for_steal_if_parent_was_stolen(w);
178
179 /* If this is to be an abnormal return, save the active exception. */
180 if (!__cilkrts_pop_tail(w)) {
181 /* Write a record to the replay log for an attempt to return to a
182 stolen parent. This must be done before the exception handler
183 invokes __cilkrts_leave_frame which will bump the pedigree so
184 the replay_wait_for_steal_if_parent_was_stolen() above will match on
185 replay */
186 replay_record_orphaned(w);
187
188 /* Now that the record/replay stuff is done, update the pedigree */
189 update_pedigree_on_leave_frame(w, sf);
190
191 /* Inline pop_frame; this may not be needed. */
192 w->current_stack_frame = sf->call_parent;
193 sf->call_parent = 0;
194 __cxa_eh_globals *state = __cxa_get_globals();
195
196 #if DEBUG_EXCEPTIONS
197 fflush(stdout);
198 char decoded[9];
199 decode_flags(sf->flags, decoded);
200 fprintf(stderr, "__cilkrts_save_except W%u sf %p/%s exc %p [%u %p] suspend\n",
201 w->self, sf, decoded, exc,
202 state->uncaughtExceptions,
203 state->caughtExceptions);
204 #endif
205
206 /* Like __cilkrts_save_exception_state except for setting the
207 rethrow flag. */
208 save_exception_info(w, state, exc, exc == NULL, "save_except");
209 {
210 full_frame *ff = w->l->frame_ff;
211 CILK_ASSERT(NULL == ff->pending_exception);
212 ff->pending_exception = w->l->pending_exception;
213 w->l->pending_exception = NULL;
214 }
215 __cilkrts_exception_from_spawn(w, sf); /* does not return */
216 }
217 /* This code path is taken when the parent is attached. It is on
218 the same stack and part of the same full frame. The caller is
219 cleaning up the Cilk frame during unwind and will reraise the
220 exception */
221
222 /* Now that the record/replay stuff is done, update the pedigree */
223 update_pedigree_on_leave_frame(w, sf);
224
225 #if DEBUG_EXCEPTIONS /* DEBUG ONLY */
226 {
227 __cxa_eh_globals *state = __cxa_get_globals();
228
229 fflush(stdout);
230 char decoded[9];
231 decode_flags(sf->flags, decoded);
232 fprintf(stderr, "__cilkrts_save_except W%d %p/%s %p->%p [%u %p] escape\n",
233 w->self, sf, decoded, exc,
234 exc ? to_cxx(exc)->nextException : 0,
235 state->uncaughtExceptions,
236 state->caughtExceptions);
237
238 /* XXX This is triggering in the user thread which gets an exception
239 from somewhere but does not get the corresponding runtime exception
240 state.
241 XXX There might be two or more uncaught exceptions. Test could be
242 (uncaught != 0) == (exc != 0). First, design tests to see if that
243 case is otherwise handled correctly. And what if there's an uncaught
244 exception that does not belong to this function? I.e. this is a return
245 from spawn in a destructor. */
246 if (exc)
247 CILK_ASSERT((int)state->uncaughtExceptions > 0);
248 /*CILK_ASSERT(state->uncaughtExceptions == (exc != 0));*/
249 }
250 #endif
251
252 /* The parent is attached so this exception can be propagated normally. */
253 return;
254 }
255
256 /* Save the exception state into the full frame, which is exiting
257 or suspending. */
258 extern "C"
__cilkrts_save_exception_state(__cilkrts_worker * w,full_frame * ff)259 void __cilkrts_save_exception_state(__cilkrts_worker *w, full_frame *ff)
260 {
261 save_exception_info(w, __cxa_get_globals(), 0, false, "undo-detach");
262 CILK_ASSERT(NULL == ff->pending_exception);
263 ff->pending_exception = w->l->pending_exception;
264 w->l->pending_exception = NULL;
265 }
266
267 /* __cilkrts_c_sync_except is like __cilkrts_c_sync except that it
268 saves exception state. __cilkrts_c_sync never returns here and
269 always reinstalls the saved exception state.
270
271 This function must be used because a parent of this function may
272 be propagating an uncaught exception. The uncaught exception
273 count must be saved by the child and passed back to the parent. */
274
275 extern "C"
__cilkrts_c_sync_except(__cilkrts_worker * w,__cilkrts_stack_frame * sf)276 NORETURN __cilkrts_c_sync_except (__cilkrts_worker *w, __cilkrts_stack_frame *sf)
277 {
278 __cxa_eh_globals *state = __cxa_get_globals();
279 _Unwind_Exception *exc = (_Unwind_Exception *)sf->except_data;
280
281 CILK_ASSERT((sf->flags & (CILK_FRAME_UNSYNCHED|CILK_FRAME_EXCEPTING)) ==
282 (CILK_FRAME_UNSYNCHED|CILK_FRAME_EXCEPTING));
283 sf->flags &= ~CILK_FRAME_EXCEPTING;
284
285 #if DEBUG_EXCEPTIONS
286 fflush(stdout);
287 char decoded[9];
288 decode_flags(sf->flags, decoded);
289 if (exc)
290 fprintf(stderr, "__cilkrts_sync_except W%u %p/%s %p->%p [%u %p]\n",
291 w->self, sf, decoded, exc,
292 to_cxx(exc)->nextException,
293 state->uncaughtExceptions,
294 state->caughtExceptions);
295 else
296 fprintf(stderr, "__cilkrts_sync_except W%d %p/%s none [%u %p]\n",
297 w->self, sf, decoded,
298 state->uncaughtExceptions,
299 state->caughtExceptions);
300 #endif
301
302 /* Here the identity of an rethrown exception is always known.
303 If exc is NULL this call is only to preserve parent state. */
304 save_exception_info(w, state, exc, false, "sync_except");
305 #if 0
306 {
307 full_frame *ff = w->l->frame_ff;
308 CILK_ASSERT(NULL == ff->pending_exception);
309 ff->pending_exception = w->l->pending_exception;
310 w->l->pending_exception = NULL;
311 }
312 #endif
313 CILK_ASSERT(!std::uncaught_exception());
314 __cilkrts_c_sync(w, sf);
315 }
316
317 void
destruct()318 pending_exception_info::destruct()
319 {
320 if (active) {
321 #if DEBUG_EXCEPTIONS
322 fprintf(stderr, "destroy exception info %p %p\n", this, active);
323 #endif
324 _Unwind_DeleteException(active);
325 active = 0;
326 } else {
327 #if DEBUG_EXCEPTIONS
328 fprintf(stderr, "destroy exception info %p\n", this);
329 #endif
330 }
331 while (runtime_state.caughtExceptions) {
332 __cxa_exception *exc = runtime_state.caughtExceptions;
333 runtime_state.caughtExceptions = exc->nextException;
334 #if DEBUG_EXCEPTIONS
335 fprintf(stderr, "destroy caught exception %p\n", this);
336 #endif
337 _Unwind_DeleteException(&exc->unwindHeader);
338 }
339 }
340
341 /*
342 * __cilkrts_merge_pending_exceptions
343 *
344 * Merge the right exception record into the left. The left is logically
345 * earlier.
346 *
347 * The active exception of E is
348 * E->active if it is non-NULL (in which case E->rethrow is false)
349 * unresolved if E->active is NULL and E->rethrow is true
350 * nil if E->active is NULL and E->rethrow is false
351 *
352 * The merged active exception is left active exception if it is not
353 * nil, otherwise the right.
354 *
355 * On entry the left state is synched and can not have an unresolved
356 * exception. The merge may result in an unresolved exception.
357 *
358 * Due to scoping rules at most one of the caught exception lists is
359 * non-NULL.
360 */
361
362 struct pending_exception_info *
__cilkrts_merge_pending_exceptions(__cilkrts_worker * w,struct pending_exception_info * left,struct pending_exception_info * right)363 __cilkrts_merge_pending_exceptions (
364 __cilkrts_worker *w,
365 struct pending_exception_info *left,
366 struct pending_exception_info *right)
367 {
368 /* If we've only got one exception, return it */
369
370 if (NULL == left) {
371 #if DEBUG_EXCEPTIONS
372 if (right) {
373 char buf[40];
374 decode_exceptions(buf, sizeof buf, right);
375 fprintf(stderr, "__cilkrts merge W%u nil %p -> %p %s\n",
376 w->self, right, right, buf);
377 }
378 #endif
379 return right;
380 }
381
382 if (NULL == right) {
383 #if DEBUG_EXCEPTIONS
384 if (left) {
385 char buf[40];
386 decode_exceptions(buf, sizeof buf, left);
387 fprintf(stderr, "__cilkrts merge W%u %p nil -> %p %s\n",
388 w->self, left, left, buf);
389 }
390 #endif
391 return left;
392 }
393
394 #if CILK_LIB_DEBUG
395 /*volatile struct pending_exception_info left_in = *left, right_in = *right;*/
396 left->check();
397 right->check();
398 #endif
399
400 #if DEBUG_EXCEPTIONS
401 {
402 char buf1[40], buf2[40];
403 decode_exceptions(buf1, sizeof buf1, left);
404 decode_exceptions(buf2, sizeof buf2, right);
405 fprintf(stderr, "__cilkrts merge W%u %p %s %p %s\n",
406 w->self, left, buf1, right, buf2);
407 }
408 #endif
409
410 /* It should not be possible for both left and right to
411 have accumulated catch blocks.
412
413 The left exception record may always have a catch
414 chain it kept when its parent was stolen.
415
416 If they are siblings, the right sibling should not
417 have accumulated any net catches. (Catch is lexically
418 scoped.)
419
420 If the right frame is a parent, it should not have entered
421 a catch block without syncing first. If it spawned in a
422 catch block, the child got its catch. */
423 __cxa_exception *caught = left->runtime_state.caughtExceptions;
424 if (caught)
425 CILK_ASSERT(!right->runtime_state.caughtExceptions);
426 else {
427 CILK_ASSERT(!left->rethrow);
428 left->rethrow = right->rethrow;
429 left->runtime_state.caughtExceptions = caught = right->runtime_state.caughtExceptions;
430 right->runtime_state.caughtExceptions = NULL;
431 }
432
433 /* Merge the uncaught exception and count of uncaught exceptions. */
434 const unsigned int right_uncaught = right->runtime_state.uncaughtExceptions;
435 if (!left->active){
436 left->active = right->active; /* could be NULL */
437 right->active = 0;
438 left->runtime_state.uncaughtExceptions += right_uncaught;
439 if (left->active)
440 /* assert is C++ exception */
441 /*CILK_ASSERT(__cxxabiv1::__is_gxx_exception_class(left->active->exception_class))*/;
442 } else {
443 /* Subtract 1 if the right exception is being destructed. */
444 left->runtime_state.uncaughtExceptions += right_uncaught - (right->active != 0);
445 }
446
447 right->destruct();
448 __cilkrts_frame_free(w, right, sizeof *right);
449
450 /* If there is no state left, return NULL. */
451 if (left->empty()) {
452 left->destruct();
453 __cilkrts_frame_free(w, left, sizeof *left);
454 left = NULL;
455 }
456
457 #if CILK_LIB_DEBUG
458 if (left)
459 left->check();
460 #endif
461
462 return left;
463 }
464
465 #if 0
466 /* __cilkrts_c_resume_except is called from the assembly language
467 restart code when a resumed frame has a pending exception.
468
469 The handler count negation on rethrow was done when the throw was
470 resolved.
471
472 The assembly language runtime must make the throw unwind to
473 the sync, spawn, or other location where the exception should
474 be injected. (This should not happen after a spawn but nothing
475 here depends on there being no exception on steal.)
476
477 This function is unused in the Intel stack based system. */
478 extern "C"
479 void __cilkrts_c_resume_except (_Unwind_Exception *exc)
480 {
481 #if DEBUG_EXCEPTIONS
482 fprintf(stderr, "resume exception %p\n", exc);
483 #endif
484 _Unwind_Reason_Code why = _Unwind_RaiseException(exc);
485 __cilkrts_bug ("Cilk runtime error: failed to reinstate suspended exception %p (%d)\n", exc, why);
486 }
487 #endif
488
489 /* Restore the caught exception chain. This assumes no C++ exception
490 code will run before the frame is resumed. If there is no exception
491 to be resumed free the object. */
492
493 extern "C"
__cilkrts_setup_for_execution_sysdep(__cilkrts_worker * w,full_frame * ff)494 void __cilkrts_setup_for_execution_sysdep(__cilkrts_worker *w, full_frame *ff)
495 {
496 // ASSERT: We own w->lock and ff->lock || P == 1
497
498 __cxa_eh_globals *state = __cxa_get_globals ();
499 struct pending_exception_info *info = w->l->pending_exception;
500
501 if (info == NULL)
502 return;
503
504 w->l->pending_exception = 0;
505
506 #if DEBUG_EXCEPTIONS
507 _Unwind_Exception *exc = info->active;
508 if (exc) {
509 fflush(stdout);
510 fprintf(stderr, "__cilkrts_resume_except W%u %p->%p [%u %p]\n",
511 w->self, exc,
512 to_cxx(exc)->nextException,
513 info->runtime_state.uncaughtExceptions,
514 info->runtime_state.caughtExceptions);
515 /*CILK_ASSERT(info->runtime_state.uncaughtExceptions > 0);*/
516 }
517 #endif
518
519 if (state->uncaughtExceptions || state->caughtExceptions)
520 __cilkrts_bug("W%u: resuming with non-empty prior exception state %u %p\n", state->uncaughtExceptions, state->caughtExceptions);
521
522 *state = info->runtime_state;
523 info->runtime_state.caughtExceptions = 0;
524 info->runtime_state.uncaughtExceptions = 0;
525
526 if (info->rethrow) {
527 info->rethrow = false;
528 /* Resuming function will rethrow. Runtime calls
529 std::terminate if there is no caught exception. */
530 ff->call_stack->flags |= CILK_FRAME_EXCEPTING;
531 }
532 if (info->active) {
533 ff->call_stack->flags |= CILK_FRAME_EXCEPTING;
534 ff->call_stack->except_data = info->active;
535 info->active = 0;
536 }
537
538 if (info->empty()) {
539 info->destruct();
540 __cilkrts_frame_free(w, info, sizeof *info);
541 w->l->pending_exception = NULL;
542 }
543
544 #if CILK_LIB_DEBUG
545 if (ff->call_stack->except_data)
546 CILK_ASSERT(std::uncaught_exception());
547 #endif
548 }
549
550 #if 0
551 extern "C"
552 struct pending_exception_info *__cilkrts_get_exception(__cilkrts_worker *w,
553 __cilkrts_stack_frame *sf)
554 {
555 struct pending_exception_info *info = w->l->pending_exception;
556
557 if (info == NULL) {
558 sf->flags &= ~CILK_FRAME_EXCEPTING;
559 return 0;
560 }
561
562 w->l->pending_exception = NULL;
563
564 /* This exception goes into the frame. */
565
566 _Unwind_Exception *exc = info->active;
567 info->active = NULL;
568 info->destruct();
569 __cilkrts_frame_free(w, info, sizeof *info);
570 info = 0;
571 sf->flags |= CILK_FRAME_EXCEPTING;
572 sf->exception = exc;
573 return 0;
574 }
575 #endif
576
577 extern "C"
__cilkrts_gcc_rethrow(__cilkrts_stack_frame * sf)578 void __attribute__((nonnull)) __cilkrts_gcc_rethrow(__cilkrts_stack_frame *sf)
579 {
580 #ifdef __CYGWIN__
581 // Cygwin doesn't support exceptions, so _Unwind_Resume isn't available
582 // Which means we can't support exceptions either
583 __cilkrts_bug("The Cygwin implementation of the Intel Cilk Plus runtime doesn't support exceptions\n");
584 #else
585 if (sf->except_data) {
586 #if CILK_LIB_DEBUG
587 CILK_ASSERT(std::uncaught_exception());
588 #endif
589 _Unwind_Resume ((_Unwind_Exception *)sf->except_data);
590 } else {
591 throw;
592 }
593 #endif // __CYGWIN__
594 }
595
596 /* End except-gcc.cpp */
597
598