OSDN Git Service

PR rtl-optimization/21299
[pf3gnuchains/gcc-fork.git] / gcc / tree-sra.c
index f0d6da9..80c4ca7 100644 (file)
@@ -1,31 +1,30 @@
 /* Scalar Replacement of Aggregates (SRA) converts some structure
    references into scalar references, exposing them to the scalar
    optimizers.
-   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
 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 version.
-   
+
 GCC is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 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, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
-#include "errors.h"
 #include "ggc.h"
 #include "tree.h"
 
@@ -46,11 +45,14 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "bitmap.h"
 #include "obstack.h"
 #include "target.h"
+/* expr.h is needed for MOVE_RATIO.  */
+#include "expr.h"
+#include "params.h"
 
 
 /* This object of this pass is to replace a non-addressable aggregate with a
    set of independent variables.  Most of the time, all of these variables
-   will be scalars.  But a secondary objective is to break up larger 
+   will be scalars.  But a secondary objective is to break up larger
    aggregates into smaller aggregates.  In the process we may find that some
    bits of the larger aggregate can be deleted as unreferenced.
 
@@ -73,6 +75,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 */
 
 
+/* The set of todo flags to return from tree_sra.  */
+static unsigned int todoflags;
+
 /* The set of aggregate variables that are candidates for scalarization.  */
 static bitmap sra_candidates;
 
@@ -84,20 +89,22 @@ static bitmap needs_copy_in;
 static bitmap sra_type_decomp_cache;
 static bitmap sra_type_inst_cache;
 
-/* One of these structures is created for each candidate aggregate
-   and each (accessed) member of such an aggregate.  */
+/* One of these structures is created for each candidate aggregate and
+   each (accessed) member or group of members of such an aggregate.  */
 struct sra_elt
 {
   /* A tree of the elements.  Used when we want to traverse everything.  */
   struct sra_elt *parent;
+  struct sra_elt *groups;
   struct sra_elt *children;
   struct sra_elt *sibling;
 
   /* If this element is a root, then this is the VAR_DECL.  If this is
      a sub-element, this is some token used to identify the reference.
      In the case of COMPONENT_REF, this is the FIELD_DECL.  In the case
-     of an ARRAY_REF, this is the (constant) index.  In the case of a
-     complex number, this is a zero or one.  */
+     of an ARRAY_REF, this is the (constant) index.  In the case of an
+     ARRAY_RANGE_REF, this is the (constant) RANGE_EXPR.  In the case
+     of a complex number, this is a zero or one.  */
   tree element;
 
   /* The type of the element.  */
@@ -117,6 +124,9 @@ struct sra_elt
   /* True if TYPE is scalar.  */
   bool is_scalar;
 
+  /* True if this element is a group of members of its parent.  */
+  bool is_group;
+
   /* True if we saw something about this element that prevents scalarization,
      such as non-constant indexing.  */
   bool cannot_scalarize;
@@ -125,12 +135,57 @@ struct sra_elt
      should happen via memcpy and not per-element.  */
   bool use_block_copy;
 
+  /* True if everything under this element has been marked TREE_NO_WARNING.  */
+  bool all_no_warning;
+
   /* A flag for use with/after random access traversals.  */
   bool visited;
 };
 
+#define IS_ELEMENT_FOR_GROUP(ELEMENT) (TREE_CODE (ELEMENT) == RANGE_EXPR)
+
+#define FOR_EACH_ACTUAL_CHILD(CHILD, ELT)                      \
+  for ((CHILD) = (ELT)->is_group                               \
+                ? next_child_for_group (NULL, (ELT))           \
+                : (ELT)->children;                             \
+       (CHILD);                                                        \
+       (CHILD) = (ELT)->is_group                               \
+                ? next_child_for_group ((CHILD), (ELT))        \
+                : (CHILD)->sibling)
+
+/* Helper function for above macro.  Return next child in group.  */
+static struct sra_elt *
+next_child_for_group (struct sra_elt *child, struct sra_elt *group)
+{
+  gcc_assert (group->is_group);
+
+  /* Find the next child in the parent.  */
+  if (child)
+    child = child->sibling;
+  else
+    child = group->parent->children;
+
+  /* Skip siblings that do not belong to the group.  */
+  while (child)
+    {
+      tree g_elt = group->element;
+      if (TREE_CODE (g_elt) == RANGE_EXPR)
+       {
+         if (!tree_int_cst_lt (child->element, TREE_OPERAND (g_elt, 0))
+             && !tree_int_cst_lt (TREE_OPERAND (g_elt, 1), child->element))
+           break;
+       }
+      else
+       gcc_unreachable ();
+
+      child = child->sibling;
+    }
+
+  return child;
+}
+
 /* Random access to the child of a parent is performed by hashing.
-   This prevents quadratic behaviour, and allows SRA to function
+   This prevents quadratic behavior, and allows SRA to function
    reasonably on larger records.  */
 static htab_t sra_map;
 
@@ -141,13 +196,15 @@ static struct obstack sra_obstack;
 static void dump_sra_elt_name (FILE *, struct sra_elt *);
 extern void debug_sra_elt_name (struct sra_elt *);
 
+/* Forward declarations.  */
+static tree generate_element_ref (struct sra_elt *);
 \f
 /* Return true if DECL is an SRA candidate.  */
 
 static bool
 is_sra_candidate_decl (tree decl)
 {
-  return DECL_P (decl) && bitmap_bit_p (sra_candidates, var_ann (decl)->uid);
+  return DECL_P (decl) && bitmap_bit_p (sra_candidates, DECL_UID (decl));
 }
 
 /* Return true if TYPE is a scalar type.  */
@@ -158,7 +215,7 @@ is_sra_scalar_type (tree type)
   enum tree_code code = TREE_CODE (type);
   return (code == INTEGER_TYPE || code == REAL_TYPE || code == VECTOR_TYPE
          || code == ENUMERAL_TYPE || code == BOOLEAN_TYPE
-         || code == CHAR_TYPE || code == POINTER_TYPE || code == OFFSET_TYPE
+         || code == POINTER_TYPE || code == OFFSET_TYPE
          || code == REFERENCE_TYPE);
 }
 
@@ -168,8 +225,8 @@ is_sra_scalar_type (tree type)
    instantiated, just that if we decide to break up the type into
    separate pieces that it can be done.  */
 
-static bool
-type_can_be_decomposed_p (tree type)
+bool
+sra_type_can_be_decomposed_p (tree type)
 {
   unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
   tree t;
@@ -180,8 +237,9 @@ type_can_be_decomposed_p (tree type)
   if (bitmap_bit_p (sra_type_decomp_cache, cache+1))
     return false;
 
-  /* The type must have a definite non-zero size.  */
-  if (TYPE_SIZE (type) == NULL || integer_zerop (TYPE_SIZE (type)))
+  /* The type must have a definite nonzero size.  */
+  if (TYPE_SIZE (type) == NULL || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+      || integer_zerop (TYPE_SIZE (type)))
     goto fail;
 
   /* The type must be a non-union aggregate.  */
@@ -270,7 +328,7 @@ decl_can_be_decomposed_p (tree var)
     }
 
   /* We must be able to decompose the variable's type.  */
