This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

pretty-ipa merge 14: EH region label sharing code


Hi,
this is last EH redirection preparation bit.  It makes code happy about case
where two regions share same label. This happens by EH redirection but also by
cfgcleanup and such.

While this is generally easy to maintain linked list of regions sharing
label, tricky part is eh cleanup where we now can have empty cleanup reached by
multiple EH handlers and none of them must be same as the one resuming it.
This is hanled by removing all the reaching EH handlers and replacing them by
outer handler of the resuming one.

It is also possible that other control flow reaches the empty handler and in
this case BB is no longer removed and resuming EH is kept in place. This
happens sometimes when EH region proves to be empty over some paths only
after edge splitting.

I also bundled in remove_eh_handler_and_replace change that allows removing
try region without associated catch and also to update prev_try pointers.  This
is really needed for EH merging, but it is bit difficult to split it out of
the patch.

Bootstrapped/regtested x86_64-linux, OK?
	* tree-eh.c (tree_remove_unreachable_handlers): Handle shared labels.
	(tree_empty_eh_handler_p): Allow non-EH predecestors; allow region
	to be reached by different label than left.
	(update_eh_edges): Update comment; remove edge_to_remove if possible
	and return true if suceeded.
	(cleanup_empty_eh): Accept sharing map; handle shared regions.
	(cleanup_eh): Compute sharing map.
	* except.c (remove_eh_handler_and_replace): Add argument if we should
	update regions.
	(remove_unreachable_regions): Update for label sharing.
	(label_to_region_map): Likewise.
	(get_next_region_sharing_label): New function.
	(remove_eh_handler_and_replace): Add update_catch_try parameter; update
	prev_try pointers.
	(remove_eh_handler): Update.
	(remove_eh_region_and_replace_by_outer_of): New function.
	* except.h (struct eh_region): Add next_region_sharing_label.
	(remove_eh_region_and_replace_by_outer_of,
	get_next_region_sharing_label): Declare.
	* tree-cfgcleanup.c (tree_forwarder_block_p): Simplify.
*** /aux/hubicka/trunk-write/gcc/tree-eh.c	Wed Apr  8 17:08:50 2009
--- tree-eh.c	Fri Apr 24 13:56:55 2009
*************** tree_remove_unreachable_handlers (void)
*** 2695,2702 ****
  	if (gimple_code (stmt) == GIMPLE_LABEL && has_eh_preds)
  	  {
  	    int uid = LABEL_DECL_UID (gimple_label_label (stmt));
! 	    int region = VEC_index (int, label_to_region, uid);
! 	    SET_BIT (reachable, region);
  	  }
  	if (gimple_code (stmt) == GIMPLE_RESX)
  	  SET_BIT (reachable, gimple_resx_region (stmt));
--- 2738,2748 ----
  	if (gimple_code (stmt) == GIMPLE_LABEL && has_eh_preds)
  	  {
  	    int uid = LABEL_DECL_UID (gimple_label_label (stmt));
! 	    int region;
! 
! 	    for (region = VEC_index (int, label_to_region, uid);
! 		 region; region = get_next_region_sharing_label (region))
! 	      SET_BIT (reachable, region);
  	  }
  	if (gimple_code (stmt) == GIMPLE_RESX)
  	  SET_BIT (reachable, gimple_resx_region (stmt));
*************** tree_empty_eh_handler_p (basic_block bb)
*** 2743,2750 ****
--- 2789,2799 ----
  {
    gimple_stmt_iterator gsi;
    int region;
+   edge_iterator ei;
+   edge e;
    use_operand_p imm_use;
    gimple use_stmt;
+   bool found = false;
  
    gsi = gsi_last_bb (bb);
  
*************** tree_empty_eh_handler_p (basic_block bb)
*** 2815,2829 ****
        if (gsi_end_p (gsi))
  	return 0;
      }
