OSDN Git Service

2004-08-17 Paul Brook <paul@codesourcery.com>
[pf3gnuchains/gcc-fork.git] / gcc / gimplify.c
index 3675c8a..21d8f45 100644 (file)
@@ -976,20 +976,34 @@ gimplify_decl_expr (tree *stmt_p)
          /* This is a variable-sized decl.  Simplify its size and mark it
             for deferred expansion.  Note that mudflap depends on the format
             of the emitted code: see mx_register_decls().  */
-         tree t, args;
+         tree t, args, addr, ptr_type;
 
          gimplify_type_sizes (TREE_TYPE (decl), stmt_p);
          gimplify_one_sizepos (&DECL_SIZE (decl), stmt_p);
          gimplify_one_sizepos (&DECL_SIZE_UNIT (decl), stmt_p);
 
+         /* All occurences of this decl in final gimplified code will be
+            replaced by indirection.  Setting DECL_VALUE_EXPR does two
+            things: First, it lets the rest of the gimplifier know what
+            replacement to use.  Second, it lets the debug info know 
+            where to find the value.  */
+         ptr_type = build_pointer_type (TREE_TYPE (decl));
+         addr = create_tmp_var (ptr_type, get_name (decl));
+         DECL_IGNORED_P (addr) = 0;
+         t = build_fold_indirect_ref (addr);
+         DECL_VALUE_EXPR (decl) = t;
+
          args = tree_cons (NULL, DECL_SIZE_UNIT (decl), NULL);
-         t = build_fold_addr_expr (decl);
-         args = tree_cons (NULL, t, args);
-         t = implicit_built_in_decls[BUILT_IN_STACK_ALLOC];
+         t = built_in_decls[BUILT_IN_ALLOCA];
          t = build_function_call_expr (t, args);
+         t = fold_convert (ptr_type, t);
+         t = build2 (MODIFY_EXPR, void_type_node, addr, t);
 
          gimplify_and_add (t, stmt_p);
-         DECL_DEFER_OUTPUT (decl) = 1;
+
+         /* Indicate that we need to restore the stack level when the
+            enclosing BIND_EXPR is exited.  */
+         gimplify_ctxp->save_stack = true;
        }
 
       if (init && init != error_mark_node)
@@ -1834,20 +1848,7 @@ gimplify_call_expr (tree *expr_p, tree *pre_p, bool want_value)
   decl = get_callee_fndecl (*expr_p);
   if (decl && DECL_BUILT_IN (decl))
     {
-      tree new;
-
-      /* If it is allocation of stack, record the need to restore the memory
-        when the enclosing bind_expr is exited.  */
-      if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_ALLOC)
-       gimplify_ctxp->save_stack = true;
-
-      /* If it is restore of the stack, reset it, since it means we are
-        regimplifying the bind_expr.  Note that we use the fact that
-        for try_finally_expr, try part is processed first.  */
-      if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_RESTORE)
-       gimplify_ctxp->save_stack = false;
-
-      new = simplify_builtin (*expr_p, !want_value);
+      tree new = simplify_builtin (*expr_p, !want_value);
 
       if (new && new != *expr_p)
        {
@@ -2362,6 +2363,167 @@ gimplify_modify_expr_to_memset (tree *expr_p, tree size, bool want_value)
   return GS_OK;
 }
 