-  if (!type_can_be_decomposed_p (TREE_TYPE (var)))
+  if (!sra_type_can_be_decomposed_p (TREE_TYPE (var)))
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
@@ -291,7 +349,7 @@ type_can_instantiate_all_elements (tree type)
 {
   if (is_sra_scalar_type (type))
     return true;
-  if (!type_can_be_decomposed_p (type))
+  if (!sra_type_can_be_decomposed_p (type))
     return false;
 
   switch (TREE_CODE (type))
@@ -327,7 +385,7 @@ type_can_instantiate_all_elements (tree type)
       return true;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -341,7 +399,11 @@ can_completely_scalarize_p (struct sra_elt *elt)
   if (elt->cannot_scalarize)
     return false;
 
-  for (c = elt->children; c ; c = c->sibling)
+  for (c = elt->children; c; c = c->sibling)
+    if (!can_completely_scalarize_p (c))
+      return false;
+
+  for (c = elt->groups; c; c = c->sibling)
     if (!can_completely_scalarize_p (c))
       return false;
 
@@ -355,18 +417,37 @@ can_completely_scalarize_p (struct sra_elt *elt)
 static hashval_t
 sra_hash_tree (tree t)
 {
+  hashval_t h;
+
   switch (TREE_CODE (t))
     {
     case VAR_DECL:
     case PARM_DECL:
     case RESULT_DECL:
-    case FIELD_DECL:
-      return DECL_UID (t);
+      h = DECL_UID (t);
+      break;
+
     case INTEGER_CST:
-      return TREE_INT_CST_LOW (t) ^ TREE_INT_CST_HIGH (t);
+      h = TREE_INT_CST_LOW (t) ^ TREE_INT_CST_HIGH (t);
+      break;
+
+    case RANGE_EXPR:
+      h = iterative_hash_expr (TREE_OPERAND (t, 0), 0);
+      h = iterative_hash_expr (TREE_OPERAND (t, 1), h);
+      break;
+
+    case FIELD_DECL:
+      /* We can have types that are compatible, but have different member
+        lists, so we can't hash fields by ID.  Use offsets instead.  */
+      h = iterative_hash_expr (DECL_FIELD_OFFSET (t), 0);
+      h = iterative_hash_expr (DECL_FIELD_BIT_OFFSET (t), h);
+      break;
+
     default:
-      abort ();
+      gcc_unreachable ();
     }
+
+  return h;
 }
 
 /* Hash function for type SRA_PAIR.  */
@@ -382,14 +463,14 @@ sra_elt_hash (const void *x)
 
   /* Take into account everything back up the chain.  Given that chain
      lengths are rarely very long, this should be acceptable.  If we
-     truely identify this as a performance problem, it should work to
+     truly identify this as a performance problem, it should work to
      hash the pointer value "e->parent".  */
   for (p = e->parent; p ; p = p->parent)
     h = (h * 65521) ^ sra_hash_tree (p->element);
 
   return h;
 }
-  
+
 /* Equality function for type SRA_PAIR.  */
 
 static int
@@ -397,20 +478,46 @@ sra_elt_eq (const void *x, const void *y)
 {
   const struct sra_elt *a = x;
   const struct sra_elt *b = y;
+  tree ae, be;
 
   if (a->parent != b->parent)
     return false;
 
-  /* All the field/decl stuff is unique.  */
-  if (a->element == b->element)
-    return true;
+  ae = a->element;
+  be = b->element;
 
-  /* The only thing left is integer equality.  */
-  if (TREE_CODE (a->element) == INTEGER_CST
-      && TREE_CODE (b->element) == INTEGER_CST)
-    return tree_int_cst_equal (a->element, b->element);
-  else
+  if (ae == be)
+    return true;
+  if (TREE_CODE (ae) != TREE_CODE (be))
     return false;
+
+  switch (TREE_CODE (ae))
+    {
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      /* These are all pointer unique.  */
+      return false;
+
+    case INTEGER_CST:
+      /* Integers are not pointer unique, so compare their values.  */
+      return tree_int_cst_equal (ae, be);
+
+    case RANGE_EXPR:
+      return
+       tree_int_cst_equal (TREE_OPERAND (ae, 0), TREE_OPERAND (be, 0))
+       && tree_int_cst_equal (TREE_OPERAND (ae, 1), TREE_OPERAND (be, 1));
+
+    case FIELD_DECL:
+      /* Fields are unique within a record, but not between
+        compatible records.  */
+      if (DECL_FIELD_CONTEXT (ae) == DECL_FIELD_CONTEXT (be))
+       return false;
+      return fields_compatible_p (ae, be);
+
+    default:
+      gcc_unreachable ();
+    }
 }
 
 /* Create or return the SRA_ELT structure for CHILD in PARENT.  PARENT
@@ -424,7 +531,10 @@ lookup_element (struct sra_elt *parent, tree child, tree type,
   struct sra_elt **slot;
   struct sra_elt *elt;
 
-  dummy.parent = parent;
+  if (parent)
+    dummy.parent = parent->is_group ? parent->parent : parent;
+  else
+    dummy.parent = NULL;
   dummy.element = child;
 
   slot = (struct sra_elt **) htab_find_slot (sra_map, &dummy, insert);
@@ -444,8 +554,17 @@ lookup_element (struct sra_elt *parent, tree child, tree type,
 
       if (parent)
        {
-         elt->sibling = parent->children;
-         parent->children = elt;
+         if (IS_ELEMENT_FOR_GROUP (elt->element))
+           {
+             elt->is_group = true;
+             elt->sibling = parent->groups;
+             parent->groups = elt;
+           }
+         else
+           {
+             elt->sibling = parent->children;
+             parent->children = elt;
+           }
        }
 
       /* If this is a parameter, then if we want to scalarize, we have
@@ -453,50 +572,14 @@ lookup_element (struct sra_elt *parent, tree child, tree type,
       if (TREE_CODE (child) == PARM_DECL)
        {
          elt->n_copies = 1;
-         bitmap_set_bit (needs_copy_in, var_ann (child)->uid);
+         bitmap_set_bit (needs_copy_in, DECL_UID (child));
        }
     }
 
   return elt;
 }
 
-/* Return true if the ARRAY_REF in EXPR is a constant, in bounds access.  */
-
-static bool
-is_valid_const_index (tree expr)
-{
-  tree dom, t, index = TREE_OPERAND (expr, 1);
-
-  if (TREE_CODE (index) != INTEGER_CST)
-    return false;
-
-  /* Watch out for stupid user tricks, indexing outside the array.
-
-     Careful, we're not called only on scalarizable types, so do not
-     assume constant array bounds.  We needn't do anything with such
-     cases, since they'll be referring to objects that we should have
-     already rejected for scalarization, so returning false is fine.  */
-
-  dom = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (expr, 0)));
-  if (dom == NULL)
-    return false;
-
-  t = TYPE_MIN_VALUE (dom);
-  if (!t || TREE_CODE (t) != INTEGER_CST)
-    return false;
-  if (tree_int_cst_lt (index, t))
-    return false;
-
-  t = TYPE_MAX_VALUE (dom);
-  if (!t || TREE_CODE (t) != INTEGER_CST)
-    return false;
-  if (tree_int_cst_lt (t, index))
-    return false;
-
-  return true;
-}
-
-/* Create or return the SRA_ELT structure for EXPR if the expression 
+/* Create or return the SRA_ELT structure for EXPR if the expression
    refers to a scalarizable variable.  */
 
 static struct sra_elt *
@@ -515,13 +598,25 @@ maybe_lookup_element_for_expr (tree expr)
       return NULL;
 
     case ARRAY_REF:
-      /* We can't scalarize variable array indicies.  */
-      if (is_valid_const_index (expr))
+      /* We can't scalarize variable array indices.  */
+      if (in_array_bounds_p (expr))
         child = TREE_OPERAND (expr, 1);
       else
        return NULL;
       break;
 
+    case ARRAY_RANGE_REF:
+      /* We can't scalarize variable array indices.  */
+      if (range_in_array_bounds_p (expr))
+       {
+         tree domain = TYPE_DOMAIN (TREE_TYPE (expr));
+         child = build2 (RANGE_EXPR, integer_type_node,
+                         TYPE_MIN_VALUE (domain), TYPE_MAX_VALUE (domain));
+       }
+      else
+       return NULL;
+      break;
+
     case COMPONENT_REF:
       /* Don't look through unions.  */
       if (TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) != RECORD_TYPE)
