diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 35 | ||||
-rw-r--r-- | gcc/except.c | 340 | ||||
-rw-r--r-- | gcc/except.h | 45 | ||||
-rw-r--r-- | gcc/flow.c | 62 | ||||
-rw-r--r-- | gcc/rtl.h | 12 |
5 files changed, 429 insertions, 65 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 46c1d3c..0409461 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,38 @@ +Tue Aug 10 10:47:42 EDT 1999 Andrew MacLeod <amacleod@cygnus.com> + + * except.h (eh_nesting_info): Add new structure defintion. + (init_eh_nesting_info, free_eh_nesting_info): Add function prototypes. + (reachable_handlers, update_rethrow_references): Add function + prototypes. + * rtl.h (struct rtvec_def): Update comments. REG_EH_RETHROW takes + a rethrow symbol instead of an integer exception region number. + * flow.c (Make_edges): Use new exception nesting routines to determine + which handlers are reachable from a CALL or asynchronous insn. + Dont add an edge for calls with a REG_EH_REGION of -1 to non-local + goto receivers. + (delete_eh_regions): Update rethrow labels, and don't delete + regions which are the target of a rethrow. + * except.c (struct func_eh_entry): Add rethrow_ref field, now we can + avoid overloading the SYMBOL_REF_USED flag. + (rethrow_symbol_map): Use new rethrow_ref field. + (rethrow_used): Use new rethrow_ref field. + (expand_rethrow): REG_EH_RETHROW now has a SYMBOL_REF instead + of an integer. Fix formatting. + (output_exception_table_entry): Use new rethrow_ref field. + (can_throw): Check for EH_REGION_NOTE before deciding + whether a CALL can throw or not. + (scan_region): Call rethrow_used() instead of accessing data structure. + (update_rethrow_references): New function to make sure only regions + which are still targets of a rethrow are flagged as such. + (process_nestinfo): New static function to initialize a handler + list for a specific region. + (init_eh_nesting_info): New function to allocate and initialize + the list of all EH handlers reachable from all regions. + (reachable_handlers): New function to retrieve the list of handlers + reachable from a specific region and insn. + (free_eh_nesting_info): New function to dispose of a list of + reachable handlers. + Tue Aug 10 10:39:31 EDT 1999 Andrew MacLeod <amacleod@cygnus.com> * flow.c (split_edge): Set JUMP_LABEL field. diff --git a/gcc/except.c b/gcc/except.c index 40e912f..952021b 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -677,8 +677,9 @@ 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 */ + int range_number; /* EH region number from EH NOTE insn's. */ + rtx rethrow_label; /* Label for rethrow. */ + int rethrow_ref; /* Is rethrow referenced? */ struct handler_info *handlers; }; @@ -981,7 +982,7 @@ rethrow_symbol_map (sym, map) { 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; + function_eh_regions[x].rethrow_ref = 1; } return function_eh_regions[x].rethrow_label; } @@ -994,8 +995,8 @@ rethrow_used (region) { if (flag_new_exceptions) { - rtx lab = function_eh_regions[find_func_region (region)].rethrow_label; - return (SYMBOL_REF_USED (lab)); + int ret = function_eh_regions[find_func_region (region)].rethrow_ref; + return ret; } return 0; } @@ -1900,23 +1901,24 @@ expand_rethrow (label) else 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; + rtx insn, val; + int region; + if (label == NULL_RTX) + label = last_rethrow_symbol; + emit_library_call (rethrow_libfunc, 0, VOIDmode, 1, label, Pmode); + region = find_func_region (eh_region_from_symbol (label)); + function_eh_regions[region].rethrow_ref = 1; /* Search backwards for the actual call insn. */ - insn = get_last_insn (); + insn = get_last_insn (); while (GET_CODE (insn) != CALL_INSN) insn = PREV_INSN (insn); delete_insns_since (insn); - - /* Mark the label/symbol on the call. */ - val = GEN_INT (eh_region_from_symbol (label)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_RETHROW, val, + + /* Mark the label/symbol on the call. */ + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_RETHROW, label, REG_NOTES (insn)); - emit_barrier (); + emit_barrier (); } else emit_jump (label); @@ -2062,7 +2064,7 @@ output_exception_table_entry (file, n) if (rethrow != NULL_RTX && !flag_new_exceptions) rethrow = NULL_RTX; if (rethrow != NULL_RTX && handler == NULL) - if (! SYMBOL_REF_USED (rethrow)) + if (! function_eh_regions[index].rethrow_ref) rethrow = NULL_RTX; @@ -2373,9 +2375,14 @@ static int can_throw (insn) rtx insn; { - /* Calls can always potentially throw exceptions. */ + /* Calls can always potentially throw exceptions, unless they have + a REG_EH_REGION note with a value of 0 or less. */ if (GET_CODE (insn) == CALL_INSN) - return 1; + { + rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note || XINT (XEXP (note, 0), 0) > 0) + return 1; + } if (asynchronous_exceptions) { @@ -2416,9 +2423,8 @@ 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))) + if (rethrow_used (n)) delete = 0; if (insn == NULL_RTX @@ -2533,6 +2539,53 @@ exception_optimize () } } } + +/* This function determines whether any of the exception regions in the + current function are targets of a rethrow or not, and set the + reference flag according. */ +void +update_rethrow_references () +{ + rtx insn; + int x, region; + int *saw_region, *saw_rethrow; + + if (!flag_new_exceptions) + return; + + saw_region = (int *) alloca (current_func_eh_entry * sizeof (int)); + saw_rethrow = (int *) alloca (current_func_eh_entry * sizeof (int)); + bzero ((char *) saw_region, (current_func_eh_entry * sizeof (int))); + bzero ((char *) saw_rethrow, (current_func_eh_entry * sizeof (int))); + + /* Determine what regions exist, and whether there are any rethrows + to those regions or not. */ + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CALL_INSN) + { + rtx note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX); + if (note) + { + region = eh_region_from_symbol (XEXP (note, 0)); + region = find_func_region (region); + saw_rethrow[region] = 1; + } + } + else + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) + { + region = find_func_region (NOTE_BLOCK_NUMBER (insn)); + saw_region[region] = 1; + } + } + + /* For any regions we did see, set the referenced flag. */ + for (x = 0; x < current_func_eh_entry; x++) + if (saw_region[x]) + function_eh_regions[x].rethrow_ref = saw_rethrow[x]; +} /* Various hooks for the DWARF 2 __throw routine. */ @@ -2853,4 +2906,249 @@ in_same_eh_region (insn1, insn2) ret = (insn_eh_region[uid1] == insn_eh_region[uid2]); return ret; } + + +/* This function will initialize the handler list for a specified block. + It may recursively call itself if the outer block hasn't been processed + yet. At some point in the future we can trim out handlers which we + know cannot be called. (ie, if a block has an INT type handler, + control will never be passed to an outer INT type handler). */ +static void +process_nestinfo (block, info, nested_eh_region) + int block; + eh_nesting_info *info; + int *nested_eh_region; +{ + handler_info *ptr, *last_ptr = NULL; + int x, y, count = 0; + int extra = 0; + handler_info **extra_handlers; + int index = info->region_index[block]; + + /* If we've already processed this block, simply return. */ + if (info->num_handlers[index] > 0) + return; + + for (ptr = get_first_handler (block); ptr; last_ptr = ptr, ptr = ptr->next) + count++; + + /* pick up any information from the next outer region. It will already + contain a summary of itself and all outer regions to it. */ + + if (nested_eh_region [block] != 0) + { + int nested_index = info->region_index[nested_eh_region[block]]; + process_nestinfo (nested_eh_region[block], info, nested_eh_region); + extra = info->num_handlers[nested_index]; + extra_handlers = info->handlers[nested_index]; + info->outer_index[index] = nested_index; + } + + /* If the last handler is either a CATCH_ALL or a cleanup, then we + won't use the outer ones since we know control will not go past the + catch-all or cleanup. */ + + if (last_ptr != NULL && (last_ptr->type_info == NULL + || last_ptr->type_info == CATCH_ALL_TYPE)) + extra = 0; + + info->num_handlers[index] = count + extra; + info->handlers[index] = (handler_info **) malloc ((count + extra) + * sizeof (handler_info **)); + + /* First put all our handlers into the list. */ + ptr = get_first_handler (block); + for (x = 0; x < count; x++) + { + info->handlers[index][x] = ptr; + ptr = ptr->next; + } + + /* Now add all the outer region handlers, if they aren't they same as + one of the types in the current block. We won't worry about + derived types yet, we'll just look for the exact type. */ + for (y =0, x = 0; x < extra ; x++) + { + int i, ok; + ok = 1; + /* Check to see if we have a type duplication. */ + for (i = 0; i < count; i++) + if (info->handlers[index][i]->type_info == extra_handlers[x]->type_info) + { + ok = 0; + /* Record one less handler. */ + (info->num_handlers[index])--; + break; + } + if (ok) + { + info->handlers[index][y + count] = extra_handlers[x]; + y++; + } + } +} + +/* This function will allocate and initialize an eh_nesting_info structure. + It returns a pointer to the completed data structure. If there are + no exception regions, a NULL value is returned. */ +eh_nesting_info * +init_eh_nesting_info () +{ + int *nested_eh_region; + int region_count = 0; + rtx eh_note = NULL_RTX; + eh_nesting_info *info; + rtx insn; + int x; + + info = (eh_nesting_info *) malloc (sizeof (eh_nesting_info)); + info->region_index = (int *) malloc ((max_label_num () + 1) * sizeof (int)); + bzero ((char *) info->region_index, (max_label_num () + 1) * sizeof (int)); + + nested_eh_region = (int *) alloca ((max_label_num () + 1) * sizeof (int)); + bzero ((char *) nested_eh_region, (max_label_num () + 1) * sizeof (int)); + /* Create the nested_eh_region list. If indexed with a block number, it + returns the block number of the next outermost region, if any. + We can count the number of regions and initialize the region_index + vector at the same time. */ + for (insn = get_insns(); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) + { + int block = NOTE_BLOCK_NUMBER (insn); + region_count++; + info->region_index[block] = region_count; + if (eh_note) + nested_eh_region [block] = + NOTE_BLOCK_NUMBER (XEXP (eh_note, 0)); + else + nested_eh_region [block] = 0; + eh_note = gen_rtx_EXPR_LIST (VOIDmode, insn, eh_note); + } + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) + eh_note = XEXP (eh_note, 1); + } + } + + /* If there are no regions, wrap it up now. */ + if (region_count == 0) + { + free (info->region_index); + free (info); + return NULL; + } + + region_count++; + info->handlers = (handler_info ***) malloc (region_count + * sizeof (handler_info ***)); + info->num_handlers = (int *) malloc (region_count * sizeof (int)); + info->outer_index = (int *) malloc (region_count * sizeof (int)); + + bzero ((char *) info->handlers, region_count * sizeof (rtx *)); + bzero ((char *) info->num_handlers, region_count * sizeof (int)); + bzero ((char *) info->outer_index, region_count * sizeof (int)); + + /* Now initialize the handler lists for all exception blocks. */ + for (x = 0; x <= max_label_num (); x++) + { + if (info->region_index[x] != 0) + process_nestinfo (x, info, nested_eh_region); + } + info->region_count = region_count; + return info; +} + + +/* This function is used to retreive the vector of handlers which + can be reached by a given insn in a given exception region. + BLOCK is the exception block the insn is in. + INFO is the eh_nesting_info structure. + INSN is the (optional) insn within the block. If insn is not NULL_RTX, + it may contain reg notes which modify its throwing behavior, and + these will be obeyed. If NULL_RTX is passed, then we simply return the + handlers for block. + HANDLERS is the address of a pointer to a vector of handler_info pointers. + Upon return, this will have the handlers which can be reached by block. + This function returns the number of elements in the handlers vector. */ +int +reachable_handlers (block, info, insn, handlers) + int block; + eh_nesting_info *info; + rtx insn ; + handler_info ***handlers; +{ + int index = 0; + *handlers = NULL; + + if (info == NULL) + return 0; + if (block > 0) + index = info->region_index[block]; + + if (insn && GET_CODE (insn) == CALL_INSN) + { + /* RETHROWs specify a region number from which we are going to rethrow. + This means we wont pass control to handlers in the specified + region, but rather any region OUTSIDE the specified region. + We accomplish this by setting block to the outer_index of the + specified region. */ + rtx note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX); + if (note) + { + index = eh_region_from_symbol (XEXP (note, 0)); + index = info->region_index[index]; + if (index) + index = info->outer_index[index]; + } + else + { + /* If there is no rethrow, we look for a REG_EH_REGION, and + we'll throw from that block. A value of 0 or less + indicates that this insn cannot throw. */ + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (note) + { + int b = XINT (XEXP (note, 0), 0); + if (b <= 0) + index = 0; + else + index = info->region_index[b]; + } + } + } + /* If we reach this point, and index is 0, there is no throw. */ + if (index == 0) + return 0; + + *handlers = info->handlers[index]; + return info->num_handlers[index]; +} + + +/* This function will free all memory associated with the eh_nesting info. */ + +void +free_eh_nesting_info (info) + eh_nesting_info *info; +{ + int x; + if (info != NULL) + { + if (info->region_index) + free (info->region_index); + if (info->num_handlers) + free (info->num_handlers); + if (info->outer_index) + free (info->outer_index); + if (info->handlers) + { + for (x = 0; x < info->region_count; x++) + if (info->handlers[x]) + free (info->handlers[x]); + free (info->handlers); + } + } +} diff --git a/gcc/except.h b/gcc/except.h index 2e3e794..73bbe4d 100644 --- a/gcc/except.h +++ b/gcc/except.h @@ -266,6 +266,11 @@ rtx rethrow_symbol_map PROTO((rtx, rtx (*)(rtx))); int rethrow_used PROTO((int)); +/* Update the rethrow references to reflect rethrows which have been + optimized away. */ + +void update_rethrow_references PROTO((void)); + /* Return the region number a this is the rethrow label for. */ int eh_region_from_symbol PROTO((rtx)); @@ -278,6 +283,46 @@ struct handler_info *get_first_handler PROTO((int)); int find_all_handler_type_matches PROTO((void ***)); +/* The eh_nesting_info structure is used to find a list of valid handlers + for any arbitrary exception region. When init_eh_nesting_info is called, + the information is all pre-calculated and entered in this structure. + REGION_INDEX is a vector over all possible region numbers. Since the + number of regions is typically much smaller than the range of block + numbers, this is a sparse vector and the other data structures are + represented as dense vectors. Indexed with an exception region number, this + returns the index to use in the other data structures to retreive the + correct information. + HANDLERS is an array of vectors which point to handler_info structures. + when indexed, it gives the list of all possible handlers which can + be reached by a throw from this exception region. + NUM_HANDLERS is the equivilent array indicating how many handler + pointers there are in the HANDLERS vector. + OUTER_INDEX indicates which index represents the information for the + outer block. 0 indicates there is no outer context. + REGION_COUNT is the number of regions. */ + +typedef struct eh_nesting +{ + int *region_index; + handler_info ***handlers; + int *num_handlers; + int *outer_index; + int region_count; +} eh_nesting_info; + +/* Initialize the eh_nesting_info structure. */ + +eh_nesting_info *init_eh_nesting_info PROTO((void)); + +/* Get a list of handlers reachable from a an exception region/insn. */ + +int reachable_handlers PROTO((int, eh_nesting_info *, rtx, + handler_info ***handlers)); + +/* Free the eh_nesting_info structure. */ + +void free_eh_nesting_info PROTO((eh_nesting_info *)); + extern void init_eh PROTO((void)); /* Initialization for the per-function EH data. */ @@ -867,6 +867,7 @@ make_edges (label_value_list, bb_eh_end) rtx *bb_eh_end; { int i; + eh_nesting_info *eh_nest_info = init_eh_nesting_info (); /* Assume no computed jump; revise as we create edges. */ current_function_has_computed_jump = 0; @@ -976,41 +977,19 @@ make_edges (label_value_list, bb_eh_end) if (code == CALL_INSN || asynchronous_exceptions) { int is_call = (code == CALL_INSN ? EDGE_ABNORMAL_CALL : 0); - handler_info *ptr; + handler_info **handler_list; + int eh_region = -1; + int num; - /* Use REG_EH_RETHROW and REG_EH_REGION if available. */ - /* ??? REG_EH_REGION is not generated presently. Is it - inteded that there be multiple notes for the regions? - or is my eh_list collection redundant with handler linking? */ + if (eh_list) + eh_region = NOTE_BLOCK_NUMBER (XEXP (eh_list, 0)); - x = find_reg_note (insn, REG_EH_RETHROW, 0); - if (!x) - x = find_reg_note (insn, REG_EH_REGION, 0); - if (x) + num = reachable_handlers (eh_region, eh_nest_info, + insn, &handler_list); + for ( ; num > 0; num--) { - if (XINT (XEXP (x, 0), 0) > 0) - { - ptr = get_first_handler (XINT (XEXP (x, 0), 0)); - while (ptr) - { - make_label_edge (bb, ptr->handler_label, - EDGE_ABNORMAL | EDGE_EH | is_call); - ptr = ptr->next; - } - } - } - else - { - for (x = eh_list; x; x = XEXP (x, 1)) - { - ptr = get_first_handler (NOTE_BLOCK_NUMBER (XEXP (x, 0))); - while (ptr) - { - make_label_edge (bb, ptr->handler_label, - EDGE_ABNORMAL | EDGE_EH | is_call); - ptr = ptr->next; - } - } + make_label_edge (bb, handler_list[num - 1]->handler_label, + EDGE_ABNORMAL | EDGE_EH | is_call); } if (code == CALL_INSN && nonlocal_goto_handler_labels) @@ -1022,10 +1001,13 @@ make_edges (label_value_list, bb_eh_end) gotos do not have their addresses taken, then only calls to those functions or to other nested functions that use them could possibly do nonlocal gotos. */ - - for (x = nonlocal_goto_handler_labels; x ; x = XEXP (x, 1)) - make_label_edge (bb, XEXP (x, 0), - EDGE_ABNORMAL | EDGE_ABNORMAL_CALL); + /* We do know that a REG_EH_REGION note with a value less + than 0 is guaranteed not to perform a non-local goto. */ + rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note || XINT (XEXP (note, 0), 0) >= 0) + for (x = nonlocal_goto_handler_labels; x ; x = XEXP (x, 1)) + make_label_edge (bb, XEXP (x, 0), + EDGE_ABNORMAL | EDGE_ABNORMAL_CALL); } } @@ -1050,6 +1032,7 @@ make_edges (label_value_list, bb_eh_end) make_edge (bb, BASIC_BLOCK (i + 1), EDGE_FALLTHRU); } } + free_eh_nesting_info (eh_nest_info); } /* Create an edge between two basic blocks. FLAGS are auxiliary information @@ -1615,6 +1598,8 @@ delete_eh_regions () { rtx insn; + update_rethrow_references (); + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (GET_CODE (insn) == NOTE) { @@ -1622,8 +1607,9 @@ delete_eh_regions () (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, + as long as it isn't 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; @@ -344,13 +344,13 @@ typedef struct rtvec_def{ 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. - A REGION of -1 indicates that it cannot throw, nor will it execute + to. a REGION of 0 indicates that a call cannot throw at all. + a REGION of -1 indicates that it cannot throw, nor will it execute a non-local goto. - 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. */ + REG_EH_RETHROW is used to indicate that a call is actually a + call to rethrow, and specifies the rethrow symbol for the 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) |