gimple-low.c (struct lower_data): Replace the_return_label and one_return_stmt with...
authorRichard Henderson <rth@redhat.com>
Tue, 8 Jun 2004 16:29:57 +0000 (09:29 -0700)
committerRichard Henderson <rth@gcc.gnu.org>
Tue, 8 Jun 2004 16:29:57 +0000 (09:29 -0700)
        * gimple-low.c (struct lower_data): Replace the_return_label and
        one_return_stmt with return_statements.
        (lower_function_body): Process the entire list of return_statements.
        (lower_return_expr): Check source value before unifying return_exprs.
        * gimplify.c (gimplify_return_expr): Force the use of a temporary
        for !aggregate_value_p.
        * tree-gimple.c: Update RETURN_EXPR grammer.

From-SVN: r82768

gcc/ChangeLog
gcc/gimple-low.c
gcc/gimplify.c
gcc/tree-gimple.c

index 56ff307aeade4983788ed7964c7105801d4b2328..1f8964f31b68019be2ebcb08a07799ff6b94cd25 100644 (file)
@@ -1,3 +1,13 @@
+2004-06-08  Richard Henderson  <rth@redhat.com>
+
+        * gimple-low.c (struct lower_data): Replace the_return_label and
+        one_return_stmt with return_statements.
+        (lower_function_body): Process the entire list of return_statements.
+        (lower_return_expr): Check source value before unifying return_exprs.   
+        * gimplify.c (gimplify_return_expr): Force the use of a temporary
+        for !aggregate_value_p.
+        * tree-gimple.c: Update RETURN_EXPR grammer.
+
 2004-06-08  Vladimir Makarov  <vmakarov@redhat.com>
 
        PR target/15598
index 458980f898f0424608d509f5d508c3e51cf3407f..aac3341b8ccee7d4220be47d3d7b1dbb46e46150 100644 (file)
@@ -47,9 +47,9 @@ struct lower_data
   /* Block the current statement belongs to.  */
   tree block;
 
-  /* Label that unifies the return statements.  */
-  tree the_return_label;
-  tree one_return_stmt;
+  /* A TREE_LIST of label and return statements to be moved to the end
+     of the function.  */
+  tree return_statements;
 };
 
 static void lower_stmt (tree_stmt_iterator *, struct lower_data *);
@@ -76,8 +76,7 @@ lower_function_body (void)
   BLOCK_CHAIN (data.block) = NULL_TREE;
   TREE_ASM_WRITTEN (data.block) = 1;
 
-  data.the_return_label = NULL_TREE;
-  data.one_return_stmt = NULL_TREE;
+  data.return_statements = NULL_TREE;
 
   *body_p = alloc_stmt_list ();
   i = tsi_start (*body_p);
@@ -86,13 +85,23 @@ lower_function_body (void)
 
   /* If we lowered any return statements, emit the representative at the
      end of the function.  */
-  if (data.one_return_stmt)
+  if (data.return_statements)
     {
-      tree t;
-      t = build (LABEL_EXPR, void_type_node, data.the_return_label);
+      tree t, x;
       i = tsi_last (*body_p);
-      tsi_link_after (&i, t, TSI_CONTINUE_LINKING);
-      tsi_link_after (&i, data.one_return_stmt, TSI_CONTINUE_LINKING);
+
+      for (t = data.return_statements; t ; t = TREE_CHAIN (t))
+       {
+         x = build (LABEL_EXPR, void_type_node, TREE_PURPOSE (t));
+          tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+
+         /* Remove the line number from the representative return statement.
+            It now fills in for many such returns.  Failure to remove this
+            will result in incorrect results for coverage analysis.  */
+         x = TREE_VALUE (t);
+         SET_EXPR_LOCUS (x, NULL);
+          tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+        }
     }
 
   if (data.block != DECL_INITIAL (current_function_decl))
@@ -392,16 +401,37 @@ lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data)
 static void
 lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data)
 {
-  tree stmt, label = data->the_return_label;
+  tree stmt = tsi_stmt (*tsi);
+  tree value, t, label;
+
+  /* Extract the value being returned.  */
+  value = TREE_OPERAND (stmt, 0);
+  if (value && TREE_CODE (value) == MODIFY_EXPR)
+    value = TREE_OPERAND (value, 1);
 
-  if (!label)
+  /* Match this up with an existing return statement that's been created.  */
+  for (t = data->return_statements; t ; t = TREE_CHAIN (t))
     {
-      data->the_return_label = label = create_artificial_label ();
-      data->one_return_stmt = tsi_stmt (*tsi);
+      tree tvalue = TREE_OPERAND (TREE_VALUE (t), 0);
+      if (tvalue && TREE_CODE (tvalue) == MODIFY_EXPR)
+       tvalue = TREE_OPERAND (tvalue, 1);
+
+      if (value == tvalue)
+       {
+         label = TREE_PURPOSE (t);
+         goto found;
+       }
     }
 
-  stmt = build (GOTO_EXPR, void_type_node, label);
-  tsi_link_before (tsi, stmt, TSI_SAME_STMT);
+  /* Not found.  Create a new label and record the return statement.  */
+  label = create_artificial_label ();
+  data->return_statements = tree_cons (label, stmt, data->return_statements);
+
+  /* Generate a goto statement and remove the return statement.  */
+ found:
+  t = build (GOTO_EXPR, void_type_node, label);
+  SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+  tsi_link_before (tsi, t, TSI_SAME_STMT);
   tsi_delink (tsi);
 }
 \f
