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