OSDN Git Service

2005-05-24 Andrew Pinski <pinskia@physics.uc.edu>
[pf3gnuchains/gcc-fork.git] / gcc / cp / cp-gimplify.c
index 5b96e39..fc8c1af 100644 (file)
@@ -1,6 +1,6 @@
 /* C++-specific tree lowering bits; see also c-gimplify.c and tree-gimple.c.
 
-   Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by Jason Merrill <jason@redhat.com>
 
 This file is part of GCC.
@@ -30,6 +30,102 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "toplev.h"
 #include "tree-gimple.h"
 #include "hashtab.h"
+#include "pointer-set.h"
+#include "flags.h"
+
+/* Local declarations.  */
+
+enum bc_t { bc_break = 0, bc_continue = 1 };
+
+static struct cp_gimplify_ctx
+{
+  /* Stack of labels which are targets for "break" or "continue",
+     linked through TREE_CHAIN.  */
+  tree current_label[2];
+} *ctxp;
+
+static void
+push_context (void)
+{
+  gcc_assert (!ctxp);
+  ctxp = ((struct cp_gimplify_ctx *)
+         xcalloc (1, sizeof (struct cp_gimplify_ctx)));
+}
+
+static void
+pop_context (void)
+{
+  gcc_assert (ctxp
+             && !ctxp->current_label[0]
+             && !ctxp->current_label[1]);
+  free (ctxp);
+  ctxp = NULL;
+}
+
+/* Begin a scope which can be exited by a break or continue statement.  BC
+   indicates which.
+
+   Just creates a label and pushes it into the current context.  */
+
+static tree
+begin_bc_block (enum bc_t bc)
+{
+  tree label = create_artificial_label ();
+  TREE_CHAIN (label) = ctxp->current_label[bc];
+  ctxp->current_label[bc] = label;
+  return label;
+}
+
+/* Finish a scope which can be exited by a break or continue statement.
+   LABEL was returned from the most recent call to begin_bc_block.  BODY is
+   an expression for the contents of the scope.
+
+   If we saw a break (or continue) in the scope, append a LABEL_EXPR to
+   body.  Otherwise, just forget the label.  */
+
+static tree
+finish_bc_block (enum bc_t bc, tree label, tree body)
+{
+  gcc_assert (label == ctxp->current_label[bc]);
+
+  if (TREE_USED (label))
+    {
+      tree t, sl = NULL;
+
+      t = build1 (LABEL_EXPR, void_type_node, label);
+
+      append_to_statement_list (body, &sl);
+      append_to_statement_list (t, &sl);
+      body = sl;
+    }
+
+  ctxp->current_label[bc] = TREE_CHAIN (label);
+  TREE_CHAIN (label) = NULL_TREE;
+  return body;
+}
+
+/* Build a GOTO_EXPR to represent a break or continue statement.  BC
+   indicates which.  */
+
+static tree
+build_bc_goto (enum bc_t bc)
+{
+  tree label = ctxp->current_label[bc];
+
+  if (label == NULL_TREE)
+    {
+      if (bc == bc_break)
+       error ("break statement not within loop or switch");
+      else
+       error ("continue statement not within loop or switch");
+
+      return NULL_TREE;
+    }
+
+  /* Mark the label used for finish_bc_block.  */
+  TREE_USED (label) = 1;
+  return build1 (GOTO_EXPR, void_type_node, label);
+}
 
 /* Genericize a TRY_BLOCK.  */
 
@@ -46,7 +142,7 @@ genericize_try_block (tree *stmt_p)
   else
     gimplify_stmt (&cleanup);
 
-  *stmt_p = build (TRY_CATCH_EXPR, void_type_node, body, cleanup);
+  *stmt_p = build2 (TRY_CATCH_EXPR, void_type_node, body, cleanup);
 }
 
 /* Genericize a HANDLER by converting to a CATCH_EXPR.  */
@@ -60,7 +156,7 @@ genericize_catch_block (tree *stmt_p)
   gimplify_stmt (&body);
 
   /* FIXME should the caught type go in TREE_TYPE?  */