index 679103e31c16a2c058ecc0e66c3cb83f23d11100..c6378aa4d29e4e320d07121076e16b0efb7ce521 100644 (file)
@@ -54,6 +54,7 @@ static struct gimplify_ctx
   tree conditional_cleanups;
   int conditions;
   tree exit_label;
+  tree return_temp;
   varray_type case_labels;
   /* The formal temporary table.  Should this be persistent?  */
   htab_t temp_htab;
@@ -888,7 +889,7 @@ static enum gimplify_status
 gimplify_return_expr (tree stmt, tree *pre_p)
 {
   tree ret_expr = TREE_OPERAND (stmt, 0);
-  tree result;
+  tree result_decl, result;
 
   if (!ret_expr || TREE_CODE (ret_expr) == RESULT_DECL)
     return GS_ALL_DONE;
@@ -897,24 +898,51 @@ gimplify_return_expr (tree stmt, tree *pre_p)
     return GS_ERROR;
 
   if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
-    result = NULL_TREE;
+    result_decl = NULL_TREE;
   else
     {
-      result = TREE_OPERAND (ret_expr, 0);
+      result_decl = TREE_OPERAND (ret_expr, 0);
 #ifdef ENABLE_CHECKING
       if ((TREE_CODE (ret_expr) != MODIFY_EXPR
           && TREE_CODE (ret_expr) != INIT_EXPR)
-         || TREE_CODE (result) != RESULT_DECL)
+         || TREE_CODE (result_decl) != RESULT_DECL)
        abort ();
 #endif
     }
 
-  /* We need to pass the full MODIFY_EXPR down so that special handling
-     can replace it with something else.  */
+  /* If aggregate_value_p is true, then we can return the bare RESULT_DECL.
+     Recall that aggregate_value_p is FALSE for any aggregate type that is
+     returned in registers.  If we're returning values in registers, then
+     we don't want to extend the lifetime of the RESULT_DECL, particularly
+     across another call.  In addition, for those aggregates for which 
+     hard_function_value generates a PARALLEL, we'll abort during normal
+     expansion of structure assignments; there's special code in expand_return
+     to handle this case that does not exist in expand_expr.  */
+  if (!result_decl
+      || aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))
+    result = result_decl;
+  else if (gimplify_ctxp->return_temp)
+    result = gimplify_ctxp->return_temp;
+  else
+    {
+      result = create_tmp_var (TREE_TYPE (result_decl), NULL);
+      gimplify_ctxp->return_temp = result;
+    }
+
+  /* Smash the lhs of the MODIFY_EXPR to the temporary we plan to use.
+     Then gimplify the whole thing.  */
+  if (result != result_decl)
+    TREE_OPERAND (ret_expr, 0) = result;
   gimplify_stmt (&TREE_OPERAND (stmt, 0));
   append_to_statement_list (TREE_OPERAND (stmt, 0), pre_p);
 
-  TREE_OPERAND (stmt, 0) = result;
+  /* If we didn't use a temporary, then the result is just the result_decl.
+     Otherwise we need a simple copy.  This should already be gimple.  */
+  if (result == result_decl)
+    ret_expr = result;
+  else
+    ret_expr = build (MODIFY_EXPR, TREE_TYPE (result), result_decl, result);
+  TREE_OPERAND (stmt, 0) = ret_expr;
 
   return GS_ALL_DONE;
 }
index 4643ac652f4eba6b4206d2e5448b1a2200cf0c0a..d3231c01fb68239f49010567c4ed1ac4e1a0ec7a 100644 (file)
@@ -79,7 +79,9 @@ Boston, MA 02111-1307, USA.  */
        GOTO_EXPR
          op0 -> LABEL_DECL | '*' ID
      | RETURN_EXPR
-         op0 -> RESULT_DECL | NULL_TREE
+         op0 -> NULL_TREE
+             | RESULT_DECL
+             | MODIFY_EXPR -> RESULT_DECL, varname
      | THROW_EXPR?  do we need/want such a thing for opts, perhaps
          to generate an ERT_THROW region?  I think so.
         Hmm...this would only work at the GIMPLE level, where we know that