OSDN Git Service

gcc/testsuite/
[pf3gnuchains/gcc-fork.git] / gcc / gimple-low.c
index a298cbb..69aa2bf 100644 (file)
@@ -1,12 +1,12 @@
 /* Tree lowering pass.  Lowers GIMPLE into unstructured form.
 
-   Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -15,9 +15,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -49,16 +48,20 @@ struct lower_data
   /* A TREE_LIST of label and return statements to be moved to the end
      of the function.  */
   tree return_statements;
+
+  /* True if the function calls __builtin_setjmp.  */
+  bool calls_builtin_setjmp;
 };
 
 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 *);
 static void lower_return_expr (tree_stmt_iterator *, struct lower_data *);
+static void lower_builtin_setjmp (tree_stmt_iterator *);
 
-/* Lowers the body of current_function_decl.  */
+/* Lower the body of current_function_decl.  */
 
-static void
+static unsigned int
 lower_function_body (void)
 {
   struct lower_data data;
@@ -113,11 +116,40 @@ lower_function_body (void)
       tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
     }
 
+  /* If the function calls __builtin_setjmp, we need to emit the computed
+     goto that will serve as the unique dispatcher for all the receivers.  */
+  if (data.calls_builtin_setjmp)
+    {
+      tree disp_label, disp_var, arg;
+
+      /* Build 'DISP_LABEL:' and insert.  */
+      disp_label = create_artificial_label ();
+      /* This mark will create forward edges from every call site.  */
+      DECL_NONLOCAL (disp_label) = 1;
+      current_function_has_nonlocal_label = 1;
+      x = build1 (LABEL_EXPR, void_type_node, disp_label);
+      tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+
+      /* Build 'DISP_VAR = __builtin_setjmp_dispatcher (DISP_LABEL);'
+        and insert.  */
+      disp_var = create_tmp_var (ptr_type_node, "setjmpvar");
+      arg = build_addr (disp_label, current_function_decl);
+      t = implicit_built_in_decls[BUILT_IN_SETJMP_DISPATCHER];
+      t = build_call_expr (t, 1, arg);
+      x = build_gimple_modify_stmt (disp_var, t);
+
+      /* Build 'goto DISP_VAR;' and insert.  */
+      tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+      x = build1 (GOTO_EXPR, void_type_node, disp_var);
+      tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
+    }
+
   gcc_assert (data.block == DECL_INITIAL (current_function_decl));
   BLOCK_SUBBLOCKS (data.block)
     = blocks_nreverse (BLOCK_SUBBLOCKS (data.block));
 
   clear_block_marks (data.block);
+  return 0;
 }
 
 struct tree_opt_pass pass_lower_cf = 
@@ -131,14 +163,14 @@ struct tree_opt_pass pass_lower_cf =
   0,                                   /* tv_id */
   PROP_gimple_any,                     /* properties_required */
   PROP_gimple_lcf,                     /* properties_provided */
-  PROP_gimple_any,                     /* properties_destroyed */
+  0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
   TODO_dump_func,                      /* todo_flags_finish */
   0                                    /* letter */
 };
 
 
-/* Lowers the EXPR.  Unlike gimplification the statements are not relowered
+/* Lower the EXPR.  Unlike gimplification the statements are not relowered
    when they are changed -- if this has to be done, the lowering routine must
    do it explicitly.  DATA is passed through the recursion.  */
 
@@ -158,14 +190,10 @@ lower_stmt_body (tree expr, struct lower_data *data)
 static void
 lower_omp_directive (tree_stmt_iterator *tsi, struct lower_data *data)
 {
-  tree clause, stmt;
+  tree stmt;
   
   stmt = tsi_stmt (*tsi);
 
-  clause = (TREE_CODE (stmt) >= OMP_PARALLEL && TREE_CODE (stmt) <= OMP_SINGLE)
-          ? OMP_CLAUSES (stmt)
-          : NULL_TREE;
-
   lower_stmt_body (OMP_BODY (stmt), data);
   tsi_link_before (tsi, stmt, TSI_SAME_STMT);
   tsi_link_before (tsi, OMP_BODY (stmt), TSI_SAME_STMT);
@@ -174,7 +202,7 @@ lower_omp_directive (tree_stmt_iterator *tsi, struct lower_data *data)
 }
 
 
-/* Lowers statement TSI.  DATA is passed through the recursion.  */
+/* Lower statement TSI.  DATA is passed through the recursion.  */
 
 static void
 lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data)
@@ -210,22 +238,44 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data)
       
     case NOP_EXPR:
     case ASM_EXPR:
-    case MODIFY_EXPR:
-    case CALL_EXPR:
     case GOTO_EXPR:
     case LABEL_EXPR:
     case SWITCH_EXPR:
-    case OMP_RETURN_EXPR:
-      break;
-
-    case OMP_PARALLEL:
+    case CHANGE_DYNAMIC_TYPE_EXPR:
     case OMP_FOR:
     case OMP_SECTIONS:
