diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5a18b75142c..f75a603edac 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,71 @@ +Tue Dec 8 15:32:56 EST 1998 Andrew MacLeod + + * eh-common.h (struct eh_context): Add table_index for rethrows. + + * rtl.h (enum reg_note): Add REG_EH_REGION and REG_EH_RETHROW reg notes. + (SYMBOL_REF_NEED_ADJUST): New flag indicating symbol needs to be + processed when inlined or unrolled (ie duplicated in some way). + + * rtl.c (reg_note_name): Add strings for new reg_note enums. + + * expr.h (rethrow_libfunc): New library decl. + + * optabs.c (rethrow_libfunc): Initialize. + + * except.h (struct eh_entry): Add new field 'rethrow_label'. + (new_eh_region_entry): No longer exported from except.c. + (duplicate_handlers): Renamed to duplicate_eh_handlers and + different prototype. + (rethrow_symbol_map, rethrow_used): New exported functions. + (eh_region_from_symbol): New exported function. + + * except.c (create_rethrow_ref): New function to create a single + SYMBOL_REF for a rethrow region. + (push_eh_entry): Initialize a rethrow ref. + (func_eh_entry): Add a rethrow_label field. + (new_eh_region_entry): Make static, and initialize the rethrow entry. + (duplicate_eh_handlers): Create a new region, and remap labels/symbols. + (eh_region_from_symbol): Find an EH region based on its rethrow symbol. + (rethrow_symbol_map): Given a label map, maps a rethrow symbol for + a region into an appropriate new symbol. + (rethrow_used): Indicate whether a rethrow symbol has been referenced. + (expand_eh_region_end): Don't issue jump around code for new-exceptions. + (end_catch_handler): Emit a barrier for new-exceptions since + control can never drop through the end of a catch block. + (expand_end_all_catch): new-exceptions never fall through a catch + block. + (expand_rethrow): use __rethrow routine for new exceptions. + (output_exception_table_entry): Generate rethrow labels, if needed. + (output_exception_table): Generate start and end rethrow labels. + (init_eh): Create rethrow symbols for beginning and end of table. + (scan_region): Don't eliminate EH regions which are the targets of + rethrows. + + * flow.c (make_edges): Add different edges for rethrow calls, + identified by having the REG_EH_RETHROW reg label. + (delete_unreachable_blocks): Don't delete regions markers which are + the target of a rethrow. + + * integrate.c (save_for_inline_eh_labelmap): New callback routine to + allow save_for_inline_copying to call duplicate_eh_handlers. + (save_for_inline_copying): Call duplicate_eh_handlers instead of + exposing internal details of exception regions. + (copy_for_inline): Check if SYMBOL_REFs need adjustment. + (expand_inline_function_eh_labelmap): New callback routine to + allow expand_inline_function to call duplicate_eh_handlers. + (expand_inline_function): Call duplicate_eh_handlers instead of + exposing internal details of exception regions. + (copy_rtx_and_substitute): Adjust SYMBOL_REFS if SYMBOL_REF_NEED_ADJUST + flag is set. + + * libgcc2.c (find_exception_handler): Generalize to enable it to + pick up processing where it left off last time for a rethrow. + (__unwinding_cleanup): New function. debug hook which is called before + unwinding when __throw finds there is nothing but cleanups left. + (throw_helper): Common parts of __throw extracted out for reuse. + (__throw): Common parts moved to throw_helper. + (__rethrow): New function for performing rethrows. + Tue Dec 8 13:11:04 1998 Jeffrey A Law (law@cygnus.com) * reload1.c (current_function_decl): Tweak declaration. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index cedcc62ceb3..40280afc8a2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,19 @@ +1998-12-08 Andrew MacLeod + + * cp/except.c (call_eh_info): use __start_cp_handler instead of + __cp_eh_info for getting the eh info pointer. Add table_index to + field list. + (push_eh_cleanup): Don't increment 'handlers' data field. + (process_start_catch_block): Don't set the 'caught' field. + + * cp/exception.cc (CP_EH_INFO): New macro for getting the + exception info pointer within library routines. + (__cp_eh_info): Use CP_EH_INFO. + (__start_cp_handler): Get exception info pointer, set caught field, + and increment the handlers field. Avoids this being done by handlers. + (__uncatch_exception, __check_eh_spec): Use CP_EH_INFO macro. + (uncaught_exception): Use CP_EH_INFO macro. + Tue Dec 8 10:48:21 1998 Jeffrey A Law (law@cygnus.com) * Make-lang.in (cxxmain.o): Depend on $(DEMANGLE_H), not demangle.h diff --git a/gcc/cp/except.c b/gcc/cp/except.c index 7198689940b..a2c2c51bace 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -249,14 +249,14 @@ call_eh_info () { tree fn; - fn = get_identifier ("__cp_eh_info"); + fn = get_identifier ("__start_cp_handler"); if (IDENTIFIER_GLOBAL_VALUE (fn)) fn = IDENTIFIER_GLOBAL_VALUE (fn); else { tree t1, t, fields[7]; - /* Declare cp_eh_info * __cp_eh_info (void), + /* Declare cp_eh_info * __start_cp_handler (void), as defined in exception.cc. */ push_obstacks_nochange (); end_temporary_allocation (); @@ -270,9 +270,11 @@ call_eh_info () get_identifier ("dynamic_handler_chain"), ptr_type_node); fields[2] = build_lang_field_decl (FIELD_DECL, get_identifier ("info"), ptr_type_node); + fields[3] = build_lang_field_decl (FIELD_DECL, + get_identifier ("table_index"), ptr_type_node); /* N.B.: The fourth field LEN is expected to be the number of fields - 1, not the total number of fields. */ - finish_builtin_type (t1, "eh_context", fields, 2, ptr_type_node); + finish_builtin_type (t1, "eh_context", fields, 3, ptr_type_node); t1 = build_pointer_type (t1); t1= make_lang_type (RECORD_TYPE); @@ -547,9 +549,6 @@ push_eh_cleanup () { int yes; - expand_expr (build_unary_op (PREINCREMENT_EXPR, get_eh_handlers (), 1), - const0_rtx, VOIDmode, EXPAND_NORMAL); - yes = suspend_momentary (); /* All cleanups must last longer than normal. */ expand_decl_cleanup (NULL_TREE, do_pop_exception ()); @@ -701,9 +700,6 @@ process_start_catch_block (declspecs, declarator) /* Fall into the catch all section. */ } - init = build_modify_expr (get_eh_caught (), NOP_EXPR, integer_one_node); - expand_expr (init, const0_rtx, VOIDmode, EXPAND_NORMAL); - emit_line_note (input_filename, lineno); } diff --git a/gcc/cp/exception.cc b/gcc/cp/exception.cc index d0533aa41ca..8e8f35f50db 100644 --- a/gcc/cp/exception.cc +++ b/gcc/cp/exception.cc @@ -117,13 +117,29 @@ __cp_exception_info (void) return &((*__get_eh_info ())->value); } -/* Compiler hook to return a pointer to the info for the current exception. +#define CP_EH_INFO ((cp_eh_info *) *__get_eh_info ()) + +/* Old Compiler hook to return a pointer to the info for the current exception. Used by get_eh_info (). */ extern "C" cp_eh_info * __cp_eh_info (void) { - return *__get_eh_info (); + cp_eh_info *p = CP_EH_INFO; + return p; +} + +/* Compiler hook to return a pointer to the info for the current exception, + Set the caught bit, and increment the number of handlers that are + looking at this exception. This makes handlers smaller. */ + +extern "C" cp_eh_info * +__start_cp_handler (void) +{ + cp_eh_info *p = CP_EH_INFO; + p->caught = 1; + p->handlers++; + return p; } /* Allocate a buffer for a cp_eh_info and an exception object of size SIZE, @@ -242,7 +258,7 @@ __cp_pop_exception (cp_eh_info *p) extern "C" void __uncatch_exception (void) { - cp_eh_info *p = __cp_eh_info (); + cp_eh_info *p = CP_EH_INFO; if (p == 0) terminate (); p->caught = false; @@ -263,7 +279,7 @@ __uncatch_exception (void) extern "C" void __check_eh_spec (int n, const void **spec) { - cp_eh_info *p = __cp_eh_info (); + cp_eh_info *p = CP_EH_INFO; for (int i = 0; i < n; ++i) { @@ -316,7 +332,7 @@ __throw_bad_typeid (void) bool std::uncaught_exception () { - cp_eh_info *p = __cp_eh_info (); + cp_eh_info *p = CP_EH_INFO; return p && ! p->caught; } diff --git a/gcc/eh-common.h b/gcc/eh-common.h index a4e07bcbd14..5d9f30c91b3 100644 --- a/gcc/eh-common.h +++ b/gcc/eh-common.h @@ -29,6 +29,8 @@ struct eh_context void **dynamic_handler_chain; /* This is language dependent part of the eh context. */ void *info; + /* This is used to remember where we threw for re-throws */ + void *table_index; /* address of exception table entry to rethrow from */ }; #ifndef EH_TABLE_LOOKUP diff --git a/gcc/except.c b/gcc/except.c index 2d6d66f9061..40aafdd21dc 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -406,6 +406,7 @@ Boston, MA 02111-1307, USA. */ #include "recog.h" #include "output.h" #include "toplev.h" +#include "obstack.h" /* One to use setjmp/longjmp method of generating code for exception handling. */ @@ -502,6 +503,15 @@ static rtx eh_return_handler; rtx eh_return_stub_label; +/* This is used for targets which can call rethrow with an offset instead + of an address. This is subtracted from the rethrow label we are + interested in. */ + +static rtx first_rethrow_symbol = NULL_RTX; +static rtx final_rethrow = NULL_RTX; +static rtx last_rethrow_symbol = NULL_RTX; + + /* Prototypes for local functions. */ static void push_eh_entry PROTO((struct eh_stack *)); @@ -526,6 +536,29 @@ rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx)); /* Various support routines to manipulate the various data structures used by the exception handling code. */ +extern struct obstack permanent_obstack; + +/* Generate a SYMBOL_REF for rethrow to use */ +static rtx +create_rethrow_ref (region_num) + int region_num; +{ + rtx def; + char *ptr; + char buf[60]; + + push_obstacks_nochange (); + end_temporary_allocation (); + + ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", region_num); + ptr = (char *) obstack_copy0 (&permanent_obstack, buf, strlen (buf)); + def = gen_rtx_SYMBOL_REF (Pmode, ptr); + SYMBOL_REF_NEED_ADJUST (def) = 1; + + pop_obstacks (); + return def; +} + /* Push a label entry onto the given STACK. */ void @@ -600,11 +633,16 @@ push_eh_entry (stack) struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); struct eh_entry *entry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry)); - entry->outer_context = gen_label_rtx (); + rtx rlab = gen_exception_label (); entry->finalization = NULL_TREE; entry->label_used = 0; - entry->exception_handler_label = gen_exception_label (); + entry->exception_handler_label = rlab; entry->false_label = NULL_RTX; + if (! flag_new_exceptions) + entry->outer_context = gen_label_rtx (); + else + entry->outer_context = create_rethrow_ref (CODE_LABEL_NUMBER (rlab)); + entry->rethrow_label = entry->outer_context; node->entry = entry; node->chain = stack->top; @@ -707,6 +745,7 @@ receive_exception_label (handler_label) struct func_eh_entry { int range_number; /* EH region number from EH NOTE insn's */ + rtx rethrow_label; /* Label for rethrow */ struct handler_info *handlers; }; @@ -719,12 +758,14 @@ static int current_func_eh_entry = 0; #define SIZE_FUNC_EH(X) (sizeof (struct func_eh_entry) * X) /* Add a new eh_entry for this function, and base it off of the information - in the EH_ENTRY parameter. A NULL parameter is invalid. The number + in the EH_ENTRY parameter. A NULL parameter is invalid. + OUTER_CONTEXT is a label which is used for rethrowing. The number returned is an number which uniquely identifies this exception range. */ -int -new_eh_region_entry (note_eh_region) +static int +new_eh_region_entry (note_eh_region, rethrow) int note_eh_region; + rtx rethrow; { if (current_func_eh_entry == num_func_eh_entries) { @@ -742,6 +783,11 @@ new_eh_region_entry (note_eh_region) } } function_eh_regions[current_func_eh_entry].range_number = note_eh_region; + if (rethrow == NULL_RTX) + function_eh_regions[current_func_eh_entry].rethrow_label = + create_rethrow_ref (note_eh_region); + else + function_eh_regions[current_func_eh_entry].rethrow_label = rethrow; function_eh_regions[current_func_eh_entry].handlers = NULL; return current_func_eh_entry++; @@ -929,34 +975,98 @@ clear_function_eh_region () } /* Make a duplicate of an exception region by copying all the handlers - for an exception region. Return the new handler index. */ + for an exception region. Return the new handler index. The final + parameter is a routine which maps old labels to new ones. */ int -duplicate_handlers (old_note_eh_region, new_note_eh_region) +duplicate_eh_handlers (old_note_eh_region, new_note_eh_region, map) int old_note_eh_region, new_note_eh_region; + rtx (*map)(rtx); { struct handler_info *ptr, *new_ptr; int new_region, region; + rtx tmp; region = find_func_region (old_note_eh_region); if (region == -1) - error ("Cannot duplicate non-existant exception region."); + fatal ("Cannot duplicate non-existant exception region."); - if (find_func_region (new_note_eh_region) != -1) - error ("Cannot duplicate EH region because new note region already exists"); + /* duplicate_eh_handlers may have been called during a symbol remap. */ + new_region = find_func_region (new_note_eh_region); + if (new_region != -1) + return (new_region); + + new_region = new_eh_region_entry (new_note_eh_region, NULL_RTX); - new_region = new_eh_region_entry (new_note_eh_region); ptr = function_eh_regions[region].handlers; for ( ; ptr; ptr = ptr->next) { - new_ptr = get_new_handler (ptr->handler_label, ptr->type_info); + new_ptr = get_new_handler (map (ptr->handler_label), ptr->type_info); add_new_handler (new_region, new_ptr); } return new_region; } + +/* Given a rethrow symbol, find the EH region number this is for. */ +int +eh_region_from_symbol (sym) + rtx sym; +{ + int x; + if (sym == last_rethrow_symbol) + return 1; + for (x = 0; x < current_func_eh_entry; x++) + if (function_eh_regions[x].rethrow_label == sym) + return function_eh_regions[x].range_number; + return -1; +} + + +/* When inlining/unrolling, we have to map the symbols passed to + __rethrow as well. This performs the remap. If a symbol isn't foiund, + the original one is returned. This is not an efficient routine, + so don't call it on everything!! */ +rtx +rethrow_symbol_map (sym, map) + rtx sym; + rtx (*map)(rtx); +{ + int x, y; + for (x = 0; x < current_func_eh_entry; x++) + if (function_eh_regions[x].rethrow_label == sym) + { + /* We've found the original region, now lets determine which region + this now maps to. */ + rtx l1 = function_eh_regions[x].handlers->handler_label; + rtx l2 = map (l1); + y = CODE_LABEL_NUMBER (l2); /* This is the new region number */ + x = find_func_region (y); /* Get the new permanent region */ + if (x == -1) /* Hmm, Doesn't exist yet */ + { + x = duplicate_eh_handlers (CODE_LABEL_NUMBER (l1), y, map); + /* Since we're mapping it, it must be used. */ + SYMBOL_REF_USED (function_eh_regions[x].rethrow_label) = 1; + } + return function_eh_regions[x].rethrow_label; + } + return sym; +} + +int +rethrow_used (region) + int region; +{ + if (flag_new_exceptions) + { + rtx lab = function_eh_regions[find_func_region (region)].rethrow_label; + return (SYMBOL_REF_USED (lab)); + } + return 0; +} + /* Routine to see if exception handling is turned on. DO_WARN is non-zero if we want to inform the user that exception @@ -1410,6 +1520,7 @@ expand_eh_region_end (handler) { struct eh_entry *entry; rtx note; + int ret, r; if (! doing_eh (0)) return; @@ -1417,9 +1528,9 @@ expand_eh_region_end (handler) entry = pop_eh_entry (&ehstack); note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END); - NOTE_BLOCK_NUMBER (note) + ret = NOTE_BLOCK_NUMBER (note) = CODE_LABEL_NUMBER (entry->exception_handler_label); - if (exceptions_via_longjmp == 0 + if (exceptions_via_longjmp == 0 && ! flag_new_exceptions /* We share outer_context between regions; only emit it once. */ && INSN_UID (entry->outer_context) == 0) { @@ -1439,7 +1550,7 @@ expand_eh_region_end (handler) entry->finalization = handler; /* create region entry in final exception table */ - new_eh_region_entry (NOTE_BLOCK_NUMBER (note)); + r = new_eh_region_entry (NOTE_BLOCK_NUMBER (note), entry->rethrow_label); enqueue_eh_entry (&ehqueue, entry); @@ -1673,8 +1784,14 @@ start_catch_handler (rtime) void end_catch_handler () { - if (! doing_eh (1) || (flag_new_exceptions && ! exceptions_via_longjmp)) + if (! doing_eh (1)) return; + + if (flag_new_exceptions && ! exceptions_via_longjmp) + { + emit_barrier (); + return; + } /* A NULL label implies the catch clause was a catch all or cleanup */ if (catchstack.top->entry->false_label == NULL_RTX) @@ -1786,7 +1903,7 @@ expand_start_all_catch () void expand_end_all_catch () { - rtx new_catch_clause, outer_context = NULL_RTX; + rtx new_catch_clause; struct eh_entry *entry; if (! doing_eh (1)) @@ -1798,11 +1915,17 @@ expand_end_all_catch () if (! exceptions_via_longjmp) { - outer_context = ehstack.top->entry->outer_context; + rtx outer_context = ehstack.top->entry->outer_context; /* Finish the rethrow region. size_zero_node is just a NOP. */ expand_eh_region_end (size_zero_node); + /* New exceptions handling models will never have a fall through + of a catch clause */ + if (!flag_new_exceptions) + expand_rethrow (outer_context); } + else + expand_rethrow (NULL_RTX); /* Code to throw out to outer context, if we fall off end of catch handlers. This is rethrow (Lresume, same id, same obj) in the @@ -1813,7 +1936,6 @@ expand_end_all_catch () do a "throw" (using the address of Lresume as the point being thrown from) so that the outer EH region can then try to process the exception. */ - expand_rethrow (outer_context); /* Now we have the complete catch sequence. */ new_catch_clause = get_insns (); @@ -1842,7 +1964,22 @@ expand_rethrow (label) if (exceptions_via_longjmp) emit_throw (); else - emit_jump (label); + if (flag_new_exceptions) + { + rtx insn, val; + if (label == NULL_RTX) + label = last_rethrow_symbol; + emit_library_call (rethrow_libfunc, 0, VOIDmode, 1, label, Pmode); + SYMBOL_REF_USED (label) = 1; + insn = get_last_insn (); + val = GEN_INT (eh_region_from_symbol (label)); + /* Mark the label/symbol on the call. */ + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_RETHROW, val, + REG_NOTES (insn)); + emit_barrier (); + } + else + emit_jump (label); } /* End all the pending exception regions on protect_list. The handlers @@ -1976,12 +2113,29 @@ output_exception_table_entry (file, n) { char buf[256]; rtx sym; - struct handler_info *handler; + struct handler_info *handler = get_first_handler (n); + int index = find_func_region (n); + rtx rethrow; + + /* form and emit the rethrow label, if needed */ + rethrow = function_eh_regions[index].rethrow_label; + if (rethrow != NULL_RTX && !flag_new_exceptions) + rethrow = NULL_RTX; + if (rethrow != NULL_RTX && handler == NULL) + if (! SYMBOL_REF_USED (rethrow)) + rethrow = NULL_RTX; - handler = get_first_handler (n); - for ( ; handler != NULL; handler = handler->next) + for ( ; handler != NULL || rethrow != NULL_RTX; handler = handler->next) { + /* rethrow label should indicate the LAST entry for a region */ + if (rethrow != NULL_RTX && (handler == NULL || handler->next == NULL)) + { + ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", n); + assemble_label(buf); + rethrow = NULL_RTX; + } + ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n); sym = gen_rtx_SYMBOL_REF (Pmode, buf); assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); @@ -1990,12 +2144,15 @@ output_exception_table_entry (file, n) sym = gen_rtx_SYMBOL_REF (Pmode, buf); assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); - assemble_integer (handler->handler_label, - POINTER_SIZE / BITS_PER_UNIT, 1); + if (handler == NULL) + assemble_integer (GEN_INT (0), POINTER_SIZE / BITS_PER_UNIT, 1); + else + assemble_integer (handler->handler_label, + POINTER_SIZE / BITS_PER_UNIT, 1); if (flag_new_exceptions) { - if (handler->type_info == NULL) + if (handler == NULL || handler->type_info == NULL) assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); else if (handler->type_info == CATCH_ALL_TYPE) @@ -2007,7 +2164,7 @@ output_exception_table_entry (file, n) } putc ('\n', file); /* blank line */ /* We only output the first label under the old scheme */ - if (! flag_new_exceptions) + if (! flag_new_exceptions || handler == NULL) break; } } @@ -2038,6 +2195,7 @@ void output_exception_table () { int i; + char buf[256]; extern FILE *asm_out_file; if (! doing_eh (0) || ! eh_table) @@ -2062,6 +2220,10 @@ output_exception_table () ; if (i != 0) assemble_integer (const0_rtx, i , 1); + + /* Generate the label for offset calculations on rethrows */ + ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", 0); + assemble_label(buf); } for (i = 0; i < eh_table_size; ++i) @@ -2071,6 +2233,9 @@ output_exception_table () clear_function_eh_region (); /* Ending marker for table. */ + /* Generate the label for end of table. */ + ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", CODE_LABEL_NUMBER (final_rethrow)); + assemble_label(buf); assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); /* for binary compatability, the old __throw checked the second @@ -2229,6 +2394,10 @@ check_exception_handler_labels () void init_eh () { + + first_rethrow_symbol = create_rethrow_ref (0); + final_rethrow = gen_exception_label (); + last_rethrow_symbol = create_rethrow_ref (CODE_LABEL_NUMBER (final_rethrow)); } /* Initialize the per-function EH information. */ @@ -2348,6 +2517,11 @@ scan_region (insn, n, delete_outer) /* Assume we can delete the region. */ int delete = 1; + int r = find_func_region (n); + /* Can't delete something which is rethrown to. */ + if (SYMBOL_REF_USED((function_eh_regions[r].rethrow_label))) + delete = 0; + if (insn == NULL_RTX || GET_CODE (insn) != NOTE || NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG diff --git a/gcc/except.h b/gcc/except.h index 540044bde02..5b4b3db4ed2 100644 --- a/gcc/except.h +++ b/gcc/except.h @@ -65,6 +65,7 @@ struct eh_entry { tree finalization; int label_used; rtx false_label; + rtx rethrow_label; }; /* A list of EH_ENTRYs. ENTRY is the entry; CHAIN points to the next @@ -177,13 +178,6 @@ typedef struct handler_info } handler_info; -/* Add a new eh_entry for this function, The parameter specifies what - exception region number NOTE insns use to delimit this range. - The integer returned is uniquely identifies this exception range - within an internal table. */ - -int new_eh_region_entry PROTO((int)); - /* Add new handler information to an exception range. The first parameter specifies the range number (returned from new_eh_entry()). The second parameter specifies the handler. By default the handler is inserted at @@ -208,8 +202,19 @@ struct handler_info *get_new_handler PROTO((rtx, void *)); /* Make a duplicate of an exception region by copying all the handlers for an exception region. Return the new handler index. */ -int duplicate_handlers PROTO((int, int)); +int duplicate_eh_handlers PROTO((int, int, rtx (*)(rtx))); +/* map symbol refs for rethrow */ + +rtx rethrow_symbol_map PROTO((rtx, rtx (*)(rtx))); + +/* Is the rethrow label for a region used? */ + +int rethrow_used PROTO((int)); + +/* Return the region number a this is the rethrow label for. */ + +int eh_region_from_symbol PROTO((rtx)); /* Get a pointer to the first handler in an exception region's list. */ diff --git a/gcc/expr.h b/gcc/expr.h index 896bded1456..134c095bd8c 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -443,6 +443,7 @@ extern rtx memset_libfunc; extern rtx bzero_libfunc; extern rtx throw_libfunc; +extern rtx rethrow_libfunc; extern rtx sjthrow_libfunc; extern rtx sjpopnthrow_libfunc; extern rtx terminate_libfunc; diff --git a/gcc/flow.c b/gcc/flow.c index cc76a9ccb2b..6a4a448e5ee 100644 --- a/gcc/flow.c +++ b/gcc/flow.c @@ -721,6 +721,7 @@ make_edges (i) int i; { rtx insn, x; + rtx pending_eh_region = NULL_RTX; /* See if control drops into the next block. */ if (i + 1 < n_basic_blocks) @@ -801,6 +802,40 @@ make_edges (i) } } + /* If this is a call with an EH_RETHROW note, then we + know its a rethrow call, and we know exactly where + this call can end up going. */ + else if (GET_CODE (insn) == CALL_INSN + && (note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX))) + { + int region = XINT (XEXP (note, 0), 0); + /* if nested region is not 0, we know for sure it has been + processed. If it is zero, we dont know whether its an + outer region, or hasn't been seen yet, so defer it */ + if (nested_eh_region[region] != 0) + { + /* start with the first region OUTSIDE the one specified + in the rethrow parameter. (since a rethrow behaves + as if a handler in the region didn't handle the + exception, so the handlers for the next outer region + are going to get a shot at it.*/ + for ( region = nested_eh_region[region]; region; + region = nested_eh_region[region]) + { + handler_info *ptr = get_first_handler (region); + for ( ; ptr ; ptr = ptr->next) + add_edge_to_label (i, ptr->handler_label); + } + } + else + { + /* Push this region onto a list, and after we've done the + whole procedure, we'll process everything on the list */ + pending_eh_region = gen_rtx_EXPR_LIST (VOIDmode, insn, + pending_eh_region); + } + } + /* If this is a CALL_INSN, then mark it as reaching the active EH handler for this CALL_INSN. If we're handling asynchronous exceptions mark every insn as reaching the active EH handler. @@ -837,6 +872,24 @@ make_edges (i) } } } + + while (pending_eh_region != NULL_RTX) + { + rtx insn = XEXP (pending_eh_region, 0); + rtx note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX); + int region = XINT (XEXP (note, 0), 0); + /* start with the first region OUTSIDE the one specified + in the rethrow parameter */ + for ( region = nested_eh_region[region]; region; + region = nested_eh_region[region]) + { + handler_info *ptr = get_first_handler (region); + for ( ; ptr ; ptr = ptr->next) + add_edge_to_label (BLOCK_NUM (insn), ptr->handler_label); + } + pending_eh_region = XEXP (pending_eh_region, 1); + } + /* We know something about the structure of the function __throw in libgcc2.c. It is the only function that ever contains eh_stub labels. It modifies its return address so that the last block returns to one of @@ -918,8 +971,9 @@ delete_unreachable_blocks () NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) { int num = CODE_LABEL_NUMBER (insn); - /* A NULL handler indicates a region is no longer needed */ - if (get_first_handler (num) == NULL) + /* A NULL handler indicates a region is no longer needed, + unless its the target of a rethrow. */ + if (get_first_handler (num) == NULL && !rethrow_used (num)) { NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; diff --git a/gcc/integrate.c b/gcc/integrate.c index 76ccf5f59b5..702e5fa4f51 100644 --- a/gcc/integrate.c +++ b/gcc/integrate.c @@ -265,6 +265,16 @@ static rtvec copy_asm_constraints_vector; /* In save_for_inline, nonzero if past the parm-initialization insns. */ static int in_nonparm_insns; +/* subroutines passed to duplicate_eh_handlers to map exception labels */ + +static rtx +save_for_inline_eh_labelmap (label) + rtx label; +{ + int index = CODE_LABEL_NUMBER (label); + return label_map[index]; +} + /* Subroutine for `save_for_inline{copying,nocopy}'. Performs initialization needed to save FNDECL's insns and info for future inline expansion. */ @@ -667,19 +677,8 @@ save_for_inline_copying (fndecl) /* we have to duplicate the handlers for the original */ if (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG) - { - handler_info *ptr, *temp; - int nr; - nr = new_eh_region_entry (new_region); - ptr = get_first_handler (NOTE_BLOCK_NUMBER (copy)); - for ( ; ptr; ptr = ptr->next) - { - temp = get_new_handler ( - label_map[CODE_LABEL_NUMBER (ptr->handler_label)], - ptr->type_info); - add_new_handler (nr, temp); - } - } + duplicate_eh_handlers (NOTE_BLOCK_NUMBER (copy), new_region, + save_for_inline_eh_labelmap); /* We have to forward these both to match the new exception region. */ @@ -1075,11 +1074,15 @@ copy_for_inline (orig) { case QUEUED: case CONST_INT: - case SYMBOL_REF: case PC: case CC0: return x; + case SYMBOL_REF: + if (! SYMBOL_REF_NEED_ADJUST (x)) + return x; + return rethrow_symbol_map (x, save_for_inline_eh_labelmap); + case CONST_DOUBLE: /* We have to make a new CONST_DOUBLE to ensure that we account for it correctly. Using the old CONST_DOUBLE_MEM data is wrong. */ @@ -1338,6 +1341,18 @@ process_reg_param (map, loc, copy) } map->reg_map[REGNO (loc)] = copy; } + +/* Used by duplicate_eh_handlers to map labels for the exception table */ +static struct inline_remap *eif_eh_map; + +static rtx +expand_inline_function_eh_labelmap (label) + rtx label; +{ + int index = CODE_LABEL_NUMBER (label); + return get_label_from_map (eif_eh_map, index); +} + /* Integrate the procedure defined by FNDECL. Note that this function may wind up calling itself. Since the static variables are not reentrant, we do not assign them until after the possibility @@ -2055,17 +2070,12 @@ expand_inline_function (fndecl, parms, target, ignore, type, /* we have to duplicate the handlers for the original */ if (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG) { - handler_info *ptr, *temp; - int nr; - nr = new_eh_region_entry (CODE_LABEL_NUMBER (label)); - ptr = get_first_handler (NOTE_BLOCK_NUMBER (copy)); - for ( ; ptr; ptr = ptr->next) - { - temp = get_new_handler ( get_label_from_map (map, - CODE_LABEL_NUMBER (ptr->handler_label)), - ptr->type_info); - add_new_handler (nr, temp); - } + /* We need to duplicate the handlers for the EH region + and we need to indicate where the label map is */ + eif_eh_map = map; + duplicate_eh_handlers (NOTE_BLOCK_NUMBER (copy), + CODE_LABEL_NUMBER (label), + expand_inline_function_eh_labelmap); } /* We have to forward these both to match the new exception @@ -2533,6 +2543,13 @@ copy_rtx_and_substitute (orig, map) map)), 0); } + else + if (SYMBOL_REF_NEED_ADJUST (orig)) + { + eif_eh_map = map; + return rethrow_symbol_map (orig, + expand_inline_function_eh_labelmap); + } return orig; diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index 0b5bfa304b0..0275525b185 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -3468,45 +3468,82 @@ old_find_exception_handler (void *pc, old_exception_table *table) return (void *) 0; } +/* find_exception_handler finds the correct handler, if there is one, to + handle an exception. + returns a pointer to the handler which controlled should be transferred + to, or NULL if there is nothing left. + Parameters: + PC - pc where the exception originates. If this is a rethrow, + then this starts out as a pointer to the exception table + entry we wish to rethrow out of. + TABLE - exception table for the current module. + EH_INFO - eh info pointer for this exception. + RETHROW - 1 if this is a rethrow. (see incoming value of PC). + CLEANUP - returned flag indicating whether this is a cleanup handler. +*/ static void * -find_exception_handler (void *pc, exception_descriptor *table, void *eh_info) +find_exception_handler (void *pc, exception_descriptor *table, + __eh_info *eh_info, int rethrow, int *cleanup) { + + void *retval = NULL; + *cleanup = 1; if (table) { + int pos = 0; /* The new model assumed the table is sorted inner-most out so the first region we find which matches is the correct one */ - int pos; - void *ret; exception_table *tab = &(table->table[0]); /* Subtract 1 from the PC to avoid hitting the next region */ - pc--; + if (rethrow) + { + /* pc is actually the region table entry to rethrow out of */ + pos = ((exception_table *) pc) - tab; + pc = ((exception_table *) pc)->end_region - 1; + + /* The label is always on the LAST handler entry for a region, + so we know the next entry is a different region, even if the + addresses are the same. Make sure its not end of table tho. */ + if (tab[pos].start_region != (void *) -1) + pos++; + } + else + pc--; /* We can't do a binary search because the table is in inner-most to outermost address ranges within functions */ - for (pos = 0; tab[pos].start_region != (void *) -1; pos++) + for ( ; tab[pos].start_region != (void *) -1; pos++) { if (tab[pos].start_region <= pc && tab[pos].end_region > pc) { if (tab[pos].match_info) { - __eh_matcher matcher = ((__eh_info *)eh_info)->match_function; + __eh_matcher matcher = eh_info->match_function; /* match info but no matcher is NOT a match */ if (matcher) { - ret = (*matcher)(eh_info, tab[pos].match_info, table); - if (ret) - return tab[pos].exception_handler; + void *ret = (*matcher)((void *) eh_info, + tab[pos].match_info, table); + if (ret) + { + if (retval == NULL) + retval = tab[pos].exception_handler; + *cleanup = 0; + break; + } } } else - return tab[pos].exception_handler; + { + if (retval == NULL) + retval = tab[pos].exception_handler; + } } } } - - return (void *) 0; + return retval; } #endif /* DWARF2_UNWIND_INFO */ #endif /* EH_TABLE_LOOKUP */ @@ -3643,54 +3680,47 @@ next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata) return caller_udata; } -/* We first search for an exception handler, and if we don't find - it, we call __terminate on the current stack frame so that we may - use the debugger to walk the stack and understand why no handler - was found. - - If we find one, then we unwind the frames down to the one that - has the handler and transfer control into the handler. */ - -/*extern void __throw(void) __attribute__ ((__noreturn__));*/ - -void -__throw () +/* Hook to call before __terminate if only cleanup handlers remain. */ +void +__unwinding_cleanup () { - struct eh_context *eh = (*get_eh_context) (); - void *saved_pc, *pc, *handler; - frame_state ustruct, ustruct2; - frame_state *udata = &ustruct; - frame_state *sub_udata = &ustruct2; - frame_state my_ustruct, *my_udata = &my_ustruct; - long args_size; - int new_exception_model; +} - /* This is required for C++ semantics. We must call terminate if we - try and rethrow an exception, when there is no exception currently - active. */ - if (! eh->info) - __terminate (); - - /* Start at our stack frame. */ -label: - udata = __frame_state_for (&&label, udata); - if (! udata) - __terminate (); +/* throw_helper performs some of the common grunt work for a throw. This + routine is called by throw and rethrows. This is pretty much split + out from the old __throw routine. An addition has been added which allows + for a dummy call to a routine __unwinding_cleanup() when there are nothing + but cleanups remaining. This allows a debugger to examine the state + at which the throw was executed, before any cleanups, rather than + at the terminate point after the stack has been unwound. */ - /* We need to get the value from the CFA register. */ - udata->cfa = __builtin_dwarf_cfa (); +static void * +throw_helper (eh, pc, my_udata, udata_p) + struct eh_context *eh; + void *pc; + frame_state *my_udata; + frame_state **udata_p; +{ + frame_state *udata = *udata_p; + frame_state ustruct; + frame_state *sub_udata = &ustruct; + void *saved_pc = pc; + void *handler; + void *handler_p; + void *pc_p; + frame_state saved_ustruct; + int new_eh_model; + int cleanup = 0; + int only_cleanup = 0; + int rethrow = 0; + int saved_state = 0; + __eh_info *eh_info = (__eh_info *)eh->info; - memcpy (my_udata, udata, sizeof (*udata)); + /* Do we find a handler based on a re-throw PC? */ + if (eh->table_index != (void *) 0) + rethrow = 1; - /* Do any necessary initialization to access arbitrary stack frames. - On the SPARC, this means flushing the register windows. */ - __builtin_unwind_init (); - - /* Now reset pc to the right throw point. */ - pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; - saved_pc = pc; - - handler = 0; + handler = (void *) 0; for (;;) { frame_state *p = udata; @@ -3702,32 +3732,64 @@ label: break; if (udata->eh_ptr == NULL) - new_exception_model = 0; + new_eh_model = 0; else - new_exception_model = (((exception_descriptor *)(udata->eh_ptr))-> + new_eh_model = (((exception_descriptor *)(udata->eh_ptr))-> runtime_id_field == NEW_EH_RUNTIME); - if (new_exception_model) - handler = find_exception_handler (pc, udata->eh_ptr, eh->info); + if (rethrow) + { + rethrow = 0; + handler = find_exception_handler (eh->table_index, udata->eh_ptr, + eh_info, 1, &cleanup); + eh->table_index = (void *)0; + } else - handler = old_find_exception_handler (pc, udata->eh_ptr); + if (new_eh_model) + handler = find_exception_handler (pc, udata->eh_ptr, eh_info, + 0, &cleanup); + else + handler = old_find_exception_handler (pc, udata->eh_ptr); - /* If we found one, we can stop searching. */ + /* If we found one, we can stop searching, if its not a cleanup. + for cleanups, we save the state, and keep looking. This allows + us to call a debug hook if there are nothing but cleanups left. */ if (handler) - { - args_size = udata->args_size; - break; - } + if (cleanup) + { + if (!saved_state) + { + saved_ustruct = *udata; + handler_p = handler; + pc_p = pc; + saved_state = 1; + only_cleanup = 1; + } + } + else + { + only_cleanup = 0; + break; + } /* Otherwise, we continue searching. We subtract 1 from PC to avoid hitting the beginning of the next region. */ pc = get_return_addr (udata, sub_udata) - 1; } + if (saved_state) + { + udata = &saved_ustruct; + handler = handler_p; + pc = pc_p; + if (only_cleanup) + __unwinding_cleanup (); + } + /* If we haven't found a handler by now, this is an unhandled exception. */ - if (! handler) - __terminate (); + if (! handler) + __terminate(); eh->handler_label = handler; @@ -3781,6 +3843,114 @@ label: copy_reg (i, udata, my_udata); } } + /* udata now refers to the frame called by the handler frame. */ + + *udata_p = udata; + return handler; +} + + +/* We first search for an exception handler, and if we don't find + it, we call __terminate on the current stack frame so that we may + use the debugger to walk the stack and understand why no handler + was found. + + If we find one, then we unwind the frames down to the one that + has the handler and transfer control into the handler. */ + +/*extern void __throw(void) __attribute__ ((__noreturn__));*/ + +void +__throw () +{ + struct eh_context *eh = (*get_eh_context) (); + void *pc, *handler; + frame_state ustruct; + frame_state *udata = &ustruct; + frame_state my_ustruct, *my_udata = &my_ustruct; + + /* This is required for C++ semantics. We must call terminate if we + try and rethrow an exception, when there is no exception currently + active. */ + if (! eh->info) + __terminate (); + + /* Start at our stack frame. */ +label: + udata = __frame_state_for (&&label, udata); + if (! udata) + __terminate (); + + /* We need to get the value from the CFA register. */ + udata->cfa = __builtin_dwarf_cfa (); + + memcpy (my_udata, udata, sizeof (*udata)); + + /* Do any necessary initialization to access arbitrary stack frames. + On the SPARC, this means flushing the register windows. */ + __builtin_unwind_init (); + + /* Now reset pc to the right throw point. */ + pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; + + handler = throw_helper (eh, pc, my_udata, &udata); + + /* Now go! */ + + __builtin_eh_return ((void *)eh, +#ifdef STACK_GROWS_DOWNWARD + udata->cfa - my_udata->cfa, +#else + my_udata->cfa - udata->cfa, +#endif + handler); + + /* Epilogue: restore the handler frame's register values and return + to the stub. */ +} + +/*extern void __rethrow(void *) __attribute__ ((__noreturn__));*/ + +void +__rethrow (index) + void *index; +{ + struct eh_context *eh = (*get_eh_context) (); + void *pc, *handler; + frame_state ustruct; + frame_state *udata = &ustruct; + frame_state my_ustruct, *my_udata = &my_ustruct; + + /* This is required for C++ semantics. We must call terminate if we + try and rethrow an exception, when there is no exception currently + active. */ + if (! eh->info) + __terminate (); + + /* This is the table index we want to rethrow from. The value of + the END_REGION label is used for the PC of the throw, and the + search begins with the next table entry. */ + eh->table_index = index; + + /* Start at our stack frame. */ +label: + udata = __frame_state_for (&&label, udata); + if (! udata) + __terminate (); + + /* We need to get the value from the CFA register. */ + udata->cfa = __builtin_dwarf_cfa (); + + memcpy (my_udata, udata, sizeof (*udata)); + + /* Do any necessary initialization to access arbitrary stack frames. + On the SPARC, this means flushing the register windows. */ + __builtin_unwind_init (); + + /* Now reset pc to the right throw point. */ + pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; + + handler = throw_helper (eh, pc, my_udata, &udata); /* Now go! */ diff --git a/gcc/optabs.c b/gcc/optabs.c index 801b8535dd1..eb5f72a94c4 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -119,6 +119,7 @@ rtx memset_libfunc; rtx bzero_libfunc; rtx throw_libfunc; +rtx rethrow_libfunc; rtx sjthrow_libfunc; rtx sjpopnthrow_libfunc; rtx terminate_libfunc; @@ -4294,6 +4295,7 @@ init_optabs () bzero_libfunc = gen_rtx_SYMBOL_REF (Pmode, "bzero"); throw_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__throw"); + rethrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__rethrow"); sjthrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__sjthrow"); sjpopnthrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__sjpopnthrow"); terminate_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__terminate"); diff --git a/gcc/rtl.c b/gcc/rtl.c index 44e998b3723..c8269625937 100644 --- a/gcc/rtl.c +++ b/gcc/rtl.c @@ -193,7 +193,8 @@ char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0", "REG_DEP_ANTI", "REG_DEP_OUTPUT", "REG_BR_PROB", "REG_EXEC_COUNT", "REG_NOALIAS", "REG_SAVE_AREA", "REG_BR_PRED", "REG_EH_CONTEXT", - "REG_FRAME_RELATED_EXPR" }; + "REG_FRAME_RELATED_EXPR", "REG_EH_REGION", + "REG_EH_RETHROW" }; static void dump_and_abort PROTO((int, int, FILE *)) ATTRIBUTE_NORETURN; static void read_name PROTO((char *, FILE *)); diff --git a/gcc/rtl.h b/gcc/rtl.h index da22adbe47a..940de7305e1 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -340,6 +340,14 @@ typedef struct rtvec_def{ REG_FRAME_RELATED_EXPR is attached to insns that are RTX_FRAME_RELATED_P, but are too complex for DWARF to interpret what they imply. The attached rtx is used instead of intuition. */ +/* REG_EH_REGION is used to indicate what exception region an INSN + belongs in. This can be used to indicate what region a call may throw + to. a REGION of 0 indicates that a call cannot throw at all. + REG_EH_RETHROW is used to indicate what that a call is actually a + call to rethrow, and specifies which region the rethrow is targetting. + This provides a way to generate the non standard flow edges required + for a rethrow. */ + #define REG_NOTES(INSN) ((INSN)->fld[6].rtx) @@ -353,7 +361,8 @@ enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4, REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15, REG_BR_PROB = 16, REG_EXEC_COUNT = 17, REG_NOALIAS = 18, REG_SAVE_AREA = 19, REG_BR_PRED = 20, REG_EH_CONTEXT = 21, - REG_FRAME_RELATED_EXPR = 22 }; + REG_FRAME_RELATED_EXPR = 22, REG_EH_REGION = 23, + REG_EH_RETHROW = 24 }; /* The base value for branch probability notes. */ #define REG_BR_PROB_BASE 10000 @@ -617,6 +626,10 @@ extern char *note_insn_name[]; /* Flag in a SYMBOL_REF for machine-specific purposes. */ #define SYMBOL_REF_FLAG(RTX) ((RTX)->volatil) +/* 1 in a SYMBOL_REF if it represents a symbol which might have to change + if its inlined or unrolled. */ +#define SYMBOL_REF_NEED_ADJUST(RTX) ((RTX)->in_struct) + /* 1 means a SYMBOL_REF has been the library function in emit_library_call. */ #define SYMBOL_REF_USED(RTX) ((RTX)->used)