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]

[tree-ssa] fix c++/12770


This could have been fixed several ways.  The simplest of which
would have been to extend block_may_fallthru to handle COND and
BIND expressions.  Another would be to make -O0 build the CFG
and clean it up.

I went this way, exchanging .lower and .eh dumps, because it also
cures a fixme that I'd left in the .lower pass.  We no longer
duplicate BIND_EXPRs before lowering, which, because of the way
tree duplication works, *should* mean that we get better debug
information for the duplicated blocks.  I havn't actually 
verified this; I meant to but forgot.

I also discovered a buglet in the process of now having to deal
with lowered COND_EXPRs for which we did not have a test case.

Bootstrapped and tested on alphaev67-linux.


r~


        PR c++/12770
        * gimple-low.c (lower_stmt_body): Take a tree, not a tree*.
        (lower_stmt): Handle EH nodes.
        (lower_bind_expr): Remove fixme.
        (block_may_fallthru): Move from tree-eh.c.  Handle COND_EXPR,
        BIND_EXPR, and TRY_FINALLY_EXPR.
        (lower_cond_expr): Use it.
        * tree-eh.c (collect_finally_tree): Ignore COND_EXPR and BIND_EXPR.
        (replace_goto_queue_cond_clause): New.
        (replace_goto_queue_1): Use it.  Split out statement_list handling.
        (replace_goto_queue_stmt_list): New.
        (-block_may_fallthru): Move to gimple-low.c.
        (lower_eh_constructs_1): Ignore BIND_EXPR.
        * tree-flow.h (block_may_fallthru): Declare.

        * tree-dump.c (dump_files): Exchange .eh and .lower passes.
        * tree-optimize.c (tree_rest_of_compilation): Likewise.
        * tree.h (enum tree_dump_index): Likewise.

Index: gimple-low.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/gimple-low.c,v
retrieving revision 1.1.4.12
diff -c -p -d -u -r1.1.4.12 gimple-low.c
--- gimple-low.c	15 Nov 2003 19:44:36 -0000	1.1.4.12
+++ gimple-low.c	16 Nov 2003 22:54:55 -0000
@@ -47,7 +47,7 @@ struct lower_data
   tree block;
 };
 
-static void lower_stmt_body (tree *, struct lower_data *);
+static void lower_stmt_body (tree, struct lower_data *);
 static void lower_stmt (tree_stmt_iterator *, struct lower_data *);
 static void lower_bind_expr (tree_stmt_iterator *, struct lower_data *);
 static void lower_cond_expr (tree_stmt_iterator *, struct lower_data *);
@@ -69,7 +69,7 @@ lower_function_body (tree *body)
 
   record_vars (BIND_EXPR_VARS (*body));
   *body = BIND_EXPR_BODY (*body);
-  lower_stmt_body (body, &data);
+  lower_stmt_body (*body, &data);
 
   if (data.block != DECL_INITIAL (current_function_decl))
     abort ();
@@ -84,15 +84,16 @@ lower_function_body (tree *body)
    do it explicitly.  DATA is passed through the recursion.  */
 
 static void
-lower_stmt_body (tree *expr, struct lower_data *data)
+lower_stmt_body (tree expr, struct lower_data *data)
 {
   tree_stmt_iterator tsi;
 
-  for (tsi = tsi_start (*expr); !tsi_end_p (tsi); )
+  for (tsi = tsi_start (expr); !tsi_end_p (tsi); )
     lower_stmt (&tsi, data);
 }
 
 /* Lowers statement TSI.  DATA is passed through the recursion.  */