+    case OMP_SECTIONS_SWITCH:
     case OMP_SECTION:
     case OMP_SINGLE:
     case OMP_MASTER:
     case OMP_ORDERED:
     case OMP_CRITICAL:
+    case OMP_RETURN:
+    case OMP_CONTINUE:
+      break;
+
+    case GIMPLE_MODIFY_STMT:
+      if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR)
+       stmt = GIMPLE_STMT_OPERAND (stmt, 1);
+      else
+       break;
+      /* FALLTHRU */
+
+    case CALL_EXPR:
+      {
+       tree decl = get_callee_fndecl (stmt);
+       if (decl
+           && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
+           && DECL_FUNCTION_CODE (decl) == BUILT_IN_SETJMP)
+         {
+           data->calls_builtin_setjmp = true;
+           lower_builtin_setjmp (tsi);
+           return;
+         }
+      }
+      break;
+
+    case OMP_PARALLEL:
       lower_omp_directive (tsi, data);
       return;
 
@@ -236,7 +286,7 @@ lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data)
   tsi_next (tsi);
 }
 
-/* Lowers a bind_expr TSI.  DATA is passed through the recursion.  */
+/* Lower a bind_expr TSI.  DATA is passed through the recursion.  */
 
 static void
 lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data)
@@ -294,7 +344,7 @@ lower_bind_expr (tree_stmt_iterator *tsi, struct lower_data *data)
    This is a subroutine of block_may_fallthru.  */
 
 static bool
-try_catch_may_fallthru (tree stmt)
+try_catch_may_fallthru (const_tree stmt)
 {
   tree_stmt_iterator i;
 
@@ -344,9 +394,11 @@ try_catch_may_fallthru (tree stmt)
    If we're wrong, we'll just delete the extra code later.  */
 
 bool
-block_may_fallthru (tree block)
+block_may_fallthru (const_tree block)
 {
-  tree stmt = expr_last (block);
+  /* This CONST_CAST is okay because expr_last returns it's argument
+     unmodified and we assign it to a const_tree.  */
+  const_tree stmt = expr_last (CONST_CAST_TREE(block));
 
   switch (stmt ? TREE_CODE (stmt) : ERROR_MARK)
     {
@@ -386,9 +438,9 @@ block_may_fallthru (tree block)
       return (block_may_fallthru (TREE_OPERAND (stmt, 0))
              && block_may_fallthru (TREE_OPERAND (stmt, 1)));
 
-    case MODIFY_EXPR:
-      if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR)
-       stmt = TREE_OPERAND (stmt, 1);
+    case GIMPLE_MODIFY_STMT:
+      if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == CALL_EXPR)
+       stmt = GIMPLE_STMT_OPERAND (stmt, 1);
       else
        return true;
       /* FALLTHRU */
@@ -405,7 +457,7 @@ block_may_fallthru (tree block)
     }
 }
 
-/* Lowers a cond_expr TSI.  DATA is passed through the recursion.  */
+/* Lower a cond_expr TSI.  DATA is passed through the recursion.  */
 
 static void
 lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data)
@@ -500,6 +552,8 @@ lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data)
   tsi_next (tsi);
 }
 
+/* Lower a return_expr TSI.  DATA is passed through the recursion.  */
+
 static void
 lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data)
 {
@@ -508,15 +562,15 @@ lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data)
 
   /* Extract the value being returned.  */
   value = TREE_OPERAND (stmt, 0);
-  if (value && TREE_CODE (value) == MODIFY_EXPR)
-    value = TREE_OPERAND (value, 1);
+  if (value && TREE_CODE (value) == GIMPLE_MODIFY_STMT)
+    value = GIMPLE_STMT_OPERAND (value, 1);
 
   /* Match this up with an existing return statement that's been created.  */
   for (t = data->return_statements; t ; t = TREE_CHAIN (t))
     {
       tree tvalue = TREE_OPERAND (TREE_VALUE (t), 0);
-      if (tvalue && TREE_CODE (tvalue) == MODIFY_EXPR)
-       tvalue = TREE_OPERAND (tvalue, 1);
+      if (tvalue && TREE_CODE (tvalue) == GIMPLE_MODIFY_STMT)
+       tvalue = GIMPLE_STMT_OPERAND (tvalue, 1);
 
       if (value == tvalue)
        {
@@ -536,6 +590,127 @@ lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data)
   tsi_link_before (tsi, t, TSI_SAME_STMT);
   tsi_delink (tsi);
 }
