Patchwork [gccgo] Fixes for tree-eh.c

login
register
mail settings
Submitter Ian Taylor
Date June 14, 2010, 7:03 a.m.
Message ID <mcrtyp6drk1.fsf@dhcp-172-17-9-151.mtv.corp.google.com>
Download mbox | patch
Permalink /patch/55480/
State New
Headers show

Comments

Ian Taylor - June 14, 2010, 7:03 a.m.
This patch fixes a problem which arose when compiling some Go code
with -O0.  The code looked like this after ehcleanup2:

  # BLOCK 11
  # PRED: 2
<L29>:
  resx 2
  # SUCC: 12 (eh)

  # BLOCK 12
  # PRED: 11 (eh)
<L30>:
  finally_tmp.1007_15 = 1;
  goto <bb 5>;
  # SUCC: 5 (fallthru)

The resx pass turned it into this, via code with this comment:

      /* We can wind up with no source region when pass_cleanup_eh shows
	 that there are no entries into an eh region and deletes it, but
	 then the block that contains the resx isn't removed.  This can
	 happen without optimization when the switch statement created by
	 lower_try_finally_switch isn't simplified to remove the eh case.

	 Resolve this by expanding the resx node to an abort.  */

  # BLOCK 11
  # PRED: 2
<L29>:
  __builtin_trap ();
  # SUCC:

  # BLOCK 12
  # PRED:
<L30>:
  finally_tmp.1007_15 = 1;
  goto <bb 5>;
  # SUCC: 5 (fallthru)

Now block 12 exists but has no predecessors.  That causes the
dominator pass to crash later.  This patch avoids the problem by
removing block 12 in this case.

I haven't been able to find a C or C++ test case which replicates
this.  I think the problem is that I can't figure out how to generate
a try/finally clause in which the finally clause does not make any
function calls and hence can not throw an exception.

This patch also brings in the trunk fix for PR 43365.  I will try to
do a merge from trunk to gccgo branch soon.

This patch committed to gccgo branch.

Ian

Patch

Index: tree-eh.c
===================================================================
--- tree-eh.c	(revision 155628)
+++ tree-eh.c	(working copy)
@@ -578,6 +578,7 @@  replace_goto_queue (struct leh_tf_state 
   if (tf->goto_queue_active == 0)
     return;
   replace_goto_queue_stmt_list (tf->top_p_seq, tf);
+  replace_goto_queue_stmt_list (eh_seq, tf);
 }
 
 /* Add a new record to the goto queue contained in TF. NEW_STMT is the
@@ -658,7 +659,6 @@  record_in_goto_queue_label (struct leh_t
      labels. */
   new_stmt = stmt;
   record_in_goto_queue (tf, new_stmt, index, true);
-
 }
 
 /* For any GIMPLE_GOTO or GIMPLE_RETURN, decide whether it leaves a try_finally
@@ -1545,6 +1545,7 @@  lower_try_finally (struct leh_state *sta
   struct leh_tf_state this_tf;
   struct leh_state this_state;
   int ndests;
+  gimple_seq old_eh_seq;
 
   /* Process the try block.  */
 
@@ -1561,6 +1562,9 @@  lower_try_finally (struct leh_state *sta
   this_state.ehp_region = state->ehp_region;
   this_state.tf = &this_tf;
 
+  old_eh_seq = eh_seq;
+  eh_seq = NULL;
+
   lower_eh_constructs_1 (&this_state, gimple_try_eval(tp));
 
   /* Determine if the try block is escaped through the bottom.  */
@@ -1616,6 +1620,20 @@  lower_try_finally (struct leh_state *sta
   if (this_tf.goto_queue_map)
     pointer_map_destroy (this_tf.goto_queue_map);
 
+  /* If there was an old (aka outer) eh_seq, append the current eh_seq.
+     If there was no old eh_seq, then the append is trivially already done.  */
+  if (old_eh_seq)
+    {
+      if (eh_seq == NULL)
+	eh_seq = old_eh_seq;
+      else
+	{
+	  gimple_seq new_eh_seq = eh_seq;
+	  eh_seq = old_eh_seq;
+	  gimple_seq_add_seq(&eh_seq, new_eh_seq);
+	}
+    }
+
   return this_tf.top_p_seq;
 }
 
@@ -2844,7 +2862,8 @@  struct gimple_opt_pass pass_refactor_eh 
 /* At the end of gimple optimization, we can lower RESX.  */
 
 static bool
-lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
+lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map,
+	    bool *any_removed)
 {
   int lp_nr;
   eh_region src_r, dst_r;
@@ -2877,7 +2896,31 @@  lower_resx (basic_block bb, gimple stmt,
       gsi_insert_before (&gsi, x, GSI_SAME_STMT);
 
       while (EDGE_COUNT (bb->succs) > 0)
-	remove_edge (EDGE_SUCC (bb, 0));
+	{
+	  edge e = EDGE_SUCC (bb, 0);
+	  basic_block bbnext = e->dest;
+
+	  remove_edge (e);
+
+	  /* If we removed the last exception edge to the destination
+	     block, remove the landing pad.  */
+	  if (lp_nr >= 0)
+	    {
+	      edge_iterator ei;
+
+	      FOR_EACH_EDGE (e, ei, bbnext->preds)
+		if (e->flags & EDGE_EH)
+		  break;
+	      if (e == NULL)
+		{
+		  eh_landing_pad lp = get_eh_landing_pad_from_number (lp_nr);
+		  remove_eh_landing_pad (lp);
+		  if (EDGE_COUNT (bbnext->preds) == 0)
+		    *any_removed = true;
+		  ret = true;
+		}
+	    }
+	}
     }
   else if (dst_r)
     {
@@ -2998,6 +3041,8 @@  execute_lower_resx (void)
   struct pointer_map_t *mnt_map;
   bool dominance_invalidated = false;
   bool any_rewritten = false;
+  bool any_removed = false;
+  unsigned int todo;
 
   mnt_map = pointer_map_create ();
 
@@ -3006,7 +3051,8 @@  execute_lower_resx (void)
       gimple last = last_stmt (bb);
       if (last && is_gimple_resx (last))
 	{
-	  dominance_invalidated |= lower_resx (bb, last, mnt_map);
+	  dominance_invalidated |= lower_resx (bb, last, mnt_map,
+					       &any_removed);
 	  any_rewritten = true;
 	}
     }
@@ -3019,7 +3065,12 @@  execute_lower_resx (void)
       free_dominance_info (CDI_POST_DOMINATORS);
     }
 
-  return any_rewritten ? TODO_update_ssa_only_virtuals : 0;
+  todo = 0;
+  if (any_removed)
+    todo |= TODO_cleanup_cfg;
+  if (any_rewritten)
+    todo |= TODO_update_ssa_only_virtuals;
+  return todo;
 }
 
 static bool