+
 static void
 lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data)
 {
@@ -105,11 +106,23 @@ lower_stmt (tree_stmt_iterator *tsi, str
     {
     case BIND_EXPR:
       lower_bind_expr (tsi, data);
-      break;
-
-    case COMPOUND_EXPR:
-      abort ();
+      return;
+    case COND_EXPR:
+      lower_cond_expr (tsi, data);
+      return;
 
+    case TRY_FINALLY_EXPR:
+    case TRY_CATCH_EXPR:
+      lower_stmt_body (TREE_OPERAND (stmt, 0), data);
+      lower_stmt_body (TREE_OPERAND (stmt, 1), data);
+      break;
+    case CATCH_EXPR:
+      lower_stmt_body (CATCH_BODY (stmt), data);
+      break;
+    case EH_FILTER_EXPR:
+      lower_stmt_body (EH_FILTER_FAILURE (stmt), data);
+      break;
+      
     case NOP_EXPR:
     case ASM_EXPR:
     case RETURN_EXPR:
@@ -118,38 +131,16 @@ lower_stmt (tree_stmt_iterator *tsi, str
     case GOTO_EXPR:
     case LABEL_EXPR:
     case VA_ARG_EXPR:
-    case RESX_EXPR:
     case SWITCH_EXPR:
-      tsi_next (tsi);
-      break;
-
-    case COND_EXPR:
-      lower_cond_expr (tsi, data);
       break;
 
     default:
       print_node_brief (stderr, "", stmt, 0);
+    case COMPOUND_EXPR:
       abort ();
     }
-}
-
-/* Record the variables in VARS.  */
-
-void
-record_vars (tree vars)
-{
-  for (; vars; vars = TREE_CHAIN (vars))
-    {
-      tree var = vars;
 
-      /* Nothing to do in this case.  */
-      if (DECL_EXTERNAL (var))
-	continue;
-
-      /* Record the variable.  */
-      cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
-					     cfun->unexpanded_var_list);
-    }
+  tsi_next (tsi);
 }
 
 /* Lowers a bind_expr TSI.  DATA is passed through the recursion.  */
@@ -175,11 +166,6 @@ lower_bind_expr (tree_stmt_iterator *tsi
       else
 	{
 	  /* We do not expect to handle duplicate blocks.  */
-	  /* ??? This is probably wrong.  We've already done some amount
-	     of code replication in tree-eh.c; we should probably be doing
-	     something like reorder_blocks, which knows how to handle
-	     duplicates.  Either that or lower bind_exprs before this
-	     can matter.  */
 	  if (TREE_ASM_WRITTEN (new_block))
 	    abort ();
 	  TREE_ASM_WRITTEN (new_block) = 1;
@@ -197,7 +183,7 @@ lower_bind_expr (tree_stmt_iterator *tsi
     }
 
   record_vars (BIND_EXPR_VARS (stmt));
-  lower_stmt_body (&BIND_EXPR_BODY (stmt), data);
+  lower_stmt_body (BIND_EXPR_BODY (stmt), data);
 
   if (new_block)
     {
@@ -214,6 +200,53 @@ lower_bind_expr (tree_stmt_iterator *tsi
   tsi_delink (tsi);
 }
 
+/* Try to determine if we can fall out of the bottom of BLOCK.  This guess
+   need not be 100% accurate; simply be conservative and return true if we
+   don't know.  This is used only to avoid stupidly generating extra code.
+   If we're wrong, we'll just delete the extra code later.  */
+
+bool
+block_may_fallthru (tree block)
+{
+  tree stmt = expr_last (block);
+
+  switch (stmt ? TREE_CODE (stmt) : ERROR_MARK)
+    {
+    case GOTO_EXPR:
+    case RETURN_EXPR:
+    case RESX_EXPR:
+    case SWITCH_EXPR:
+      /* Easy cases.  If the last statement of the block implies 
+	 control transfer, then we can't fall through.  */
+      return false;
+
+    case COND_EXPR:
+      if (block_may_fallthru (COND_EXPR_THEN (stmt)))
+	return true;
+      return block_may_fallthru (COND_EXPR_ELSE (stmt));
+
+    case BIND_EXPR:
+      return block_may_fallthru (BIND_EXPR_BODY (stmt));
+
+    case TRY_FINALLY_EXPR:
+      return block_may_fallthru (TREE_OPERAND (stmt, 1));
+
+    case MODIFY_EXPR:
+      if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR)
+	stmt = TREE_OPERAND (stmt, 1);
+      else
+	return true;
+      /* FALLTHRU */
+
+    case CALL_EXPR:
+      /* Functions that do not return do not fall through.  */
+      return (call_expr_flags (stmt) & ECF_NORETURN) == 0;
+
+    default:
+      return true;
+    }
+}
+
 /* Lowers a cond_expr TSI.  DATA is passed through the recursion.  */
 
 static void
@@ -224,12 +257,12 @@ lower_cond_expr (tree_stmt_iterator *tsi
   tree then_branch, else_branch;
   tree then_goto, else_goto;
   
-  lower_stmt_body (&COND_EXPR_THEN (stmt), data);
-  lower_stmt_body (&COND_EXPR_ELSE (stmt), data);
-
   then_branch = COND_EXPR_THEN (stmt);
   else_branch = COND_EXPR_ELSE (stmt);
 
+  lower_stmt_body (then_branch, data);
+  lower_stmt_body (else_branch, data);
+
   then_goto = expr_only (then_branch);
   then_is_goto = then_goto && simple_goto_p (then_goto);
 
@@ -280,10 +313,12 @@ lower_cond_expr (tree_stmt_iterator *tsi
 
       if (then_label)
 	{
+	  bool may_fallthru = block_may_fallthru (then_branch);
+
 	  tsi_link_after (tsi, then_label, TSI_CONTINUE_LINKING);
 	  tsi_link_after (tsi, then_branch, TSI_CONTINUE_LINKING);
   
-	  if (else_label)
+	  if (else_label && may_fallthru)
 	    {
 	      end_label = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
 	      t = build_and_jump (&LABEL_EXPR_LABEL (end_label));
@@ -305,6 +340,26 @@ lower_cond_expr (tree_stmt_iterator *tsi
   COND_EXPR_ELSE (stmt) = else_goto;
 
   tsi_next (tsi);
+}
+
+
+/* Record the variables in VARS.  */
+
+void
+record_vars (tree vars)
+{
+  for (; vars; vars = TREE_CHAIN (vars))
+    {
+      tree var = vars;
+
+      /* Nothing to do in this case.  */
+      if (DECL_EXTERNAL (var))
+	continue;
+
+      /* Record the variable.  */
+      cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
+					     cfun->unexpanded_var_list);
+    }
 }
 
 /* Check whether to expand a variable VAR.  */
Index: tree-dump.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-dump.c,v
retrieving revision 1.6.2.49
diff -c -p -d -u -r1.6.2.49 tree-dump.c
--- tree-dump.c	11 Nov 2003 18:04:46 -0000	1.6.2.49
+++ tree-dump.c	16 Nov 2003 22:54:55 -0000
@@ -653,8 +653,8 @@ static struct dump_file_info dump_files[
   {".inlined", "tree-inlined", 0, 0},
   {".gimple", "tree-gimple", 0, 0},
   {".useless", "tree-useless", 0, 0},
-  {".eh", "tree-eh", 0, 0},
   {".lower", "tree-lower", 0, 0},
+  {".eh", "tree-eh", 0, 0},
   {".cfg", "tree-cfg", 0, 0},
   {".dot", "tree-dot", 0, 0},
   {".tail", "tree-tail", 0, 0},
Index: tree-eh.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-eh.c,v
retrieving revision 1.1.2.15
diff -c -p -d -u -r1.1.2.15 tree-eh.c
--- tree-eh.c	14 Nov 2003 23:29:07 -0000	1.1.2.15
+++ tree-eh.c	16 Nov 2003 22:54:55 -0000
@@ -160,15 +160,6 @@ collect_finally_tree (tree t, tree regio
       t = TREE_OPERAND (t, 1);
       goto tailrecurse;
 
-    case COND_EXPR:
-      collect_finally_tree (COND_EXPR_THEN (t), region);
-      t = COND_EXPR_ELSE (t);
-      goto tailrecurse;
-
-    case BIND_EXPR:
-      t = BIND_EXPR_BODY (t);
-      goto tailrecurse;
-
     case TRY_CATCH_EXPR:
       collect_finally_tree (TREE_OPERAND (t, 0), region);
       t = TREE_OPERAND (t, 1);
@@ -316,71 +307,100 @@ find_goto_replacement (struct leh_tf_sta
   return (ret ? ret->repl_stmt : NULL);
 }
 
-/* Replace all goto queue members.  */
-/* ??? This search and replace nonsense wouldn't be necessary
-   if we had a reasonable statement connection mechanism.  The
-   nature of these COMPOUND_EXPRs is such that we can't store
-   a pointer to a statement and hope to be able to replace it
-   later, when the tree has been restructured.  */
-/* ??? We now have a reasonable connection mechansim.  Update.  */
+/* A subroutine of replace_goto_queue_1.  Handles the sub-clauses of a
+   lowered COND_EXPR.  If, by chance, the replacement is a simple goto,
+   then we can just splat it in, otherwise we add the new stmts immediately
+   after the COND_EXPR and redirect.  */
 
-static tree
-replace_goto_queue_1 (tree t, struct leh_tf_state *tf)
+static void
+replace_goto_queue_cond_clause (tree *tp, struct leh_tf_state *tf,
+				tree_stmt_iterator *tsi)
+{
+  tree new, one, label;
+
+  new = find_goto_replacement (tf, *tp);
+  if (!new)
+    return;
+
+  one = expr_only (new);
+  if (one && TREE_CODE (one) == GOTO_EXPR)
+    {
+      *tp = one;
+      return;
+    }
+
+  label = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
+  *tp = build_and_jump (&LABEL_EXPR_LABEL (label));
+
+  tsi_link_after (tsi, label, TSI_CONTINUE_LINKING);
+  tsi_link_after (tsi, new, TSI_CONTINUE_LINKING);
+}
+
+/* The real work of replace_goto_queue.  Returns with TSI updated to 
+   point to the next statement.  */
+
+static void replace_goto_queue_stmt_list (tree, struct leh_tf_state *);
+
+static void
+replace_goto_queue_1 (tree t, struct leh_tf_state *tf, tree_stmt_iterator *tsi)
 {
   switch (TREE_CODE (t))
     {
     case GOTO_EXPR:
     case RETURN_EXPR:
-      return find_goto_replacement (tf, t);
-
-    case STATEMENT_LIST:
-      {
-	tree_stmt_iterator i = tsi_start (t);
-	while (!tsi_end_p (i))
-	  {
-	    t = replace_goto_queue_1 (tsi_stmt (i), tf);
-	    if (t)
-	      {
-		tsi_link_before (&i, t, TSI_SAME_STMT);
-		tsi_delink (&i);
-	      }
-	    else
-	      tsi_next (&i);
-	  }
-      }
+      t = find_goto_replacement (tf, t);
+      if (t)
+	{
+	  tsi_link_before (tsi, t, TSI_SAME_STMT);
+	  tsi_delink (tsi);
+	  return;
+	}
       break;
 
     case COND_EXPR:
-      replace_goto_queue_1 (COND_EXPR_THEN (t), tf);
-      replace_goto_queue_1 (COND_EXPR_ELSE (t), tf);
-      break;
-    case BIND_EXPR:
-      replace_goto_queue_1 (BIND_EXPR_BODY (t), tf);
+      replace_goto_queue_cond_clause (&COND_EXPR_THEN (t), tf, tsi);
+      replace_goto_queue_cond_clause (&COND_EXPR_ELSE (t), tf, tsi);
       break;
+
     case TRY_FINALLY_EXPR:
     case TRY_CATCH_EXPR:
-      replace_goto_queue_1 (TREE_OPERAND (t, 0), tf);
-      replace_goto_queue_1 (TREE_OPERAND (t, 1), tf);
+      replace_goto_queue_stmt_list (TREE_OPERAND (t, 0), tf);
+      replace_goto_queue_stmt_list (TREE_OPERAND (t, 1), tf);
       break;
     case CATCH_EXPR:
-      replace_goto_queue_1 (CATCH_BODY (t), tf);
+      replace_goto_queue_stmt_list (CATCH_BODY (t), tf);
       break;
     case EH_FILTER_EXPR:
-      replace_goto_queue_1 (EH_FILTER_FAILURE (t), tf);
+      replace_goto_queue_stmt_list (EH_FILTER_FAILURE (t), tf);
       break;
 
+    case STATEMENT_LIST:
+      abort ();
+
     default:
       /* These won't have gotos in them.  */
       break;
     }
 
-  return NULL;
+  tsi_next (tsi);
 }
 
+/* A subroutine of replace_goto_queue.  Handles STATEMENT_LISTs.  */
+
+static void
+replace_goto_queue_stmt_list (tree t, struct leh_tf_state *tf)
+{
+  tree_stmt_iterator i = tsi_start (t);
+  while (!tsi_end_p (i))
+    replace_goto_queue_1 (tsi_stmt (i), tf, &i);
+}
+
+/* Replace all goto queue members.  */
+
 static void
 replace_goto_queue (struct leh_tf_state *tf)
 {
-  replace_goto_queue_1 (*tf->top_p, tf);
+  replace_goto_queue_stmt_list (*tf->top_p, tf);
 }
 
 /* For any GOTO_EXPR or RETURN_EXPR, decide whether it leaves a try_finally
@@ -594,42 +614,6 @@ do_goto_redirection (struct goto_queue_n
   append_to_statement_list (x, &q->repl_stmt);
 }
 
-/* Try to determine if we can fall out of the bottom of BLOCK.  This guess
-   need not be 100% accurate; simply be conservative and return true if we
-   don't know.  This is used only to avoid stupidly generating extra code.
-   If we're wrong, we'll just delete the extra code later.  */
-
-static bool
-block_may_fallthru (tree block)
-{
-  tree stmt = expr_last (block);
-
-  switch (TREE_CODE (stmt))
-    {
-    case GOTO_EXPR:
-    case RETURN_EXPR:
-    case RESX_EXPR:
-      /* Easy cases.  If the last statement of the block implies 
-	 control transfer, then we can't fall through.  */
-      return false;
-
-    case MODIFY_EXPR:
-      if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR)
-	stmt = TREE_OPERAND (stmt, 1);
-      else
-	return true;
-      /* FALLTHRU */
-
-    case CALL_EXPR:
-      /* Functions that do not return do not fall through.  */
-      return (call_expr_flags (stmt) & ECF_NORETURN) == 0;
-
-    default:
-      /* ??? Could search back through other composite structures.  */
-      return true;
-    }
-}
-
 /* We want to transform
 	try { body; } catch { stuff; }
    to
@@ -1509,9 +1493,6 @@ lower_eh_constructs_1 (struct leh_state 
     case COND_EXPR:
       lower_eh_constructs_1 (state, &COND_EXPR_THEN (t));
       lower_eh_constructs_1 (state, &COND_EXPR_ELSE (t));
-      break;
-    case BIND_EXPR:
-      lower_eh_constructs_1 (state, &BIND_EXPR_BODY (t));
       break;
 
     case CALL_EXPR:
Index: tree-flow.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-flow.h,v
retrieving revision 1.1.4.151
diff -c -p -d -u -r1.1.4.151 tree-flow.h
--- tree-flow.h	16 Nov 2003 11:00:40 -0000	1.1.4.151
+++ tree-flow.h	16 Nov 2003 22:54:55 -0000
@@ -535,7 +535,6 @@ extern void debug_dominator_optimization
 /* In tree-ssa-dce.c  */
 void tree_ssa_dce (tree, enum tree_dump_index);
 
-
 /* In tree-ssa-copyprop.c  */
 void tree_ssa_copyprop (tree, enum tree_dump_index);
 void propagate_copy (tree *, tree);
@@ -558,6 +557,7 @@ extern bool tree_can_throw_external (tre
 
 /* In gimple-low.c  */
 void lower_function_body (tree *);
+extern bool block_may_fallthru (tree block);
 
 #include "tree-flow-inline.h"
 
Index: tree-optimize.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-optimize.c,v
retrieving revision 1.1.4.71
diff -c -p -d -u -r1.1.4.71 tree-optimize.c
--- tree-optimize.c	12 Nov 2003 22:06:26 -0000	1.1.4.71
+++ tree-optimize.c	16 Nov 2003 22:54:55 -0000
@@ -297,13 +297,8 @@ tree_rest_of_compilation (tree fndecl, b
   remove_useless_stmts (&DECL_SAVED_TREE (fndecl));
   dump_function (TDI_useless, fndecl);
 
-  /* Run a pass to lower magic exception handling constructs into,
-     well, less magic though not completely mundane constructs.  */
-  lower_eh_constructs (&DECL_SAVED_TREE (fndecl));
-
   /* Lower the structured statements.  */
   lower_function_body (&DECL_SAVED_TREE (fndecl));
-  chain = DECL_SAVED_TREE (fndecl);
 
   /* Avoid producing notes for blocks.  */
   cfun->dont_emit_block_notes = 1;
@@ -311,7 +306,12 @@ tree_rest_of_compilation (tree fndecl, b
 
   dump_function (TDI_lower, fndecl);
 
+  /* Run a pass to lower magic exception handling constructs into,
+     well, less magic though not completely mundane constructs.  */
+  lower_eh_constructs (&DECL_SAVED_TREE (fndecl));
+
   /* Invoke the SSA tree optimizer.  */
+  chain = DECL_SAVED_TREE (fndecl);
   if (optimize >= 1 && !flag_disable_tree_ssa)
     optimize_function_tree (fndecl, &chain);
 
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.342.2.126
diff -c -p -d -u -r1.342.2.126 tree.h
--- tree.h	16 Nov 2003 08:24:02 -0000	1.342.2.126
+++ tree.h	16 Nov 2003 22:54:55 -0000
@@ -3531,8 +3531,8 @@ enum tree_dump_index
 				   within it.  */
   TDI_gimple,			/* dump each function after gimplifying it.  */
   TDI_useless,			/* dump after cleaning useless bits.  */
-  TDI_eh,			/* dump after lowering eh.  */
   TDI_lower,			/* dump after lowering containers.  */
+  TDI_eh,			/* dump after lowering eh.  */
   TDI_cfg,			/* dump the flowgraph for each function.  */
   TDI_dot,			/* create a dot graph file for each 
 				   function's flowgraph.  */
Index: testsuite/g++.dg/eh/goto1.C
===================================================================
RCS file: testsuite/g++.dg/eh/goto1.C
diff -N testsuite/g++.dg/eh/goto1.C
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/g++.dg/eh/goto1.C	16 Nov 2003 22:54:55 -0000
@@ -0,0 +1,34 @@
+extern "C" void abort ();
+
+static int count;
+
+struct S {
+  S() { ++count; }
+  ~S() { --count; }
+};
+
+int foo(int p)
+{
+  S s1;
+  {
+    S s2;
+    if (p)
+      goto L;
+    else
+      return 1;
+  }
+  foo (p);
+ L:
+  return 0;
+}
+
+int main()
+{
+  foo(0);
+  if (count != 0)
+    abort ();
+  foo(1);
+  if (count != 0)
+    abort ();
+  return 0;
+}


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