@@ -558,9 +653,10 @@ struct sra_walk_fns
   /* Invoked when ELT is required as a unit.  Note that ELT might refer to
      a leaf node, in which case this is a simple scalar reference.  *EXPR_P
      points to the location of the expression.  IS_OUTPUT is true if this
-     is a left-hand-side reference.  */
+     is a left-hand-side reference.  USE_ALL is true if we saw something we
+     couldn't quite identify and had to force the use of the entire object.  */
   void (*use) (struct sra_elt *elt, tree *expr_p,
-              block_stmt_iterator *bsi, bool is_output);
+              block_stmt_iterator *bsi, bool is_output, bool use_all);
 
   /* Invoked when we have a copy between two scalarizable references.  */
   void (*copy) (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
@@ -582,7 +678,7 @@ struct sra_walk_fns
 };
 
 #ifdef ENABLE_CHECKING
-/* Invoked via walk_tree, if *TP contains an candidate decl, return it.  */
+/* Invoked via walk_tree, if *TP contains a candidate decl, return it.  */
 
 static tree
 sra_find_candidate_decl (tree *tp, int *walk_subtrees,
@@ -613,6 +709,8 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
 {
   tree expr = *expr_p;
   tree inner = expr;
+  bool disable_scalarization = false;
+  bool use_all_p = false;
 
   /* We're looking to collect a reference expression between EXPR and INNER,
      such that INNER is a scalarizable decl and all other nodes through EXPR
@@ -630,7 +728,10 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
        if (is_sra_candidate_decl (inner))
          {
            struct sra_elt *elt = maybe_lookup_element_for_expr (expr);
-           fns->use (elt, expr_p, bsi, is_output);
+           if (disable_scalarization)
+             elt->cannot_scalarize = true;
+           else
+             fns->use (elt, expr_p, bsi, is_output, use_all_p);
          }
        return;
 
@@ -641,20 +742,14 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
           index reference inside a loop being overridden by several constant
           index references during loop setup.  It's possible that this could
           be avoided by using dynamic usage counts based on BB trip counts
-          (based on loop analysis or profiling), but that hardly seems worth 
+          (based on loop analysis or profiling), but that hardly seems worth
           the effort.  */
        /* ??? Hack.  Figure out how to push this into the scan routines
           without duplicating too much code.  */
-       if (!is_valid_const_index (inner))
+       if (!in_array_bounds_p (inner))
          {
-           if (fns->initial_scan)
-             {
-               struct sra_elt *elt
-                 = maybe_lookup_element_for_expr (TREE_OPERAND (inner, 0));
-               if (elt)
-                 elt->cannot_scalarize = true;
-             }
-           return;
+           disable_scalarization = true;
+           goto use_all;
          }
        /* ??? Are we assured that non-constant bounds and stride will have
           the same value everywhere?  I don't think Fortran will...  */
@@ -663,6 +758,18 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
        inner = TREE_OPERAND (inner, 0);
        break;
 
+      case ARRAY_RANGE_REF:
+       if (!range_in_array_bounds_p (inner))
+         {
+           disable_scalarization = true;
+           goto use_all;
+         }
+       /* ??? See above non-constant bounds and stride .  */
+       if (TREE_OPERAND (inner, 2) || TREE_OPERAND (inner, 3))
+         goto use_all;
+       inner = TREE_OPERAND (inner, 0);
+       break;
+
       case COMPONENT_REF:
        /* A reference to a union member constitutes a reference to the
           entire union.  */
@@ -681,31 +788,31 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
 
       case BIT_FIELD_REF:
        /* A bit field reference (access to *multiple* fields simultaneously)
-          is not currently scalarized.  Consider this an access to the 
+          is not currently scalarized.  Consider this an access to the
           complete outer element, to which walk_tree will bring us next.  */
        goto use_all;
 
-      case ARRAY_RANGE_REF:
-       /* Similarly, an subrange reference is used to modify indexing.  Which
-          means that the canonical element names that we have won't work.  */
-       goto use_all;
-
       case VIEW_CONVERT_EXPR:
       case NOP_EXPR:
        /* Similarly, a view/nop explicitly wants to look at an object in a
           type other than the one we've scalarized.  */
        goto use_all;
 
+      case WITH_SIZE_EXPR:
+       /* This is a transparent wrapper.  The entire inner expression really
+          is being used.  */
+       goto use_all;
+
       use_all:
         expr_p = &TREE_OPERAND (inner, 0);
        inner = expr = *expr_p;
+       use_all_p = true;
        break;
 
       default:
 #ifdef ENABLE_CHECKING
        /* Validate that we're not missing any references.  */
-       if (walk_tree (&inner, sra_find_candidate_decl, NULL, NULL))
-         abort ();
+       gcc_assert (!walk_tree (&inner, sra_find_candidate_decl, NULL, NULL));
 #endif
        return;
       }
@@ -765,6 +872,31 @@ sra_walk_modify_expr (tree expr, block_stmt_iterator *bsi,
       return;
     }
 
+  /* If the RHS is scalarizable, handle it.  There are only two cases.  */
+  if (rhs_elt)
+    {
+      if (!rhs_elt->is_scalar)
+       fns->ldst (rhs_elt, lhs, bsi, false);
+      else
+       fns->use (rhs_elt, &TREE_OPERAND (expr, 1), bsi, false, false);
+    }
+
+  /* If it isn't scalarizable, there may be scalarizable variables within, so
+     check for a call or else walk the RHS to see if we need to do any
+     copy-in operations.  We need to do it before the LHS is scalarized so
+     that the statements get inserted in the proper place, before any
+     copy-out operations.  */
+  else
+    {
+      tree call = get_call_expr_in (rhs);
+      if (call)
+       sra_walk_call_expr (call, bsi, fns);
+      else
+       sra_walk_expr (&TREE_OPERAND (expr, 1), bsi, false, fns);
+    }
+
+  /* Likewise, handle the LHS being scalarizable.  We have cases similar
+     to those above, but also want to handle RHS being constant.  */
   if (lhs_elt)
     {
       /* If this is an assignment from a constant, or constructor, then
@@ -780,44 +912,26 @@ sra_walk_modify_expr (tree expr, block_stmt_iterator *bsi,
               && TREE_STATIC (rhs)
               && TREE_READONLY (rhs)
               && targetm.binds_local_p (rhs))
-       {
-         if (DECL_INITIAL (rhs) != error_mark_node)
-           fns->init (lhs_elt, DECL_INITIAL (rhs), bsi);
-       }
+       fns->init (lhs_elt, DECL_INITIAL (rhs), bsi);
 
       /* If this is a copy from a non-scalarizable lvalue, invoke LDST.
         The lvalue requirement prevents us from trying to directly scalarize
         the result of a function call.  Which would result in trying to call
         the function multiple times, and other evil things.  */
-      else if (!lhs_elt->is_scalar && is_gimple_addr_expr_arg (rhs))
+      else if (!lhs_elt->is_scalar && is_gimple_addressable (rhs))
        fns->ldst (lhs_elt, rhs, bsi, true);
-       
+
       /* Otherwise we're being used in some context that requires the
         aggregate to be seen as a whole.  Invoke USE.  */
       else
-       fns->use (lhs_elt, &TREE_OPERAND (expr, 0), bsi, true);
-    }
-  else
-    {
-      /* LHS_ELT being null only means that the LHS as a whole is not a
-        scalarizable reference.  There may be occurrences of scalarizable
-        variables within, which implies a USE.  */
-      sra_walk_expr (&TREE_OPERAND (expr, 0), bsi, true, fns);
+       fns->use (lhs_elt, &TREE_OPERAND (expr, 0), bsi, true, false);
     }
 
-  /* Likewise for the right-hand side.  The only difference here is that
-     we don't have to handle constants, and the RHS may be a call.  */
-  if (rhs_elt)
-    {
-      if (!rhs_elt->is_scalar)
-       fns->ldst (rhs_elt, lhs, bsi, false);
-      else
-       fns->use (rhs_elt, &TREE_OPERAND (expr, 1), bsi, false);
-    }
-  else if (TREE_CODE (rhs) == CALL_EXPR)
-    sra_walk_call_expr (rhs, bsi, fns);
+  /* Similarly to above, LHS_ELT being null only means that the LHS as a
+     whole is not a scalarizable reference.  There may be occurrences of
+     scalarizable variables within, which implies a USE.  */
   else
-    sra_walk_expr (&TREE_OPERAND (expr, 1), bsi, false, fns);
+    sra_walk_expr (&TREE_OPERAND (expr, 0), bsi, true, fns);
 }
 
 /* Entry point to the walk functions.  Search the entire function,
@@ -828,13 +942,13 @@ static void
 sra_walk_function (const struct sra_walk_fns *fns)
 {
   basic_block bb;
-  block_stmt_iterator si;
+  block_stmt_iterator si, ni;
 
   /* ??? Phase 4 could derive some benefit to walking the function in
      dominator tree order.  */
 
   FOR_EACH_BB (bb)
