summaryrefslogtreecommitdiff
path: root/gcc/except.c
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2009-03-29 13:32:13 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2009-03-29 13:32:13 +0000
commit4c5fcca622ac108db1d93965d1d11701d85e70e5 (patch)
tree1577a1a0b3cbaa114690527d99170c1034533481 /gcc/except.c
parentb8c0c8668e11fc6af6bc4debceeed05510849846 (diff)
downloadgcc-4c5fcca622ac108db1d93965d1d11701d85e70e5.tar.gz
PR middle-end/28850
* tree-pass.h (pass_cleanup_eh): New function. (remove_unreachable_regions): Break code handling RTL to rtl_remove_unreachable_regions; remove ERT_MUST_NOT_THROW that can not be reached by runtime. (can_be_reached_by_runtime): New function. (label_to_region_map): New function. (num_eh_regions): New function. (rtl_remove_unreachable_regions): New function. (convert_from_eh_region_ranges): Call rtl_remove_unreachable_regions. (remove_eh_region): New function. * except.h: Include sbitmap and vecprim. (remove_eh_region, remove_unreachable_regions, label_to_region_map, num_eh_regions): Declare. * passes.c (init_optimization_passes): Schedule cleanup_eh. * Makefile.in (EXCEPT_H): New; replace all uses of except.h by it. * tree-eh.c (tree_remove_unreachable_handlers): New function. (tree_empty_eh_handler_p): New function. (cleanup_empty_eh): New function. (cleanup_eh): New function. (pass_cleanup_eh): New function. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@145233 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/except.c')
-rw-r--r--gcc/except.c241
1 files changed, 203 insertions, 38 deletions
diff --git a/gcc/except.c b/gcc/except.c
index c6a5ca51c1c..41f799af07e 100644
--- a/gcc/except.c
+++ b/gcc/except.c
@@ -227,8 +227,6 @@ static hashval_t t2r_hash (const void *);
static void add_type_for_runtime (tree);
static tree lookup_type_for_runtime (tree);
-static void remove_unreachable_regions (rtx);
-
static int ttypes_filter_eq (const void *, const void *);
static hashval_t ttypes_filter_hash (const void *);
static int ehspec_filter_eq (const void *, const void *);
@@ -622,69 +620,145 @@ collect_eh_region_array (void)
}
}
-/* Remove all regions whose labels are not reachable from insns. */
+/* R is MUST_NOT_THROW region that is not reachable via local
+ RESX instructions. It still must be kept in the tree in case runtime
+ can unwind through it, or we will eliminate out terminate call
+ runtime would do otherwise. Return TRUE if R contains throwing statements
+ or some of the exceptions in inner regions can be unwound up to R.
+
+ CONTAINS_STMT is bitmap of all regions that contains some throwing
+ statements.
+
+ Function looks O(^3) at first sight. In fact the function is called at most
+ once for every MUST_NOT_THROW in EH tree from remove_unreachable_regions
+ Because the outer loop walking subregions does not dive in MUST_NOT_THROW,
+ the outer loop examines every region at most once. The inner loop
+ is doing unwinding from the throwing statement same way as we do during
+ CFG construction, so it is O(^2) in size of EH tree, but O(n) in size
+ of CFG. In practice Eh trees are wide, not deep, so this is not
+ a problem. */
-static void
-remove_unreachable_regions (rtx insns)
+static bool
+can_be_reached_by_runtime (sbitmap contains_stmt, struct eh_region *r)
{
- int i, *uid_region_num;
- bool *reachable;
- struct eh_region *r;
- rtx insn;
+ struct eh_region *i = r->inner;
+ unsigned n;
+ bitmap_iterator bi;
- uid_region_num = XCNEWVEC (int, get_max_uid ());
- reachable = XCNEWVEC (bool, cfun->eh->last_region_number + 1);
-
- for (i = cfun->eh->last_region_number; i > 0; --i)
+ if (TEST_BIT (contains_stmt, r->region_number))
+ return true;
+ if (r->aka)
+ EXECUTE_IF_SET_IN_BITMAP (r->aka, 0, n, bi)
+ if (TEST_BIT (contains_stmt, n))
+ return true;
+ if (!i)
+ return false;
+ while (1)
{
- r = VEC_index (eh_region, cfun->eh->region_array, i);
- if (!r || r->region_number != i)
- continue;
-
- if (r->resume)
+ /* It is pointless to look into MUST_NOT_THROW
+ or dive into subregions. They never unwind up. */
+ if (i->type != ERT_MUST_NOT_THROW)
{
- gcc_assert (!uid_region_num[INSN_UID (r->resume)]);
- uid_region_num[INSN_UID (r->resume)] = i;
+ bool found = TEST_BIT (contains_stmt, i->region_number);
+ if (!found)
+ EXECUTE_IF_SET_IN_BITMAP (i->aka, 0, n, bi)
+ if (TEST_BIT (contains_stmt, n))
+ {
+ found = true;
+ break;
+ }
+ /* We have nested region that contains throwing statement.
+ See if resuming might lead up to the resx or we get locally
+ caught sooner. If we get locally caught sooner, we either
+ know region R is not reachable or it would have direct edge
+ from the EH resx and thus consider region reachable at
+ firest place. */
+ if (found)
+ {
+ struct eh_region *i1 = i;
+ tree type_thrown = NULL_TREE;
+
+ if (i1->type == ERT_THROW)
+ {
+ type_thrown = i1->u.eh_throw.type;
+ i1 = i1->outer;
+ }
+ for (; i1 != r; i1 = i1->outer)
+ if (reachable_next_level (i1, type_thrown, NULL,
+ false) >= RNL_CAUGHT)
+ break;
+ if (i1 == r)
+ return true;
+ }
}
- if (r->label)
+ /* If there are sub-regions, process them. */
+ if (i->type != ERT_MUST_NOT_THROW && i->inner)
+ i = i->inner;
+ /* If there are peers, process them. */
+ else if (i->next_peer)
+ i = i->next_peer;
+ /* Otherwise, step back up the tree to the next peer. */
+ else
{
- gcc_assert (!uid_region_num[INSN_UID (r->label)]);
- uid_region_num[INSN_UID (r->label)] = i;
+ do
+ {
+ i = i->outer;
+ if (i == r)
+ return false;
+ }
+ while (i->next_peer == NULL);
+ i = i->next_peer;
}
}
+}
- for (insn = insns; insn; insn = NEXT_INSN (insn))
- reachable[uid_region_num[INSN_UID (insn)]] = true;
+/* Remove all regions whose labels are not reachable.
+ REACHABLE is bitmap of all regions that are used by the function
+ CONTAINS_STMT is bitmap of all regions that contains stmt (or NULL). */
+void
+remove_unreachable_regions (sbitmap reachable, sbitmap contains_stmt)
+{
+ int i;
+ struct eh_region *r;
for (i = cfun->eh->last_region_number; i > 0; --i)
{
r = VEC_index (eh_region, cfun->eh->region_array, i);
- if (r && r->region_number == i && !reachable[i])
+ if (!r)
+ continue;
+ if (r->region_number == i && !TEST_BIT (reachable, i) && !r->resume)
{
bool kill_it = true;
+
+ r->tree_label = NULL;
switch (r->type)
{
case ERT_THROW:
/* Don't remove ERT_THROW regions if their outer region
- is reachable. */
- if (r->outer && reachable[r->outer->region_number])
+ is reachable. */
+ if (r->outer && TEST_BIT (reachable, r->outer->region_number))
kill_it = false;
break;
-
case ERT_MUST_NOT_THROW:
/* MUST_NOT_THROW regions are implementable solely in the
- runtime, but their existence continues to affect calls
- within that region. Never delete them here. */
- kill_it = false;
+ runtime, but we need them when inlining function.
+
+ Keep them if outer region is not MUST_NOT_THROW a well
+ and if they contain some statement that might unwind through
+ them. */
+ if ((!r->outer || r->outer->type != ERT_MUST_NOT_THROW)
+ && (!contains_stmt
+ || can_be_reached_by_runtime (contains_stmt, r)))
+ kill_it = false;
break;
-
case ERT_TRY:
{
/* TRY regions are reachable if any of its CATCH regions
are reachable. */
struct eh_region *c;
- for (c = r->u.eh_try.eh_catch; c ; c = c->u.eh_catch.next_catch)
- if (reachable[c->region_number])
+ for (c = r->u.eh_try.eh_catch; c;
+ c = c->u.eh_catch.next_catch)
+ if (TEST_BIT (reachable, c->region_number))
{
kill_it = false;
break;
@@ -697,11 +771,91 @@ remove_unreachable_regions (rtx insns)
}
if (kill_it)
- remove_eh_handler (r);
+ {
+ if (dump_file)
+ fprintf (dump_file, "Removing unreachable eh region %i\n",
+ r->region_number);
+ remove_eh_handler (r);
+ }
}
}
+#ifdef ENABLE_CHECKING
+ verify_eh_tree (cfun);
+#endif
+}
+
+/* Return array mapping LABEL_DECL_UID to region such that region's tree_label
+ is identical to label. */
+
+VEC(int,heap) *
+label_to_region_map (void)
+{
+ VEC(int,heap) * label_to_region = NULL;
+ int i;
+
+ VEC_safe_grow_cleared (int, heap, label_to_region,
+ cfun->cfg->last_label_uid + 1);
+ for (i = cfun->eh->last_region_number; i > 0; --i)
+ {
+ struct eh_region *r = VEC_index (eh_region, cfun->eh->region_array, i);
+ if (r && r->tree_label && LABEL_DECL_UID (r->tree_label) >= 0)
+ {
+ if ((unsigned) LABEL_DECL_UID (r->tree_label) >
+ VEC_length (int, label_to_region))
+ VEC_safe_grow_cleared (int, heap, label_to_region,
+ LABEL_DECL_UID (r->tree_label));
+ VEC_replace (int, label_to_region, LABEL_DECL_UID (r->tree_label),
+ i);
+ }
+ }
+ return label_to_region;
+}
+
+/* Return number of EH regions. */
+int
+num_eh_regions (void)
+{
+ return cfun->eh->last_region_number + 1;
+}
+
+/* Remove all regions whose labels are not reachable from insns. */
+
+static void
+rtl_remove_unreachable_regions (rtx insns)
+{
+ int i, *uid_region_num;
+ sbitmap reachable;
+ struct eh_region *r;
+ rtx insn;
+
+ uid_region_num = XCNEWVEC (int, get_max_uid ());
+ reachable = sbitmap_alloc (cfun->eh->last_region_number + 1);
+ sbitmap_zero (reachable);
- free (reachable);
+ for (i = cfun->eh->last_region_number; i > 0; --i)
+ {
+ r = VEC_index (eh_region, cfun->eh->region_array, i);
+ if (!r || r->region_number != i)
+ continue;
+
+ if (r->resume)
+ {
+ gcc_assert (!uid_region_num[INSN_UID (r->resume)]);
+ uid_region_num[INSN_UID (r->resume)] = i;
+ }
+ if (r->label)
+ {
+ gcc_assert (!uid_region_num[INSN_UID (r->label)]);
+ uid_region_num[INSN_UID (r->label)] = i;
+ }
+ }
+
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ SET_BIT (reachable, uid_region_num[INSN_UID (insn)]);
+
+ remove_unreachable_regions (reachable, NULL);
+
+ sbitmap_free (reachable);
free (uid_region_num);
}
@@ -726,7 +880,7 @@ convert_from_eh_region_ranges (void)
region->label = DECL_RTL_IF_SET (region->tree_label);
}
- remove_unreachable_regions (insns);
+ rtl_remove_unreachable_regions (insns);
}
static void
@@ -2324,6 +2478,17 @@ maybe_remove_eh_handler (rtx label)
remove_eh_handler (region);
}
+/* Remove Eh region R that has turned out to have no code in its handler. */
+
+void
+remove_eh_region (int r)
+{
+ struct eh_region *region;
+
+ region = VEC_index (eh_region, cfun->eh->region_array, r);
+ remove_eh_handler (region);
+}
+
/* Invokes CALLBACK for every exception handler label. Only used by old
loop hackery; should not be used by new code. */