-  *stmt_p = build (CATCH_EXPR, void_type_node, type, body);
+  *stmt_p = build2 (CATCH_EXPR, void_type_node, type, body);
 }
 
 /* Genericize an EH_SPEC_BLOCK by converting it to a
@@ -84,9 +180,10 @@ genericize_eh_spec_block (tree *stmt_p)
 static void
 gimplify_if_stmt (tree *stmt_p)
 {
-  tree stmt, then_, else_;
+  tree stmt, cond, then_, else_;
 
   stmt = *stmt_p;
+  cond = IF_COND (stmt);
   then_ = THEN_CLAUSE (stmt);
   else_ = ELSE_CLAUSE (stmt);
 
@@ -95,7 +192,185 @@ gimplify_if_stmt (tree *stmt_p)
   if (!else_)
     else_ = build_empty_stmt ();
 
-  stmt = build (COND_EXPR, void_type_node, IF_COND (stmt), then_, else_);
+  if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
+    stmt = then_;
+  else if (integer_zerop (cond) && !TREE_SIDE_EFFECTS (then_))
+    stmt = else_;
+  else
+    stmt = build3 (COND_EXPR, void_type_node, cond, then_, else_);
+  *stmt_p = stmt;
+}
+
+/* Build a generic representation of one of the C loop forms.  COND is the
+   loop condition or NULL_TREE.  BODY is the (possibly compound) statement
+   controlled by the loop.  INCR is the increment expression of a for-loop,
+   or NULL_TREE.  COND_IS_FIRST indicates whether the condition is
+   evaluated before the loop body as in while and for loops, or after the
+   loop body as in do-while loops.  */
+
+static tree
+gimplify_cp_loop (tree cond, tree body, tree incr, bool cond_is_first)
+{
+  tree top, entry, exit, cont_block, break_block, stmt_list, t;
+  location_t stmt_locus;
+
+  stmt_locus = input_location;
+  stmt_list = NULL_TREE;
+  entry = NULL_TREE;
+
+  break_block = begin_bc_block (bc_break);
+  cont_block = begin_bc_block (bc_continue);
+
+  /* If condition is zero don't generate a loop construct.  */
+  if (cond && integer_zerop (cond))
+    {
+      top = NULL_TREE;
+      exit = NULL_TREE;
+      if (cond_is_first)
+       {
+         t = build_bc_goto (bc_break);
+         append_to_statement_list (t, &stmt_list);
+       }
+    }
+  else
+    {
+      /* If we use a LOOP_EXPR here, we have to feed the whole thing
+        back through the main gimplifier to lower it.  Given that we
+        have to gimplify the loop body NOW so that we can resolve
+        break/continue stmts, seems easier to just expand to gotos.  */
+      top = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
+
+      /* If we have an exit condition, then we build an IF with gotos either
+        out of the loop, or to the top of it.  If there's no exit condition,
+        then we just build a jump back to the top.  */
+      exit = build_and_jump (&LABEL_EXPR_LABEL (top));
+      if (cond && !integer_nonzerop (cond))
+       {
+         t = build_bc_goto (bc_break);
+         exit = build3 (COND_EXPR, void_type_node, cond, exit, t);
+         exit = fold (exit);
+         gimplify_stmt (&exit);
+
+         if (cond_is_first)
+           {
+             if (incr)
+               {
+                 entry = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
+                 t = build_and_jump (&LABEL_EXPR_LABEL (entry));
+               }
+             else
+               t = build_bc_goto (bc_continue);
+             append_to_statement_list (t, &stmt_list);
+           }
+       }
+    }
+
+  gimplify_stmt (&body);
+  gimplify_stmt (&incr);
+
+  body = finish_bc_block (bc_continue, cont_block, body);
+
+  append_to_statement_list (top, &stmt_list);
+  append_to_statement_list (body, &stmt_list);
+  append_to_statement_list (incr, &stmt_list);
+  append_to_statement_list (entry, &stmt_list);
+  append_to_statement_list (exit, &stmt_list);
+
+  annotate_all_with_locus (&stmt_list, stmt_locus);
+
+  return finish_bc_block (bc_break, break_block, stmt_list);
+}
+
+/* Gimplify a FOR_STMT node.  Move the stuff in the for-init-stmt into the
+   prequeue and hand off to gimplify_cp_loop.  */
+
+static void
+gimplify_for_stmt (tree *stmt_p, tree *pre_p)
+{
+  tree stmt = *stmt_p;
+
+  if (FOR_INIT_STMT (stmt))
+    gimplify_and_add (FOR_INIT_STMT (stmt), pre_p);
+
+  *stmt_p = gimplify_cp_loop (FOR_COND (stmt), FOR_BODY (stmt),
+                             FOR_EXPR (stmt), 1);
+}
+
+/* Gimplify a WHILE_STMT node.  */
+
+static void
+gimplify_while_stmt (tree *stmt_p)
+{
+  tree stmt = *stmt_p;
+  *stmt_p = gimplify_cp_loop (WHILE_COND (stmt), WHILE_BODY (stmt),
+                             NULL_TREE, 1);
+}
+
+/* Gimplify a DO_STMT node.  */
+
+static void
+gimplify_do_stmt (tree *stmt_p)
+{
+  tree stmt = *stmt_p;
+  *stmt_p = gimplify_cp_loop (DO_COND (stmt), DO_BODY (stmt),
+                             NULL_TREE, 0);
+}
+
+/* Genericize a SWITCH_STMT by turning it into a SWITCH_EXPR.  */
+
+static void
+gimplify_switch_stmt (tree *stmt_p)
+{
+  tree stmt = *stmt_p;
+  tree break_block, body;
+  location_t stmt_locus = input_location;
+
+  break_block = begin_bc_block (bc_break);
+
+  body = SWITCH_STMT_BODY (stmt);
+  if (!body)
+    body = build_empty_stmt ();
+
+  *stmt_p = build3 (SWITCH_EXPR, SWITCH_STMT_TYPE (stmt),
+                   SWITCH_STMT_COND (stmt), body, NULL_TREE);
+  SET_EXPR_LOCATION (*stmt_p, stmt_locus);
+  gimplify_stmt (stmt_p);
+
+  *stmt_p = finish_bc_block (bc_break, break_block, *stmt_p);
+}
+
+/*  Gimplify an EXPR_STMT node.  */
+
+static void
+gimplify_expr_stmt (tree *stmt_p)
+{
+  tree stmt = EXPR_STMT_EXPR (*stmt_p);
+
+  if (stmt == error_mark_node)
+    stmt = NULL;
+
+  /* Gimplification of a statement expression will nullify the
+     statement if all its side effects are moved to *PRE_P and *POST_P.
+
+     In this case we will not want to emit the gimplified statement.
+     However, we may still want to emit a warning, so we do that before
+     gimplification.  */
+  if (stmt && (extra_warnings || warn_unused_value))
+    {
+      if (!TREE_SIDE_EFFECTS (stmt))
+       {
+         if (!IS_EMPTY_STMT (stmt)
+             && !VOID_TYPE_P (TREE_TYPE (stmt))
+             && !TREE_NO_WARNING (stmt))
+           warning (0, "statement with no effect");
+       }
+      else if (warn_unused_value)
+       warn_if_unused_value (stmt, input_location);
+    }
+
+  if (stmt == NULL_TREE)
+    stmt = alloc_stmt_list ();
+
   *stmt_p = stmt;
 }
 
