From 927a6b6b50770a413d2540c4957f5a422bb98c2d Mon Sep 17 00:00:00 2001 From: hubicka Date: Sat, 25 Apr 2009 18:27:19 +0000 Subject: [PATCH] * tree.c (list_equal_p): New function. * tree.h (list_equal_p): Declare. * coretypes.h (edge_def, edge, const_edge, basic_block_def basic_block_def, basic_block, const_basic_block): New. * tree-eh.c (make_eh_edge): EH edges are not abnormal. (redirect_eh_edge): New function. (make_eh_edge_update_phi): EH edges are not abnormal. * except.c: Include tree-flow.h. (list_match): New function. (eh_region_replaceable_by_p): New function. (replace_region): New function. (hash_type_list): New function. (hash_eh_region): New function. (eh_regions_equal_p): New function. (merge_peers): New function. (remove_unreachable_regions): Verify EH tree when checking; merge peers. (copy_eh_region_1): New function. (copy_eh_region): New function. (push_reachable_handler): New function. (build_post_landing_pads, dw2_build_landing_pads): Be ready for regions without label but with live RESX. * except.h (redirect_eh_edge_to_label): New. * tree-flow.h (redirect_eh_edge): New. * coretypes.h (edge_def, edge, const_edge, basic_block_def basic_block_def, basic_block, const_basic_block): Remove. * Makefile.in (except.o): Add dependency on tree-flow.h * tree-cfg.c (gimple_redirect_edge_and_branch): Handle EH edges. * basic-block.h (edge, const_edge, basic_block, const_basic_block): Remove. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@146776 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 33 ++ gcc/Makefile.in | 2 +- gcc/basic-block.h | 5 - gcc/coretypes.h | 6 + gcc/except.c | 477 ++++++++++++++++++++++++++++ gcc/except.h | 1 + gcc/testsuite/ChangeLog | 4 + gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C | 2 +- gcc/tree-cfg.c | 3 + gcc/tree-eh.c | 58 +++- gcc/tree-flow.h | 8 +- gcc/tree.c | 12 + gcc/tree.h | 1 + 13 files changed, 594 insertions(+), 18 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6d2ae2296a3..47f98fa95cc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,36 @@ +2009-04-25 Jan Hubicka + + * tree.c (list_equal_p): New function. + * tree.h (list_equal_p): Declare. + * coretypes.h (edge_def, edge, const_edge, basic_block_def + basic_block_def, basic_block, const_basic_block): New. + * tree-eh.c (make_eh_edge): EH edges are not abnormal. + (redirect_eh_edge): New function. + (make_eh_edge_update_phi): EH edges are not abnormal. + * except.c: Include tree-flow.h. + (list_match): New function. + (eh_region_replaceable_by_p): New function. + (replace_region): New function. + (hash_type_list): New function. + (hash_eh_region): New function. + (eh_regions_equal_p): New function. + (merge_peers): New function. + (remove_unreachable_regions): Verify EH tree when checking; + merge peers. + (copy_eh_region_1): New function. + (copy_eh_region): New function. + (push_reachable_handler): New function. + (build_post_landing_pads, dw2_build_landing_pads): Be ready for + regions without label but with live RESX. + * except.h (redirect_eh_edge_to_label): New. + * tree-flow.h (redirect_eh_edge): New. + * coretypes.h (edge_def, edge, const_edge, basic_block_def + basic_block_def, basic_block, const_basic_block): Remove. + * Makefile.in (except.o): Add dependency on tree-flow.h + * tree-cfg.c (gimple_redirect_edge_and_branch): Handle EH edges. + * basic-block.h (edge, const_edge, basic_block, const_basic_block): + Remove. + 2009-04-25 Eric Botcazou PR bootstrap/39645 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 743cf104363..8b97805917c 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -2525,7 +2525,7 @@ except.o : except.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ langhooks.h insn-config.h hard-reg-set.h $(BASIC_BLOCK_H) output.h \ dwarf2asm.h dwarf2out.h $(TOPLEV_H) $(HASHTAB_H) intl.h $(GGC_H) \ gt-$(EXCEPT_H) $(CGRAPH_H) $(INTEGRATE_H) $(DIAGNOSTIC_H) dwarf2.h \ - $(TARGET_H) $(TM_P_H) $(TREE_PASS_H) $(TIMEVAR_H) + $(TARGET_H) $(TM_P_H) $(TREE_PASS_H) $(TIMEVAR_H) $(TREE_FLOW_H) expr.o : expr.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) $(FUNCTION_H) $(REGS_H) $(EXPR_H) $(OPTABS_H) \ libfuncs.h $(INSN_ATTR_H) insn-config.h $(RECOG_H) output.h \ diff --git a/gcc/basic-block.h b/gcc/basic-block.h index da262ac72b0..19ccd48edd6 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -147,8 +147,6 @@ struct GTY(()) edge_def { in profile.c */ }; -typedef struct edge_def *edge; -typedef const struct edge_def *const_edge; DEF_VEC_P(edge); DEF_VEC_ALLOC_P(edge,gc); DEF_VEC_ALLOC_P(edge,heap); @@ -277,9 +275,6 @@ struct GTY(()) gimple_bb_info { gimple_seq phi_nodes; }; -typedef struct basic_block_def *basic_block; -typedef const struct basic_block_def *const_basic_block; - DEF_VEC_P(basic_block); DEF_VEC_ALLOC_P(basic_block,gc); DEF_VEC_ALLOC_P(basic_block,heap); diff --git a/gcc/coretypes.h b/gcc/coretypes.h index a04953e40bf..11583fffe44 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -97,6 +97,12 @@ enum tls_model { TLS_MODEL_LOCAL_EXEC }; +struct edge_def; +typedef struct edge_def *edge; +typedef const struct edge_def *const_edge; +struct basic_block_def; +typedef struct basic_block_def *basic_block; +typedef const struct basic_block_def *const_basic_block; #else struct _dont_use_rtx_here_; diff --git a/gcc/except.c b/gcc/except.c index 599ad6fe0c6..5b8ed7c3743 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -77,6 +77,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "tree-pass.h" #include "timevar.h" +#include "tree-flow.h" /* Provide defaults for stuff that may not be defined when using sjlj exceptions. */ @@ -628,6 +629,238 @@ bring_to_root (struct eh_region *r) cfun->eh->region_tree = r; } +/* Return true if region R2 can be replaced by R1. */ + +static bool +eh_region_replaceable_by_p (const struct eh_region *r1, + const struct eh_region *r2) +{ + /* Regions are semantically same if they are of same type, + have same label and type. */ + if (r1->type != r2->type) + return false; + if (r1->tree_label != r2->tree_label) + return false; + + /* Verify that also region type dependent data are the same. */ + switch (r1->type) + { + case ERT_MUST_NOT_THROW: + case ERT_CLEANUP: + break; + case ERT_TRY: + { + struct eh_region *c1, *c2; + for (c1 = r1->u.eh_try.eh_catch, + c2 = r2->u.eh_try.eh_catch; + c1 && c2; + c1 = c1->u.eh_catch.next_catch, + c2 = c2->u.eh_catch.next_catch) + if (!eh_region_replaceable_by_p (c1, c2)) + return false; + if (c1 || c2) + return false; + } + break; + case ERT_CATCH: + if (!list_equal_p (r1->u.eh_catch.type_list, r2->u.eh_catch.type_list)) + return false; + if (!list_equal_p (r1->u.eh_catch.filter_list, + r2->u.eh_catch.filter_list)) + return false; + break; + case ERT_ALLOWED_EXCEPTIONS: + if (!list_equal_p (r1->u.allowed.type_list, r2->u.allowed.type_list)) + return false; + if (r1->u.allowed.filter != r2->u.allowed.filter) + return false; + break; + case ERT_THROW: + if (r1->u.eh_throw.type != r2->u.eh_throw.type) + return false; + break; + default: + gcc_unreachable (); + } + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Regions %i and %i match\n", r1->region_number, + r2->region_number); + return true; +} + +/* Replace region R2 by R1. */ + +static void +replace_region (struct eh_region *r1, struct eh_region *r2) +{ + struct eh_region *next1 = r1->u.eh_try.eh_catch; + struct eh_region *next2 = r2->u.eh_try.eh_catch; + bool is_try = r1->type == ERT_TRY; + + gcc_assert (r1->type != ERT_CATCH); + remove_eh_handler_and_replace (r2, r1, false); + if (is_try) + { + while (next1) + { + r1 = next1; + r2 = next2; + gcc_assert (next1->type == ERT_CATCH); + gcc_assert (next2->type == ERT_CATCH); + next1 = next1->u.eh_catch.next_catch; + next2 = next2->u.eh_catch.next_catch; + remove_eh_handler_and_replace (r2, r1, false); + } + } +} + +/* Return hash value of type list T. */ + +static hashval_t +hash_type_list (tree t) +{ + hashval_t val = 0; + for (; t; t = TREE_CHAIN (t)) + val = iterative_hash_hashval_t (TREE_HASH (TREE_VALUE (t)), val); + return val; +} + +/* Hash EH regions so semantically same regions get same hash value. */ + +static hashval_t +hash_eh_region (const void *r) +{ + const struct eh_region *region = (const struct eh_region *)r; + hashval_t val = region->type; + + if (region->tree_label) + val = iterative_hash_hashval_t (LABEL_DECL_UID (region->tree_label), val); + switch (region->type) + { + case ERT_MUST_NOT_THROW: + case ERT_CLEANUP: + break; + case ERT_TRY: + { + struct eh_region *c; + for (c = region->u.eh_try.eh_catch; + c; c = c->u.eh_catch.next_catch) + val = iterative_hash_hashval_t (hash_eh_region (c), val); + } + break; + case ERT_CATCH: + val = iterative_hash_hashval_t (hash_type_list + (region->u.eh_catch.type_list), val); + break; + case ERT_ALLOWED_EXCEPTIONS: + val = iterative_hash_hashval_t + (hash_type_list (region->u.allowed.type_list), val); + val = iterative_hash_hashval_t (region->u.allowed.filter, val); + break; + case ERT_THROW: + val |= iterative_hash_hashval_t (TYPE_UID (region->u.eh_throw.type), val); + break; + default: + gcc_unreachable (); + } + return val; +} + +/* Return true if regions R1 and R2 are equal. */ + +static int +eh_regions_equal_p (const void *r1, const void *r2) +{ + return eh_region_replaceable_by_p ((const struct eh_region *)r1, + (const struct eh_region *)r2); +} + +/* Walk all peers of REGION and try to merge those regions + that are semantically equivalent. Look into subregions + recursively too. */ + +static bool +merge_peers (struct eh_region *region) +{ + struct eh_region *r1, *r2, *outer = NULL, *next; + bool merged = false; + int num_regions = 0; + if (region) + outer = region->outer; + else + return false; + + /* First see if there is inner region equivalent to region + in question. EH control flow is acyclic so we know we + can merge them. */ + if (outer) + for (r1 = region; r1; r1 = next) + { + next = r1->next_peer; + if (r1->type == ERT_CATCH) + continue; + if (eh_region_replaceable_by_p (r1->outer, r1)) + { + replace_region (r1->outer, r1); + merged = true; + } + else + num_regions ++; + } + + /* Get new first region and try to match the peers + for equivalence. */ + if (outer) + region = outer->inner; + else + region = cfun->eh->region_tree; + + /* There are few regions to inspect: + N^2 loop matching each region with each region + will do the job well. */ + if (num_regions < 10) + { + for (r1 = region; r1; r1 = r1->next_peer) + { + if (r1->type == ERT_CATCH) + continue; + for (r2 = r1->next_peer; r2; r2 = next) + { + next = r2->next_peer; + if (eh_region_replaceable_by_p (r1, r2)) + { + replace_region (r1, r2); + merged = true; + } + } + } + } + /* Or use hashtable to avoid N^2 behaviour. */ + else + { + htab_t hash; + hash = htab_create (num_regions, hash_eh_region, + eh_regions_equal_p, NULL); + for (r1 = region; r1; r1 = next) + { + void **slot; + + next = r1->next_peer; + if (r1->type == ERT_CATCH) + continue; + slot = htab_find_slot (hash, r1, INSERT); + if (!*slot) + *slot = r1; + else + replace_region ((struct eh_region *)*slot, r1); + } + htab_delete (hash); + } + for (r1 = region; r1; r1 = r1->next_peer) + merged |= merge_peers (r1->inner); + return merged; +} + /* 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). */ @@ -748,6 +981,7 @@ remove_unreachable_regions (sbitmap reachable, sbitmap contains_stmt) else bring_to_root (r); } + merge_peers (cfun->eh->region_tree); #ifdef ENABLE_CHECKING verify_eh_tree (cfun); #endif @@ -1140,6 +1374,238 @@ duplicate_eh_regions (struct function *ifun, duplicate_eh_regions_map map, return eh_offset; } +/* Return new copy of eh region OLD inside region NEW_OUTER. + Do not care about updating the tree otherwise. */ + +static struct eh_region * +copy_eh_region_1 (struct eh_region *old, struct eh_region *new_outer) +{ + struct eh_region *new_eh = gen_eh_region (old->type, new_outer); + new_eh->u = old->u; + new_eh->tree_label = old->tree_label; + new_eh->may_contain_throw = old->may_contain_throw; + VEC_safe_grow (eh_region, gc, cfun->eh->region_array, + cfun->eh->last_region_number + 1); + VEC_replace (eh_region, cfun->eh->region_array, new_eh->region_number, new_eh); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Copying region %i to %i\n", old->region_number, new_eh->region_number); + return new_eh; +} + +/* Return new copy of eh region OLD inside region NEW_OUTER. + + Copy whole catch-try chain if neccesary and update cleanup region prev_try + pointers. + + PREV_TRY_MAP points to outer TRY region if it was copied in trace already. */ + +static struct eh_region * +copy_eh_region (struct eh_region *old, struct eh_region *new_outer, + struct eh_region *prev_try_map) +{ + struct eh_region *r, *n, *old_try, *new_try, *ret = NULL; + VEC(eh_region,heap) *catch_list = NULL; + + if (old->type != ERT_CATCH) + { + gcc_assert (old->type != ERT_TRY); + r = copy_eh_region_1 (old, new_outer); + if (r->type == ERT_CLEANUP && prev_try_map) + { + gcc_assert (r->u.cleanup.prev_try); + r->u.cleanup.prev_try = prev_try_map; + } + return r; + } + + /* Locate and copy corresponding TRY. */ + for (old_try = old->next_peer; old_try->type == ERT_CATCH; old_try = old_try->next_peer) + continue; + gcc_assert (old_try->type == ERT_TRY); + new_try = gen_eh_region_try (new_outer); + new_try->tree_label = old_try->tree_label; + new_try->may_contain_throw = old_try->may_contain_throw; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Copying try-catch regions. Try: %i to %i\n", + old_try->region_number, new_try->region_number); + VEC_safe_grow (eh_region, gc, cfun->eh->region_array, + cfun->eh->last_region_number + 1); + VEC_replace (eh_region, cfun->eh->region_array, new_try->region_number, new_try); + + /* In order to keep CATCH list in order, we need to copy in reverse order. */ + for (r = old_try->u.eh_try.last_catch; r->type == ERT_CATCH; r = r->next_peer) + VEC_safe_push (eh_region, heap, catch_list, r); + + while (VEC_length (eh_region, catch_list)) + { + r = VEC_pop (eh_region, catch_list); + + /* Duplicate CATCH. */ + n = gen_eh_region_catch (new_try, r->u.eh_catch.type_list); + n->tree_label = r->tree_label; + n->may_contain_throw = r->may_contain_throw; + VEC_safe_grow (eh_region, gc, cfun->eh->region_array, + cfun->eh->last_region_number + 1); + VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n); + n->tree_label = r->tree_label; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Copying try-catch regions. Catch: %i to %i\n", + r->region_number, n->region_number); + if (r == old) + ret = n; + } + VEC_free (eh_region, heap, catch_list); + gcc_assert (ret); + return ret; +} + +/* Callback for forach_reachable_handler that push REGION into single VECtor DATA. */ + +static void +push_reachable_handler (struct eh_region *region, void *data) +{ + VEC(eh_region,heap) **trace = (VEC(eh_region,heap) **) data; + VEC_safe_push (eh_region, heap, *trace, region); +} + +/* Redirect EH edge E that to NEW_DEST_LABEL. + IS_RESX, INLINABLE_CALL and REGION_NMUBER match the parameter of + foreach_reachable_handler. */ + +struct eh_region * +redirect_eh_edge_to_label (edge e, tree new_dest_label, bool is_resx, + bool inlinable_call, int region_number) +{ + struct eh_region *outer, *prev_try_map = NULL; + struct eh_region *region; + VEC (eh_region, heap) * trace = NULL; + int i; + int start_here = -1; + basic_block old_bb = e->dest; + struct eh_region *old, *r = NULL; + bool update_inplace = true; + edge_iterator ei; + edge e2; + + /* If there is only one EH edge, we don't need to duplicate; + just update labels in the tree. */ + FOR_EACH_EDGE (e2, ei, old_bb->preds) + if ((e2->flags & EDGE_EH) && e2 != e) + { + update_inplace = false; + break; + } + + region = VEC_index (eh_region, cfun->eh->region_array, region_number); + gcc_assert (region); + + foreach_reachable_handler (region_number, is_resx, inlinable_call, + push_reachable_handler, &trace); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + dump_eh_tree (dump_file, cfun); + fprintf (dump_file, "Trace: "); + for (i = 0; i < (int) VEC_length (eh_region, trace); i++) + fprintf (dump_file, " %i", VEC_index (eh_region, trace, i)->region_number); + fprintf (dump_file, " inplace: %i\n", update_inplace); + } + + if (update_inplace) + { + /* In easy route just walk trace and update all occurences of the label. */ + for (i = 0; i < (int) VEC_length (eh_region, trace); i++) + { + r = VEC_index (eh_region, trace, i); + if (r->tree_label && label_to_block (r->tree_label) == old_bb) + { + r->tree_label = new_dest_label; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Updating label for region %i\n", + r->region_number); + } + } + r = region; + } + else + { + /* Now look for outermost handler that reffers to the basic block in question. + We start our duplication there. */ + for (i = 0; i < (int) VEC_length (eh_region, trace); i++) + { + r = VEC_index (eh_region, trace, i); + if (r->tree_label && label_to_block (r->tree_label) == old_bb) + start_here = i; + } + outer = VEC_index (eh_region, trace, start_here)->outer; + gcc_assert (start_here >= 0); + + /* And now do the dirty job! */ + for (i = start_here; i >= 0; i--) + { + old = VEC_index (eh_region, trace, i); + gcc_assert (!outer || old->outer != outer->outer); + + /* Copy region and update label. */ + r = copy_eh_region (old, outer, prev_try_map); + VEC_replace (eh_region, trace, i, r); + if (r->tree_label && label_to_block (r->tree_label) == old_bb) + { + r->tree_label = new_dest_label; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Updating label for region %i\n", + r->region_number); + } + + /* We got into copying CATCH. copy_eh_region already did job + of copying all catch blocks corresponding to the try. Now + we need to update labels in all of them and see trace. + + We continue nesting into TRY region corresponding to CATCH: + When duplicating EH tree contaiing subregions of CATCH, + the CATCH region itself is never inserted to trace so we + never get here anyway. */ + if (r->type == ERT_CATCH) + { + /* Walk other catch regions we copied and update labels as needed. */ + for (r = r->next_peer; r->type == ERT_CATCH; r = r->next_peer) + if (r->tree_label && label_to_block (r->tree_label) == old_bb) + { + r->tree_label = new_dest_label; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Updating label for region %i\n", + r->region_number); + } + gcc_assert (r->type == ERT_TRY); + + /* Skip sibling catch regions from the trace. + They are already updated. */ + while (i > 0 && VEC_index (eh_region, trace, i - 1)->outer == old->outer) + { + gcc_assert (VEC_index (eh_region, trace, i - 1)->type == ERT_CATCH); + i--; + } + } + + /* Cleanup regions points to outer TRY blocks. */ + if (r->type == ERT_TRY) + prev_try_map = r; + outer = r; + } + + if (is_resx || region->type == ERT_THROW) + r = copy_eh_region (region, outer, prev_try_map); + } + + VEC_free (eh_region, heap, trace); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + dump_eh_tree (dump_file, cfun); + fprintf (dump_file, "New region: %i\n", r->region_number); + } + return r; +} + /* Return region number of region that is outer to both if REGION_A and REGION_B in IFUN. */ @@ -1478,6 +1944,12 @@ build_post_landing_pads (void) switch (region->type) { case ERT_TRY: + /* It is possible that TRY region is kept alive only because some of + contained catch region still have RESX instruction but they are + reached via their copies. In this case we need to do nothing. */ + if (!region->u.eh_try.eh_catch->label) + break; + /* ??? Collect the set of all non-overlapping catch handlers all the way up the chain until blocked by a cleanup. */ /* ??? Outer try regions can share landing pads with inner @@ -1537,6 +2009,8 @@ build_post_landing_pads (void) break; case ERT_ALLOWED_EXCEPTIONS: + if (!region->label) + break; region->post_landing_pad = gen_label_rtx (); start_sequence (); @@ -1679,6 +2153,9 @@ dw2_build_landing_pads (void) && region->type != ERT_ALLOWED_EXCEPTIONS) continue; + if (!region->post_landing_pad) + continue; + start_sequence (); region->landing_pad = gen_label_rtx (); diff --git a/gcc/except.h b/gcc/except.h index 14a263a5e88..50d6b314efd 100644 --- a/gcc/except.h +++ b/gcc/except.h @@ -278,4 +278,5 @@ extern void set_eh_throw_stmt_table (struct function *, struct htab *); extern void remove_unreachable_regions (sbitmap, sbitmap); extern VEC(int,heap) * label_to_region_map (void); extern int num_eh_regions (void); +extern struct eh_region *redirect_eh_edge_to_label (struct edge_def *, tree, bool, bool, int); extern int get_next_region_sharing_label (int); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index af9f6bc632f..c05006b8442 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2009-04-25 Jan Hubicka + + * g++.dg/tree-ssa/ehcleanup-1.C: Update. + 2009-04-25 Janus Weil PR fortran/39688 diff --git a/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C b/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C index f4c81d35282..3de72aac590 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C +++ b/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C @@ -1,4 +1,4 @@ -// { dg-options "-O2 -fdump-tree-ehcleanup1" } +// { dg-options "-O2 -fdump-tree-ehcleanup1-details" } extern void can_throw (); class a { diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index d514f450a85..329932dd314 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -4800,6 +4800,9 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) if (e->dest == dest) return NULL; + if (e->flags & EDGE_EH) + return redirect_eh_edge (e, dest); + gsi = gsi_last_bb (bb); stmt = gsi_end_p (gsi) ? NULL : gsi_stmt (gsi); diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index c9268f9853d..80014e11292 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -1962,7 +1962,7 @@ make_eh_edge (struct eh_region *region, void *data) src = gimple_bb (stmt); dst = label_to_block (lab); - make_edge (src, dst, EDGE_ABNORMAL | EDGE_EH); + make_edge (src, dst, EDGE_EH); } /* See if STMT is call that might be inlined. */ @@ -2019,6 +2019,50 @@ make_eh_edges (gimple stmt) EDGE_SUCC (bb, 0)->probability = REG_BR_PROB_BASE; } +/* Redirect EH edge E to NEW_BB. */ + +edge +redirect_eh_edge (edge e, basic_block new_bb) +{ + gimple stmt = gsi_stmt (gsi_last_bb (e->src)); + int region_nr, new_region_nr; + bool is_resx; + bool inlinable = false; + tree label = gimple_block_label (new_bb); + struct eh_region *r; + + if (gimple_code (stmt) == GIMPLE_RESX) + { + region_nr = gimple_resx_region (stmt); + is_resx = true; + } + else + { + region_nr = lookup_stmt_eh_region (stmt); + gcc_assert (region_nr >= 0); + is_resx = false; + inlinable = inlinable_call_p (stmt); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Redirecting EH edge %i->%i to %i, region %i, resx %i\n", + e->src->index, e->dest->index, new_bb->index, region_nr, is_resx); + r = redirect_eh_edge_to_label (e, label, is_resx, inlinable, region_nr); + new_region_nr = get_eh_region_number (r); + if (new_region_nr != region_nr) + { + if (is_resx) + gimple_resx_set_region (stmt, new_region_nr); + else + { + remove_stmt_from_eh_region (stmt); + add_stmt_to_eh_region (stmt, new_region_nr); + } + } + e = ssa_redirect_edge (e, new_bb); + return e; +} + static bool mark_eh_edge_found_error; /* Mark edge make_eh_edge would create for given region by setting it aux @@ -2702,7 +2746,9 @@ tree_remove_unreachable_handlers (void) SET_BIT (reachable, region); } if (gimple_code (stmt) == GIMPLE_RESX) - SET_BIT (reachable, gimple_resx_region (stmt)); + SET_BIT (reachable, + VEC_index (eh_region, cfun->eh->region_array, + gimple_resx_region (stmt))->region_number); if ((region = lookup_stmt_eh_region (stmt)) >= 0) SET_BIT (contains_stmt, region); } @@ -2937,7 +2983,7 @@ make_eh_edge_and_update_phi (struct eh_region *region, void *data) } dominance_info_invalidated = true; e2 = find_edge (info->bb_to_remove, dst); - e = make_edge (src, dst, EDGE_ABNORMAL | EDGE_EH); + e = make_edge (src, dst, EDGE_EH); e->aux = e; gcc_assert (e2); for (si = gsi_start_phis (dst); !gsi_end_p (si); gsi_next (&si)) @@ -3091,7 +3137,11 @@ cleanup_empty_eh (basic_block bb, VEC(int,heap) * label_to_region) is really dead. */ if (found && !has_non_eh_preds) - remove_eh_region (region); + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Empty EH handler %i removed.\n", region); + remove_eh_region (region); + } else if (!removed_some) return false; diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index b59774f29ce..8cb69acfa56 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -32,13 +32,6 @@ along with GCC; see the file COPYING3. If not see #include "ipa-reference.h" #include "tree-ssa-alias.h" -/* Forward declare structures for the garbage collector GTY markers. */ -#ifndef GCC_BASIC_BLOCK_H -struct edge_def; -typedef struct edge_def *edge; -struct basic_block_def; -typedef struct basic_block_def *basic_block; -#endif struct static_var_ann_d; @@ -974,5 +967,6 @@ unsigned int execute_fixup_cfg (void); void swap_tree_operands (gimple, tree *, tree *); int least_common_multiple (int, int); +edge redirect_eh_edge (edge e, basic_block new_bb); #endif /* _TREE_FLOW_H */ diff --git a/gcc/tree.c b/gcc/tree.c index 0b023774b43..57e1d3210a8 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -9320,4 +9320,16 @@ block_ultimate_origin (const_tree block) } } +/* Return true if T1 and T2 are equivalent lists. */ + +bool +list_equal_p (const_tree t1, const_tree t2) +{ + for (; t1 && t2; t1 = TREE_CHAIN (t1) , t2 = TREE_CHAIN (t2)) + if (TREE_VALUE (t1) != TREE_VALUE (t2)) + return false; + return !t1 && !t2; +} + + #include "gt-tree.h" diff --git a/gcc/tree.h b/gcc/tree.h index ef1b42031a3..4cd9b7d7fc1 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5134,6 +5134,7 @@ struct GTY(()) tree_map_base { extern int tree_map_base_eq (const void *, const void *); extern unsigned int tree_map_base_hash (const void *); extern int tree_map_base_marked_p (const void *); +extern bool list_equal_p (const_tree, const_tree); /* Map from a tree to another tree. */ -- 2.11.0