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
54struct 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
70void pending_exception_info::check() const
71{
72    if (active)
73        CILK_ASSERT((int)runtime_state.uncaughtExceptions > 0);
74}
75
76void 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
90bool
91pending_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>
99static void
100decode_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
112static void
113save_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
140static 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
163extern "C"
164CILK_ABI_THROWS_VOID
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. */
258extern "C"
259void __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
275extern "C"
276NORETURN __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
317void
318pending_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
362struct pending_exception_info *
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. */
478extern "C"
479void __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
493extern "C"
494void __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
551extern "C"
552struct 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
577extern "C"
578void __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