-    for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
+    for (si = bsi_start (bb); !bsi_end_p (si); si = ni)
       {
        tree stmt, t;
        stmt_ann_t ann;
@@ -842,11 +956,12 @@ sra_walk_function (const struct sra_walk_fns *fns)
        stmt = bsi_stmt (si);
        ann = stmt_ann (stmt);
 
+       ni = si;
+       bsi_next (&ni);
+
        /* If the statement has no virtual operands, then it doesn't
           make any structure references that we care about.  */
-       if (NUM_V_MAY_DEFS (V_MAY_DEF_OPS (ann)) == 0
-           && NUM_VUSES (VUSE_OPS (ann)) == 0
-           && NUM_V_MUST_DEFS (V_MUST_DEF_OPS (ann)) == 0)
+       if (ZERO_SSA_OPERANDS (stmt, (SSA_OP_VIRTUAL_DEFS | SSA_OP_VUSE)))
          continue;
 
        switch (TREE_CODE (stmt))
@@ -890,19 +1005,19 @@ sra_walk_function (const struct sra_walk_fns *fns)
 static bool
 find_candidates_for_sra (void)
 {
-  size_t i;
   bool any_set = false;
+  tree var;
+  referenced_var_iterator rvi;
 
-  for (i = 0; i < num_referenced_vars; i++)
+  FOR_EACH_REFERENCED_VAR (var, rvi)
     {
-      tree var = referenced_var (i);
       if (decl_can_be_decomposed_p (var))
         {
-          bitmap_set_bit (sra_candidates, var_ann (var)->uid);
+          bitmap_set_bit (sra_candidates, DECL_UID (var));
           any_set = true;
         }
     }
+
   return any_set;
 }
 
@@ -917,7 +1032,7 @@ find_candidates_for_sra (void)
 static void
 scan_use (struct sra_elt *elt, tree *expr_p ATTRIBUTE_UNUSED,
          block_stmt_iterator *bsi ATTRIBUTE_UNUSED,
-         bool is_output ATTRIBUTE_UNUSED)
+         bool is_output ATTRIBUTE_UNUSED, bool use_all ATTRIBUTE_UNUSED)
 {
   elt->n_uses += 1;
 }
@@ -957,6 +1072,9 @@ scan_dump (struct sra_elt *elt)
 
   for (c = elt->children; c ; c = c->sibling)
     scan_dump (c);
+
+  for (c = elt->groups; c ; c = c->sibling)
+    scan_dump (c);
 }
 
 /* Entry point to phase 2.  Scan the entire function, building up
@@ -968,21 +1086,22 @@ scan_function (void)
   static const struct sra_walk_fns fns = {
     scan_use, scan_copy, scan_init, scan_ldst, true
   };
+  bitmap_iterator bi;
 
   sra_walk_function (&fns);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
-      size_t i;
+      unsigned i;
 
       fputs ("\nScan results:\n", dump_file);
-      EXECUTE_IF_SET_IN_BITMAP (sra_candidates, 0, i,
+      EXECUTE_IF_SET_IN_BITMAP (sra_candidates, 0, i, bi)
        {
          tree var = referenced_var (i);
          struct sra_elt *elt = lookup_element (NULL, var, NULL, NO_INSERT);
          if (elt)
            scan_dump (elt);
-       });
+       }
       fputc ('\n', dump_file);
     }
 }
@@ -1043,7 +1162,7 @@ build_element_name (struct sra_elt *elt)
 {
   build_element_name_1 (elt);
   obstack_1grow (&sra_obstack, '\0');
-  return obstack_finish (&sra_obstack);
+  return XOBFINISH (&sra_obstack, char *);
 }
 
 /* Instantiate an element as an independent variable.  */
@@ -1060,14 +1179,31 @@ instantiate_element (struct sra_elt *elt)
 
   elt->replacement = var = make_rename_temp (elt->type, "SR");
   DECL_SOURCE_LOCATION (var) = DECL_SOURCE_LOCATION (base);
-  TREE_NO_WARNING (var) = TREE_NO_WARNING (base);
-  DECL_ARTIFICIAL (var) = DECL_ARTIFICIAL (base);
+  DECL_ARTIFICIAL (var) = 1;
+
+  if (TREE_THIS_VOLATILE (elt->type))
+    {
+      TREE_THIS_VOLATILE (var) = 1;
+      TREE_SIDE_EFFECTS (var) = 1;
+    }
 
   if (DECL_NAME (base) && !DECL_IGNORED_P (base))
     {
       char *pretty_name = build_element_name (elt);
       DECL_NAME (var) = get_identifier (pretty_name);
       obstack_free (&sra_obstack, pretty_name);
+
+      SET_DECL_DEBUG_EXPR (var, generate_element_ref (elt));
+      DECL_DEBUG_EXPR_IS_FROM (var) = 1;
+      
+      DECL_IGNORED_P (var) = 0;
+      TREE_NO_WARNING (var) = TREE_NO_WARNING (base);
+    }
+  else
+    {
+      DECL_IGNORED_P (var) = 1;
+      /* ??? We can't generate any warning that would be meaningful.  */
+      TREE_NO_WARNING (var) = 1;
     }
 
   if (dump_file)
@@ -1109,10 +1245,19 @@ decide_instantiation_1 (struct sra_elt *elt, unsigned int parent_uses,
     }
   else
     {
-      struct sra_elt *c;
+      struct sra_elt *c, *group;
       unsigned int this_uses = elt->n_uses + parent_uses;
       unsigned int this_copies = elt->n_copies + parent_copies;
 
+      /* Consider groups of sub-elements as weighing in favour of
+        instantiation whatever their size.  */
+      for (group = elt->groups; group ; group = group->sibling)
+       FOR_EACH_ACTUAL_CHILD (c, group)
+         {
+           c->n_uses += group->n_uses;
+           c->n_copies += group->n_copies;
+         }
+
       for (c = elt->children; c ; c = c->sibling)
        decide_instantiation_1 (c, this_uses, this_copies);
     }
@@ -1202,7 +1347,7 @@ instantiate_missing_elements (struct sra_elt *elt)
       break;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -1216,6 +1361,10 @@ decide_block_copy (struct sra_elt *elt)
   struct sra_elt *c;
   bool any_inst;
 
+  /* We shouldn't be invoked on groups of sub-elements as they must
+     behave like their parent as far as block copy is concerned.  */
+  gcc_assert (!elt->is_group);
+
   /* If scalarization is disabled, respect it.  */
   if (elt->cannot_scalarize)
     {
@@ -1228,6 +1377,20 @@ decide_block_copy (struct sra_elt *elt)
          fputc ('\n', dump_file);
        }
 
+      /* Disable scalarization of sub-elements */
+      for (c = elt->children; c; c = c->sibling)
+       {
+         c->cannot_scalarize = 1;
+         decide_block_copy (c);
+       }
+
+      /* Groups behave like their parent.  */
+      for (c = elt->groups; c; c = c->sibling)
+       {
+         c->cannot_scalarize = 1;
+         c->use_block_copy = 1;
+       }
+
       return false;
     }
 