+
+/* Lower a __builtin_setjmp TSI.
+
+   __builtin_setjmp is passed a pointer to an array of five words (not
+   all will be used on all machines).  It operates similarly to the C
+   library function of the same name, but is more efficient.
+
+   It is lowered into 3 other builtins, namely __builtin_setjmp_setup,
+   __builtin_setjmp_dispatcher and __builtin_setjmp_receiver, but with
+   __builtin_setjmp_dispatcher shared among all the instances; that's
+   why it is only emitted at the end by lower_function_body.
+
+   After full lowering, the body of the function should look like:
+
+    {
+      void * setjmpvar.0;
+      int D.1844;
+      int D.2844;
+
+      [...]
+
+      __builtin_setjmp_setup (&buf, &<D1847>);
+      D.1844 = 0;
+      goto <D1846>;
+      <D1847>:;
+      __builtin_setjmp_receiver (&<D1847>);
+      D.1844 = 1;
+      <D1846>:;
+      if (D.1844 == 0) goto <D1848>; else goto <D1849>;
+
+      [...]
+
+      __builtin_setjmp_setup (&buf, &<D2847>);
+      D.2844 = 0;
+      goto <D2846>;
+      <D2847>:;
+      __builtin_setjmp_receiver (&<D2847>);
+      D.2844 = 1;
+      <D2846>:;
+      if (D.2844 == 0) goto <D2848>; else goto <D2849>;
+
+      [...]
+
+      <D3850>:;
+      return;
+      <D3853>: [non-local];
+      setjmpvar.0 = __builtin_setjmp_dispatcher (&<D3853>);
+      goto setjmpvar.0;
+    }
+
+   The dispatcher block will be both the unique destination of all the
+   abnormal call edges and the unique source of all the abnormal edges
+   to the receivers, thus keeping the complexity explosion localized.  */
+
+static void
+lower_builtin_setjmp (tree_stmt_iterator *tsi)
+{
+  tree stmt = tsi_stmt (*tsi);
+  tree cont_label = create_artificial_label ();
+  tree next_label = create_artificial_label ();
+  tree dest, t, arg;
+
+  /* NEXT_LABEL is the label __builtin_longjmp will jump to.  Its address is
+     passed to both __builtin_setjmp_setup and __builtin_setjmp_receiver.  */
+  FORCED_LABEL (next_label) = 1;
+
+  if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
+    {
+      dest = GIMPLE_STMT_OPERAND (stmt, 0);
+      stmt = GIMPLE_STMT_OPERAND (stmt, 1);
+    }
+  else
+    dest = NULL_TREE;
+
+  /* Build '__builtin_setjmp_setup (BUF, NEXT_LABEL)' and insert.  */
+  arg = build_addr (next_label, current_function_decl);
+  t = implicit_built_in_decls[BUILT_IN_SETJMP_SETUP];
+  t = build_call_expr (t, 2, CALL_EXPR_ARG (stmt, 0), arg);
+  SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+  tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+  /* Build 'DEST = 0' and insert.  */
+  if (dest)
+    {
+      t = build_gimple_modify_stmt (dest, fold_convert (TREE_TYPE (dest),
+                                                       integer_zero_node));
+      SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+      tsi_link_before (tsi, t, TSI_SAME_STMT);
+    }
+
+  /* Build 'goto CONT_LABEL' and insert.  */
+  t = build1 (GOTO_EXPR, void_type_node, cont_label);
+  tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+  /* Build 'NEXT_LABEL:' and insert.  */
+  t = build1 (LABEL_EXPR, void_type_node, next_label);
+  tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+  /* Build '__builtin_setjmp_receiver (NEXT_LABEL)' and insert.  */
+  arg = build_addr (next_label, current_function_decl);
+  t = implicit_built_in_decls[BUILT_IN_SETJMP_RECEIVER];
+  t = build_call_expr (t, 1, arg);
+  SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+  tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+  /* Build 'DEST = 1' and insert.  */
+  if (dest)
+    {
+      t = build_gimple_modify_stmt (dest, fold_convert (TREE_TYPE (dest),
+                                                       integer_one_node));
+      SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
+      tsi_link_before (tsi, t, TSI_SAME_STMT);
+    }
+
+  /* Build 'CONT_LABEL:' and insert.  */
+  t = build1 (LABEL_EXPR, void_type_node, cont_label);
+  tsi_link_before (tsi, t, TSI_SAME_STMT);
+
+  /* Remove the call to __builtin_setjmp.  */
+  tsi_delink (tsi);
+}
 \f
 
 /* Record the variables in VARS into function FN.  */
@@ -543,10 +718,8 @@ lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data)
 void
 record_vars_into (tree vars, tree fn)
 {
-  struct function *saved_cfun = cfun;
-
   if (fn != current_function_decl)
-    cfun = DECL_STRUCT_FUNCTION (fn);
+    push_cfun (DECL_STRUCT_FUNCTION (fn));
 
   for (; vars; vars = TREE_CHAIN (vars))
     {
@@ -567,7 +740,7 @@ record_vars_into (tree vars, tree fn)
     }
 
   if (fn != current_function_decl)
-    cfun = saved_cfun;
+    pop_cfun ();
 }
 
 
@@ -610,10 +783,11 @@ mark_blocks_with_used_vars (tree block)
 
 /* Mark the used attribute on blocks correctly.  */
   
-static void
+static unsigned int
 mark_used_blocks (void)
 {  
   mark_blocks_with_used_vars (DECL_INITIAL (current_function_decl));
+  return 0;
 }