except.h (eh_nesting_info): Add new structure defintion.

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.

From-SVN: r28647
This commit is contained in:
Andrew MacLeod 1999-08-10 16:19:16 +00:00 committed by Andrew Macleod
parent a8688bd6e9
commit 1ef1bf063b
5 changed files with 429 additions and 65 deletions

View File

@ -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.

View File

@ -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);
}
}
}

View File

@ -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. */

View File

@ -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;

View File

@ -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)