3038054c68
From-SVN: r204173
598 lines
20 KiB
C++
598 lines
20 KiB
C++
/* except-gcc.cpp -*-C++-*-
|
|
*
|
|
*************************************************************************
|
|
*
|
|
* @copyright
|
|
* Copyright (C) 2009-2013, Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* @copyright
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* @copyright
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
**************************************************************************/
|
|
|
|
#include "except-gcc.h"
|
|
#include "except.h"
|
|
#include "sysdep.h"
|
|
#include "bug.h"
|
|
#include "local_state.h"
|
|
#include "full_frame.h"
|
|
#include "scheduler.h"
|
|
#include "frame_malloc.h"
|
|
#include "pedigrees.h"
|
|
|
|
#include <stdint.h>
|
|
#include <typeinfo>
|
|
|
|
#define DEBUG_EXCEPTIONS 0
|
|
|
|
struct pending_exception_info
|
|
{
|
|
void make(__cxa_eh_globals *, _Unwind_Exception *, bool);
|
|
void destruct();
|
|
bool empty() const;
|
|
void check() const;
|
|
/* Active exception at time of suspend. */
|
|
_Unwind_Exception *active;
|
|
/* If true the most recently caught exception is to be rethrown
|
|
on resume. This handling is technically incorrect but allows
|
|
running without compiler support; the proper standards-compliant
|
|
method is to save the exception in the previous field. */
|
|
bool rethrow;
|
|
struct __cxa_eh_globals runtime_state;
|
|
};
|
|
|
|
void pending_exception_info::check() const
|
|
{
|
|
if (active)
|
|
CILK_ASSERT((int)runtime_state.uncaughtExceptions > 0);
|
|
}
|
|
|
|
void pending_exception_info::make(__cxa_eh_globals *state_in,
|
|
_Unwind_Exception *exc_in, bool rethrow_in)
|
|
{
|
|
active = exc_in;
|
|
rethrow = rethrow_in;
|
|
runtime_state = *state_in;
|
|
/* Read and clear C++ runtime state. */
|
|
state_in->caughtExceptions = 0;
|
|
state_in->uncaughtExceptions = 0;
|
|
#if CILK_LIB_DEBUG
|
|
check();
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
pending_exception_info::empty() const
|
|
{
|
|
return !active && !rethrow && !runtime_state.caughtExceptions &&
|
|
!runtime_state.uncaughtExceptions;
|
|
}
|
|
|
|
#if DEBUG_EXCEPTIONS
|
|
#include <stdio.h>
|
|
static void
|
|
decode_exceptions(char *out, size_t len, struct pending_exception_info *info)
|
|
{
|
|
if (info->empty())
|
|
snprintf(out, len, "[empty]");
|
|
else if (info->rethrow)
|
|
snprintf(out, len, "[rethrow %p]",
|
|
info->runtime_state.caughtExceptions);
|
|
else
|
|
snprintf(out, len, "[throw %p]", (void *)info->active);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
save_exception_info(__cilkrts_worker *w,
|
|
__cxa_eh_globals *state,
|
|
_Unwind_Exception *exc,
|
|
bool rethrow,
|
|
const char *why)
|
|
{
|
|
struct pending_exception_info *info =
|
|
(struct pending_exception_info *)__cilkrts_frame_malloc(w, sizeof (struct pending_exception_info));
|
|
CILK_ASSERT(info);
|
|
info->make(state, exc, rethrow);
|
|
|
|
#if DEBUG_EXCEPTIONS
|
|
{
|
|
char buf[40];
|
|
decode_exceptions(buf, sizeof buf, info);
|
|
fprintf(stderr, "make exception info W%u %p %s (%s)\n",
|
|
w->self, info, buf, why);
|
|
}
|
|
#endif
|
|
|
|
CILK_ASSERT(w->l->pending_exception == 0);
|
|
w->l->pending_exception = info;
|
|
}
|
|
|
|
#if DEBUG_EXCEPTIONS
|
|
#include <stdio.h> /* DEBUG */
|
|
|
|
static void decode_flags(int flags, char out[9])
|
|
{
|
|
out[0] = (flags & CILK_FRAME_STOLEN) ? 'S' : '_';
|
|
out[1] = (flags & CILK_FRAME_UNSYNCHED) ? 'U' : '_';
|
|
out[2] = (flags & CILK_FRAME_DETACHED) ? 'D' : '_';
|
|
out[3] = (flags & CILK_FRAME_EXCEPTING) ? 'X' : '_';
|
|
out[4] = '\0';
|
|
}
|
|
#endif
|
|
|
|
/* __cilkrts_save_except is called from the runtime epilogue
|
|
when a function is returning with an exception pending.
|
|
|
|
If the function has a parent to which it could return normally,
|
|
return and have the caller call _Unwind_Resume, the same as if
|
|
an exception filter had not matched.
|
|
|
|
Otherwise save the exception in the worker.
|
|
|
|
If this is a return from a ordinary call that must go through
|
|
the runtime, the assembly epilogue must have saved the call-saved
|
|
register state in the parent frame. */
|
|
|
|
extern "C"
|
|
CILK_ABI_THROWS_VOID
|
|
__cilkrts_return_exception(__cilkrts_stack_frame *sf)
|
|
{
|
|
__cilkrts_worker *w = sf->worker;
|
|
_Unwind_Exception *exc = (_Unwind_Exception *)sf->except_data;
|
|
|
|
CILK_ASSERT(sf->flags & CILK_FRAME_DETACHED);
|
|
sf->flags &= ~CILK_FRAME_DETACHED;
|
|
|
|
/*
|
|
* If we are in replay mode, and a steal occurred during the recording
|
|
* phase, stall till a steal actually occurs.
|
|
*/
|
|
replay_wait_for_steal_if_parent_was_stolen(w);
|
|
|
|
/* If this is to be an abnormal return, save the active exception. */
|
|
if (!__cilkrts_pop_tail(w)) {
|
|
/* Write a record to the replay log for an attempt to return to a
|
|
stolen parent. This must be done before the exception handler
|
|
invokes __cilkrts_leave_frame which will bump the pedigree so
|
|
the replay_wait_for_steal_if_parent_was_stolen() above will match on
|
|
replay */
|
|
replay_record_orphaned(w);
|
|
|
|
/* Now that the record/replay stuff is done, update the pedigree */
|
|
update_pedigree_on_leave_frame(w, sf);
|
|
|
|
/* Inline pop_frame; this may not be needed. */
|
|
w->current_stack_frame = sf->call_parent;
|
|
sf->call_parent = 0;
|
|
__cxa_eh_globals *state = __cxa_get_globals();
|
|
|
|
#if DEBUG_EXCEPTIONS
|
|
fflush(stdout);
|
|
char decoded[9];
|
|
decode_flags(sf->flags, decoded);
|
|
fprintf(stderr, "__cilkrts_save_except W%u sf %p/%s exc %p [%u %p] suspend\n",
|
|
w->self, sf, decoded, exc,
|
|
state->uncaughtExceptions,
|
|
state->caughtExceptions);
|
|
#endif
|
|
|
|
/* Like __cilkrts_save_exception_state except for setting the
|
|
rethrow flag. */
|
|
save_exception_info(w, state, exc, exc == NULL, "save_except");
|
|
{
|
|
full_frame *ff = w->l->frame_ff;
|
|
CILK_ASSERT(NULL == ff->pending_exception);
|
|
ff->pending_exception = w->l->pending_exception;
|
|
w->l->pending_exception = NULL;
|
|
}
|
|
__cilkrts_exception_from_spawn(w, sf); /* does not return */
|
|
}
|
|
/* This code path is taken when the parent is attached. It is on
|
|
the same stack and part of the same full frame. The caller is
|
|
cleaning up the Cilk frame during unwind and will reraise the
|
|
exception */
|
|
|
|
/* Now that the record/replay stuff is done, update the pedigree */
|
|
update_pedigree_on_leave_frame(w, sf);
|
|
|
|
#if DEBUG_EXCEPTIONS /* DEBUG ONLY */
|
|
{
|
|
__cxa_eh_globals *state = __cxa_get_globals();
|
|
|
|
fflush(stdout);
|
|
char decoded[9];
|
|
decode_flags(sf->flags, decoded);
|
|
fprintf(stderr, "__cilkrts_save_except W%d %p/%s %p->%p [%u %p] escape\n",
|
|
w->self, sf, decoded, exc,
|
|
exc ? to_cxx(exc)->nextException : 0,
|
|
state->uncaughtExceptions,
|
|
state->caughtExceptions);
|
|
|
|
/* XXX This is triggering in the user thread which gets an exception
|
|
from somewhere but does not get the corresponding runtime exception
|
|
state.
|
|
XXX There might be two or more uncaught exceptions. Test could be
|
|
(uncaught != 0) == (exc != 0). First, design tests to see if that
|
|
case is otherwise handled correctly. And what if there's an uncaught
|
|
exception that does not belong to this function? I.e. this is a return
|
|
from spawn in a destructor. */
|
|
if (exc)
|
|
CILK_ASSERT((int)state->uncaughtExceptions > 0);
|
|
/*CILK_ASSERT(state->uncaughtExceptions == (exc != 0));*/
|
|
}
|
|
#endif
|
|
|
|
/* The parent is attached so this exception can be propagated normally. */
|
|
return;
|
|
}
|
|
|
|
/* Save the exception state into the full frame, which is exiting
|
|
or suspending. */
|
|
extern "C"
|
|
void __cilkrts_save_exception_state(__cilkrts_worker *w, full_frame *ff)
|
|
{
|
|
save_exception_info(w, __cxa_get_globals(), 0, false, "undo-detach");
|
|
CILK_ASSERT(NULL == ff->pending_exception);
|
|
ff->pending_exception = w->l->pending_exception;
|
|
w->l->pending_exception = NULL;
|
|
}
|
|
|
|
/* __cilkrts_c_sync_except is like __cilkrts_c_sync except that it
|
|
saves exception state. __cilkrts_c_sync never returns here and
|
|
always reinstalls the saved exception state.
|
|
|
|
This function must be used because a parent of this function may
|
|
be propagating an uncaught exception. The uncaught exception
|
|
count must be saved by the child and passed back to the parent. */
|
|
|
|
extern "C"
|
|
NORETURN __cilkrts_c_sync_except (__cilkrts_worker *w, __cilkrts_stack_frame *sf)
|
|
{
|
|
__cxa_eh_globals *state = __cxa_get_globals();
|
|
_Unwind_Exception *exc = (_Unwind_Exception *)sf->except_data;
|
|
|
|
CILK_ASSERT((sf->flags & (CILK_FRAME_UNSYNCHED|CILK_FRAME_EXCEPTING)) ==
|
|
(CILK_FRAME_UNSYNCHED|CILK_FRAME_EXCEPTING));
|
|
sf->flags &= ~CILK_FRAME_EXCEPTING;
|
|
|
|
#if DEBUG_EXCEPTIONS
|
|
fflush(stdout);
|
|
char decoded[9];
|
|
decode_flags(sf->flags, decoded);
|
|
if (exc)
|
|
fprintf(stderr, "__cilkrts_sync_except W%u %p/%s %p->%p [%u %p]\n",
|
|
w->self, sf, decoded, exc,
|
|
to_cxx(exc)->nextException,
|
|
state->uncaughtExceptions,
|
|
state->caughtExceptions);
|
|
else
|
|
fprintf(stderr, "__cilkrts_sync_except W%d %p/%s none [%u %p]\n",
|
|
w->self, sf, decoded,
|
|
state->uncaughtExceptions,
|
|
state->caughtExceptions);
|
|
#endif
|
|
|
|
/* Here the identity of an rethrown exception is always known.
|
|
If exc is NULL this call is only to preserve parent state. */
|
|
save_exception_info(w, state, exc, false, "sync_except");
|
|
#if 0
|
|
{
|
|
full_frame *ff = w->l->frame_ff;
|
|
CILK_ASSERT(NULL == ff->pending_exception);
|
|
ff->pending_exception = w->l->pending_exception;
|
|
w->l->pending_exception = NULL;
|
|
}
|
|
#endif
|
|
CILK_ASSERT(!std::uncaught_exception());
|
|
__cilkrts_c_sync(w, sf);
|
|
}
|
|
|
|
void
|
|
pending_exception_info::destruct()
|
|
{
|
|
if (active) {
|
|
#if DEBUG_EXCEPTIONS
|
|
fprintf(stderr, "destroy exception info %p %p\n", this, active);
|
|
#endif
|
|
_Unwind_DeleteException(active);
|
|
active = 0;
|
|
} else {
|
|
#if DEBUG_EXCEPTIONS
|
|
fprintf(stderr, "destroy exception info %p\n", this);
|
|
#endif
|
|
}
|
|
while (runtime_state.caughtExceptions) {
|
|
__cxa_exception *exc = runtime_state.caughtExceptions;
|
|
runtime_state.caughtExceptions = exc->nextException;
|
|
#if DEBUG_EXCEPTIONS
|
|
fprintf(stderr, "destroy caught exception %p\n", this);
|
|
#endif
|
|
_Unwind_DeleteException(&exc->unwindHeader);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* __cilkrts_merge_pending_exceptions
|
|
*
|
|
* Merge the right exception record into the left. The left is logically
|
|
* earlier.
|
|
*
|
|
* The active exception of E is
|
|
* E->active if it is non-NULL (in which case E->rethrow is false)
|
|
* unresolved if E->active is NULL and E->rethrow is true
|
|
* nil if E->active is NULL and E->rethrow is false
|
|
*
|
|
* The merged active exception is left active exception if it is not
|
|
* nil, otherwise the right.
|
|
*
|
|
* On entry the left state is synched and can not have an unresolved
|
|
* exception. The merge may result in an unresolved exception.
|
|
*
|
|
* Due to scoping rules at most one of the caught exception lists is
|
|
* non-NULL.
|
|
*/
|
|
|
|
struct pending_exception_info *
|
|
__cilkrts_merge_pending_exceptions (
|
|
__cilkrts_worker *w,
|
|
struct pending_exception_info *left,
|
|
struct pending_exception_info *right)
|
|
{
|
|
/* If we've only got one exception, return it */
|
|
|
|
if (NULL == left) {
|
|
#if DEBUG_EXCEPTIONS
|
|
if (right) {
|
|
char buf[40];
|
|
decode_exceptions(buf, sizeof buf, right);
|
|
fprintf(stderr, "__cilkrts merge W%u nil %p -> %p %s\n",
|
|
w->self, right, right, buf);
|
|
}
|
|
#endif
|
|
return right;
|
|
}
|
|
|
|
if (NULL == right) {
|
|
#if DEBUG_EXCEPTIONS
|
|
if (left) {
|
|
char buf[40];
|
|
decode_exceptions(buf, sizeof buf, left);
|
|
fprintf(stderr, "__cilkrts merge W%u %p nil -> %p %s\n",
|
|
w->self, left, left, buf);
|
|
}
|
|
#endif
|
|
return left;
|
|
}
|
|
|
|
#if CILK_LIB_DEBUG
|
|
/*volatile struct pending_exception_info left_in = *left, right_in = *right;*/
|
|
left->check();
|
|
right->check();
|
|
#endif
|
|
|
|
#if DEBUG_EXCEPTIONS
|
|
{
|
|
char buf1[40], buf2[40];
|
|
decode_exceptions(buf1, sizeof buf1, left);
|
|
decode_exceptions(buf2, sizeof buf2, right);
|
|
fprintf(stderr, "__cilkrts merge W%u %p %s %p %s\n",
|
|
w->self, left, buf1, right, buf2);
|
|
}
|
|
#endif
|
|
|
|
/* It should not be possible for both left and right to
|
|
have accumulated catch blocks.
|
|
|
|
The left exception record may always have a catch
|
|
chain it kept when its parent was stolen.
|
|
|
|
If they are siblings, the right sibling should not
|
|
have accumulated any net catches. (Catch is lexically
|
|
scoped.)
|
|
|
|
If the right frame is a parent, it should not have entered
|
|
a catch block without syncing first. If it spawned in a
|
|
catch block, the child got its catch. */
|
|
__cxa_exception *caught = left->runtime_state.caughtExceptions;
|
|
if (caught)
|
|
CILK_ASSERT(!right->runtime_state.caughtExceptions);
|
|
else {
|
|
CILK_ASSERT(!left->rethrow);
|
|
left->rethrow = right->rethrow;
|
|
left->runtime_state.caughtExceptions = caught = right->runtime_state.caughtExceptions;
|
|
right->runtime_state.caughtExceptions = NULL;
|
|
}
|
|
|
|
/* Merge the uncaught exception and count of uncaught exceptions. */
|
|
const unsigned int right_uncaught = right->runtime_state.uncaughtExceptions;
|
|
if (!left->active){
|
|
left->active = right->active; /* could be NULL */
|
|
right->active = 0;
|
|
left->runtime_state.uncaughtExceptions += right_uncaught;
|
|
if (left->active)
|
|
/* assert is C++ exception */
|
|
/*CILK_ASSERT(__cxxabiv1::__is_gxx_exception_class(left->active->exception_class))*/;
|
|
} else {
|
|
/* Subtract 1 if the right exception is being destructed. */
|
|
left->runtime_state.uncaughtExceptions += right_uncaught - (right->active != 0);
|
|
}
|
|
|
|
right->destruct();
|
|
__cilkrts_frame_free(w, right, sizeof *right);
|
|
|
|
/* If there is no state left, return NULL. */
|
|
if (left->empty()) {
|
|
left->destruct();
|
|
__cilkrts_frame_free(w, left, sizeof *left);
|
|
left = NULL;
|
|
}
|
|
|
|
#if CILK_LIB_DEBUG
|
|
if (left)
|
|
left->check();
|
|
#endif
|
|
|
|
return left;
|
|
}
|
|
|
|
#if 0
|
|
/* __cilkrts_c_resume_except is called from the assembly language
|
|
restart code when a resumed frame has a pending exception.
|
|
|
|
The handler count negation on rethrow was done when the throw was
|
|
resolved.
|
|
|
|
The assembly language runtime must make the throw unwind to
|
|
the sync, spawn, or other location where the exception should
|
|
be injected. (This should not happen after a spawn but nothing
|
|
here depends on there being no exception on steal.)
|
|
|
|
This function is unused in the Intel stack based system. */
|
|
extern "C"
|
|
void __cilkrts_c_resume_except (_Unwind_Exception *exc)
|
|
{
|
|
#if DEBUG_EXCEPTIONS
|
|
fprintf(stderr, "resume exception %p\n", exc);
|
|
#endif
|
|
_Unwind_Reason_Code why = _Unwind_RaiseException(exc);
|
|
__cilkrts_bug ("Cilk runtime error: failed to reinstate suspended exception %p (%d)\n", exc, why);
|
|
}
|
|
#endif
|
|
|
|
/* Restore the caught exception chain. This assumes no C++ exception
|
|
code will run before the frame is resumed. If there is no exception
|
|
to be resumed free the object. */
|
|
|
|
extern "C"
|
|
void __cilkrts_setup_for_execution_sysdep(__cilkrts_worker *w, full_frame *ff)
|
|
{
|
|
// ASSERT: We own w->lock and ff->lock || P == 1
|
|
|
|
__cxa_eh_globals *state = __cxa_get_globals ();
|
|
struct pending_exception_info *info = w->l->pending_exception;
|
|
|
|
if (info == NULL)
|
|
return;
|
|
|
|
w->l->pending_exception = 0;
|
|
|
|
#if DEBUG_EXCEPTIONS
|
|
_Unwind_Exception *exc = info->active;
|
|
if (exc) {
|
|
fflush(stdout);
|
|
fprintf(stderr, "__cilkrts_resume_except W%u %p->%p [%u %p]\n",
|
|
w->self, exc,
|
|
to_cxx(exc)->nextException,
|
|
info->runtime_state.uncaughtExceptions,
|
|
info->runtime_state.caughtExceptions);
|
|
/*CILK_ASSERT(info->runtime_state.uncaughtExceptions > 0);*/
|
|
}
|
|
#endif
|
|
|
|
if (state->uncaughtExceptions || state->caughtExceptions)
|
|
__cilkrts_bug("W%u: resuming with non-empty prior exception state %u %p\n", state->uncaughtExceptions, state->caughtExceptions);
|
|
|
|
*state = info->runtime_state;
|
|
info->runtime_state.caughtExceptions = 0;
|
|
info->runtime_state.uncaughtExceptions = 0;
|
|
|
|
if (info->rethrow) {
|
|
info->rethrow = false;
|
|
/* Resuming function will rethrow. Runtime calls
|
|
std::terminate if there is no caught exception. */
|
|
ff->call_stack->flags |= CILK_FRAME_EXCEPTING;
|
|
}
|
|
if (info->active) {
|
|
ff->call_stack->flags |= CILK_FRAME_EXCEPTING;
|
|
ff->call_stack->except_data = info->active;
|
|
info->active = 0;
|
|
}
|
|
|
|
if (info->empty()) {
|
|
info->destruct();
|
|
__cilkrts_frame_free(w, info, sizeof *info);
|
|
w->l->pending_exception = NULL;
|
|
}
|
|
|
|
#if CILK_LIB_DEBUG
|
|
if (ff->call_stack->except_data)
|
|
CILK_ASSERT(std::uncaught_exception());
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
extern "C"
|
|
struct pending_exception_info *__cilkrts_get_exception(__cilkrts_worker *w,
|
|
__cilkrts_stack_frame *sf)
|
|
{
|
|
struct pending_exception_info *info = w->l->pending_exception;
|
|
|
|
if (info == NULL) {
|
|
sf->flags &= ~CILK_FRAME_EXCEPTING;
|
|
return 0;
|
|
}
|
|
|
|
w->l->pending_exception = NULL;
|
|
|
|
/* This exception goes into the frame. */
|
|
|
|
_Unwind_Exception *exc = info->active;
|
|
info->active = NULL;
|
|
info->destruct();
|
|
__cilkrts_frame_free(w, info, sizeof *info);
|
|
info = 0;
|
|
sf->flags |= CILK_FRAME_EXCEPTING;
|
|
sf->exception = exc;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
extern "C"
|
|
void __attribute__((nonnull)) __cilkrts_gcc_rethrow(__cilkrts_stack_frame *sf)
|
|
{
|
|
#ifdef __CYGWIN__
|
|
// Cygwin doesn't support exceptions, so _Unwind_Resume isn't available
|
|
// Which means we can't support exceptions either
|
|
__cilkrts_bug("The Cygwin implementation of the Intel Cilk Plus runtime doesn't support exceptions\n");
|
|
#else
|
|
if (sf->except_data) {
|
|
#if CILK_LIB_DEBUG
|
|
CILK_ASSERT(std::uncaught_exception());
|
|
#endif
|
|
_Unwind_Resume ((_Unwind_Exception *)sf->except_data);
|
|
} else {
|
|
throw;
|
|
}
|
|
#endif // __CYGWIN__
|
|
}
|
|
|
|
/* End except-gcc.cpp */
|
|
|