diff options
author | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-03-29 13:32:13 +0000 |
---|---|---|
committer | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-03-29 13:32:13 +0000 |
commit | 4c5fcca622ac108db1d93965d1d11701d85e70e5 (patch) | |
tree | 1577a1a0b3cbaa114690527d99170c1034533481 /gcc/except.c | |
parent | b8c0c8668e11fc6af6bc4debceeed05510849846 (diff) | |
download | gcc-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.c | 241 |
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. */ |