!   while (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
!     {
!       if (gimple_label_label (gsi_stmt (gsi))
!       	  == get_eh_region_no_tree_label (region))
!         return region;
!       gsi_prev (&gsi);
!       if (gsi_end_p (gsi))
! 	return 0;
!     }
    return 0;
  }
  
--- 2864,2880 ----
        if (gsi_end_p (gsi))
  	return 0;
      }
!   if (gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
!     return 0;
! 
!   /* Be sure that there is at least on EH region reaching the block directly.
!      After EH edge redirection, it is possible that block is reached by one handler
!      but resumed by different.  */
!   FOR_EACH_EDGE (e, ei, bb->preds)
!     if ((e->flags & EDGE_EH))
!       found = true;
!   if (found)
!     return region;
    return 0;
  }
  
*************** make_eh_edge_and_update_phi (struct eh_r
*** 2955,2963 ****
  
  /* Make EH edges corresponding to STMT while updating PHI nodes after removal
     empty cleanup BB_TO_REMOVE joined to BB containing STMT
!    by EDGE_TO_REMOVE.  */
  
! static void
  update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove)
  {
    int region_nr;
--- 3006,3017 ----
  
  /* Make EH edges corresponding to STMT while updating PHI nodes after removal
     empty cleanup BB_TO_REMOVE joined to BB containing STMT
!    by EDGE_TO_REMOVE.
  
!    Return if EDGE_TO_REMOVE was really removed.  It might stay reachable when
!    not all EH regions are cleaned up.  */
! 
! static bool
  update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove)
  {
    int region_nr;
*************** update_eh_edges (gimple stmt, basic_bloc
*** 2967,2972 ****
--- 3021,3027 ----
    edge_iterator ei;
    edge e;
    int probability_sum = 0;
+   bool removed = false;
  
    info.bb_to_remove = bb_to_remove;
    info.bb = gimple_bb (stmt);
*************** update_eh_edges (gimple stmt, basic_bloc
*** 2980,2987 ****
    else
      {
        region_nr = lookup_stmt_eh_region (stmt);
-       if (region_nr < 0)
- 	return;
        is_resx = false;
        inlinable = inlinable_call_p (stmt);
      }
--- 3035,3040 ----
*************** update_eh_edges (gimple stmt, basic_bloc
*** 2993,3001 ****
    /* And remove edges we didn't marked. */
    for (ei = ei_start (info.bb->succs); (e = ei_safe_edge (ei)); )
      {
!       if ((e->flags & EDGE_EH) && !e->aux && e != edge_to_remove)
  	{
  	  dominance_info_invalidated = true;
  	  remove_edge (e);
  	}
        else
--- 3046,3056 ----
    /* And remove edges we didn't marked. */
    for (ei = ei_start (info.bb->succs); (e = ei_safe_edge (ei)); )
      {
!       if ((e->flags & EDGE_EH) && !e->aux)
  	{
  	  dominance_info_invalidated = true;
+ 	  if (e == edge_to_remove)
+ 	    removed = true;
  	  remove_edge (e);
  	}
        else
*************** update_eh_edges (gimple stmt, basic_bloc
*** 3011,3026 ****
       we get fewer consistency errors in the dumps.  */
    if (is_resx && EDGE_COUNT (info.bb->succs) && !probability_sum)
      EDGE_SUCC (info.bb, 0)->probability = REG_BR_PROB_BASE;
  }
  
  /* Look for basic blocks containing empty exception handler and remove them.
     This is similar to jump forwarding, just across EH edges.  */
  
  static bool
! cleanup_empty_eh (basic_block bb)
  {
    int region;
    gimple_stmt_iterator si;
  
    /* When handler of EH region winds up to be empty, we can safely
       remove it.  This leads to inner EH regions to be redirected
--- 3066,3083 ----
       we get fewer consistency errors in the dumps.  */
    if (is_resx && EDGE_COUNT (info.bb->succs) && !probability_sum)
      EDGE_SUCC (info.bb, 0)->probability = REG_BR_PROB_BASE;
+   return removed;
  }
  
  /* Look for basic blocks containing empty exception handler and remove them.
     This is similar to jump forwarding, just across EH edges.  */
  
  static bool
! cleanup_empty_eh (basic_block bb, VEC(int,heap) * label_to_region)
  {
    int region;
    gimple_stmt_iterator si;
+   edge_iterator ei;
  
    /* When handler of EH region winds up to be empty, we can safely
       remove it.  This leads to inner EH regions to be redirected
*************** cleanup_empty_eh (basic_block bb)
*** 3030,3048 ****
        && all_phis_safe_to_merge (bb))
      {
        edge e;
  
!       remove_eh_region (region);
  
!       while ((e = ei_safe_edge (ei_start (bb->preds))))
  	{
  	  basic_block src = e->src;
! 	  gcc_assert (e->flags & EDGE_EH);
  	  if (stmt_can_throw_internal (last_stmt (src)))
! 	    update_eh_edges (last_stmt (src), bb, e);
! 	  remove_edge (e);
  	}
-       if (dump_file)
- 	fprintf (dump_file, "Empty EH handler %i removed\n", region);
  
        /* Verify that we eliminated all uses of PHI we are going to remove.
           If we didn't, rebuild SSA on affected variable (this is allowed only
--- 3087,3153 ----
        && all_phis_safe_to_merge (bb))
      {
        edge e;
+       bool found = false, removed_some = false, has_non_eh_preds = false;
+       gimple_stmt_iterator gsi;
  
!       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
!         if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
! 	  {
! 	    int uid = LABEL_DECL_UID (gimple_label_label (gsi_stmt (gsi)));
! 	    int r = VEC_index (int, label_to_region, uid);
! 	    int next;
  
! 	    while (r)
! 	      {
! 		next = get_next_region_sharing_label (r);
! 		if (r == region)
! 		  found = true;
! 		else
! 		  {
! 		     removed_some = true;
! 		     remove_eh_region_and_replace_by_outer_of (r, region);
! 		     if (dump_file)
! 		       fprintf (dump_file, "Empty EH handler %i removed and "
! 		       		"replaced by %i\n", r, region);
! 		  }
! 		r = next;
! 	      }
! 	  }
! 	else
! 	  break;
!       gcc_assert (found || removed_some);
!       FOR_EACH_EDGE (e, ei, bb->preds)
! 	if (!(e->flags & EDGE_EH))
! 	  has_non_eh_preds = true;
! 
!       /* When block is empty EH cleanup, but it is reachable via non-EH code too,
! 	 we can not remove the region it is resumed via, because doing so will
! 	 lead to redirection of its RESX edges.
! 
! 	 This case will be handled later after edge forwarding if the EH cleanup
! 	 is really dead.  */
! 
!       if (found && !has_non_eh_preds)
!         remove_eh_region (region);
!       else if (!removed_some)
!         return false;
! 
!       for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
  	{
  	  basic_block src = e->src;
! 	  if (!(e->flags & EDGE_EH))
! 	    {
! 	      ei_next (&ei);
! 	      continue;
! 	    }
  	  if (stmt_can_throw_internal (last_stmt (src)))
! 	    {
! 	      if (!update_eh_edges (last_stmt (src), bb, e))
! 	        ei_next (&ei);
! 	    }
! 	  else
! 	    remove_edge (e);
  	}
  
        /* Verify that we eliminated all uses of PHI we are going to remove.
           If we didn't, rebuild SSA on affected variable (this is allowed only
*************** cleanup_empty_eh (basic_block bb)
*** 3091,3097 ****
  		}
  	    }
  	}
!       delete_basic_block (bb);
        return true;
      }
    return false;
--- 3196,3203 ----
  		}
  	    }
  	}
!       if (!ei_safe_edge (ei_start (bb->preds)))
!         delete_basic_block (bb);
        return true;
      }
    return false;
*************** cleanup_eh (void)
*** 3111,3116 ****
--- 3217,3223 ----
  {
    bool changed = false;
    basic_block bb;
+   VEC(int,heap) * label_to_region;
    int i;
  
    if (!cfun->eh)
*************** cleanup_eh (void)
*** 3123,3136 ****
  
    if (optimize)
      {
        dominance_info_invalidated = false;
        /* We cannot use FOR_EACH_BB, since the basic blocks may get removed.  */
        for (i = NUM_FIXED_BLOCKS; i < last_basic_block; i++)
  	{
  	  bb = BASIC_BLOCK (i);
  	  if (bb)
! 	    changed |= cleanup_empty_eh (bb);
  	}
        if (dominance_info_invalidated)
  	{
  	  free_dominance_info (CDI_DOMINATORS);
--- 3230,3245 ----
  
    if (optimize)
      {
+       label_to_region = label_to_region_map ();
        dominance_info_invalidated = false;
        /* We cannot use FOR_EACH_BB, since the basic blocks may get removed.  */
        for (i = NUM_FIXED_BLOCKS; i < last_basic_block; i++)
  	{
  	  bb = BASIC_BLOCK (i);
  	  if (bb)
! 	    changed |= cleanup_empty_eh (bb, label_to_region);
  	}
+       VEC_free (int, heap, label_to_region);
        if (dominance_info_invalidated)
  	{
  	  free_dominance_info (CDI_DOMINATORS);
*** /aux/hubicka/trunk-write/gcc/except.c	Wed Apr 22 15:38:18 2009
--- except.c	Wed Apr 22 20:07:02 2009
*************** static void sjlj_build_landing_pads (voi
*** 146,152 ****
  
  static void remove_eh_handler (struct eh_region *);
  static void remove_eh_handler_and_replace (struct eh_region *,
! 					   struct eh_region *);
  
  /* The return value of reachable_next_level.  */
  enum reachable_code
--- 147,153 ----
  
  static void remove_eh_handler (struct eh_region *);
  static void remove_eh_handler_and_replace (struct eh_region *,
! 					   struct eh_region *, bool);
  
  /* The return value of reachable_next_level.  */
  enum reachable_code
*************** remove_unreachable_regions (sbitmap reac
*** 743,754 ****
  	    fprintf (dump_file, "Replacing MUST_NOT_THROW region %i by %i\n",
  		     r->region_number,
  		     first_must_not_throw->region_number);
! 	  remove_eh_handler_and_replace (r, first_must_not_throw);
  	  first_must_not_throw->may_contain_throw |= r->may_contain_throw;
  	}
        else
  	bring_to_root (r);
      }
  #ifdef ENABLE_CHECKING
    verify_eh_tree (cfun);
  #endif
--- 979,990 ----
  	    fprintf (dump_file, "Replacing MUST_NOT_THROW region %i by %i\n",
  		     r->region_number,
  		     first_must_not_throw->region_number);
! 	  remove_eh_handler_and_replace (r, first_must_not_throw, false);
  	  first_must_not_throw->may_contain_throw |= r->may_contain_throw;
  	}
        else
  	bring_to_root (r);
      }
  #ifdef ENABLE_CHECKING
    verify_eh_tree (cfun);
  #endif
*************** remove_unreachable_regions (sbitmap reac
*** 758,768 ****
  /* 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);
--- 995,1006 ----
  /* 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;
+   int idx;
  
    VEC_safe_grow_cleared (int, heap, label_to_region,
  			 cfun->cfg->last_label_uid + 1);
*************** label_to_region_map (void)
*** 770,777 ****
      {
        struct eh_region *r = VEC_index (eh_region, cfun->eh->region_array, i);
        if (r && r->region_number == i
!       	  && r->tree_label && LABEL_DECL_UID (r->tree_label) >= 0)
  	{
  	  VEC_replace (int, label_to_region, LABEL_DECL_UID (r->tree_label),
  		       i);
  	}
--- 1008,1021 ----
      {
        struct eh_region *r = VEC_index (eh_region, cfun->eh->region_array, i);
        if (r && r->region_number == i
! 	  && r->tree_label && LABEL_DECL_UID (r->tree_label) >= 0)
  	{
+ 	  if ((idx = VEC_index (int, label_to_region,
+ 				LABEL_DECL_UID (r->tree_label))) != 0)
+ 	      r->next_region_sharing_label =
+ 	      VEC_index (eh_region, cfun->eh->region_array, idx);
+ 	  else
+ 	    r->next_region_sharing_label = NULL;
  	  VEC_replace (int, label_to_region, LABEL_DECL_UID (r->tree_label),
  		       i);
  	}
*************** num_eh_regions (void)
*** 786,791 ****
--- 1030,1049 ----
    return cfun->eh->last_region_number + 1;
  }
  
+ /* Return next region sharing same label as REGION.  */
+ 
+ int
+ get_next_region_sharing_label (int region)
+ {
+   struct eh_region *r;
+   if (!region)
+     return 0;
+   r = VEC_index (eh_region, cfun->eh->region_array, region);
+   if (!r || !r->next_region_sharing_label)
+     return 0;
+   return r->next_region_sharing_label->region_number;
+ }
+ 
  /* Set up EH labels for RTL.  */
  
  void
*************** finish_eh_generation (void)
*** 2163,2178 ****
  
  /* This section handles removing dead code for flow.  */
  
! /* Splice REGION from the region tree and replace it by REPLACE etc.  */
  
  static void
  remove_eh_handler_and_replace (struct eh_region *region,
! 			       struct eh_region *replace)
  {
    struct eh_region **pp, **pp_start, *p, *outer, *inner;
    rtx lab;
  
    outer = region->outer;
    /* For the benefit of efficiently handling REG_EH_REGION notes,
       replace this region in the region array with its containing
       region.  Note that previous region deletions may result in
--- 2718,2763 ----
  
  /* This section handles removing dead code for flow.  */
  
! /* Splice REGION from the region tree and replace it by REPLACE etc.
!    When UPDATE_CATCH_TRY is true mind updating links from catch to try
!    region.*/
  
  static void
  remove_eh_handler_and_replace (struct eh_region *region,
! 			       struct eh_region *replace,
! 			       bool update_catch_try)
  {
    struct eh_region **pp, **pp_start, *p, *outer, *inner;
    rtx lab;
  
    outer = region->outer;
+ 
+   /* When we are moving the region in EH tree, update prev_try pointers.  */
+   if (outer != replace && region->inner)
+     {
+       struct eh_region *prev_try = find_prev_try (replace);
+       p = region->inner;
+       while (p != region)
+         {
+ 	  if (p->type == ERT_CLEANUP)
+ 	    p->u.cleanup.prev_try = prev_try;
+           if (p->type != ERT_TRY
+ 	      && p->type != ERT_MUST_NOT_THROW
+ 	      && (p->type != ERT_ALLOWED_EXCEPTIONS
+ 	          || p->u.allowed.type_list)
+ 	      && p->inner)
+ 	    p = p->inner;
+ 	  else if (p->next_peer)
+ 	    p = p->next_peer;
+ 	  else
+ 	    {
+ 	      while (p != region && !p->next_peer)
+ 	        p = p->outer;
+ 	      if (p != region)
+ 		p = p->next_peer;
+ 	    }
+ 	}
+     }
    /* For the benefit of efficiently handling REG_EH_REGION notes,
       replace this region in the region array with its containing
       region.  Note that previous region deletions may result in
*************** remove_eh_handler_and_replace (struct eh
*** 2228,2234 ****
        *pp_start = inner;
      }
  
!   if (region->type == ERT_CATCH)
      {
        struct eh_region *eh_try, *next, *prev;
  
--- 2813,2820 ----
        *pp_start = inner;
      }
  
!   if (region->type == ERT_CATCH
!       && update_catch_try)
      {
        struct eh_region *eh_try, *next, *prev;
  
*************** remove_eh_handler_and_replace (struct eh
*** 2262,2268 ****
  static void
  remove_eh_handler (struct eh_region *region)
  {
!   remove_eh_handler_and_replace (region, region->outer);
  }
  
  /* Remove Eh region R that has turned out to have no code in its handler.  */
--- 2848,2854 ----
  static void
  remove_eh_handler (struct eh_region *region)
  {
!   remove_eh_handler_and_replace (region, region->outer, true);
  }
  
  /* Remove Eh region R that has turned out to have no code in its handler.  */
*************** remove_eh_region (int r)
*** 2276,2281 ****
--- 2862,2880 ----
    remove_eh_handler (region);
  }
  
+ /* Remove Eh region R that has turned out to have no code in its handler
+    and replace in by R2.  */
+ 
+ void
+ remove_eh_region_and_replace_by_outer_of (int r, int r2)
+ {
+   struct eh_region *region, *region2;
+ 
+   region = VEC_index (eh_region, cfun->eh->region_array, r);
+   region2 = VEC_index (eh_region, cfun->eh->region_array, r2);
+   remove_eh_handler_and_replace (region, region2->outer, true);
+ }
+ 
  /* Invokes CALLBACK for every exception handler label.  Only used by old
     loop hackery; should not be used by new code.  */
  
*** /aux/hubicka/trunk-write/gcc/except.h	Wed Apr 22 15:38:18 2009
--- except.h	Sun Apr 19 19:53:37 2009
*************** struct eh_region GTY(())
*** 34,39 ****
--- 34,42 ----
    struct eh_region *inner;
    struct eh_region *next_peer;
  
+   /* List of regions sharing label.  */
+   struct eh_region *next_region_sharing_label;
+ 
    /* An identifier for this region.  */
    int region_number;
  
*************** extern bool can_throw_external (const_rt
*** 151,161 ****
--- 154,164 ----
  /* Set TREE_NOTHROW and cfun->all_throwers_are_sibcalls.  */
  extern unsigned int set_nothrow_function_flags (void);
  
  extern void init_eh (void);
  extern void init_eh_for_function (void);
  
  extern rtx reachable_handlers (rtx);
  void remove_eh_region (int);
+ void remove_eh_region_and_replace_by_outer_of (int, int);
  
  extern void convert_from_eh_region_ranges (void);
  extern unsigned int convert_to_eh_region_ranges (void);
*************** struct throw_stmt_node GTY(())
*** 269,277 ****
--- 277,286 ----
    gimple stmt;
    int region_nr;
  };
  
  extern struct htab *get_eh_throw_stmt_table (struct function *);
  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 int get_next_region_sharing_label (int);
*** /aux/hubicka/trunk-write/gcc/tree-cfgcleanup.c	Mon Feb 23 13:04:19 2009
--- tree-cfgcleanup.c	Thu Apr  9 01:25:26 2009
*************** static bool
*** 221,229 ****
  tree_forwarder_block_p (basic_block bb, bool phi_wanted)
  {
    gimple_stmt_iterator gsi;
-   edge_iterator ei;
-   edge e, succ;
-   basic_block dest;
  
    /* BB must have a single outgoing edge.  */
    if (single_succ_p (bb) != 1
--- 221,226 ----
*************** tree_forwarder_block_p (basic_block bb, 
*** 274,296 ****
        if (dest->loop_father->header == dest)
  	return false;
      }
- 
-   /* If we have an EH edge leaving this block, make sure that the
-      destination of this block has only one predecessor.  This ensures
-      that we don't get into the situation where we try to remove two
-      forwarders that go to the same basic block but are handlers for
-      different EH regions.  */
-   succ = single_succ_edge (bb);
-   dest = succ->dest;
-   FOR_EACH_EDGE (e, ei, bb->preds)
-     {
-       if (e->flags & EDGE_EH)
-         {
- 	  if (!single_pred_p (dest))
- 	    return false;
- 	}
-     }
- 
    return true;
  }
  
--- 271,276 ----


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]