@@ -1240,33 +1403,47 @@ decide_block_copy (struct sra_elt *elt)
       tree size_tree = TYPE_SIZE_UNIT (elt->type);
       bool use_block_copy = true;
 
+      /* Tradeoffs for COMPLEX types pretty much always make it better
+        to go ahead and split the components.  */
+      if (TREE_CODE (elt->type) == COMPLEX_TYPE)
+       use_block_copy = false;
+
       /* Don't bother trying to figure out the rest if the structure is
         so large we can't do easy arithmetic.  This also forces block
         copies for variable sized structures.  */
-      if (host_integerp (size_tree, 1))
+      else if (host_integerp (size_tree, 1))
        {
          unsigned HOST_WIDE_INT full_size, inst_size = 0;
-         unsigned int inst_count;
+         unsigned int max_size, max_count, inst_count, full_count;
+
+         /* If the sra-max-structure-size parameter is 0, then the
+            user has not overridden the parameter and we can choose a
+            sensible default.  */
+         max_size = SRA_MAX_STRUCTURE_SIZE
+           ? SRA_MAX_STRUCTURE_SIZE
+           : MOVE_RATIO * UNITS_PER_WORD;
+         max_count = SRA_MAX_STRUCTURE_COUNT
+           ? SRA_MAX_STRUCTURE_COUNT
+           : MOVE_RATIO;
 
          full_size = tree_low_cst (size_tree, 1);
+         full_count = count_type_elements (elt->type, false);
+         inst_count = sum_instantiated_sizes (elt, &inst_size);
 
-         /* ??? What to do here.  If there are two fields, and we've only 
+         /* ??? What to do here.  If there are two fields, and we've only
             instantiated one, then instantiating the other is clearly a win.
             If there are a large number of fields then the size of the copy
             is much more of a factor.  */
 
          /* If the structure is small, and we've made copies, go ahead
             and instantiate, hoping that the copies will go away.  */
-         if (full_size <= (unsigned) MOVE_RATIO * UNITS_PER_WORD
+         if (full_size <= max_size
+             && (full_count - inst_count) <= max_count
              && elt->n_copies > elt->n_uses)
            use_block_copy = false;
-         else
-           {
-             inst_count = sum_instantiated_sizes (elt, &inst_size);
-
-             if (inst_size * 4 >= full_size * 3)
-               use_block_copy = false;
-           }
+         else if (inst_count * 100 >= full_count * SRA_FIELD_STRUCTURE_RATIO
+                  && inst_size * 100 >= full_size * SRA_FIELD_STRUCTURE_RATIO)
+           use_block_copy = false;
 
          /* In order to avoid block copy, we have to be able to instantiate
             all elements of the type.  See if this is possible.  */
@@ -1275,8 +1452,13 @@ decide_block_copy (struct sra_elt *elt)
                  || !type_can_instantiate_all_elements (elt->type)))
            use_block_copy = true;
        }
+
       elt->use_block_copy = use_block_copy;
 
