This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
EH flow patch
- To: egcs-patches@egcs.cygnus.com
- Subject: EH flow patch
- From: Andrew Macleod <amacleod@cygnus.com>
- Date: Tue, 10 Aug 1999 09:37:06 -0700
I've checked the following patch in. This was approved by Jim
wilson and bootstraps on Solaris. This is required to build libstdtc++
with -fnew-exceptions.
Basically, this patch makes flow and the EH regions play together a lot better.
Flow wasn't treating REG_EH_RETHROW's correctly, and it turns out there
were also a few latent bugs with the rethrow code too. Anyway, This
patch changes a number of things:
REG_EH_RETHROW now has a symbol_ref of the rethrow symbol instead of a
region number. This allows rethrows to be inlined properly.
A set of routines to figure out the region nesting order and determine
exactly which handlers can be reached from any given insn now exist. A side
effect of this is that now when we build the flow graph, we only make
edges to the actual handlers which can be reached, instead of all of them.
If the current region has a catch_all clause, we don't have edges to any
of the outer handlers. This is only effective with -fnew-exceptions.
rethrow regions are better tracked. We were way to pessimistic before,
and a lot of uneccessary regions were in the region table than where
needed to be. Now just whats needed is there. (-fnew-exceptions only)
We no longer overload the value of SYMBOL_REF_USED for rethrow symbols.
There's a flag in the structure now.
There were a few places where we were not observing the REG_EH_REGION note
properly, so I fixed those.
This patch should solve a lot of EH and optimization problems. Its also
sets things up well in preparation for more extensive use of
the REG_EH_REGION note in functions which don't throw.
Next we should attach that note to all the EH runtime routines
which get called (ie __cp_push_exception, etc) then we should see some
dramatic decreases in the exception table size. (again, -fnew-exceptions only)
Andrew
* 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.
Index: gcc/except.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/except.h,v
retrieving revision 1.28
diff -c -p -r1.28 except.h
*** except.h 1999/01/06 20:44:22 1.28
--- except.h 1999/06/22 13:19:59
*************** rtx rethrow_symbol_map
*** 213,218 ****
--- 213,223 ----
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));
*************** struct handler_info *get_first_handler
*** 224,229 ****
--- 229,274 ----
/* Find all the runtime handlers type matches currently referenced */
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));
Index: gcc/rtl.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/rtl.h,v
retrieving revision 1.105
diff -c -p -r1.105 rtl.h
*** rtl.h 1999/04/27 17:08:34 1.105
--- rtl.h 1999/06/22 13:20:01
*************** typedef struct rtvec_def{
*** 344,356 ****
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
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. */
#define REG_NOTES(INSN) ((INSN)->fld[6].rtx)
--- 344,356 ----
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
a non-local goto.
! 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)
Index: gcc/flow.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/flow.c,v
retrieving revision 1.123
diff -c -p -r1.123 flow.c
*** flow.c 1999/05/26 08:50:01 1.123
--- flow.c 1999/06/22 13:20:06
*************** make_edges (label_value_list, bb_eh_end)
*** 869,874 ****
--- 869,875 ----
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;
*************** make_edges (label_value_list, bb_eh_end)
*** 978,1018 ****
if (code == CALL_INSN || asynchronous_exceptions)
{
int is_call = (code == CALL_INSN ? EDGE_ABNORMAL_CALL : 0);
! handler_info *ptr;
!
! /* 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? */
!
! x = find_reg_note (insn, REG_EH_RETHROW, 0);
! if (!x)
! x = find_reg_note (insn, REG_EH_REGION, 0);
! if (x)
! {
! 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;
! }
! }
}
if (code == CALL_INSN && nonlocal_goto_handler_labels)
--- 979,997 ----
if (code == CALL_INSN || asynchronous_exceptions)
{
int is_call = (code == CALL_INSN ? EDGE_ABNORMAL_CALL : 0);
! handler_info **handler_list;
! int eh_region = -1;
! int num;
!
! if (eh_list)
! eh_region = NOTE_BLOCK_NUMBER (XEXP (eh_list, 0));
!
! num = reachable_handlers (eh_region, eh_nest_info,
! insn, &handler_list);
! for ( ; num > 0; num--)
{
! make_label_edge (bb, handler_list[num - 1]->handler_label,
! EDGE_ABNORMAL | EDGE_EH | is_call);
}
if (code == CALL_INSN && nonlocal_goto_handler_labels)
*************** make_edges (label_value_list, bb_eh_end)
*** 1024,1033 ****
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);
}
}
--- 1003,1015 ----
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. */
! /* 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);
}
}
*************** make_edges (label_value_list, bb_eh_end)
*** 1052,1057 ****
--- 1034,1040 ----
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
*************** delete_eh_regions ()
*** 1600,1605 ****
--- 1583,1590 ----
{
rtx insn;
+ update_rethrow_references ();
+
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE)
{
*************** delete_eh_regions ()
*** 1607,1614 ****
(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)
{
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
--- 1592,1600 ----
(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,
! 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;
Index: gcc/except.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/except.c,v
retrieving revision 1.82
diff -c -p -r1.82 except.c
*** except.c 1999/04/15 19:54:09 1.82
--- except.c 1999/06/22 13:20:10
*************** receive_exception_label (handler_label)
*** 740,747 ****
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;
};
--- 740,748 ----
struct func_eh_entry
{
! 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;
};
*************** rethrow_symbol_map (sym, map)
*** 1044,1050 ****
{
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;
}
--- 1045,1051 ----
{
x = duplicate_eh_handlers (CODE_LABEL_NUMBER (l1), y, map);
/* Since we're mapping it, it must be used. */
! function_eh_regions[x].rethrow_ref = 1;
}
return function_eh_regions[x].rethrow_label;
}
*************** rethrow_used (region)
*** 1057,1064 ****
{
if (flag_new_exceptions)
{
! rtx lab = function_eh_regions[find_func_region (region)].rethrow_label;
! return (SYMBOL_REF_USED (lab));
}
return 0;
}
--- 1058,1065 ----
{
if (flag_new_exceptions)
{
! int ret = function_eh_regions[find_func_region (region)].rethrow_ref;
! return ret;
}
return 0;
}
*************** expand_rethrow (label)
*** 1965,1987 ****
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;
/* Search backwards for the actual call 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,
REG_NOTES (insn));
! emit_barrier ();
}
else
emit_jump (label);
--- 1966,1989 ----
else
if (flag_new_exceptions)
{
! 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 ();
while (GET_CODE (insn) != CALL_INSN)
insn = PREV_INSN (insn);
delete_insns_since (insn);
!
! /* Mark the label/symbol on the call. */
! REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_RETHROW, label,
REG_NOTES (insn));
! emit_barrier ();
}
else
emit_jump (label);
*************** output_exception_table_entry (file, n)
*** 2127,2133 ****
if (rethrow != NULL_RTX && !flag_new_exceptions)
rethrow = NULL_RTX;
if (rethrow != NULL_RTX && handler == NULL)
! if (! SYMBOL_REF_USED (rethrow))
rethrow = NULL_RTX;
--- 2129,2135 ----
if (rethrow != NULL_RTX && !flag_new_exceptions)
rethrow = NULL_RTX;
if (rethrow != NULL_RTX && handler == NULL)
! if (! function_eh_regions[index].rethrow_ref)
rethrow = NULL_RTX;
*************** static int
*** 2481,2489 ****
can_throw (insn)
rtx insn;
{
! /* Calls can always potentially throw exceptions. */
if (GET_CODE (insn) == CALL_INSN)
! return 1;
if (asynchronous_exceptions)
{
--- 2483,2496 ----
can_throw (insn)
rtx insn;
{
! /* 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)
! {
! rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
! if (!note || XINT (XEXP (note, 0), 0) > 0)
! return 1;
! }
if (asynchronous_exceptions)
{
*************** scan_region (insn, n, delete_outer)
*** 2524,2532 ****
/* 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
--- 2531,2538 ----
/* Assume we can delete the region. */
int delete = 1;
/* Can't delete something which is rethrown to. */
! if (rethrow_used (n))
delete = 0;
if (insn == NULL_RTX
*************** exception_optimize ()
*** 2641,2646 ****
--- 2647,2699 ----
}
}
}
+
+ /* 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. */
*************** in_same_eh_region (insn1, insn2)
*** 2959,2962 ****
--- 3012,3260 ----
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);
+ }
+ }
+ }