+/* A subroutine of gimplify_init_ctor_preeval.  Called via walk_tree,
+   determine, cautiously, if a CONSTRUCTOR overlaps the lhs of an
+   assignment.  Returns non-null if we detect a potential overlap.  */
+
+struct gimplify_init_ctor_preeval_data
+{
+  /* The base decl of the lhs object.  May be NULL, in which case we
+     have to assume the lhs is indirect.  */
+  tree lhs_base_decl;
+
+  /* The alias set of the lhs object.  */
+  int lhs_alias_set;
+};
+
+static tree
+gimplify_init_ctor_preeval_1 (tree *tp, int *walk_subtrees, void *xdata)
+{
+  struct gimplify_init_ctor_preeval_data *data
+    = (struct gimplify_init_ctor_preeval_data *) xdata;
+  tree t = *tp;
+
+  /* If we find the base object, obviously we have overlap.  */
+  if (data->lhs_base_decl == t)
+    return t;
+
+  /* If the constructor component is indirect, determine if we have a
+     potential overlap with the lhs.  The only bits of information we
+     have to go on at this point are addressability and alias sets.  */
+  if (TREE_CODE (t) == INDIRECT_REF
+      && (!data->lhs_base_decl || TREE_ADDRESSABLE (data->lhs_base_decl))
+      && alias_sets_conflict_p (data->lhs_alias_set, get_alias_set (t)))
+    return t;
+
+  if (DECL_P (t) || TYPE_P (t))
+    *walk_subtrees = 0;
+  return NULL;
+}
+
+/* A subroutine of gimplify_init_constructor.  Pre-evaluate *EXPR_P,
+   force values that overlap with the lhs (as described by *DATA)
+   into temporaries.  */
+
+static void
+gimplify_init_ctor_preeval (tree *expr_p, tree *pre_p, tree *post_p,
+                           struct gimplify_init_ctor_preeval_data *data)
+{
+  enum gimplify_status one;
+
+  /* If the value is invariant, then there's nothing to pre-evaluate.
+     But ensure it doesn't have any side-effects since a SAVE_EXPR is
+     invariant but has side effects and might contain a reference to
+     the object we're initializing.  */
+  if (TREE_INVARIANT (*expr_p) && !TREE_SIDE_EFFECTS (*expr_p))
+    return;
+
+  /* If the type has non-trivial constructors, we can't pre-evaluate.  */
+  if (TREE_ADDRESSABLE (TREE_TYPE (*expr_p)))
+    return;
+
+  /* Recurse for nested constructors.  */
+  if (TREE_CODE (*expr_p) == CONSTRUCTOR)
+    {
+      tree list;
+      for (list = CONSTRUCTOR_ELTS (*expr_p); list ; list = TREE_CHAIN (list))
+       gimplify_init_ctor_preeval (&TREE_VALUE (list), pre_p, post_p, data);
+      return;
+    }
+
+  /* We can't preevaluate if the type contains a placeholder.  */
+  if (type_contains_placeholder_p (TREE_TYPE (*expr_p)))
+    return;
+
+  /* Gimplify the constructor element to something appropriate for the rhs
+     of a MODIFY_EXPR.  Given that we know the lhs is an aggregate, we know
+     the gimplifier will consider this a store to memory.  Doing this 
+     gimplification now means that we won't have to deal with complicated
+     language-specific trees, nor trees like SAVE_EXPR that can induce
+     exponential search behaviour.  */
+  one = gimplify_expr (expr_p, pre_p, post_p, is_gimple_mem_rhs, fb_rvalue);
+  if (one == GS_ERROR)
+    {
+      *expr_p = NULL;
+      return;
+    }
+
+  /* If we gimplified to a bare decl, we can be sure that it doesn't overlap
+     with the lhs, since "a = { .x=a }" doesn't make sense.  This will
+     always be true for all scalars, since is_gimple_mem_rhs insists on a
+     temporary variable for them.  */
+  if (DECL_P (*expr_p))
+    return;
+
+  /* If this is of variable size, we have no choice but to assume it doesn't
+     overlap since we can't make a temporary for it.  */
+  if (!TREE_CONSTANT (TYPE_SIZE (TREE_TYPE (*expr_p))))
+    return;
+
+  /* Otherwise, we must search for overlap ...  */
+  if (!walk_tree (expr_p, gimplify_init_ctor_preeval_1, data, NULL))
+    return;
+
+  /* ... and if found, force the value into a temporary.  */
+  *expr_p = get_formal_tmp_var (*expr_p, pre_p);
+}
+
+/* A subroutine of gimplify_init_constructor.  Generate individual
+   MODIFY_EXPRs for a CONSTRUCTOR.  OBJECT is the LHS against which the
+   assignments should happen.  LIST is the CONSTRUCTOR_ELTS of the
+   CONSTRUCTOR.  CLEARED is true if the entire LHS object has been
+   zeroed first.  */
+
+static void
+gimplify_init_ctor_eval (tree object, tree list, tree *pre_p, bool cleared)
+{
+  tree array_elt_type = NULL;
+
+  if (TREE_CODE (TREE_TYPE (object)) == ARRAY_TYPE)
+    array_elt_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
+
+  for (; list; list = TREE_CHAIN (list))
+    {
+      tree purpose, value, cref, init;
+
+      purpose = TREE_PURPOSE (list);
+      value = TREE_VALUE (list);
+
+      /* NULL values are created above for gimplification errors.  */
+      if (value == NULL)
+       continue;
+
+      if (cleared && initializer_zerop (value))
+       continue;
+
+      if (array_elt_type)
+       {
+         /* ??? Here's to hoping the front end fills in all of the indicies,
+            so we don't have to figure out what's missing ourselves.  */
+         if (!purpose)
+           abort ();
+         /* ??? Need to handle this.  */
+         if (TREE_CODE (purpose) == RANGE_EXPR)
+           abort ();
+
+         cref = build (ARRAY_REF, array_elt_type, unshare_expr (object),
+                       purpose, NULL_TREE, NULL_TREE);
+       }
+      else
+       cref = build (COMPONENT_REF, TREE_TYPE (purpose),
+                     unshare_expr (object), purpose, NULL_TREE);
+
+      if (TREE_CODE (value) == CONSTRUCTOR)
+       gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
+                                pre_p, cleared);
+      else
+       {
+         init = build (MODIFY_EXPR, TREE_TYPE (cref), cref, value);
+         gimplify_and_add (init, pre_p);
+       }
+    }
+}
+
 /* A subroutine of gimplify_modify_expr.  Break out elements of a
    CONSTRUCTOR used as an initializer into separate MODIFY_EXPRs.
 
@@ -2373,7 +2535,7 @@ static enum gimplify_status
 gimplify_init_constructor (tree *expr_p, tree *pre_p,
                           tree *post_p, bool want_value)
 {
-  tree object = TREE_OPERAND (*expr_p, 0);
+  tree object;
   tree ctor = TREE_OPERAND (*expr_p, 1);
   tree type = TREE_TYPE (ctor);
   enum gimplify_status ret;
@@ -2382,6 +2544,12 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
   if (TREE_CODE (ctor) != CONSTRUCTOR)
     return GS_UNHANDLED;
 
+  ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
+                      is_gimple_lvalue, fb_lvalue);
+  if (ret == GS_ERROR)
+    return ret;
+  object = TREE_OPERAND (*expr_p, 0);
+
   elt_list = CONSTRUCTOR_ELTS (ctor);
 
   ret = GS_ALL_DONE;
@@ -2392,7 +2560,8 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
     case QUAL_UNION_TYPE:
     case ARRAY_TYPE:
       {
-       HOST_WIDE_INT i, num_elements, num_nonzero_elements;
+       struct gimplify_init_ctor_preeval_data preeval_data;
+       HOST_WIDE_INT num_elements, num_nonzero_elements;
        HOST_WIDE_INT num_nonconstant_elements;
        bool cleared;
 
@@ -2400,19 +2569,10 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
           individual elements.  The exception is that a CONSTRUCTOR node
           with no elements indicates zero-initialization of the whole.  */
        if (elt_list == NULL)
