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