@@ -210,12 +485,8 @@ cp_gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p)
       break;
 
     case EMPTY_CLASS_EXPR:
-      {
-       /* Yes, an INTEGER_CST with RECORD_TYPE.  */
-       tree i = build_int_2 (0, 0);
-       TREE_TYPE (i) = TREE_TYPE (*expr_p);
-       *expr_p = i;
-      }
+      /* We create an INTEGER_CST with RECORD_TYPE and value zero.  */
+      *expr_p = build_int_cst (TREE_TYPE (*expr_p), 0);
       ret = GS_OK;
       break;
 
@@ -251,6 +522,41 @@ cp_gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p)
       ret = GS_OK;
       break;
 
+    case FOR_STMT:
+      gimplify_for_stmt (expr_p, pre_p);
+      ret = GS_ALL_DONE;
+      break;
+
+    case WHILE_STMT:
+      gimplify_while_stmt (expr_p);
+      ret = GS_ALL_DONE;
+      break;
+
+    case DO_STMT:
+      gimplify_do_stmt (expr_p);
+      ret = GS_ALL_DONE;
+      break;
+
+    case SWITCH_STMT:
+      gimplify_switch_stmt (expr_p);
+      ret = GS_ALL_DONE;
+      break;
+
+    case CONTINUE_STMT:
+      *expr_p = build_bc_goto (bc_continue);
+      ret = GS_ALL_DONE;
+      break;
+
+    case BREAK_STMT:
+      *expr_p = build_bc_goto (bc_break);
+      ret = GS_ALL_DONE;
+      break;
+
+    case EXPR_STMT:
+      gimplify_expr_stmt (expr_p);
+      ret = GS_OK;
+      break;
+
     default:
       ret = c_gimplify_expr (expr_p, pre_p, post_p);
       break;