-         {
-           if (want_value)
-             {
-               *expr_p = object;
-               return GS_OK;
-             }
-           else
-             return GS_UNHANDLED;
-         }
+         break;
 
        categorize_ctor_elements (ctor, &num_nonzero_elements,
                                  &num_nonconstant_elements);
-       num_elements = count_type_elements (TREE_TYPE (ctor));
 
        /* If a const aggregate variable is being initialized, then it
           should never be a lose to promote the variable to be static.  */
@@ -2467,6 +2627,7 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
            if (size > 0 && !can_move_by_pieces (size, align))
              {
                tree new = create_tmp_var_raw (type, "C");
+
                gimple_add_tmp_var (new);
                TREE_STATIC (new) = 1;
                TREE_READONLY (new) = 1;
@@ -2479,7 +2640,11 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
                walk_tree (&DECL_INITIAL (new), force_labels_r, NULL, NULL);
 
                TREE_OPERAND (*expr_p, 1) = new;
-               break;
+
+               /* This is no longer an assignment of a CONSTRUCTOR, but
+                  we still may have processing to do on the LHS.  So
+                  pretend we didn't do anything here to let that happen.  */
+               return GS_UNHANDLED;
              }
          }
 
@@ -2490,6 +2655,8 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
           parts in, then generate code for the non-constant parts.  */
        /* TODO.  There's code in cp/typeck.c to do this.  */
 