+      /* Groups behave like their parent.  */
+      for (c = elt->groups; c; c = c->sibling)
+       c->use_block_copy = use_block_copy;
+
       if (dump_file)
        {
          fprintf (dump_file, "Using %s for ",
@@ -1307,14 +1489,15 @@ decide_instantiations (void)
 {
   unsigned int i;
   bool cleared_any;
-  struct bitmap_head_def done_head;
+  bitmap_head done_head;
+  bitmap_iterator bi;
 
   /* We cannot clear bits from a bitmap we're iterating over,
      so save up all the bits to clear until the end.  */
-  bitmap_initialize (&done_head, 1);
+  bitmap_initialize (&done_head, &bitmap_default_obstack);
   cleared_any = false;
 
-  EXECUTE_IF_SET_IN_BITMAP (sra_candidates, 0, i,
+  EXECUTE_IF_SET_IN_BITMAP (sra_candidates, 0, i, bi)
     {
       tree var = referenced_var (i);
       struct sra_elt *elt = lookup_element (NULL, var, NULL, NO_INSERT);
@@ -1329,16 +1512,19 @@ decide_instantiations (void)
          bitmap_set_bit (&done_head, i);
          cleared_any = true;
        }
-    });
+    }
 
   if (cleared_any)
     {
-      bitmap_operation (sra_candidates, sra_candidates, &done_head, 
-                       BITMAP_AND_COMPL);
-      bitmap_operation (needs_copy_in, needs_copy_in, &done_head, 
-                       BITMAP_AND_COMPL);
+      bitmap_and_compl_into (sra_candidates, &done_head);
+      bitmap_and_compl_into (needs_copy_in, &done_head);
     }
   bitmap_clear (&done_head);
+  
+  if (!bitmap_empty_p (sra_candidates))
+    todoflags |= TODO_update_smt_usage;
+
+  mark_set_for_renaming (sra_candidates);
 
   if (dump_file)
     fputc ('\n', dump_file);
@@ -1351,32 +1537,54 @@ decide_instantiations (void)
    renaming. This becomes necessary when we modify all of a non-scalar.  */
 
 static void
-mark_all_v_defs (tree stmt)
+mark_all_v_defs_1 (tree stmt)
 {
-  v_may_def_optype v_may_defs;
-  v_must_def_optype v_must_defs;
-  size_t i, n;
+  tree sym;
+  ssa_op_iter iter;
 
-  get_stmt_operands (stmt);
+  update_stmt_if_modified (stmt);
 
-  v_may_defs = V_MAY_DEF_OPS (stmt_ann (stmt));
-  n = NUM_V_MAY_DEFS (v_may_defs);
-  for (i = 0; i < n; i++)
+  FOR_EACH_SSA_TREE_OPERAND (sym, stmt, iter, SSA_OP_ALL_VIRTUALS)
     {
-      tree sym = V_MAY_DEF_RESULT (v_may_defs, i);
       if (TREE_CODE (sym) == SSA_NAME)
        sym = SSA_NAME_VAR (sym);
-      bitmap_set_bit (vars_to_rename, var_ann (sym)->uid);
+      mark_sym_for_renaming (sym);
     }
+}
 
-  v_must_defs = V_MUST_DEF_OPS (stmt_ann (stmt));
-  n = NUM_V_MUST_DEFS (v_must_defs);
-  for (i = 0; i < n; i++)
+
+/* Mark all the variables in virtual operands in all the statements in
+   LIST for renaming.  */
+
+static void
+mark_all_v_defs (tree list)
+{
+  if (TREE_CODE (list) != STATEMENT_LIST)
+    mark_all_v_defs_1 (list);
+  else
     {
-      tree sym = V_MUST_DEF_OP (v_must_defs, i);
-      if (TREE_CODE (sym) == SSA_NAME)
-       sym = SSA_NAME_VAR (sym);
-      bitmap_set_bit (vars_to_rename, var_ann (sym)->uid);
+      tree_stmt_iterator i;
+      for (i = tsi_start (list); !tsi_end_p (i); tsi_next (&i))
+       mark_all_v_defs_1 (tsi_stmt (i));
+    }
+}
+
+/* Mark every replacement under ELT with TREE_NO_WARNING.  */
+
+static void
+mark_no_warning (struct sra_elt *elt)
+{
+  if (!elt->all_no_warning)
+    {
+      if (elt->replacement)
+       TREE_NO_WARNING (elt->replacement) = 1;
+      else
+       {
+         struct sra_elt *c;
+         FOR_EACH_ACTUAL_CHILD (c, elt)
+           mark_no_warning (c);
+       }
+      elt->all_no_warning = true;
     }
 }
 
@@ -1388,19 +1596,32 @@ generate_one_element_ref (struct sra_elt *elt, tree base)
   switch (TREE_CODE (TREE_TYPE (base)))
     {
     case RECORD_TYPE:
-      return build (COMPONENT_REF, elt->type, base, elt->element, NULL);
+      {
+       tree field = elt->element;
+
+       /* Watch out for compatible records with differing field lists.  */
+       if (DECL_FIELD_CONTEXT (field) != TYPE_MAIN_VARIANT (TREE_TYPE (base)))
+         field = find_compatible_field (TREE_TYPE (base), field);
+
+        return build3 (COMPONENT_REF, elt->type, base, field, NULL);
+      }
 
     case ARRAY_TYPE:
-      return build (ARRAY_REF, elt->type, base, elt->element, NULL, NULL);
+      todoflags |= TODO_update_smt_usage;
+      if (TREE_CODE (elt->element) == RANGE_EXPR)
+       return build4 (ARRAY_RANGE_REF, elt->type, base,
+                      TREE_OPERAND (elt->element, 0), NULL, NULL);
+      else
+       return build4 (ARRAY_REF, elt->type, base, elt->element, NULL, NULL);
 
     case COMPLEX_TYPE:
       if (elt->element == integer_zero_node)
-       return build (REALPART_EXPR, elt->type, base);
+       return build1 (REALPART_EXPR, elt->type, base);
       else
-       return build (IMAGPART_EXPR, elt->type, base);
+       return build1 (IMAGPART_EXPR, elt->type, base);
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -1427,17 +1648,32 @@ generate_copy_inout (struct sra_elt *elt, bool copy_out, tree expr,
   struct sra_elt *c;
   tree t;
 
-  if (elt->replacement)
+  if (!copy_out && TREE_CODE (expr) == SSA_NAME
+      && TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
+    {
+      tree r, i;
+
+      c = lookup_element (elt, integer_zero_node, NULL, NO_INSERT);
+      r = c->replacement;
+      c = lookup_element (elt, integer_one_node, NULL, NO_INSERT);
+      i = c->replacement;
+
+      t = build2 (COMPLEX_EXPR, elt->type, r, i);
+      t = build2 (MODIFY_EXPR, void_type_node, expr, t);
+      SSA_NAME_DEF_STMT (expr) = t;
+      append_to_statement_list (t, list_p);
+    }
+  else if (elt->replacement)
     {
       if (copy_out)
-       t = build (MODIFY_EXPR, void_type_node, elt->replacement, expr);
+       t = build2 (MODIFY_EXPR, void_type_node, elt->replacement, expr);
       else
-       t = build (MODIFY_EXPR, void_type_node, expr, elt->replacement);
+       t = build2 (MODIFY_EXPR, void_type_node, expr, elt->replacement);
       append_to_statement_list (t, list_p);
     }
   else
     {
-      for (c = elt->children; c ; c = c->sibling)
+      FOR_EACH_ACTUAL_CHILD (c, elt)
        {
          t = generate_one_element_ref (c, unshare_expr (expr));
          generate_copy_inout (c, copy_out, t, list_p);
@@ -1454,11 +1690,10 @@ generate_element_copy (struct sra_elt *dst, struct sra_elt *src, tree *list_p)
 {
   struct sra_elt *dc, *sc;
 
-  for (dc = dst->children; dc ; dc = dc->sibling)
+  FOR_EACH_ACTUAL_CHILD (dc, dst)
     {
       sc = lookup_element (src, dc->element, NULL, NO_INSERT);
-      if (sc == NULL)
-       abort ();
+      gcc_assert (sc);
       generate_element_copy (dc, sc, list_p);
     }
 
@@ -1466,11 +1701,10 @@ generate_element_copy (struct sra_elt *dst, struct sra_elt *src, tree *list_p)
     {
       tree t;
 
-      if (src->replacement == NULL)
-       abort ();
+      gcc_assert (src->replacement);
 
-      t = build (MODIFY_EXPR, void_type_node, dst->replacement,
-                src->replacement);
+      t = build2 (MODIFY_EXPR, void_type_node, dst->replacement,
+                 src->replacement);
       append_to_statement_list (t, list_p);
     }
 }
@@ -1485,54 +1719,74 @@ generate_element_zero (struct sra_elt *elt, tree *list_p)
 {
   struct sra_elt *c;
 
-  for (c = elt->children; c ; c = c->sibling)
+  if (elt->visited)
+    {
+      elt->visited = false;
+      return;
+    }
+
+  FOR_EACH_ACTUAL_CHILD (c, elt)
     generate_element_zero (c, list_p);
 
-  if (elt->visited)
-    elt->visited = false;
-  else if (elt->replacement)
+  if (elt->replacement)
     {
       tree t;
 
-      if (elt->is_scalar)
-       t = fold_convert (elt->type, integer_zero_node);
-      else
-       /* We generated a replacement for a non-scalar?  */
-       abort ();
+      gcc_assert (elt->is_scalar);
+      t = fold_convert (elt->type, integer_zero_node);
 
-      t = build (MODIFY_EXPR, void_type_node, elt->replacement, t);
+      t = build2 (MODIFY_EXPR, void_type_node, elt->replacement, t);
       append_to_statement_list (t, list_p);
     }
 }
 
+/* Generate an assignment VAR = INIT, where INIT may need gimplification.
+   Add the result to *LIST_P.  */
+
+static void
+generate_one_element_init (tree var, tree init, tree *list_p)
+{
+  /* The replacement can be almost arbitrarily complex.  Gimplify.  */
+  tree stmt = build2 (MODIFY_EXPR, void_type_node, var, init);
+  gimplify_and_add (stmt, list_p);
+}
+
 /* Generate a set of assignment statements in *LIST_P to set all instantiated
    elements under ELT with the contents of the initializer INIT.  In addition,
    mark all assigned elements VISITED; this allows easy coordination with
-   generate_element_zero.  */
+   generate_element_zero.  Return false if we found a case we couldn't
+   handle.  */
 
-static void
-generate_element_init (struct sra_elt *elt, tree init, tree *list_p)
+static bool
+generate_element_init_1 (struct sra_elt *elt, tree init, tree *list_p)
 {
-  enum tree_code init_code = TREE_CODE (init);
+  bool result = true;
+  enum tree_code init_code;
   struct sra_elt *sub;
   tree t;
+  unsigned HOST_WIDE_INT idx;
+  tree value, purpose;
+
+  /* We can be passed DECL_INITIAL of a static variable.  It might have a
+     conversion, which we strip off here.  */
+  STRIP_USELESS_TYPE_CONVERSION (init);
+  init_code = TREE_CODE (init);
 
   if (elt->is_scalar)
     {
       if (elt->replacement)
        {
-         t = build (MODIFY_EXPR, void_type_node, elt->replacement, init);
-         append_to_statement_list (t, list_p);
+         generate_one_element_init (elt->replacement, init, list_p);
          elt->visited = true;
        }
-      return;
+      return result;
     }
 
   switch (init_code)
     {
     case COMPLEX_CST:
     case COMPLEX_EXPR:
-      for (sub = elt->children; sub ; sub = sub->sibling)
+      FOR_EACH_ACTUAL_CHILD (sub, elt)
        {
          if (sub->element == integer_zero_node)
            t = (init_code == COMPLEX_EXPR
@@ -1540,23 +1794,68 @@ generate_element_init (struct sra_elt *elt, tree init, tree *list_p)
          else
            t = (init_code == COMPLEX_EXPR
                 ? TREE_OPERAND (init, 1) : TREE_IMAGPART (init));
-         generate_element_init (sub, t, list_p);
+         result &= generate_element_init_1 (sub, t, list_p);
        }
       break;
 
     case CONSTRUCTOR:
-      for (t = CONSTRUCTOR_ELTS (init); t ; t = TREE_CHAIN (t))
+      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, purpose, value)
        {
-         sub = lookup_element (elt, TREE_PURPOSE (t), NULL, NO_INSERT);
-         if (sub == NULL)
-           continue;
-         generate_element_init (sub, TREE_VALUE (t), list_p);
+         if (TREE_CODE (purpose) == RANGE_EXPR)
+           {
+             tree lower = TREE_OPERAND (purpose, 0);
+             tree upper = TREE_OPERAND (purpose, 1);
+
+             while (1)
+               {
+                 sub = lookup_element (elt, lower, NULL, NO_INSERT);
+                 if (sub != NULL)
+                   result &= generate_element_init_1 (sub, value, list_p);
+                 if (tree_int_cst_equal (lower, upper))
+                   break;
+                 lower = int_const_binop (PLUS_EXPR, lower,
+                                          integer_one_node, true);
+               }
+           }
+         else
+           {
+             sub = lookup_element (elt, purpose, NULL, NO_INSERT);
+             if (sub != NULL)
+               result &= generate_element_init_1 (sub, value, list_p);
+           }
        }
       break;
 
     default:
-      abort ();
+      elt->visited = true;
+      result = false;
     }
+
+  return result;
+}
+
+/* A wrapper function for generate_element_init_1 that handles cleanup after
+   gimplification.  */
+
+static bool
+generate_element_init (struct sra_elt *elt, tree init, tree *list_p)
+{
+  bool ret;
+
+  push_gimplify_context ();
+  ret = generate_element_init_1 (elt, init, list_p);
+  pop_gimplify_context (NULL);
+
+  /* The replacement can expose previously unreferenced variables.  */
+  if (ret && *list_p)
+    {
+      tree_stmt_iterator i;
+
+      for (i = tsi_start (*list_p); !tsi_end_p (i); tsi_next (&i))
+       find_new_referenced_vars (tsi_stmt_ptr (i));
+    }
+
+  return ret;
 }
 
 /* Insert STMT on all the outgoing edges out of BB.  Note that if BB
@@ -1567,10 +1866,11 @@ void
 insert_edge_copies (tree stmt, basic_block bb)
 {
   edge e;
+  edge_iterator ei;
   bool first_copy;
 
   first_copy = true;
-  for (e = bb->succ; e; e = e->succ_next)
+  FOR_EACH_EDGE (e, ei, bb->succs)
     {
       /* We don't need to insert copies on abnormal edges.  The
         value of the scalar replacement is not guaranteed to
@@ -1583,14 +1883,14 @@ insert_edge_copies (tree stmt, basic_block bb)
              first_copy = false;
            }
          else
-           bsi_insert_on_edge (e, lhd_unsave_expr_now (stmt));
+           bsi_insert_on_edge (e, unsave_expr_now (stmt));
        }
     }
 }
 
 /* Helper function to insert LIST before BSI, and set up line number info.  */
 
-static void
+void
 sra_insert_before (block_stmt_iterator *bsi, tree list)
 {
   tree stmt = bsi_stmt (*bsi);
@@ -1602,7 +1902,7 @@ sra_insert_before (block_stmt_iterator *bsi, tree list)
 
 /* Similarly, but insert after BSI.  Handles insertion onto edges as well.  */
 
-static void
+void
 sra_insert_after (block_stmt_iterator *bsi, tree list)
 {
   tree stmt = bsi_stmt (*bsi);
@@ -1613,7 +1913,7 @@ sra_insert_after (block_stmt_iterator *bsi, tree list)
   if (stmt_ends_bb_p (stmt))
     insert_edge_copies (list, bsi->bb);
   else
-    bsi_insert_after (bsi, list, BSI_CONTINUE_LINKING);
+    bsi_insert_after (bsi, list, BSI_SAME_STMT);
 }
 
 /* Similarly, but replace the statement at BSI.  */
@@ -1622,7 +1922,7 @@ static void
 sra_replace (block_stmt_iterator *bsi, tree list)
 {
   sra_insert_before (bsi, list);
-  bsi_remove (bsi);
+  bsi_remove (bsi, false);
   if (bsi_end_p (*bsi))
     *bsi = bsi_last (bsi->bb);
   else
@@ -1630,12 +1930,12 @@ sra_replace (block_stmt_iterator *bsi, tree list)
 }
 
 /* Scalarize a USE.  To recap, this is either a simple reference to ELT,
-   if elt is scalar, or some ocurrence of ELT that requires a complete
+   if elt is scalar, or some occurrence of ELT that requires a complete
    aggregate.  IS_OUTPUT is true if ELT is being modified.  */
 
 static void
 scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
-              bool is_output)
+              bool is_output, bool use_all)
 {
   tree list = NULL, stmt = bsi_stmt (*bsi);
 
@@ -1646,7 +1946,7 @@ scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
       if (is_output)
        mark_all_v_defs (stmt);
       *expr_p = elt->replacement;
-      modify_stmt (stmt);
+      update_stmt (stmt);
     }
   else
     {
@@ -1666,13 +1966,15 @@ scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
       generate_copy_inout (elt, is_output, generate_element_ref (elt), &list);
       if (list == NULL)
        return;
+      mark_all_v_defs (list);
       if (is_output)
+       sra_insert_after (bsi, list);
+      else
        {
-         mark_all_v_defs (expr_first (list));
-         sra_insert_after (bsi, list);
+         sra_insert_before (bsi, list);
+         if (use_all)
+           mark_no_warning (elt);
        }
-      else
-       sra_insert_before (bsi, list);
     }
 }
 
@@ -1690,21 +1992,18 @@ scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
       /* If we have two scalar operands, modify the existing statement.  */
       stmt = bsi_stmt (*bsi);
 
-#ifdef ENABLE_CHECKING
       /* See the commentary in sra_walk_function concerning
         RETURN_EXPR, and why we should never see one here.  */
-      if (TREE_CODE (stmt) != MODIFY_EXPR)
-       abort ();
-#endif
+      gcc_assert (TREE_CODE (stmt) == MODIFY_EXPR);
 
       TREE_OPERAND (stmt, 0) = lhs_elt->replacement;
       TREE_OPERAND (stmt, 1) = rhs_elt->replacement;
-      modify_stmt (stmt);
+      update_stmt (stmt);
     }
   else if (lhs_elt->use_block_copy || rhs_elt->use_block_copy)
     {
       /* If either side requires a block copy, then sync the RHS back
-        to the original structure, leave the original assignment 
+        to the original structure, leave the original assignment
         statement (which will perform the block copy), then load the
         LHS values out of its now-updated original structure.  */
       /* ??? Could perform a modified pair-wise element copy.  That
@@ -1716,7 +2015,7 @@ scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
                           generate_element_ref (rhs_elt), &list);
       if (list)
        {
-         mark_all_v_defs (expr_first (list));
+         mark_all_v_defs (list);
          sra_insert_before (bsi, list);
        }
 
@@ -1724,7 +2023,10 @@ scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
       generate_copy_inout (lhs_elt, true,
                           generate_element_ref (lhs_elt), &list);
       if (list)
-       sra_insert_after (bsi, list);
+       {
+         mark_all_v_defs (list);
+         sra_insert_after (bsi, list);
+       }
     }
   else
     {
@@ -1737,8 +2039,8 @@ scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
 
       list = NULL;
       generate_element_copy (lhs_elt, rhs_elt, &list);
-      if (list == NULL)
-       abort ();
+      gcc_assert (list);
+      mark_all_v_defs (list);
       sra_replace (bsi, list);
     }
 }
@@ -1751,31 +2053,54 @@ scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
 static void
 scalarize_init (struct sra_elt *lhs_elt, tree rhs, block_stmt_iterator *bsi)
 {
+  bool result = true;
   tree list = NULL;
 
   /* Generate initialization statements for all members extant in the RHS.  */
   if (rhs)
-    generate_element_init (lhs_elt, rhs, &list);
+    {
+      /* Unshare the expression just in case this is from a decl's initial.  */
+      rhs = unshare_expr (rhs);
+      result = generate_element_init (lhs_elt, rhs, &list);
+    }
 
   /* CONSTRUCTOR is defined such that any member not mentioned is assigned
      a zero value.  Initialize the rest of the instantiated elements.  */
   generate_element_zero (lhs_elt, &list);
-  if (list == NULL)
-    return;
 
-  if (lhs_elt->use_block_copy)
+  if (!result)
+    {
+      /* If we failed to convert the entire initializer, then we must
+        leave the structure assignment in place and must load values
+        from the structure into the slots for which we did not find
+        constants.  The easiest way to do this is to generate a complete
+        copy-out, and then follow that with the constant assignments
+        that we were able to build.  DCE will clean things up.  */
+      tree list0 = NULL;
+      generate_copy_inout (lhs_elt, true, generate_element_ref (lhs_elt),
+                          &list0);
+      append_to_statement_list (list, &list0);
+      list = list0;
+    }
+
+  if (lhs_elt->use_block_copy || !result)
     {
       /* Since LHS is not fully instantiated, we must leave the structure
         assignment in place.  Treating this case differently from a USE
         exposes constants to later optimizations.  */
-      mark_all_v_defs (expr_first (list));
-      sra_insert_after (bsi, list);
+      if (list)
+       {
+         mark_all_v_defs (list);
+         sra_insert_after (bsi, list);
+       }
     }
   else
     {
       /* The LHS is fully instantiated.  The list of initializations
         replaces the original structure assignment.  */
+      gcc_assert (list);
       mark_all_v_defs (bsi_stmt (*bsi));
+      mark_all_v_defs (list);
       sra_replace (bsi, list);
     }
 }
@@ -1793,7 +2118,7 @@ mark_notrap (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
       TREE_THIS_NOTRAP (t) = 1;
       *walk_subtrees = 0;
     }
-  else if (DECL_P (t) || TYPE_P (t))
+  else if (IS_TYPE_OR_DECL_P (t))
     *walk_subtrees = 0;
 
   return NULL;
@@ -1808,14 +2133,13 @@ scalarize_ldst (struct sra_elt *elt, tree other,
                block_stmt_iterator *bsi, bool is_output)
 {
   /* Shouldn't have gotten called for a scalar.  */
-  if (elt->replacement)
-    abort ();
+  gcc_assert (!elt->replacement);
 
   if (elt->use_block_copy)
     {
       /* Since ELT is not fully instantiated, we have to leave the
         block copy in place.  Treat this as a USE.  */
-      scalarize_use (elt, NULL, bsi, is_output);
+      scalarize_use (elt, NULL, bsi, is_output, false);
     }
   else
     {
@@ -1827,8 +2151,8 @@ scalarize_ldst (struct sra_elt *elt, tree other,
 
       mark_all_v_defs (stmt);
       generate_copy_inout (elt, is_output, other, &list);
-      if (list == NULL)
-       abort ();
+      mark_all_v_defs (list);
+      gcc_assert (list);
 
       /* Preserve EH semantics.  */
       if (stmt_ends_bb_p (stmt))
@@ -1843,7 +2167,7 @@ scalarize_ldst (struct sra_elt *elt, tree other,
 
          /* Replace the old statement with this new representative.  */
          bsi_replace (bsi, first, true);
-         
+
          if (!tsi_end_p (tsi))
            {
              /* If any reference would trap, then they all would.  And more
@@ -1872,17 +2196,21 @@ static void
 scalarize_parms (void)
 {
   tree list = NULL;
-  size_t i;
+  unsigned i;
+  bitmap_iterator bi;
 
-  EXECUTE_IF_SET_IN_BITMAP (needs_copy_in, 0, i,
-    { 
+  EXECUTE_IF_SET_IN_BITMAP (needs_copy_in, 0, i, bi)
+    {
       tree var = referenced_var (i);
       struct sra_elt *elt = lookup_element (NULL, var, NULL, NO_INSERT);
       generate_copy_inout (elt, true, var, &list);
-    });
+    }
 
   if (list)
-    insert_edge_copies (list, ENTRY_BLOCK_PTR);
+    {
+      insert_edge_copies (list, ENTRY_BLOCK_PTR);
+      mark_all_v_defs (list);
+    }
 }
 
 /* Entry point to phase 4.  Update the function to match replacements.  */
@@ -1896,7 +2224,7 @@ scalarize_function (void)
 
   sra_walk_function (&fns);
   scalarize_parms ();
-  bsi_commit_edge_inserts (NULL);
+  bsi_commit_edge_inserts ();
 }
 
 \f
@@ -1920,6 +2248,10 @@ dump_sra_elt_name (FILE *f, struct sra_elt *elt)
            fputc ('.', f);
          print_generic_expr (f, elt->element, dump_flags);
        }
+      else if (TREE_CODE (elt->element) == RANGE_EXPR)
+       fprintf (f, "["HOST_WIDE_INT_PRINT_DEC".."HOST_WIDE_INT_PRINT_DEC"]",
+                TREE_INT_CST_LOW (TREE_OPERAND (elt->element, 0)),
+                TREE_INT_CST_LOW (TREE_OPERAND (elt->element, 1)));
       else
        fprintf (f, "[" HOST_WIDE_INT_PRINT_DEC "]",
                 TREE_INT_CST_LOW (elt->element));
@@ -1935,17 +2267,27 @@ debug_sra_elt_name (struct sra_elt *elt)
   fputc ('\n', stderr);
 }
 
+void 
+sra_init_cache (void)
+{
+  if (sra_type_decomp_cache) 
+    return;
+
+  sra_type_decomp_cache = BITMAP_ALLOC (NULL);
+  sra_type_inst_cache = BITMAP_ALLOC (NULL);
+}
+
 /* Main entry point.  */
 
-static void
+static unsigned int
 tree_sra (void)
 {
   /* Initialize local variables.  */
+  todoflags = 0;
   gcc_obstack_init (&sra_obstack);
-  sra_candidates = BITMAP_XMALLOC ();
-  needs_copy_in = BITMAP_XMALLOC ();
-  sra_type_decomp_cache = BITMAP_XMALLOC ();
-  sra_type_inst_cache = BITMAP_XMALLOC ();
+  sra_candidates = BITMAP_ALLOC (NULL);
+  needs_copy_in = BITMAP_ALLOC (NULL);
+  sra_init_cache ();
   sra_map = htab_create (101, sra_elt_hash, sra_elt_eq, NULL);
 
   /* Scan.  If we find anything, instantiate and scalarize.  */
@@ -1959,11 +2301,12 @@ tree_sra (void)
   /* Free allocated memory.  */
   htab_delete (sra_map);
   sra_map = NULL;
-  BITMAP_XFREE (sra_candidates);
-  BITMAP_XFREE (needs_copy_in);
-  BITMAP_XFREE (sra_type_decomp_cache);
-  BITMAP_XFREE (sra_type_inst_cache);
+  BITMAP_FREE (sra_candidates);
+  BITMAP_FREE (needs_copy_in);
+  BITMAP_FREE (sra_type_decomp_cache);
+  BITMAP_FREE (sra_type_inst_cache);
   obstack_free (&sra_obstack, NULL);
+  return todoflags;
 }
 
 static bool
@@ -1972,7 +2315,7 @@ gate_sra (void)
   return flag_tree_sra != 0;
 }
 
-struct tree_opt_pass pass_sra = 
+struct tree_opt_pass pass_sra =
 {
   "sra",                               /* name */
   gate_sra,                            /* gate */
@@ -1981,10 +2324,12 @@ struct tree_opt_pass pass_sra =
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
   TV_TREE_SRA,                         /* tv_id */
-  PROP_cfg | PROP_ssa,                 /* properties_required */
+  PROP_cfg | PROP_ssa | PROP_alias,    /* properties_required */
   0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
+  PROP_smt_usage,                      /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func | TODO_rename_vars
-    | TODO_ggc_collect | TODO_verify_ssa  /* todo_flags_finish */
+  TODO_dump_func /* todo_flags_finish */
+  | TODO_update_ssa
+  | TODO_ggc_collect | TODO_verify_ssa,
+  0                                    /* letter */
 };