@@ -278,8 +584,7 @@ static tree
 cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 {
   tree stmt = *stmt_p;
-  htab_t htab = (htab_t) data;
-  void **slot;
+  struct pointer_set_t *p_set = (struct pointer_set_t*) data;
 
   if (is_invisiref_parm (stmt))
     {
@@ -289,8 +594,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
     }
 
   /* Other than invisiref parms, don't walk the same tree twice.  */
-  slot = htab_find_slot (htab, stmt, INSERT);
-  if (*slot)
+  if (pointer_set_contains (p_set, stmt))
     {
       *walk_subtrees = 0;
       return NULL_TREE;
@@ -307,17 +611,21 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
           && is_invisiref_parm (TREE_OPERAND (stmt, 0)))
     /* Don't dereference an invisiref RESULT_DECL inside a RETURN_EXPR.  */
     *walk_subtrees = 0;
-  else if (DECL_P (stmt) || TYPE_P (stmt))
+  else if (IS_TYPE_OR_DECL_P (stmt))
     *walk_subtrees = 0;
 
   /* Due to the way voidify_wrapper_expr is written, we don't get a chance
      to lower this construct before scanning it, so we need to lower these
      before doing anything else.  */
   else if (TREE_CODE (stmt) == CLEANUP_STMT)
-    *stmt_p = build (CLEANUP_EH_ONLY (stmt) ? TRY_CATCH_EXPR : TRY_FINALLY_EXPR,
-                    void_type_node, CLEANUP_BODY (stmt), CLEANUP_EXPR (stmt));
-
-  *slot = *stmt_p;
+    *stmt_p = build2 (CLEANUP_EH_ONLY (stmt) ? TRY_CATCH_EXPR
+                                            : TRY_FINALLY_EXPR,
+                     void_type_node,
+                     CLEANUP_BODY (stmt),
+                     CLEANUP_EXPR (stmt));
+
+  pointer_set_insert (p_set, *stmt_p);
+  
   return NULL;
 }
 
@@ -325,23 +633,23 @@ void
 cp_genericize (tree fndecl)
 {
   tree t;
-  htab_t htab;
+  struct pointer_set_t *p_set;
 
   /* Fix up the types of parms passed by invisible reference.  */
   for (t = DECL_ARGUMENTS (fndecl); t; t = TREE_CHAIN (t))
-    {
-      if (DECL_BY_REFERENCE (t))
-       abort ();
-      if (TREE_ADDRESSABLE (TREE_TYPE (t)))
-       {
-         if (DECL_ARG_TYPE (t) == TREE_TYPE (t))
-           abort ();
-         TREE_TYPE (t) = DECL_ARG_TYPE (t);
-         DECL_BY_REFERENCE (t) = 1;
-         TREE_ADDRESSABLE (t) = 0;
-         relayout_decl (t);
-       }
-    }
+    if (TREE_ADDRESSABLE (TREE_TYPE (t)))
+      {
+       /* If a function's arguments are copied to create a thunk,
+          then DECL_BY_REFERENCE will be set -- but the type of the
+          argument will be a pointer type, so we will never get
+          here.  */
+       gcc_assert (!DECL_BY_REFERENCE (t));
+       gcc_assert (DECL_ARG_TYPE (t) != TREE_TYPE (t));
+       TREE_TYPE (t) = DECL_ARG_TYPE (t);
+       DECL_BY_REFERENCE (t) = 1;
+       TREE_ADDRESSABLE (t) = 0;
+       relayout_decl (t);
+      }
 
   /* Do the same for the return value.  */
   if (TREE_ADDRESSABLE (TREE_TYPE (DECL_RESULT (fndecl))))
@@ -359,10 +667,12 @@ cp_genericize (tree fndecl)
 
   /* We do want to see every occurrence of the parms, so we can't just use
      walk_tree's hash functionality.  */
-  htab = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
-  walk_tree (&DECL_SAVED_TREE (fndecl), cp_genericize_r, htab, NULL);
-  htab_delete (htab);
+  p_set = pointer_set_create ();
+  walk_tree (&DECL_SAVED_TREE (fndecl), cp_genericize_r, p_set, NULL);
+  pointer_set_destroy (p_set);
 
   /* Do everything else.  */
+  push_context ();
   c_genericize (fndecl);
+  pop_context ();
 }