+       num_elements = count_type_elements (TREE_TYPE (ctor));
+
        /* If there are "lots" of zeros, then block clear the object first.  */
        cleared = false;
        if (num_elements - num_nonzero_elements > CLEAR_RATIO
@@ -2509,60 +2676,31 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
                tree nelts = array_type_nelts (type);
                if (!host_integerp (nelts, 1)
                    || tree_low_cst (nelts, 1) + 1 != len)
-                 cleared = 1;;
+                 cleared = true;
              }
            else if (len != fields_length (type))
-             cleared = 1;
+             cleared = true;
          }
 
        if (cleared)
          {
            /* Zap the CONSTRUCTOR element list, which simplifies this case.
               Note that we still have to gimplify, in order to handle the
-              case of variable sized types.  Make an unshared copy of
-              OBJECT before that so we can match a PLACEHOLDER_EXPR to it
-              later, if needed.  */
+              case of variable sized types.  Avoid shared tree structures.  */
            CONSTRUCTOR_ELTS (ctor) = NULL_TREE;
-           object = unshare_expr (TREE_OPERAND (*expr_p, 0));
+           object = unshare_expr (object);
            gimplify_stmt (expr_p);
            append_to_statement_list (*expr_p, pre_p);
          }
 
-       for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list))
-         {
-           tree purpose, value, cref, init;
-
-           purpose = TREE_PURPOSE (elt_list);
-           value = TREE_VALUE (elt_list);
-
-           if (cleared && initializer_zerop (value))
-             continue;
-
-           if (TREE_CODE (type) == ARRAY_TYPE)
-             {
-               tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
-
-               /* ??? Here's to hoping the front end fills in all of the
-                  indicies, so we don't have to figure out what's missing
-                  ourselves.  */
-               if (!purpose)
-                 abort ();
-               /* ??? Need to handle this.  */
-               if (TREE_CODE (purpose) == RANGE_EXPR)
-                 abort ();
-
-               cref = build (ARRAY_REF, t, unshare_expr (object), purpose,
-                             NULL_TREE, NULL_TREE);
-             }
-           else
-             cref = build (COMPONENT_REF, TREE_TYPE (purpose),
-                           unshare_expr (object), purpose, NULL_TREE);
-
-           init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
+       preeval_data.lhs_base_decl = get_base_address (object);
+       if (!DECL_P (preeval_data.lhs_base_decl))
+         preeval_data.lhs_base_decl = NULL;
+       preeval_data.lhs_alias_set = get_alias_set (object);
 
-           /* Each member initialization is a full-expression.  */
-           gimplify_and_add (init, pre_p);
-         }
+       gimplify_init_ctor_preeval (&TREE_OPERAND (*expr_p, 1),
+                                   pre_p, post_p, &preeval_data);
+       gimplify_init_ctor_eval (object, elt_list, pre_p, cleared);
 
        *expr_p = NULL_TREE;
       }
@@ -3781,9 +3919,19 @@ gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p,
                abort ();
 #endif
              ret = GS_ERROR;
+             break;
            }
-         else
-           ret = GS_ALL_DONE;
+
+         /* If this is a local variable sized decl, it must be accessed
+            indirectly.  Perform that substitution.  */
+         if (DECL_VALUE_EXPR (tmp))
+           {
+             *expr_p = unshare_expr (DECL_VALUE_EXPR (tmp));
+             ret = GS_OK;
+             break;
+           }
+
+         ret = GS_ALL_DONE;
          break;
 
        case SSA_NAME: