]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/tree-sra.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / tree-sra.c
diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
new file mode 100644 (file)
index 0000000..6149ff5
--- /dev/null
@@ -0,0 +1,3728 @@
+/* Scalar Replacement of Aggregates (SRA) converts some structure
+   references into scalar references, exposing them to the scalar
+   optimizers.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   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 3, 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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "ggc.h"
+#include "tree.h"
+
+/* These RTL headers are needed for basic-block.h.  */
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "diagnostic.h"
+#include "langhooks.h"
+#include "tree-inline.h"
+#include "tree-flow.h"
+#include "gimple.h"
+#include "tree-dump.h"
+#include "tree-pass.h"
+#include "timevar.h"
+#include "flags.h"
+#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
+   aggregates into smaller aggregates.  In the process we may find that some
+   bits of the larger aggregate can be deleted as unreferenced.
+
+   This substitution is done globally.  More localized substitutions would
+   be the purvey of a load-store motion pass.
+
+   The optimization proceeds in phases:
+
+     (1) Identify variables that have types that are candidates for
+        decomposition.
+
+     (2) Scan the function looking for the ways these variables are used.
+        In particular we're interested in the number of times a variable
+        (or member) is needed as a complete unit, and the number of times
+        a variable (or member) is copied.
+
+     (3) Based on the usage profile, instantiate substitution variables.
+
+     (4) Scan the function making replacements.
+*/
+
+
+/* True if this is the "early" pass, before inlining.  */
+static bool early_sra;
+
+/* 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;
+
+/* Set of scalarizable PARM_DECLs that need copy-in operations at the
+   beginning of the function.  */
+static bitmap needs_copy_in;
+
+/* Sets of bit pairs that cache type decomposition and instantiation.  */
+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 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 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.  */
+  tree type;
+
+  /* A VAR_DECL, for any sub-element we've decided to replace.  */
+  tree replacement;
+
+  /* The number of times the element is referenced as a whole.  I.e.
+     given "a.b.c", this would be incremented for C, but not for A or B.  */
+  unsigned int n_uses;
+
+  /* The number of times the element is copied to or from another
+     scalarizable element.  */
+  unsigned int n_copies;
+
+  /* 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;
+
+  /* True if we've decided that structure-to-structure assignment
+     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;
+
+  /* True if there is BIT_FIELD_REF on the lhs with a vector. */
+  bool is_vector_lhs;
+
+  /* 1 if the element is a field that is part of a block, 2 if the field
+     is the block itself, 0 if it's neither.  */
+  char in_bitfld_block;
+};
+
+#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 behavior, and allows SRA to function
+   reasonably on larger records.  */
+static htab_t sra_map;
+
+/* All structures are allocated out of the following obstack.  */
+static struct obstack sra_obstack;
+
+/* Debugging functions.  */
+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 *);
+static gimple_seq sra_build_assignment (tree dst, tree src);
+static void mark_all_v_defs_seq (gimple_seq);
+static void mark_all_v_defs_stmt (gimple);
+
+\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, DECL_UID (decl));
+}
+
+/* Return true if TYPE is a scalar type.  */
+
+static bool
+is_sra_scalar_type (tree type)
+{
+  enum tree_code code = TREE_CODE (type);
+  return (code == INTEGER_TYPE || code == REAL_TYPE || code == VECTOR_TYPE
+         || code == FIXED_POINT_TYPE
+         || code == ENUMERAL_TYPE || code == BOOLEAN_TYPE
+         || code == POINTER_TYPE || code == OFFSET_TYPE
+         || code == REFERENCE_TYPE);
+}
+
+/* Return true if TYPE can be decomposed into a set of independent variables.
+
+   Note that this doesn't imply that all elements of TYPE can be
+   instantiated, just that if we decide to break up the type into
+   separate pieces that it can be done.  */
+
+bool
+sra_type_can_be_decomposed_p (tree type)
+{
+  unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
+  tree t;
+
+  /* Avoid searching the same type twice.  */
+  if (bitmap_bit_p (sra_type_decomp_cache, cache+0))
+    return true;
+  if (bitmap_bit_p (sra_type_decomp_cache, cache+1))
+    return false;
+
+  /* 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.  */
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      {
+       bool saw_one_field = false;
+
+       for (t = TYPE_FIELDS (type); t ; t = TREE_CHAIN (t))
+         if (TREE_CODE (t) == FIELD_DECL)
+           {
+             /* Reject incorrectly represented bit fields.  */
+             if (DECL_BIT_FIELD (t)
+                 && INTEGRAL_TYPE_P (TREE_TYPE (t))
+                 && (tree_low_cst (DECL_SIZE (t), 1)
+                     != TYPE_PRECISION (TREE_TYPE (t))))
+               goto fail;
+
+             saw_one_field = true;
+           }
+
+       /* Record types must have at least one field.  */
+       if (!saw_one_field)
+         goto fail;
+      }
+      break;
+
+    case ARRAY_TYPE:
+      /* Array types must have a fixed lower and upper bound.  */
+      t = TYPE_DOMAIN (type);
+      if (t == NULL)
+       goto fail;
+      if (TYPE_MIN_VALUE (t) == NULL || !TREE_CONSTANT (TYPE_MIN_VALUE (t)))
+       goto fail;
+      if (TYPE_MAX_VALUE (t) == NULL || !TREE_CONSTANT (TYPE_MAX_VALUE (t)))
+       goto fail;
+      break;
+
+    case COMPLEX_TYPE:
+      break;
+
+    default:
+      goto fail;
+    }
+
+  bitmap_set_bit (sra_type_decomp_cache, cache+0);
+  return true;
+
+ fail:
+  bitmap_set_bit (sra_type_decomp_cache, cache+1);
+  return false;
+}
+
+/* Returns true if the TYPE is one of the available va_list types.
+   Otherwise it returns false.
+   Note, that for multiple calling conventions there can be more
+   than just one va_list type present.  */
+
+static bool
+is_va_list_type (tree type)
+{
+  tree h;
+
+  if (type == NULL_TREE)
+    return false;
+  h = targetm.canonical_va_list_type (type);
+  if (h == NULL_TREE)
+    return false;
+  if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (h))
+        return true;
+  return false;
+}
+
+/* Return true if DECL can be decomposed into a set of independent
+   (though not necessarily scalar) variables.  */
+
+static bool
+decl_can_be_decomposed_p (tree var)
+{
+  /* Early out for scalars.  */
+  if (is_sra_scalar_type (TREE_TYPE (var)))
+    return false;
+
+  /* The variable must not be aliased.  */
+  if (!is_gimple_non_addressable (var))
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Cannot scalarize variable ");
+         print_generic_expr (dump_file, var, dump_flags);
+         fprintf (dump_file, " because it must live in memory\n");
+       }
+      return false;
+    }
+
+  /* The variable must not be volatile.  */
+  if (TREE_THIS_VOLATILE (var))
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Cannot scalarize variable ");
+         print_generic_expr (dump_file, var, dump_flags);
+         fprintf (dump_file, " because it is declared volatile\n");
+       }
+      return false;
+    }
+
+  /* We must be able to decompose the variable's type.  */
+  if (!sra_type_can_be_decomposed_p (TREE_TYPE (var)))
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Cannot scalarize variable ");
+         print_generic_expr (dump_file, var, dump_flags);
+         fprintf (dump_file, " because its type cannot be decomposed\n");
+       }
+      return false;
+    }
+
+  /* HACK: if we decompose a va_list_type_node before inlining, then we'll
+     confuse tree-stdarg.c, and we won't be able to figure out which and
+     how many arguments are accessed.  This really should be improved in
+     tree-stdarg.c, as the decomposition is truly a win.  This could also
+     be fixed if the stdarg pass ran early, but this can't be done until
+     we've aliasing information early too.  See PR 30791.  */
+  if (early_sra && is_va_list_type (TREE_TYPE (var)))
+    return false;
+
+  return true;
+}
+
+/* Return true if TYPE can be *completely* decomposed into scalars.  */
+
+static bool
+type_can_instantiate_all_elements (tree type)
+{
+  if (is_sra_scalar_type (type))
+    return true;
+  if (!sra_type_can_be_decomposed_p (type))
+    return false;
+
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      {
+       unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
+       tree f;
+
+       if (bitmap_bit_p (sra_type_inst_cache, cache+0))
+         return true;
+       if (bitmap_bit_p (sra_type_inst_cache, cache+1))
+         return false;
+
+       for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+         if (TREE_CODE (f) == FIELD_DECL)
+           {
+             if (!type_can_instantiate_all_elements (TREE_TYPE (f)))
+               {
+                 bitmap_set_bit (sra_type_inst_cache, cache+1);
+                 return false;
+               }
+           }
+
+       bitmap_set_bit (sra_type_inst_cache, cache+0);
+       return true;
+      }
+
+    case ARRAY_TYPE:
+      return type_can_instantiate_all_elements (TREE_TYPE (type));
+
+    case COMPLEX_TYPE:
+      return true;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Test whether ELT or some sub-element cannot be scalarized.  */
+
+static bool
+can_completely_scalarize_p (struct sra_elt *elt)
+{
+  struct sra_elt *c;
+
+  if (elt->cannot_scalarize)
+    return false;
+
+  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;
+
+  return true;
+}
+
+\f
+/* A simplified tree hashing algorithm that only handles the types of
+   trees we expect to find in sra_elt->element.  */
+
+static hashval_t
+sra_hash_tree (tree t)
+{
+  hashval_t h;
+
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      h = DECL_UID (t);
+      break;
+
+    case INTEGER_CST:
+      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;
+
+    case BIT_FIELD_REF:
+      /* Don't take operand 0 into account, that's our parent.  */
+      h = iterative_hash_expr (TREE_OPERAND (t, 1), 0);
+      h = iterative_hash_expr (TREE_OPERAND (t, 2), h);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return h;
+}
+
+/* Hash function for type SRA_PAIR.  */
+
+static hashval_t
+sra_elt_hash (const void *x)
+{
+  const struct sra_elt *const e = (const struct sra_elt *) x;
+  const struct sra_elt *p;
+  hashval_t h;
+
+  h = sra_hash_tree (e->element);
+
+  /* Take into account everything except bitfield blocks back up the
+     chain.  Given that chain lengths are rarely very long, this
+     should be acceptable.  If we 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)
+    if (!p->in_bitfld_block)
+      h = (h * 65521) ^ sra_hash_tree (p->element);
+
+  return h;
+}
+
+/* Equality function for type SRA_PAIR.  */
+
+static int
+sra_elt_eq (const void *x, const void *y)
+{
+  const struct sra_elt *const a = (const struct sra_elt *) x;
+  const struct sra_elt *const b = (const struct sra_elt *) y;
+  tree ae, be;
+  const struct sra_elt *ap = a->parent;
+  const struct sra_elt *bp = b->parent;
+
+  if (ap)
+    while (ap->in_bitfld_block)
+      ap = ap->parent;
+  if (bp)
+    while (bp->in_bitfld_block)
+      bp = bp->parent;
+
+  if (ap != bp)
+    return false;
+
+  ae = a->element;
+  be = b->element;
+
+  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);
+
+    case BIT_FIELD_REF:
+      return
+       tree_int_cst_equal (TREE_OPERAND (ae, 1), TREE_OPERAND (be, 1))
+       && tree_int_cst_equal (TREE_OPERAND (ae, 2), TREE_OPERAND (be, 2));
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Create or return the SRA_ELT structure for CHILD in PARENT.  PARENT
+   may be null, in which case CHILD must be a DECL.  */
+
+static struct sra_elt *
+lookup_element (struct sra_elt *parent, tree child, tree type,
+               enum insert_option insert)
+{
+  struct sra_elt dummy;
+  struct sra_elt **slot;
+  struct sra_elt *elt;
+
+  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);
+  if (!slot && insert == NO_INSERT)
+    return NULL;
+
+  elt = *slot;
+  if (!elt && insert == INSERT)
+    {
+      *slot = elt = XOBNEW (&sra_obstack, struct sra_elt);
+      memset (elt, 0, sizeof (*elt));
+
+      elt->parent = parent;
+      elt->element = child;
+      elt->type = type;
+      elt->is_scalar = is_sra_scalar_type (type);
+
+      if (parent)
+       {
+         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
+        one copy from the true function parameter.  Count it now.  */
+      if (TREE_CODE (child) == PARM_DECL)
+       {
+         elt->n_copies = 1;
+         bitmap_set_bit (needs_copy_in, DECL_UID (child));
+       }
+    }
+
+  return elt;
+}
+
+/* Create or return the SRA_ELT structure for EXPR if the expression
+   refers to a scalarizable variable.  */
+
+static struct sra_elt *
+maybe_lookup_element_for_expr (tree expr)
+{
+  struct sra_elt *elt;
+  tree child;
+
+  switch (TREE_CODE (expr))
+    {
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      if (is_sra_candidate_decl (expr))
+       return lookup_element (NULL, expr, TREE_TYPE (expr), INSERT);
+      return NULL;
+
+    case ARRAY_REF:
+      /* 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:
+      {
+       tree type = TREE_TYPE (TREE_OPERAND (expr, 0));
+       /* Don't look through unions.  */
+       if (TREE_CODE (type) != RECORD_TYPE)
+         return NULL;
+       /* Neither through variable-sized records.  */
+       if (TYPE_SIZE (type) == NULL_TREE
+           || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+         return NULL;
+       child = TREE_OPERAND (expr, 1);
+      }
+      break;
+
+    case REALPART_EXPR:
+      child = integer_zero_node;
+      break;
+    case IMAGPART_EXPR:
+      child = integer_one_node;
+      break;
+
+    default:
+      return NULL;
+    }
+
+  elt = maybe_lookup_element_for_expr (TREE_OPERAND (expr, 0));
+  if (elt)
+    return lookup_element (elt, child, TREE_TYPE (expr), INSERT);
+  return NULL;
+}
+
+\f
+/* Functions to walk just enough of the tree to see all scalarizable
+   references, and categorize them.  */
+
+/* A set of callbacks for phases 2 and 4.  They'll be invoked for the
+   various kinds of references seen.  In all cases, *GSI is an iterator
+   pointing to the statement being processed.  */
+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.  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,
+              gimple_stmt_iterator *gsi, 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,
+               gimple_stmt_iterator *gsi);
+
+  /* Invoked when ELT is initialized from a constant.  VALUE may be NULL,
+     in which case it should be treated as an empty CONSTRUCTOR.  */
+  void (*init) (struct sra_elt *elt, tree value, gimple_stmt_iterator *gsi);
+
+  /* Invoked when we have a copy between one scalarizable reference ELT
+     and one non-scalarizable reference OTHER without side-effects. 
+     IS_OUTPUT is true if ELT is on the left-hand side.  */
+  void (*ldst) (struct sra_elt *elt, tree other,
+               gimple_stmt_iterator *gsi, bool is_output);
+
+  /* True during phase 2, false during phase 4.  */
+  /* ??? This is a hack.  */
+  bool initial_scan;
+};
+
+#ifdef ENABLE_CHECKING
+/* Invoked via walk_tree, if *TP contains a candidate decl, return it.  */
+
+static tree
+sra_find_candidate_decl (tree *tp, int *walk_subtrees,
+                        void *data ATTRIBUTE_UNUSED)
+{
+  tree t = *tp;
+  enum tree_code code = TREE_CODE (t);
+
+  if (code == VAR_DECL || code == PARM_DECL || code == RESULT_DECL)
+    {
+      *walk_subtrees = 0;
+      if (is_sra_candidate_decl (t))
+       return t;
+    }
+  else if (TYPE_P (t))
+    *walk_subtrees = 0;
+
+  return NULL;
+}
+#endif
+
+/* Walk most expressions looking for a scalarizable aggregate.
+   If we find one, invoke FNS->USE.  */
+
+static void
+sra_walk_expr (tree *expr_p, gimple_stmt_iterator *gsi, bool is_output,
+              const struct sra_walk_fns *fns)
+{
+  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
+     are references that we can scalarize.  If we come across something that
+     we can't scalarize, we reset EXPR.  This has the effect of making it
+     appear that we're referring to the larger expression as a whole.  */
+
+  while (1)
+    switch (TREE_CODE (inner))
+      {
+      case VAR_DECL:
+      case PARM_DECL:
+      case RESULT_DECL:
+       /* If there is a scalarizable decl at the bottom, then process it.  */
+       if (is_sra_candidate_decl (inner))
+         {
+           struct sra_elt *elt = maybe_lookup_element_for_expr (expr);
+           if (disable_scalarization)
+             elt->cannot_scalarize = true;
+           else
+             fns->use (elt, expr_p, gsi, is_output, use_all_p);
+         }
+       return;
+
+      case ARRAY_REF:
+       /* Non-constant index means any member may be accessed.  Prevent the
+          expression from being scalarized.  If we were to treat this as a
+          reference to the whole array, we can wind up with a single dynamic
+          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
+          the effort.  */
+       /* ??? Hack.  Figure out how to push this into the scan routines
+          without duplicating too much code.  */
+       if (!in_array_bounds_p (inner))
+         {
+           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...  */
+       if (TREE_OPERAND (inner, 2) || TREE_OPERAND (inner, 3))
+         goto use_all;
+       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:
+       {
+         tree type = TREE_TYPE (TREE_OPERAND (inner, 0));
+         /* Don't look through unions.  */
+         if (TREE_CODE (type) != RECORD_TYPE)
+           goto use_all;
+         /* Neither through variable-sized records.  */
+         if (TYPE_SIZE (type) == NULL_TREE
+             || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+           goto use_all;
+         inner = TREE_OPERAND (inner, 0);
+       }
+       break;
+
+      case REALPART_EXPR:
+      case IMAGPART_EXPR:
+       inner = TREE_OPERAND (inner, 0);
+       break;
+
+      case BIT_FIELD_REF:
+       /* A bit field reference to a specific vector is scalarized but for
+          ones for inputs need to be marked as used on the left hand size so
+          when we scalarize it, we can mark that variable as non renamable.  */
+       if (is_output
+           && TREE_CODE (TREE_TYPE (TREE_OPERAND (inner, 0))) == VECTOR_TYPE)
+         {
+           struct sra_elt *elt
+             = maybe_lookup_element_for_expr (TREE_OPERAND (inner, 0));
+           if (elt)
+             elt->is_vector_lhs = true;
+         }
+
+       /* A bit field reference (access to *multiple* fields simultaneously)
+          is not currently scalarized.  Consider this an access to the full
+          outer element, to which walk_tree will bring us next.  */
+       goto use_all;
+
+      CASE_CONVERT:
+       /* Similarly, a nop explicitly wants to look at an object in a
+          type other than the one we've scalarized.  */
+       goto use_all;
+
+      case VIEW_CONVERT_EXPR:
+       /* Likewise for a view conversion, but with an additional twist:
+          it can be on the LHS and, in this case, an access to the full
+          outer element would mean a killing def.  So we need to punt
+          if we haven't already a full access to the current element,
+          because we cannot pretend to have a killing def if we only
+          have a partial access at some level.  */
+       if (is_output && !use_all_p && inner != expr)
+         disable_scalarization = true;
+       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.  */
+       gcc_assert (!walk_tree (&inner, sra_find_candidate_decl, NULL, NULL));
+#endif
+       return;
+      }
+}
+
+/* Walk the arguments of a GIMPLE_CALL looking for scalarizable aggregates.
+   If we find one, invoke FNS->USE.  */
+
+static void
+sra_walk_gimple_call (gimple stmt, gimple_stmt_iterator *gsi,
+                   const struct sra_walk_fns *fns)
+{
+  int i;
+  int nargs = gimple_call_num_args (stmt);
+
+  for (i = 0; i < nargs; i++)
+    sra_walk_expr (gimple_call_arg_ptr (stmt, i), gsi, false, fns);
+
+  if (gimple_call_lhs (stmt))
+    sra_walk_expr (gimple_call_lhs_ptr (stmt), gsi, true, fns);
+}
+
+/* Walk the inputs and outputs of a GIMPLE_ASM looking for scalarizable
+   aggregates.  If we find one, invoke FNS->USE.  */
+
+static void
+sra_walk_gimple_asm (gimple stmt, gimple_stmt_iterator *gsi,
+                  const struct sra_walk_fns *fns)
+{
+  size_t i;
+  for (i = 0; i < gimple_asm_ninputs (stmt); i++)
+    sra_walk_expr (&TREE_VALUE (gimple_asm_input_op (stmt, i)), gsi, false, fns);
+  for (i = 0; i < gimple_asm_noutputs (stmt); i++)
+    sra_walk_expr (&TREE_VALUE (gimple_asm_output_op (stmt, i)), gsi, true, fns);
+}
+
+/* Walk a GIMPLE_ASSIGN and categorize the assignment appropriately.  */
+
+static void
+sra_walk_gimple_assign (gimple stmt, gimple_stmt_iterator *gsi,
+                       const struct sra_walk_fns *fns)
+{
+  struct sra_elt *lhs_elt = NULL, *rhs_elt = NULL;
+  tree lhs, rhs;
+
+  /* If there is more than 1 element on the RHS, only walk the lhs.  */
+  if (!gimple_assign_single_p (stmt))
+    {
+      sra_walk_expr (gimple_assign_lhs_ptr (stmt), gsi, true, fns);
+      return;
+    }
+
+  lhs = gimple_assign_lhs (stmt);
+  rhs = gimple_assign_rhs1 (stmt);
+  lhs_elt = maybe_lookup_element_for_expr (lhs);
+  rhs_elt = maybe_lookup_element_for_expr (rhs);
+
+  /* If both sides are scalarizable, this is a COPY operation.  */
+  if (lhs_elt && rhs_elt)
+    {
+      fns->copy (lhs_elt, rhs_elt, gsi);
+      return;
+    }
+
+  /* If the RHS is scalarizable, handle it.  There are only two cases.  */
+  if (rhs_elt)
+    {
+      if (!rhs_elt->is_scalar && !TREE_SIDE_EFFECTS (lhs))
+       fns->ldst (rhs_elt, lhs, gsi, false);
+      else
+       fns->use (rhs_elt, gimple_assign_rhs1_ptr (stmt), gsi, 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
+    sra_walk_expr (gimple_assign_rhs1_ptr (stmt), gsi, 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
+        we have access to all of the elements individually.  Invoke INIT.  */
+      if (TREE_CODE (rhs) == COMPLEX_EXPR
+         || TREE_CODE (rhs) == COMPLEX_CST
+         || TREE_CODE (rhs) == CONSTRUCTOR)
+       fns->init (lhs_elt, rhs, gsi);
+
+      /* If this is an assignment from read-only memory, treat this as if
+        we'd been passed the constructor directly.  Invoke INIT.  */
+      else if (TREE_CODE (rhs) == VAR_DECL
+              && TREE_STATIC (rhs)
+              && !DECL_EXTERNAL (rhs)
+              && TREE_READONLY (rhs)
+              && targetm.binds_local_p (rhs))
+       fns->init (lhs_elt, DECL_INITIAL (rhs), gsi);
+
+      /* 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
+              && !TREE_SIDE_EFFECTS (rhs) && is_gimple_addressable (rhs))
+       fns->ldst (lhs_elt, rhs, gsi, 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, gimple_assign_lhs_ptr (stmt), gsi, true, false);
+    }
+
+  /* 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 (gimple_assign_lhs_ptr (stmt), gsi, true, fns);
+}
+
+/* Entry point to the walk functions.  Search the entire function,
+   invoking the callbacks in FNS on each of the references to
+   scalarizable variables.  */
+
+static void
+sra_walk_function (const struct sra_walk_fns *fns)
+{
+  basic_block bb;
+  gimple_stmt_iterator si, ni;
+
+  /* ??? Phase 4 could derive some benefit to walking the function in
+     dominator tree order.  */
+
+  FOR_EACH_BB (bb)
+    for (si = gsi_start_bb (bb); !gsi_end_p (si); si = ni)
+      {
+       gimple stmt;
+
+       stmt = gsi_stmt (si);
+
+       ni = si;
+       gsi_next (&ni);
+
+       /* If the statement has no virtual operands, then it doesn't
+          make any structure references that we care about.  */
+       if (gimple_aliases_computed_p (cfun)
+           && ZERO_SSA_OPERANDS (stmt, (SSA_OP_VIRTUAL_DEFS | SSA_OP_VUSE)))
+             continue;
+
+       switch (gimple_code (stmt))
+         {
+         case GIMPLE_RETURN:
+           /* If we have "return <retval>" then the return value is
+              already exposed for our pleasure.  Walk it as a USE to
+              force all the components back in place for the return.
+              */
+           if (gimple_return_retval (stmt)  == NULL_TREE)
+             ;
+           else
+             sra_walk_expr (gimple_return_retval_ptr (stmt), &si, false,
+                             fns);
+           break;
+
+         case GIMPLE_ASSIGN:
+           sra_walk_gimple_assign (stmt, &si, fns);
+           break;
+         case GIMPLE_CALL:
+           sra_walk_gimple_call (stmt, &si, fns);
+           break;
+         case GIMPLE_ASM:
+           sra_walk_gimple_asm (stmt, &si, fns);
+           break;
+
+         default:
+           break;
+         }
+      }
+}
+\f
+/* Phase One: Scan all referenced variables in the program looking for
+   structures that could be decomposed.  */
+
+static bool
+find_candidates_for_sra (void)
+{
+  bool any_set = false;
+  tree var;
+  referenced_var_iterator rvi;
+
+  FOR_EACH_REFERENCED_VAR (var, rvi)
+    {
+      if (decl_can_be_decomposed_p (var))
+        {
+          bitmap_set_bit (sra_candidates, DECL_UID (var));
+          any_set = true;
+        }
+    }
+
+  return any_set;
+}
+
+\f
+/* Phase Two: Scan all references to scalarizable variables.  Count the
+   number of times they are used or copied respectively.  */
+
+/* Callbacks to fill in SRA_WALK_FNS.  Everything but USE is
+   considered a copy, because we can decompose the reference such that
+   the sub-elements needn't be contiguous.  */
+
+static void
+scan_use (struct sra_elt *elt, tree *expr_p ATTRIBUTE_UNUSED,
+         gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
+         bool is_output ATTRIBUTE_UNUSED, bool use_all ATTRIBUTE_UNUSED)
+{
+  elt->n_uses += 1;
+}
+
+static void
+scan_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
+          gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED)
+{
+  lhs_elt->n_copies += 1;
+  rhs_elt->n_copies += 1;
+}
+
+static void
+scan_init (struct sra_elt *lhs_elt, tree rhs ATTRIBUTE_UNUSED,
+          gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED)
+{
+  lhs_elt->n_copies += 1;
+}
+
+static void
+scan_ldst (struct sra_elt *elt, tree other ATTRIBUTE_UNUSED,
+          gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
+          bool is_output ATTRIBUTE_UNUSED)
+{
+  elt->n_copies += 1;
+}
+
+/* Dump the values we collected during the scanning phase.  */
+
+static void
+scan_dump (struct sra_elt *elt)
+{
+  struct sra_elt *c;
+
+  dump_sra_elt_name (dump_file, elt);
+  fprintf (dump_file, ": n_uses=%u n_copies=%u\n", elt->n_uses, elt->n_copies);
+
+  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
+   scalarization data structures, recording copies and uses.  */
+
+static void
+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))
+    {
+      unsigned i;
+
+      fputs ("\nScan results:\n", dump_file);
+      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);
+    }
+}
+\f
+/* Phase Three: Make decisions about which variables to scalarize, if any.
+   All elements to be scalarized have replacement variables made for them.  */
+
+/* A subroutine of build_element_name.  Recursively build the element
+   name on the obstack.  */
+
+static void
+build_element_name_1 (struct sra_elt *elt)
+{
+  tree t;
+  char buffer[32];
+
+  if (elt->parent)
+    {
+      build_element_name_1 (elt->parent);
+      obstack_1grow (&sra_obstack, '$');
+
+      if (TREE_CODE (elt->parent->type) == COMPLEX_TYPE)
+       {
+         if (elt->element == integer_zero_node)
+           obstack_grow (&sra_obstack, "real", 4);
+         else
+           obstack_grow (&sra_obstack, "imag", 4);
+         return;
+       }
+    }
+
+  t = elt->element;
+  if (TREE_CODE (t) == INTEGER_CST)
+    {
+      /* ??? Eh.  Don't bother doing double-wide printing.  */
+      sprintf (buffer, HOST_WIDE_INT_PRINT_DEC, TREE_INT_CST_LOW (t));
+      obstack_grow (&sra_obstack, buffer, strlen (buffer));
+    }
+  else if (TREE_CODE (t) == BIT_FIELD_REF)
+    {
+      sprintf (buffer, "B" HOST_WIDE_INT_PRINT_DEC,
+              tree_low_cst (TREE_OPERAND (t, 2), 1));
+      obstack_grow (&sra_obstack, buffer, strlen (buffer));
+      sprintf (buffer, "F" HOST_WIDE_INT_PRINT_DEC,
+              tree_low_cst (TREE_OPERAND (t, 1), 1));
+      obstack_grow (&sra_obstack, buffer, strlen (buffer));
+    }
+  else
+    {
+      tree name = DECL_NAME (t);
+      if (name)
+       obstack_grow (&sra_obstack, IDENTIFIER_POINTER (name),
+                     IDENTIFIER_LENGTH (name));
+      else
+       {
+         sprintf (buffer, "D%u", DECL_UID (t));
+         obstack_grow (&sra_obstack, buffer, strlen (buffer));
+       }
+    }
+}
+
+/* Construct a pretty variable name for an element's replacement variable.
+   The name is built on the obstack.  */
+
+static char *
+build_element_name (struct sra_elt *elt)
+{
+  build_element_name_1 (elt);
+  obstack_1grow (&sra_obstack, '\0');
+  return XOBFINISH (&sra_obstack, char *);
+}
+
+/* Instantiate an element as an independent variable.  */
+
+static void
+instantiate_element (struct sra_elt *elt)
+{
+  struct sra_elt *base_elt;
+  tree var, base;
+  bool nowarn = TREE_NO_WARNING (elt->element);
+
+  for (base_elt = elt; base_elt->parent; base_elt = base_elt->parent)
+    if (!nowarn)
+      nowarn = TREE_NO_WARNING (base_elt->parent->element);
+  base = base_elt->element;
+
+  elt->replacement = var = make_rename_temp (elt->type, "SR");
+
+  if (DECL_P (elt->element)
+      && !tree_int_cst_equal (DECL_SIZE (var), DECL_SIZE (elt->element)))
+    {
+      DECL_SIZE (var) = DECL_SIZE (elt->element);
+      DECL_SIZE_UNIT (var) = DECL_SIZE_UNIT (elt->element);
+
+      elt->in_bitfld_block = 1;
+      elt->replacement = fold_build3 (BIT_FIELD_REF, elt->type, var,
+                                     DECL_SIZE (var),
+                                     BYTES_BIG_ENDIAN
+                                     ? size_binop (MINUS_EXPR,
+                                                   TYPE_SIZE (elt->type),
+                                                   DECL_SIZE (var))
+                                     : bitsize_int (0));
+    }
+
+  /* For vectors, if used on the left hand side with BIT_FIELD_REF,
+     they are not a gimple register.  */
+  if (TREE_CODE (TREE_TYPE (var)) == VECTOR_TYPE && elt->is_vector_lhs)
+    DECL_GIMPLE_REG_P (var) = 0;
+
+  DECL_SOURCE_LOCATION (var) = DECL_SOURCE_LOCATION (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) = nowarn;
+    }
+  else
+    {
+      DECL_IGNORED_P (var) = 1;
+      /* ??? We can't generate any warning that would be meaningful.  */
+      TREE_NO_WARNING (var) = 1;
+    }
+
+  /* Zero-initialize bit-field scalarization variables, to avoid
+     triggering undefined behavior.  */
+  if (TREE_CODE (elt->element) == BIT_FIELD_REF
+      || (var != elt->replacement
+         && TREE_CODE (elt->replacement) == BIT_FIELD_REF))
+    {
+      gimple_seq init = sra_build_assignment (var,
+                                              fold_convert (TREE_TYPE (var),
+                                                            integer_zero_node)
+                                             );
+      insert_edge_copies_seq (init, ENTRY_BLOCK_PTR);
+      mark_all_v_defs_seq (init);
+    }
+
+  if (dump_file)
+    {
+      fputs ("  ", dump_file);
+      dump_sra_elt_name (dump_file, elt);
+      fputs (" -> ", dump_file);
+      print_generic_expr (dump_file, var, dump_flags);
+      fputc ('\n', dump_file);
+    }
+}
+
+/* Make one pass across an element tree deciding whether or not it's
+   profitable to instantiate individual leaf scalars.
+
+   PARENT_USES and PARENT_COPIES are the sum of the N_USES and N_COPIES
+   fields all the way up the tree.  */
+
+static void
+decide_instantiation_1 (struct sra_elt *elt, unsigned int parent_uses,
+                       unsigned int parent_copies)
+{
+  if (dump_file && !elt->parent)
+    {
+      fputs ("Initial instantiation for ", dump_file);
+      dump_sra_elt_name (dump_file, elt);
+      fputc ('\n', dump_file);
+    }
+
+  if (elt->cannot_scalarize)
+    return;
+
+  if (elt->is_scalar)
+    {
+      /* The decision is simple: instantiate if we're used more frequently
+        than the parent needs to be seen as a complete unit.  */
+      if (elt->n_uses + elt->n_copies + parent_copies > parent_uses)
+       instantiate_element (elt);
+    }
+  else
+    {
+      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);
+    }
+}
+
+/* Compute the size and number of all instantiated elements below ELT.
+   We will only care about this if the size of the complete structure
+   fits in a HOST_WIDE_INT, so we don't have to worry about overflow.  */
+
+static unsigned int
+sum_instantiated_sizes (struct sra_elt *elt, unsigned HOST_WIDE_INT *sizep)
+{
+  if (elt->replacement)
+    {
+      *sizep += TREE_INT_CST_LOW (TYPE_SIZE_UNIT (elt->type));
+      return 1;
+    }
+  else
+    {
+      struct sra_elt *c;
+      unsigned int count = 0;
+
+      for (c = elt->children; c ; c = c->sibling)
+       count += sum_instantiated_sizes (c, sizep);
+
+      return count;
+    }
+}
+
+/* Instantiate fields in ELT->TYPE that are not currently present as
+   children of ELT.  */
+
+static void instantiate_missing_elements (struct sra_elt *elt);
+
+static struct sra_elt *
+instantiate_missing_elements_1 (struct sra_elt *elt, tree child, tree type)
+{
+  struct sra_elt *sub = lookup_element (elt, child, type, INSERT);
+  if (sub->is_scalar)
+    {
+      if (sub->replacement == NULL)
+       instantiate_element (sub);
+    }
+  else
+    instantiate_missing_elements (sub);
+  return sub;
+}
+
+/* Obtain the canonical type for field F of ELEMENT.  */
+
+static tree
+canon_type_for_field (tree f, tree element)
+{
+  tree field_type = TREE_TYPE (f);
+
+  /* canonicalize_component_ref() unwidens some bit-field types (not
+     marked as DECL_BIT_FIELD in C++), so we must do the same, lest we
+     may introduce type mismatches.  */
+  if (INTEGRAL_TYPE_P (field_type)
+      && DECL_MODE (f) != TYPE_MODE (field_type))
+    field_type = TREE_TYPE (get_unwidened (build3 (COMPONENT_REF,
+                                                  field_type,
+                                                  element,
+                                                  f, NULL_TREE),
+                                          NULL_TREE));
+
+  return field_type;
+}
+
+/* Look for adjacent fields of ELT starting at F that we'd like to
+   scalarize as a single variable.  Return the last field of the
+   group.  */
+
+static tree
+try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
+{
+  int count;
+  unsigned HOST_WIDE_INT align, bit, size, alchk;
+  enum machine_mode mode;
+  tree first = f, prev;
+  tree type, var;
+  struct sra_elt *block;
+
+  /* Point fields are typically best handled as standalone entities.  */
+  if (POINTER_TYPE_P (TREE_TYPE (f)))
+    return f;
+    
+  if (!is_sra_scalar_type (TREE_TYPE (f))
+      || !host_integerp (DECL_FIELD_OFFSET (f), 1)
+      || !host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)
+      || !host_integerp (DECL_SIZE (f), 1)
+      || lookup_element (elt, f, NULL, NO_INSERT))
+    return f;
+
+  block = elt;
+
+  /* For complex and array objects, there are going to be integer
+     literals as child elements.  In this case, we can't just take the
+     alignment and mode of the decl, so we instead rely on the element
+     type.
+
+     ??? We could try to infer additional alignment from the full
+     object declaration and the location of the sub-elements we're
+     accessing.  */
+  for (count = 0; !DECL_P (block->element); count++)
+    block = block->parent;
+
+  align = DECL_ALIGN (block->element);
+  alchk = GET_MODE_BITSIZE (DECL_MODE (block->element));
+
+  if (count)
+    {
+      type = TREE_TYPE (block->element);
+      while (count--)
+       type = TREE_TYPE (type);
+
+      align = TYPE_ALIGN (type);
+      alchk = GET_MODE_BITSIZE (TYPE_MODE (type));
+    }
+
+  if (align < alchk)
+    align = alchk;
+
+  /* Coalescing wider fields is probably pointless and
+     inefficient.  */
+  if (align > BITS_PER_WORD)
+    align = BITS_PER_WORD;
+
+  bit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+    + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+  size = tree_low_cst (DECL_SIZE (f), 1);
+
+  alchk = align - 1;
+  alchk = ~alchk;
+
+  if ((bit & alchk) != ((bit + size - 1) & alchk))
+    return f;
+
+  /* Find adjacent fields in the same alignment word.  */
+
+  for (prev = f, f = TREE_CHAIN (f);
+       f && TREE_CODE (f) == FIELD_DECL
+        && is_sra_scalar_type (TREE_TYPE (f))
+        && host_integerp (DECL_FIELD_OFFSET (f), 1)
+        && host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)
+        && host_integerp (DECL_SIZE (f), 1)
+        && !lookup_element (elt, f, NULL, NO_INSERT);
+       prev = f, f = TREE_CHAIN (f))
+    {
+      unsigned HOST_WIDE_INT nbit, nsize;
+
+      nbit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+       + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+      nsize = tree_low_cst (DECL_SIZE (f), 1);
+
+      if (bit + size == nbit)
+       {
+         if ((bit & alchk) != ((nbit + nsize - 1) & alchk))
+           {
+             /* If we're at an alignment boundary, don't bother
+                growing alignment such that we can include this next
+                field.  */
+             if ((nbit & alchk)
+                 || GET_MODE_BITSIZE (DECL_MODE (f)) <= align)
+               break;
+
+             align = GET_MODE_BITSIZE (DECL_MODE (f));
+             alchk = align - 1;
+             alchk = ~alchk;
+
+             if ((bit & alchk) != ((nbit + nsize - 1) & alchk))
+               break;
+           }
+         size += nsize;
+       }
+      else if (nbit + nsize == bit)
+       {
+         if ((nbit & alchk) != ((bit + size - 1) & alchk))
+           {
+             if ((bit & alchk)
+                 || GET_MODE_BITSIZE (DECL_MODE (f)) <= align)
+               break;
+
+             align = GET_MODE_BITSIZE (DECL_MODE (f));
+             alchk = align - 1;
+             alchk = ~alchk;
+
+             if ((nbit & alchk) != ((bit + size - 1) & alchk))
+               break;
+           }
+         bit = nbit;
+         size += nsize;
+       }
+      else
+       break;
+    }
+
+  f = prev;
+
+  if (f == first)
+    return f;
+
+  gcc_assert ((bit & alchk) == ((bit + size - 1) & alchk));
+
+  /* Try to widen the bit range so as to cover padding bits as well.  */
+
+  if ((bit & ~alchk) || size != align)
+    {
+      unsigned HOST_WIDE_INT mbit = bit & alchk;
+      unsigned HOST_WIDE_INT msize = align;
+
+      for (f = TYPE_FIELDS (elt->type);
+          f; f = TREE_CHAIN (f))
+       {
+         unsigned HOST_WIDE_INT fbit, fsize;
+
+         /* Skip the fields from first to prev.  */
+         if (f == first)
+           {
+             f = prev;
+             continue;
+           }
+
+         if (!(TREE_CODE (f) == FIELD_DECL
+               && host_integerp (DECL_FIELD_OFFSET (f), 1)
+               && host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)))
+           continue;
+
+         fbit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+           + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+
+         /* If we're past the selected word, we're fine.  */
+         if ((bit & alchk) < (fbit & alchk))
+           continue;
+
+         if (host_integerp (DECL_SIZE (f), 1))
+           fsize = tree_low_cst (DECL_SIZE (f), 1);
+         else
+           /* Assume a variable-sized field takes up all space till
+              the end of the word.  ??? Endianness issues?  */
+           fsize = align - (fbit & alchk);
+
+         if ((fbit & alchk) < (bit & alchk))
+           {
+             /* A large field might start at a previous word and
+                extend into the selected word.  Exclude those
+                bits.  ??? Endianness issues? */
+             HOST_WIDE_INT diff = fbit + fsize - mbit;
+
+             if (diff <= 0)
+               continue;
+
+             mbit += diff;
+             msize -= diff;
+           }
+         else
+           {
+             /* Non-overlapping, great.  */
+             if (fbit + fsize <= mbit
+                 || mbit + msize <= fbit)
+               continue;
+
+             if (fbit <= mbit)
+               {
+                 unsigned HOST_WIDE_INT diff = fbit + fsize - mbit;
+                 mbit += diff;
+                 msize -= diff;
+               }
+             else if (fbit > mbit)
+               msize -= (mbit + msize - fbit);
+             else
+               gcc_unreachable ();
+           }
+       }
+
+      bit = mbit;
+      size = msize;
+    }
+
+  /* Now we know the bit range we're interested in.  Find the smallest
+     machine mode we can use to access it.  */
+
+  for (mode = smallest_mode_for_size (size, MODE_INT);
+       ;
+       mode = GET_MODE_WIDER_MODE (mode))
+    {
+      gcc_assert (mode != VOIDmode);
+
+      alchk = GET_MODE_PRECISION (mode) - 1;
+      alchk = ~alchk;
+
+      if ((bit & alchk) == ((bit + size - 1) & alchk))
+       break;
+    }
+
+  gcc_assert (~alchk < align);
+
+  /* Create the field group as a single variable.  */
+
+  /* We used to create a type for the mode above, but size turns
+     to be out not of mode-size.  As we need a matching type
+     to build a BIT_FIELD_REF, use a nonstandard integer type as
+     fallback.  */
+  type = lang_hooks.types.type_for_size (size, 1);
+  if (!type || TYPE_PRECISION (type) != size)
+    type = build_nonstandard_integer_type (size, 1);
+  gcc_assert (type);
+  var = build3 (BIT_FIELD_REF, type, NULL_TREE,
+               bitsize_int (size), bitsize_int (bit));
+
+  block = instantiate_missing_elements_1 (elt, var, type);
+  gcc_assert (block && block->is_scalar);
+
+  var = block->replacement;
+  block->in_bitfld_block = 2;
+
+  /* Add the member fields to the group, such that they access
+     portions of the group variable.  */
+
+  for (f = first; f != TREE_CHAIN (prev); f = TREE_CHAIN (f))
+    {
+      tree field_type = canon_type_for_field (f, elt->element);
+      struct sra_elt *fld = lookup_element (block, f, field_type, INSERT);
+
+      gcc_assert (fld && fld->is_scalar && !fld->replacement);
+
+      fld->replacement = fold_build3 (BIT_FIELD_REF, field_type, var,
+                                     bitsize_int (TYPE_PRECISION (field_type)),
+                                     bitsize_int
+                                     ((TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f))
+                                       * BITS_PER_UNIT
+                                       + (TREE_INT_CST_LOW
+                                          (DECL_FIELD_BIT_OFFSET (f)))
+                                       - (TREE_INT_CST_LOW
+                                          (TREE_OPERAND (block->element, 2))))
+                                      & ~alchk));
+      fld->in_bitfld_block = 1;
+    }
+
+  return prev;
+}
+
+static void
+instantiate_missing_elements (struct sra_elt *elt)
+{
+  tree type = elt->type;
+
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      {
+       tree f;
+       for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+         if (TREE_CODE (f) == FIELD_DECL)
+           {
+             tree last = try_instantiate_multiple_fields (elt, f);
+
+             if (last != f)
+               {
+                 f = last;
+                 continue;
+               }
+
+             instantiate_missing_elements_1 (elt, f,
+                                             canon_type_for_field
+                                             (f, elt->element));
+           }
+       break;
+      }
+
+    case ARRAY_TYPE:
+      {
+       tree i, max, subtype;
+
+       i = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
+       max = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
+       subtype = TREE_TYPE (type);
+
+       while (1)
+         {
+           instantiate_missing_elements_1 (elt, i, subtype);
+           if (tree_int_cst_equal (i, max))
+             break;
+           i = int_const_binop (PLUS_EXPR, i, integer_one_node, true);
+         }
+
+       break;
+      }
+
+    case COMPLEX_TYPE:
+      type = TREE_TYPE (type);
+      instantiate_missing_elements_1 (elt, integer_zero_node, type);
+      instantiate_missing_elements_1 (elt, integer_one_node, type);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return true if there is only one non aggregate field in the record, TYPE.
+   Return false otherwise.  */
+
+static bool
+single_scalar_field_in_record_p (tree type)
+{
+   int num_fields = 0;
+   tree field;
+   if (TREE_CODE (type) != RECORD_TYPE)
+     return false;
+
+   for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+     if (TREE_CODE (field) == FIELD_DECL)
+       {
+         num_fields++;
+
+         if (num_fields == 2)
+           return false;
+        
+         if (AGGREGATE_TYPE_P (TREE_TYPE (field)))
+           return false;
+       }
+
+   return true;
+}
+
+/* Make one pass across an element tree deciding whether to perform block
+   or element copies.  If we decide on element copies, instantiate all
+   elements.  Return true if there are any instantiated sub-elements.  */
+
+static bool
+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)
+    {
+      elt->use_block_copy = 1;
+
+      if (dump_file)
+       {
+         fputs ("Scalarization disabled for ", dump_file);
+         dump_sra_elt_name (dump_file, 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;
+    }
+
+  /* Don't decide if we've no uses and no groups.  */
+  if (elt->n_uses == 0 && elt->n_copies == 0 && elt->groups == NULL)
+    ;
+
+  else if (!elt->is_scalar)
+    {
+      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.  */
+      else if (host_integerp (size_tree, 1))
+       {
+         unsigned HOST_WIDE_INT full_size, inst_size = 0;
+         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 (optimize_function_for_speed_p (cfun)) * UNITS_PER_WORD;
+         max_count = SRA_MAX_STRUCTURE_COUNT
+           ? SRA_MAX_STRUCTURE_COUNT
+           : MOVE_RATIO (optimize_function_for_speed_p (cfun));
+
+         full_size = tree_low_cst (size_tree, 1);
+         full_count = count_type_elements (elt->type, false);
+         inst_count = sum_instantiated_sizes (elt, &inst_size);
+
+         /* If there is only one scalar field in the record, don't block copy.  */
+         if (single_scalar_field_in_record_p (elt->type))
+           use_block_copy = false;
+
+         /* ??? 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 <= max_size
+             && (full_count - inst_count) <= max_count
+             && elt->n_copies > elt->n_uses)
+           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.  */
+         if (!use_block_copy
+             && (!can_completely_scalarize_p (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 ",
+                  use_block_copy ? "block-copy" : "element-copy");
+         dump_sra_elt_name (dump_file, elt);
+         fputc ('\n', dump_file);
+       }
+
+      if (!use_block_copy)
+       {
+         instantiate_missing_elements (elt);
+         return true;
+       }
+    }
+
+  any_inst = elt->replacement != NULL;
+
+  for (c = elt->children; c ; c = c->sibling)
+    any_inst |= decide_block_copy (c);
+
+  return any_inst;
+}
+
+/* Entry point to phase 3.  Instantiate scalar replacement variables.  */
+
+static void
+decide_instantiations (void)
+{
+  unsigned int i;
+  bool cleared_any;
+  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, &bitmap_default_obstack);
+  cleared_any = false;
+
+  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)
+       {
+         decide_instantiation_1 (elt, 0, 0);
+         if (!decide_block_copy (elt))
+           elt = NULL;
+       }
+      if (!elt)
+       {
+         bitmap_set_bit (&done_head, i);
+         cleared_any = true;
+       }
+    }
+
+  if (cleared_any)
+    {
+      bitmap_and_compl_into (sra_candidates, &done_head);
+      bitmap_and_compl_into (needs_copy_in, &done_head);
+    }
+  bitmap_clear (&done_head);
+  
+  mark_set_for_renaming (sra_candidates);
+
+  if (dump_file)
+    fputc ('\n', dump_file);
+}
+
+\f
+/* Phase Four: Update the function to match the replacements created.  */
+
+/* Mark all the variables in VDEF/VUSE operators for STMT for
+   renaming. This becomes necessary when we modify all of a
+   non-scalar.  */
+
+static void
+mark_all_v_defs_stmt (gimple stmt)
+{
+  tree sym;
+  ssa_op_iter iter;
+
+  update_stmt_if_modified (stmt);
+
+  FOR_EACH_SSA_TREE_OPERAND (sym, stmt, iter, SSA_OP_ALL_VIRTUALS)
+    {
+      if (TREE_CODE (sym) == SSA_NAME)
+       sym = SSA_NAME_VAR (sym);
+      mark_sym_for_renaming (sym);
+    }
+}
+
+
+/* Mark all the variables in virtual operands in all the statements in
+   LIST for renaming.  */
+
+static void
+mark_all_v_defs_seq (gimple_seq seq)
+{
+  gimple_stmt_iterator gsi;
+
+  for (gsi = gsi_start (seq); !gsi_end_p (gsi); gsi_next (&gsi))
+    mark_all_v_defs_stmt (gsi_stmt (gsi));
+}
+
+/* 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;
+    }
+}
+
+/* Build a single level component reference to ELT rooted at BASE.  */
+
+static tree
+generate_one_element_ref (struct sra_elt *elt, tree base)
+{
+  switch (TREE_CODE (TREE_TYPE (base)))
+    {
+    case RECORD_TYPE:
+      {
+       tree field = elt->element;
+
+       /* We can't test elt->in_bitfld_block here because, when this is
+          called from instantiate_element, we haven't set this field
+          yet.  */
+       if (TREE_CODE (field) == BIT_FIELD_REF)
+         {
+           tree ret = unshare_expr (field);
+           TREE_OPERAND (ret, 0) = base;
+           return ret;
+         }
+
+       /* 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:
+      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 build1 (REALPART_EXPR, elt->type, base);
+      else
+       return build1 (IMAGPART_EXPR, elt->type, base);
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Build a full component reference to ELT rooted at its native variable.  */
+
+static tree
+generate_element_ref (struct sra_elt *elt)
+{
+  if (elt->parent)
+    return generate_one_element_ref (elt, generate_element_ref (elt->parent));
+  else
+    return elt->element;
+}
+
+/* Return true if BF is a bit-field that we can handle like a scalar.  */
+
+static bool
+scalar_bitfield_p (tree bf)
+{
+  return (TREE_CODE (bf) == BIT_FIELD_REF
+         && (is_gimple_reg (TREE_OPERAND (bf, 0))
+             || (TYPE_MODE (TREE_TYPE (TREE_OPERAND (bf, 0))) != BLKmode
+                 && (!TREE_SIDE_EFFECTS (TREE_OPERAND (bf, 0))
+                     || (GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE
+                                                      (TREE_OPERAND (bf, 0))))
+                         <= BITS_PER_WORD)))));
+}
+
+/* Create an assignment statement from SRC to DST.  */
+
+static gimple_seq
+sra_build_assignment (tree dst, tree src)
+{
+  gimple stmt;
+  gimple_seq seq = NULL, seq2 = NULL;
+  /* Turning BIT_FIELD_REFs into bit operations enables other passes
+     to do a much better job at optimizing the code.
+     From dst = BIT_FIELD_REF <var, sz, off> we produce
+
+       SR.1 = (scalar type) var;
+       SR.2 = SR.1 >> off;
+       SR.3 = SR.2 & ((1 << sz) - 1);
+       ... possible sign extension of SR.3 ...
+       dst = (destination type) SR.3;
+   */
+  if (scalar_bitfield_p (src))
+    {
+      tree var, shift, width;
+      tree utype, stype;
+      bool unsignedp = (INTEGRAL_TYPE_P (TREE_TYPE (src))
+                       ? TYPE_UNSIGNED (TREE_TYPE (src)) : true);
+      struct gimplify_ctx gctx;
+
+      var = TREE_OPERAND (src, 0);
+      width = TREE_OPERAND (src, 1);
+      /* The offset needs to be adjusted to a right shift quantity
+        depending on the endianness.  */
+      if (BYTES_BIG_ENDIAN)
+       {
+         tree tmp = size_binop (PLUS_EXPR, width, TREE_OPERAND (src, 2));
+         shift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), tmp);
+       }
+      else
+       shift = TREE_OPERAND (src, 2);
+
+      /* In weird cases we have non-integral types for the source or
+        destination object.
+        ???  For unknown reasons we also want an unsigned scalar type.  */
+      stype = TREE_TYPE (var);
+      if (!INTEGRAL_TYPE_P (stype))
+       stype = lang_hooks.types.type_for_size (TREE_INT_CST_LOW
+                                               (TYPE_SIZE (stype)), 1);
+      else if (!TYPE_UNSIGNED (stype))
+       stype = unsigned_type_for (stype);
+
+      utype = TREE_TYPE (dst);
+      if (!INTEGRAL_TYPE_P (utype))
+       utype = lang_hooks.types.type_for_size (TREE_INT_CST_LOW
+                                               (TYPE_SIZE (utype)), 1);
+      else if (!TYPE_UNSIGNED (utype))
+       utype = unsigned_type_for (utype);
+
+      /* Convert the base var of the BIT_FIELD_REF to the scalar type
+        we use for computation if we cannot use it directly.  */
+      if (INTEGRAL_TYPE_P (TREE_TYPE (var)))
+       var = fold_convert (stype, var);
+      else
+       var = fold_build1 (VIEW_CONVERT_EXPR, stype, var);
+
+      if (!integer_zerop (shift))
+       var = fold_build2 (RSHIFT_EXPR, stype, var, shift);
+
+      /* If we need a masking operation, produce one.  */
+      if (TREE_INT_CST_LOW (width) == TYPE_PRECISION (stype))
+       unsignedp = true;
+      else
+       {
+         tree one = build_int_cst_wide (stype, 1, 0);
+         tree mask = int_const_binop (LSHIFT_EXPR, one, width, 0);
+         mask = int_const_binop (MINUS_EXPR, mask, one, 0);
+         var = fold_build2 (BIT_AND_EXPR, stype, var, mask);
+       }
+
+      /* After shifting and masking, convert to the target type.  */
+      var = fold_convert (utype, var);
+
+      /* Perform sign extension, if required.
+        ???  This should never be necessary.  */
+      if (!unsignedp)
+       {
+         tree signbit = int_const_binop (LSHIFT_EXPR,
+                                         build_int_cst_wide (utype, 1, 0),
+                                         size_binop (MINUS_EXPR, width,
+                                                     bitsize_int (1)), 0);
+
+         var = fold_build2 (BIT_XOR_EXPR, utype, var, signbit);
+         var = fold_build2 (MINUS_EXPR, utype, var, signbit);
+       }
+
+      /* fold_build3 (BIT_FIELD_REF, ...) sometimes returns a cast.  */
+      STRIP_NOPS (dst);
+
+      /* Finally, move and convert to the destination.  */
+      if (INTEGRAL_TYPE_P (TREE_TYPE (dst)))
+       var = fold_convert (TREE_TYPE (dst), var);
+      else
+       var = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (dst), var);
+
+      push_gimplify_context (&gctx);
+      gctx.into_ssa = true;
+      gctx.allow_rhs_cond_expr = true;
+
+      gimplify_assign (dst, var, &seq);
+
+      if (gimple_referenced_vars (cfun))
+       for (var = gctx.temps; var; var = TREE_CHAIN (var))
+         add_referenced_var (var);
+      pop_gimplify_context (NULL);
+
+      return seq;
+    }
+
+  /* fold_build3 (BIT_FIELD_REF, ...) sometimes returns a cast.  */
+  if (CONVERT_EXPR_P (dst))
+    {
+      STRIP_NOPS (dst);
+      src = fold_convert (TREE_TYPE (dst), src);
+    }
+  /* It was hoped that we could perform some type sanity checking
+     here, but since front-ends can emit accesses of fields in types
+     different from their nominal types and copy structures containing
+     them as a whole, we'd have to handle such differences here.
+     Since such accesses under different types require compatibility
+     anyway, there's little point in making tests and/or adding
+     conversions to ensure the types of src and dst are the same.
+     So we just assume type differences at this point are ok.
+     The only exception we make here are pointer types, which can be different
+     in e.g. structurally equal, but non-identical RECORD_TYPEs.  */
+  else if (POINTER_TYPE_P (TREE_TYPE (dst))
+          && !useless_type_conversion_p (TREE_TYPE (dst), TREE_TYPE (src)))
+    src = fold_convert (TREE_TYPE (dst), src);
+
+  /* ???  Only call the gimplifier if we need to.  Otherwise we may 
+     end up substituting with DECL_VALUE_EXPR - see PR37380.  */
+  if (!handled_component_p (src)
+      && !SSA_VAR_P (src))
+    {
+      src = force_gimple_operand (src, &seq2, false, NULL_TREE);
+      gimple_seq_add_seq (&seq, seq2);
+    }
+  stmt = gimple_build_assign (dst, src);
+  gimple_seq_add_stmt (&seq, stmt);
+  return seq;
+}
+
+/* BIT_FIELD_REFs must not be shared.  sra_build_elt_assignment()
+   takes care of assignments, but we must create copies for uses.  */
+#define REPLDUP(t) (TREE_CODE (t) != BIT_FIELD_REF ? (t) : unshare_expr (t))
+
+/* Emit an assignment from SRC to DST, but if DST is a scalarizable
+   BIT_FIELD_REF, turn it into bit operations.  */
+
+static gimple_seq
+sra_build_bf_assignment (tree dst, tree src)
+{
+  tree var, type, utype, tmp, tmp2, tmp3;
+  gimple_seq seq;
+  gimple stmt;
+  tree cst, cst2, mask;
+  tree minshift, maxshift;
+
+  if (TREE_CODE (dst) != BIT_FIELD_REF)
+    return sra_build_assignment (dst, src);
+
+  var = TREE_OPERAND (dst, 0);
+
+  if (!scalar_bitfield_p (dst))
+    return sra_build_assignment (REPLDUP (dst), src);
+
+  seq = NULL;
+
+  cst = fold_convert (bitsizetype, TREE_OPERAND (dst, 2));
+  cst2 = size_binop (PLUS_EXPR,
+                    fold_convert (bitsizetype, TREE_OPERAND (dst, 1)),
+                    cst);
+
+  if (BYTES_BIG_ENDIAN)
+    {
+      maxshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst);
+      minshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst2);
+    }
+  else
+    {
+      maxshift = cst2;
+      minshift = cst;
+    }
+
+  type = TREE_TYPE (var);
+  if (!INTEGRAL_TYPE_P (type))
+    type = lang_hooks.types.type_for_size
+      (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (var))), 1);
+  if (TYPE_UNSIGNED (type))
+    utype = type;
+  else
+    utype = unsigned_type_for (type);
+
+  mask = build_int_cst_wide (utype, 1, 0);
+  if (TREE_INT_CST_LOW (maxshift) == TYPE_PRECISION (utype))
+    cst = build_int_cst_wide (utype, 0, 0);
+  else
+    cst = int_const_binop (LSHIFT_EXPR, mask, maxshift, true);
+  if (integer_zerop (minshift))
+    cst2 = mask;
+  else
+    cst2 = int_const_binop (LSHIFT_EXPR, mask, minshift, true);
+  mask = int_const_binop (MINUS_EXPR, cst, cst2, true);
+  mask = fold_build1 (BIT_NOT_EXPR, utype, mask);
+
+  if (TYPE_MAIN_VARIANT (utype) != TYPE_MAIN_VARIANT (TREE_TYPE (var))
+      && !integer_zerop (mask))
+    {
+      tmp = var;
+      if (!is_gimple_variable (tmp))
+       tmp = unshare_expr (var);
+      else
+       TREE_NO_WARNING (var) = true;
+
+      tmp2 = make_rename_temp (utype, "SR");
+
+      if (INTEGRAL_TYPE_P (TREE_TYPE (var)))
+       tmp = fold_convert (utype, tmp);
+      else
+       tmp = fold_build1 (VIEW_CONVERT_EXPR, utype, tmp);
+
+      stmt = gimple_build_assign (tmp2, tmp);
+      gimple_seq_add_stmt (&seq, stmt);
+    }
+  else
+    tmp2 = var;
+
+  if (!integer_zerop (mask))
+    {
+      tmp = make_rename_temp (utype, "SR");
+      stmt = gimple_build_assign (tmp, fold_build2 (BIT_AND_EXPR, utype,
+                                                   tmp2, mask));
+      gimple_seq_add_stmt (&seq, stmt);
+    }
+  else
+    tmp = mask;
+
+  if (is_gimple_reg (src) && INTEGRAL_TYPE_P (TREE_TYPE (src)))
+    tmp2 = src;
+  else if (INTEGRAL_TYPE_P (TREE_TYPE (src)))
+    {
+      gimple_seq tmp_seq;
+      tmp2 = make_rename_temp (TREE_TYPE (src), "SR");
+      tmp_seq = sra_build_assignment (tmp2, src);
+      gimple_seq_add_seq (&seq, tmp_seq);
+    }
+  else
+    {
+      gimple_seq tmp_seq;
+      tmp2 = make_rename_temp
+       (lang_hooks.types.type_for_size
+        (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (src))),
+         1), "SR");
+      tmp_seq = sra_build_assignment (tmp2, fold_build1 (VIEW_CONVERT_EXPR,
+                                                     TREE_TYPE (tmp2), src));
+      gimple_seq_add_seq (&seq, tmp_seq);
+    }
+
+  if (!TYPE_UNSIGNED (TREE_TYPE (tmp2)))
+    {
+      gimple_seq tmp_seq;
+      tree ut = unsigned_type_for (TREE_TYPE (tmp2));
+      tmp3 = make_rename_temp (ut, "SR");
+      tmp2 = fold_convert (ut, tmp2);
+      tmp_seq = sra_build_assignment (tmp3, tmp2);
+      gimple_seq_add_seq (&seq, tmp_seq);
+
+      tmp2 = fold_build1 (BIT_NOT_EXPR, utype, mask);
+      tmp2 = int_const_binop (RSHIFT_EXPR, tmp2, minshift, true);
+      tmp2 = fold_convert (ut, tmp2);
+      tmp2 = fold_build2 (BIT_AND_EXPR, ut, tmp3, tmp2);
+
+      if (tmp3 != tmp2)
+       {
+         tmp3 = make_rename_temp (ut, "SR");
+         tmp_seq = sra_build_assignment (tmp3, tmp2);
+          gimple_seq_add_seq (&seq, tmp_seq);
+       }
+
+      tmp2 = tmp3;
+    }
+
+  if (TYPE_MAIN_VARIANT (TREE_TYPE (tmp2)) != TYPE_MAIN_VARIANT (utype))
+    {
+      gimple_seq tmp_seq;
+      tmp3 = make_rename_temp (utype, "SR");
+      tmp2 = fold_convert (utype, tmp2);
+      tmp_seq = sra_build_assignment (tmp3, tmp2);
+      gimple_seq_add_seq (&seq, tmp_seq);
+      tmp2 = tmp3;
+    }
+
+  if (!integer_zerop (minshift))
+    {
+      tmp3 = make_rename_temp (utype, "SR");
+      stmt = gimple_build_assign (tmp3, fold_build2 (LSHIFT_EXPR, utype,
+                                                    tmp2, minshift));
+      gimple_seq_add_stmt (&seq, stmt);
+      tmp2 = tmp3;
+    }
+
+  if (utype != TREE_TYPE (var))
+    tmp3 = make_rename_temp (utype, "SR");
+  else
+    tmp3 = var;
+  stmt = gimple_build_assign (tmp3, fold_build2 (BIT_IOR_EXPR, utype,
+                                                tmp, tmp2));
+      gimple_seq_add_stmt (&seq, stmt);
+
+  if (tmp3 != var)
+    {
+      if (TREE_TYPE (var) == type)
+       stmt = gimple_build_assign (var, fold_convert (type, tmp3));
+      else
+       stmt = gimple_build_assign (var, fold_build1 (VIEW_CONVERT_EXPR,
+                                                     TREE_TYPE (var), tmp3));
+      gimple_seq_add_stmt (&seq, stmt);
+    }
+
+  return seq;
+}
+
+/* Expand an assignment of SRC to the scalarized representation of
+   ELT.  If it is a field group, try to widen the assignment to cover
+   the full variable.  */
+
+static gimple_seq
+sra_build_elt_assignment (struct sra_elt *elt, tree src)
+{
+  tree dst = elt->replacement;
+  tree var, tmp, cst, cst2;
+  gimple stmt;
+  gimple_seq seq;
+
+  if (TREE_CODE (dst) != BIT_FIELD_REF
+      || !elt->in_bitfld_block)
+    return sra_build_assignment (REPLDUP (dst), src);
+
+  var = TREE_OPERAND (dst, 0);
+
+  /* Try to widen the assignment to the entire variable.
+     We need the source to be a BIT_FIELD_REF as well, such that, for
+     BIT_FIELD_REF<d,sz,dp> = BIT_FIELD_REF<s,sz,sp>,
+     by design, conditions are met such that we can turn it into
+     d = BIT_FIELD_REF<s,dw,sp-dp>.  */
+  if (elt->in_bitfld_block == 2
+      && TREE_CODE (src) == BIT_FIELD_REF)
+    {
+      tmp = src;
+      cst = TYPE_SIZE (TREE_TYPE (var));
+      cst2 = size_binop (MINUS_EXPR, TREE_OPERAND (src, 2),
+                        TREE_OPERAND (dst, 2));
+
+      src = TREE_OPERAND (src, 0);
+
+      /* Avoid full-width bit-fields.  */
+      if (integer_zerop (cst2)
+         && tree_int_cst_equal (cst, TYPE_SIZE (TREE_TYPE (src))))
+       {
+         if (INTEGRAL_TYPE_P (TREE_TYPE (src))
+             && !TYPE_UNSIGNED (TREE_TYPE (src)))
+           src = fold_convert (unsigned_type_for (TREE_TYPE (src)), src);
+
+         /* If a single conversion won't do, we'll need a statement
+            list.  */
+         if (TYPE_MAIN_VARIANT (TREE_TYPE (var))
+             != TYPE_MAIN_VARIANT (TREE_TYPE (src)))
+           {
+              gimple_seq tmp_seq;
+             seq = NULL;
+
+             if (!INTEGRAL_TYPE_P (TREE_TYPE (src)))
+               src = fold_build1 (VIEW_CONVERT_EXPR,
+                                  lang_hooks.types.type_for_size
+                                  (TREE_INT_CST_LOW
+                                   (TYPE_SIZE (TREE_TYPE (src))),
+                                   1), src);
+             gcc_assert (TYPE_UNSIGNED (TREE_TYPE (src)));
+
+             tmp = make_rename_temp (TREE_TYPE (src), "SR");
+             stmt = gimple_build_assign (tmp, src);
+             gimple_seq_add_stmt (&seq, stmt);
+
+             tmp_seq = sra_build_assignment (var,
+                                             fold_convert (TREE_TYPE (var),
+                                                           tmp));
+             gimple_seq_add_seq (&seq, tmp_seq);
+
+             return seq;
+           }
+
+         src = fold_convert (TREE_TYPE (var), src);
+       }
+      else
+       {
+         src = fold_convert (TREE_TYPE (var), tmp);
+       }
+
+      return sra_build_assignment (var, src);
+    }
+
+  return sra_build_bf_assignment (dst, src);
+}
+
+/* Generate a set of assignment statements in *LIST_P to copy all
+   instantiated elements under ELT to or from the equivalent structure
+   rooted at EXPR.  COPY_OUT controls the direction of the copy, with
+   true meaning to copy out of EXPR into ELT.  */
+
+static void
+generate_copy_inout (struct sra_elt *elt, bool copy_out, tree expr,
+                    gimple_seq *seq_p)
+{
+  struct sra_elt *c;
+  gimple_seq tmp_seq;
+  tree t;
+
+  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);
+      tmp_seq = sra_build_bf_assignment (expr, t);
+      SSA_NAME_DEF_STMT (expr) = gimple_seq_last_stmt (tmp_seq);
+      gimple_seq_add_seq (seq_p, tmp_seq);
+    }
+  else if (elt->replacement)
+    {
+      if (copy_out)
+       tmp_seq = sra_build_elt_assignment (elt, expr);
+      else
+       tmp_seq = sra_build_bf_assignment (expr, REPLDUP (elt->replacement));
+      gimple_seq_add_seq (seq_p, tmp_seq);
+    }
+  else
+    {
+      FOR_EACH_ACTUAL_CHILD (c, elt)
+       {
+         t = generate_one_element_ref (c, unshare_expr (expr));
+         generate_copy_inout (c, copy_out, t, seq_p);
+       }
+    }
+}
+
+/* Generate a set of assignment statements in *LIST_P to copy all instantiated
+   elements under SRC to their counterparts under DST.  There must be a 1-1
+   correspondence of instantiated elements.  */
+
+static void
+generate_element_copy (struct sra_elt *dst, struct sra_elt *src, gimple_seq *seq_p)
+{
+  struct sra_elt *dc, *sc;
+
+  FOR_EACH_ACTUAL_CHILD (dc, dst)
+    {
+      sc = lookup_element (src, dc->element, NULL, NO_INSERT);
+      if (!sc && dc->in_bitfld_block == 2)
+       {
+         struct sra_elt *dcs;
+
+         FOR_EACH_ACTUAL_CHILD (dcs, dc)
+           {
+             sc = lookup_element (src, dcs->element, NULL, NO_INSERT);
+             gcc_assert (sc);
+             generate_element_copy (dcs, sc, seq_p);
+           }
+
+         continue;
+       }
+
+      /* If DST and SRC are structs with the same elements, but do not have
+        the same TYPE_MAIN_VARIANT, then lookup of DST FIELD_DECL in SRC
+        will fail.  Try harder by finding the corresponding FIELD_DECL
+        in SRC.  */
+      if (!sc)
+       {
+         tree f;
+
+         gcc_assert (useless_type_conversion_p (dst->type, src->type));
+         gcc_assert (TREE_CODE (dc->element) == FIELD_DECL);
+         for (f = TYPE_FIELDS (src->type); f ; f = TREE_CHAIN (f))
+           if (simple_cst_equal (DECL_FIELD_OFFSET (f),
+                                 DECL_FIELD_OFFSET (dc->element)) > 0
+               && simple_cst_equal (DECL_FIELD_BIT_OFFSET (f),
+                                    DECL_FIELD_BIT_OFFSET (dc->element)) > 0
+               && simple_cst_equal (DECL_SIZE (f),
+                                    DECL_SIZE (dc->element)) > 0
+               && (useless_type_conversion_p (TREE_TYPE (dc->element),
+                                              TREE_TYPE (f))
+                   || (POINTER_TYPE_P (TREE_TYPE (dc->element))
+                       && POINTER_TYPE_P (TREE_TYPE (f)))))
+             break;
+         gcc_assert (f != NULL_TREE);
+         sc = lookup_element (src, f, NULL, NO_INSERT);
+       }
+
+      generate_element_copy (dc, sc, seq_p);
+    }
+
+  if (dst->replacement)
+    {
+      gimple_seq tmp_seq;
+
+      gcc_assert (src->replacement);
+
+      tmp_seq = sra_build_elt_assignment (dst, REPLDUP (src->replacement));
+      gimple_seq_add_seq (seq_p, tmp_seq);
+    }
+}
+
+/* Generate a set of assignment statements in *LIST_P to zero all instantiated
+   elements under ELT.  In addition, do not assign to elements that have been
+   marked VISITED but do reset the visited flag; this allows easy coordination
+   with generate_element_init.  */
+
+static void
+generate_element_zero (struct sra_elt *elt, gimple_seq *seq_p)
+{
+  struct sra_elt *c;
+
+  if (elt->visited)
+    {
+      elt->visited = false;
+      return;
+    }
+
+  if (!elt->in_bitfld_block)
+    FOR_EACH_ACTUAL_CHILD (c, elt)
+      generate_element_zero (c, seq_p);
+
+  if (elt->replacement)
+    {
+      tree t;
+      gimple_seq tmp_seq;
+
+      gcc_assert (elt->is_scalar);
+      t = fold_convert (elt->type, integer_zero_node);
+
+      tmp_seq = sra_build_elt_assignment (elt, t);
+      gimple_seq_add_seq (seq_p, tmp_seq);
+    }
+}
+
+/* Generate an assignment VAR = INIT, where INIT may need gimplification.
+   Add the result to *LIST_P.  */
+
+static void
+generate_one_element_init (struct sra_elt *elt, tree init, gimple_seq *seq_p)
+{
+  gimple_seq tmp_seq = sra_build_elt_assignment (elt, init);
+  gimple_seq_add_seq (seq_p, tmp_seq);
+}
+
+/* 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.  Return false if we found a case we couldn't
+   handle.  */
+
+static bool
+generate_element_init_1 (struct sra_elt *elt, tree init, gimple_seq *seq_p)
+{
+  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)
+       {
+         generate_one_element_init (elt, init, seq_p);
+         elt->visited = true;
+       }
+      return result;
+    }
+
+  switch (init_code)
+    {
+    case COMPLEX_CST:
+    case COMPLEX_EXPR:
+      FOR_EACH_ACTUAL_CHILD (sub, elt)
+       {
+         if (sub->element == integer_zero_node)
+           t = (init_code == COMPLEX_EXPR
+                ? TREE_OPERAND (init, 0) : TREE_REALPART (init));
+         else
+           t = (init_code == COMPLEX_EXPR
+                ? TREE_OPERAND (init, 1) : TREE_IMAGPART (init));
+         result &= generate_element_init_1 (sub, t, seq_p);
+       }
+      break;
+
+    case CONSTRUCTOR:
+      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, purpose, value)
+       {
+         /* Array constructors are routinely created with NULL indices.  */
+         if (purpose == NULL_TREE)
+           {
+             result = false;
+             break;
+           }
+         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, seq_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, seq_p);
+           }
+       }
+      break;
+
+    default:
+      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, gimple_seq *seq_p)
+{
+  bool ret;
+  struct gimplify_ctx gctx;
+
+  push_gimplify_context (&gctx);
+  ret = generate_element_init_1 (elt, init, seq_p);
+  pop_gimplify_context (NULL);
+
+  /* The replacement can expose previously unreferenced variables.  */
+  if (ret && *seq_p)
+    {
+      gimple_stmt_iterator i;
+
+      for (i = gsi_start (*seq_p); !gsi_end_p (i); gsi_next (&i))
+       find_new_referenced_vars (gsi_stmt (i));
+    }
+
+  return ret;
+}
+
+/* Insert a gimple_seq SEQ on all the outgoing edges out of BB.  Note that
+   if BB has more than one edge, STMT will be replicated for each edge.
+   Also, abnormal edges will be ignored.  */
+
+void
+insert_edge_copies_seq (gimple_seq seq, basic_block bb)
+{
+  edge e;
+  edge_iterator ei;
+  unsigned n_copies = -1;
+
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    if (!(e->flags & EDGE_ABNORMAL)) 
+      n_copies++;
+
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    if (!(e->flags & EDGE_ABNORMAL)) 
+      gsi_insert_seq_on_edge (e, n_copies-- > 0 ? gimple_seq_copy (seq) : seq);
+}
+
+/* Helper function to insert LIST before GSI, and set up line number info.  */
+
+void
+sra_insert_before (gimple_stmt_iterator *gsi, gimple_seq seq)
+{
+  gimple stmt = gsi_stmt (*gsi);
+
+  if (gimple_has_location (stmt))
+    annotate_all_with_location (seq, gimple_location (stmt));
+  gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+}
+
+/* Similarly, but insert after GSI.  Handles insertion onto edges as well.  */
+
+void
+sra_insert_after (gimple_stmt_iterator *gsi, gimple_seq seq)
+{
+  gimple stmt = gsi_stmt (*gsi);
+
+  if (gimple_has_location (stmt))
+    annotate_all_with_location (seq, gimple_location (stmt));
+
+  if (stmt_ends_bb_p (stmt))
+    insert_edge_copies_seq (seq, gsi_bb (*gsi));
+  else
+    gsi_insert_seq_after (gsi, seq, GSI_SAME_STMT);
+}
+
+/* Similarly, but replace the statement at GSI.  */
+
+static void
+sra_replace (gimple_stmt_iterator *gsi, gimple_seq seq)
+{
+  sra_insert_before (gsi, seq);
+  gsi_remove (gsi, false);
+  if (gsi_end_p (*gsi))
+    *gsi = gsi_last (gsi_seq (*gsi));
+  else
+    gsi_prev (gsi);
+}
+
+/* Data structure that bitfield_overlaps_p fills in with information
+   about the element passed in and how much of it overlaps with the
+   bit-range passed it to.  */
+
+struct bitfield_overlap_info
+{
+  /* The bit-length of an element.  */
+  tree field_len;
+
+  /* The bit-position of the element in its parent.  */
+  tree field_pos;
+
+  /* The number of bits of the element that overlap with the incoming
+     bit range.  */
+  tree overlap_len;
+
+  /* The first bit of the element that overlaps with the incoming bit
+     range.  */
+  tree overlap_pos;
+};
+
+/* Return true if a BIT_FIELD_REF<(FLD->parent), BLEN, BPOS>
+   expression (referenced as BF below) accesses any of the bits in FLD,
+   false if it doesn't.  If DATA is non-null, its field_len and
+   field_pos are filled in such that BIT_FIELD_REF<(FLD->parent),
+   field_len, field_pos> (referenced as BFLD below) represents the
+   entire field FLD->element, and BIT_FIELD_REF<BFLD, overlap_len,
+   overlap_pos> represents the portion of the entire field that
+   overlaps with BF.  */
+
+static bool
+bitfield_overlaps_p (tree blen, tree bpos, struct sra_elt *fld,
+                    struct bitfield_overlap_info *data)
+{
+  tree flen, fpos;
+  bool ret;
+
+  if (TREE_CODE (fld->element) == FIELD_DECL)
+    {
+      flen = fold_convert (bitsizetype, DECL_SIZE (fld->element));
+      fpos = fold_convert (bitsizetype, DECL_FIELD_OFFSET (fld->element));
+      fpos = size_binop (MULT_EXPR, fpos, bitsize_int (BITS_PER_UNIT));
+      fpos = size_binop (PLUS_EXPR, fpos, DECL_FIELD_BIT_OFFSET (fld->element));
+    }
+  else if (TREE_CODE (fld->element) == BIT_FIELD_REF)
+    {
+      flen = fold_convert (bitsizetype, TREE_OPERAND (fld->element, 1));
+      fpos = fold_convert (bitsizetype, TREE_OPERAND (fld->element, 2));
+    }
+  else if (TREE_CODE (fld->element) == INTEGER_CST)
+    {
+      tree domain_type = TYPE_DOMAIN (TREE_TYPE (fld->parent->element));
+      flen = fold_convert (bitsizetype, TYPE_SIZE (fld->type));
+      fpos = fold_convert (bitsizetype, fld->element);
+      if (domain_type && TYPE_MIN_VALUE (domain_type))
+       fpos = size_binop (MINUS_EXPR, fpos,
+                          fold_convert (bitsizetype,
+                                        TYPE_MIN_VALUE (domain_type)));
+      fpos = size_binop (MULT_EXPR, flen, fpos);
+    }
+  else
+    gcc_unreachable ();
+
+  gcc_assert (host_integerp (blen, 1)
+             && host_integerp (bpos, 1)
+             && host_integerp (flen, 1)
+             && host_integerp (fpos, 1));
+
+  ret = ((!tree_int_cst_lt (fpos, bpos)
+         && tree_int_cst_lt (size_binop (MINUS_EXPR, fpos, bpos),
+                             blen))
+        || (!tree_int_cst_lt (bpos, fpos)
+            && tree_int_cst_lt (size_binop (MINUS_EXPR, bpos, fpos),
+                                flen)));
+
+  if (!ret)
+    return ret;
+
+  if (data)
+    {
+      tree bend, fend;
+
+      data->field_len = flen;
+      data->field_pos = fpos;
+
+      fend = size_binop (PLUS_EXPR, fpos, flen);
+      bend = size_binop (PLUS_EXPR, bpos, blen);
+
+      if (tree_int_cst_lt (bend, fend))
+       data->overlap_len = size_binop (MINUS_EXPR, bend, fpos);
+      else
+       data->overlap_len = NULL;
+
+      if (tree_int_cst_lt (fpos, bpos))
+       {
+         data->overlap_pos = size_binop (MINUS_EXPR, bpos, fpos);
+         data->overlap_len = size_binop (MINUS_EXPR,
+                                         data->overlap_len
+                                         ? data->overlap_len
+                                         : data->field_len,
+                                         data->overlap_pos);
+       }
+      else
+       data->overlap_pos = NULL;
+    }
+
+  return ret;
+}
+
+/* Add to LISTP a sequence of statements that copies BLEN bits between
+   VAR and the scalarized elements of ELT, starting a bit VPOS of VAR
+   and at bit BPOS of ELT.  The direction of the copy is given by
+   TO_VAR.  */
+
+static void
+sra_explode_bitfield_assignment (tree var, tree vpos, bool to_var,
+                                gimple_seq *seq_p, tree blen, tree bpos,
+                                struct sra_elt *elt)
+{
+  struct sra_elt *fld;
+  struct bitfield_overlap_info flp;
+
+  FOR_EACH_ACTUAL_CHILD (fld, elt)
+    {
+      tree flen, fpos;
+
+      if (!bitfield_overlaps_p (blen, bpos, fld, &flp))
+       continue;
+
+      flen = flp.overlap_len ? flp.overlap_len : flp.field_len;
+      fpos = flp.overlap_pos ? flp.overlap_pos : bitsize_int (0);
+
+      if (fld->replacement)
+       {
+         tree infld, invar, type;
+          gimple_seq st;
+
+         infld = fld->replacement;
+
+         type = unsigned_type_for (TREE_TYPE (infld));
+         if (TYPE_PRECISION (type) != TREE_INT_CST_LOW (flen))
+           type = build_nonstandard_integer_type (TREE_INT_CST_LOW (flen), 1);
+
+         if (TREE_CODE (infld) == BIT_FIELD_REF)
+           {
+             fpos = size_binop (PLUS_EXPR, fpos, TREE_OPERAND (infld, 2));
+             infld = TREE_OPERAND (infld, 0);
+           }
+         else if (BYTES_BIG_ENDIAN && DECL_P (fld->element)
+                  && !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (infld)),
+                                          DECL_SIZE (fld->element)))
+           {
+             fpos = size_binop (PLUS_EXPR, fpos,
+                                TYPE_SIZE (TREE_TYPE (infld)));
+             fpos = size_binop (MINUS_EXPR, fpos,
+                                DECL_SIZE (fld->element));
+           }
+
+         infld = fold_build3 (BIT_FIELD_REF, type, infld, flen, fpos);
+
+         invar = size_binop (MINUS_EXPR, flp.field_pos, bpos);
+         if (flp.overlap_pos)
+           invar = size_binop (PLUS_EXPR, invar, flp.overlap_pos);
+         invar = size_binop (PLUS_EXPR, invar, vpos);
+
+         invar = fold_build3 (BIT_FIELD_REF, type, var, flen, invar);
+
+         if (to_var)
+           st = sra_build_bf_assignment (invar, infld);
+         else
+           st = sra_build_bf_assignment (infld, invar);
+
+         gimple_seq_add_seq (seq_p, st);
+       }
+      else
+       {
+         tree sub = size_binop (MINUS_EXPR, flp.field_pos, bpos);
+         sub = size_binop (PLUS_EXPR, vpos, sub);
+         if (flp.overlap_pos)
+           sub = size_binop (PLUS_EXPR, sub, flp.overlap_pos);
+
+         sra_explode_bitfield_assignment (var, sub, to_var, seq_p,
+                                          flen, fpos, fld);
+       }
+    }
+}
+
+/* Add to LISTBEFOREP statements that copy scalarized members of ELT
+   that overlap with BIT_FIELD_REF<(ELT->element), BLEN, BPOS> back
+   into the full variable, and to LISTAFTERP, if non-NULL, statements
+   that copy the (presumably modified) overlapping portions of the
+   full variable back to the scalarized variables.  */
+
+static void
+sra_sync_for_bitfield_assignment (gimple_seq *seq_before_p,
+                                  gimple_seq *seq_after_p,
+                                 tree blen, tree bpos,
+                                 struct sra_elt *elt)
+{
+  struct sra_elt *fld;
+  struct bitfield_overlap_info flp;
+
+  FOR_EACH_ACTUAL_CHILD (fld, elt)
+    if (bitfield_overlaps_p (blen, bpos, fld, &flp))
+      {
+       if (fld->replacement || (!flp.overlap_len && !flp.overlap_pos))
+         {
+           generate_copy_inout (fld, false, generate_element_ref (fld),
+                                seq_before_p);
+           mark_no_warning (fld);
+           if (seq_after_p)
+             generate_copy_inout (fld, true, generate_element_ref (fld),
+                                  seq_after_p);
+         }
+       else
+         {
+           tree flen = flp.overlap_len ? flp.overlap_len : flp.field_len;
+           tree fpos = flp.overlap_pos ? flp.overlap_pos : bitsize_int (0);
+
+           sra_sync_for_bitfield_assignment (seq_before_p, seq_after_p,
+                                             flen, fpos, fld);
+         }
+      }
+}
+
+/* Scalarize a USE.  To recap, this is either a simple reference to ELT,
+   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, gimple_stmt_iterator *gsi,
+              bool is_output, bool use_all)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree bfexpr;
+
+  if (elt->replacement)
+    {
+      tree replacement = elt->replacement;
+
+      /* If we have a replacement, then updating the reference is as
+        simple as modifying the existing statement in place.  */
+      if (is_output
+         && TREE_CODE (elt->replacement) == BIT_FIELD_REF
+         && is_gimple_reg (TREE_OPERAND (elt->replacement, 0))
+         && is_gimple_assign (stmt)
+         && gimple_assign_lhs_ptr (stmt) == expr_p)
+       {
+          gimple_seq newseq;
+          /* RHS must be a single operand. */
+          gcc_assert (gimple_assign_single_p (stmt));
+         newseq = sra_build_elt_assignment (elt, gimple_assign_rhs1 (stmt));
+         sra_replace (gsi, newseq);
+         return;
+       }
+      else if (!is_output
+              && TREE_CODE (elt->replacement) == BIT_FIELD_REF
+              && is_gimple_assign (stmt)
+              && gimple_assign_rhs1_ptr (stmt) == expr_p)
+       {
+         tree tmp = make_rename_temp
+           (TREE_TYPE (gimple_assign_lhs (stmt)), "SR");
+         gimple_seq newseq = sra_build_assignment (tmp, REPLDUP (elt->replacement));
+
+         sra_insert_before (gsi, newseq);
+         replacement = tmp;
+       }
+      if (is_output)
+         mark_all_v_defs_stmt (stmt);
+      *expr_p = REPLDUP (replacement);
+      update_stmt (stmt);
+    }
+  else if (use_all && is_output
+          && is_gimple_assign (stmt)
+          && TREE_CODE (bfexpr
+                        = gimple_assign_lhs (stmt)) == BIT_FIELD_REF
+          && &TREE_OPERAND (bfexpr, 0) == expr_p
+          && INTEGRAL_TYPE_P (TREE_TYPE (bfexpr))
+          && TREE_CODE (TREE_TYPE (*expr_p)) == RECORD_TYPE)
+    {
+      gimple_seq seq_before = NULL;
+      gimple_seq seq_after = NULL;
+      tree blen = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 1));
+      tree bpos = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 2));
+      bool update = false;
+
+      if (!elt->use_block_copy)
+       {
+         tree type = TREE_TYPE (bfexpr);
+         tree var = make_rename_temp (type, "SR"), tmp, vpos;
+          gimple st;
+
+         gimple_assign_set_lhs (stmt, var);
+         update = true;
+
+         if (!TYPE_UNSIGNED (type))
+           {
+             type = unsigned_type_for (type);
+             tmp = make_rename_temp (type, "SR");
+             st = gimple_build_assign (tmp, fold_convert (type, var));
+             gimple_seq_add_stmt (&seq_after, st);
+             var = tmp;
+           }
+
+         /* If VAR is wider than BLEN bits, it is padded at the
+            most-significant end.  We want to set VPOS such that
+            <BIT_FIELD_REF VAR BLEN VPOS> would refer to the
+            least-significant BLEN bits of VAR.  */
+         if (BYTES_BIG_ENDIAN)
+           vpos = size_binop (MINUS_EXPR, TYPE_SIZE (type), blen);
+         else
+           vpos = bitsize_int (0);
+         sra_explode_bitfield_assignment
+           (var, vpos, false, &seq_after, blen, bpos, elt);
+       }
+      else
+       sra_sync_for_bitfield_assignment
+         (&seq_before, &seq_after, blen, bpos, elt);
+
+      if (seq_before)
+       {
+         mark_all_v_defs_seq (seq_before);
+         sra_insert_before (gsi, seq_before);
+       }
+      if (seq_after)
+       {
+         mark_all_v_defs_seq (seq_after);
+         sra_insert_after (gsi, seq_after);
+       }
+
+      if (update)
+       update_stmt (stmt);
+    }
+  else if (use_all && !is_output
+          && is_gimple_assign (stmt)
+          && TREE_CODE (bfexpr
+                        = gimple_assign_rhs1 (stmt)) == BIT_FIELD_REF
+          && &TREE_OPERAND (gimple_assign_rhs1 (stmt), 0) == expr_p
+          && INTEGRAL_TYPE_P (TREE_TYPE (bfexpr))
+          && TREE_CODE (TREE_TYPE (*expr_p)) == RECORD_TYPE)
+    {
+      gimple_seq seq = NULL;
+      tree blen = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 1));
+      tree bpos = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 2));
+      bool update = false;
+
+      if (!elt->use_block_copy)
+       {
+         tree type = TREE_TYPE (bfexpr);
+         tree var = make_rename_temp (type, "SR"), tmp, vpos;
+         gimple st = NULL;
+
+         gimple_assign_set_rhs1 (stmt, var);
+         update = true;
+
+         if (!TYPE_UNSIGNED (type))
+           {
+             type = unsigned_type_for (type);
+             tmp = make_rename_temp (type, "SR");
+             st = gimple_build_assign (var,
+                                       fold_convert (TREE_TYPE (var), tmp));
+             var = tmp;
+           }
+
+         gimple_seq_add_stmt (&seq,
+                               gimple_build_assign
+                                (var, build_int_cst_wide (type, 0, 0)));
+
+         /* If VAR is wider than BLEN bits, it is padded at the
+            most-significant end.  We want to set VPOS such that
+            <BIT_FIELD_REF VAR BLEN VPOS> would refer to the
+            least-significant BLEN bits of VAR.  */
+         if (BYTES_BIG_ENDIAN)
+           vpos = size_binop (MINUS_EXPR, TYPE_SIZE (type), blen);
+         else
+           vpos = bitsize_int (0);
+         sra_explode_bitfield_assignment
+           (var, vpos, true, &seq, blen, bpos, elt);
+
+         if (st)
+           gimple_seq_add_stmt (&seq, st);
+       }
+      else
+       sra_sync_for_bitfield_assignment
+         (&seq, NULL, blen, bpos, elt);
+
+      if (seq)
+       {
+         mark_all_v_defs_seq (seq);
+         sra_insert_before (gsi, seq);
+       }
+
+      if (update)
+       update_stmt (stmt);
+    }
+  else
+    {
+      gimple_seq seq = NULL;
+
+      /* Otherwise we need some copies.  If ELT is being read, then we
+        want to store all (modified) sub-elements back into the
+        structure before the reference takes place.  If ELT is being
+        written, then we want to load the changed values back into
+        our shadow variables.  */
+      /* ??? We don't check modified for reads, we just always write all of
+        the values.  We should be able to record the SSA number of the VOP
+        for which the values were last read.  If that number matches the
+        SSA number of the VOP in the current statement, then we needn't
+        emit an assignment.  This would also eliminate double writes when
+        a structure is passed as more than one argument to a function call.
+        This optimization would be most effective if sra_walk_function
+        processed the blocks in dominator order.  */
+
+      generate_copy_inout (elt, is_output, generate_element_ref (elt), &seq);
+      if (seq == NULL)
+       return;
+      mark_all_v_defs_seq (seq);
+      if (is_output)
+       sra_insert_after (gsi, seq);
+      else
+       {
+         sra_insert_before (gsi, seq);
+         if (use_all)
+           mark_no_warning (elt);
+       }
+    }
+}
+
+/* Scalarize a COPY.  To recap, this is an assignment statement between
+   two scalarizable references, LHS_ELT and RHS_ELT.  */
+
+static void
+scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
+               gimple_stmt_iterator *gsi)
+{
+  gimple_seq seq;
+  gimple stmt;
+
+  if (lhs_elt->replacement && rhs_elt->replacement)
+    {
+      /* If we have two scalar operands, modify the existing statement.  */
+      stmt = gsi_stmt (*gsi);
+
+      /* See the commentary in sra_walk_function concerning
+        RETURN_EXPR, and why we should never see one here.  */
+      gcc_assert (is_gimple_assign (stmt));
+      gcc_assert (gimple_assign_copy_p (stmt));
+
+
+      gimple_assign_set_lhs (stmt, lhs_elt->replacement);
+      gimple_assign_set_rhs1 (stmt, REPLDUP (rhs_elt->replacement));
+      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
+        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
+        would at least allow those elements that are instantiated in
+        both structures to be optimized well.  */
+
+      seq = NULL;
+      generate_copy_inout (rhs_elt, false,
+                          generate_element_ref (rhs_elt), &seq);
+      if (seq)
+       {
+         mark_all_v_defs_seq (seq);
+         sra_insert_before (gsi, seq);
+       }
+
+      seq = NULL;
+      generate_copy_inout (lhs_elt, true,
+                          generate_element_ref (lhs_elt), &seq);
+      if (seq)
+       {
+         mark_all_v_defs_seq (seq);
+         sra_insert_after (gsi, seq);
+       }
+    }
+  else
+    {
+      /* Otherwise both sides must be fully instantiated.  In which
+        case perform pair-wise element assignments and replace the
+        original block copy statement.  */
+
+      stmt = gsi_stmt (*gsi);
+      mark_all_v_defs_stmt (stmt);
+
+      seq = NULL;
+      generate_element_copy (lhs_elt, rhs_elt, &seq);
+      gcc_assert (seq);
+      mark_all_v_defs_seq (seq);
+      sra_replace (gsi, seq);
+    }
+}
+
+/* Scalarize an INIT.  To recap, this is an assignment to a scalarizable
+   reference from some form of constructor: CONSTRUCTOR, COMPLEX_CST or
+   COMPLEX_EXPR.  If RHS is NULL, it should be treated as an empty
+   CONSTRUCTOR.  */
+
+static void
+scalarize_init (struct sra_elt *lhs_elt, tree rhs, gimple_stmt_iterator *gsi)
+{
+  bool result = true;
+  gimple_seq seq = NULL, init_seq = NULL;
+
+  /* Generate initialization statements for all members extant in the RHS.  */
+  if (rhs)
+    {
+      /* Unshare the expression just in case this is from a decl's initial.  */
+      rhs = unshare_expr (rhs);
+      result = generate_element_init (lhs_elt, rhs, &init_seq);
+    }
+
+  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.  */
+      gimple_seq seq0 = NULL;
+      generate_copy_inout (lhs_elt, true, generate_element_ref (lhs_elt),
+                          &seq0);
+      gimple_seq_add_seq (&seq0, seq);
+      seq = seq0;
+    }
+  else
+    {
+      /* 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, &seq);
+      gimple_seq_add_seq (&seq, init_seq);
+    }
+
+  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.  */
+      if (seq)
+       {
+         mark_all_v_defs_seq (seq);
+         sra_insert_after (gsi, seq);
+       }
+    }
+  else
+    {
+      /* The LHS is fully instantiated.  The list of initializations
+        replaces the original structure assignment.  */
+      gcc_assert (seq);
+      mark_all_v_defs_stmt (gsi_stmt (*gsi));
+      mark_all_v_defs_seq (seq);
+      sra_replace (gsi, seq);
+    }
+}
+
+/* A subroutine of scalarize_ldst called via walk_tree.  Set TREE_NO_TRAP
+   on all INDIRECT_REFs.  */
+
+static tree
+mark_notrap (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+{
+  tree t = *tp;
+
+  if (TREE_CODE (t) == INDIRECT_REF)
+    {
+      TREE_THIS_NOTRAP (t) = 1;
+      *walk_subtrees = 0;
+    }
+  else if (IS_TYPE_OR_DECL_P (t))
+    *walk_subtrees = 0;
+
+  return NULL;
+}
+
+/* Scalarize a LDST.  To recap, this is an assignment between one scalarizable
+   reference ELT and one non-scalarizable reference OTHER.  IS_OUTPUT is true
+   if ELT is on the left-hand side.  */
+
+static void
+scalarize_ldst (struct sra_elt *elt, tree other,
+               gimple_stmt_iterator *gsi, bool is_output)
+{
+  /* Shouldn't have gotten called for a scalar.  */
+  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, gsi, is_output, false);
+    }
+  else
+    {
+      /* The interesting case is when ELT is fully instantiated.  In this
+        case we can have each element stored/loaded directly to/from the
+        corresponding slot in OTHER.  This avoids a block copy.  */
+
+      gimple_seq seq = NULL;
+      gimple stmt = gsi_stmt (*gsi);
+
+      mark_all_v_defs_stmt (stmt);
+      generate_copy_inout (elt, is_output, other, &seq);
+      gcc_assert (seq);
+      mark_all_v_defs_seq (seq);
+
+      /* Preserve EH semantics.  */
+      if (stmt_ends_bb_p (stmt))
+       {
+         gimple_stmt_iterator si;
+         gimple first;
+          gimple_seq blist = NULL;
+         bool thr = stmt_could_throw_p (stmt);
+
+         /* If the last statement of this BB created an EH edge
+            before scalarization, we have to locate the first
+            statement that can throw in the new statement list and
+            use that as the last statement of this BB, such that EH
+            semantics is preserved.  All statements up to this one
+            are added to the same BB.  All other statements in the
+            list will be added to normal outgoing edges of the same
+            BB.  If they access any memory, it's the same memory, so
+            we can assume they won't throw.  */
+         si = gsi_start (seq);
+         for (first = gsi_stmt (si);
+              thr && !gsi_end_p (si) && !stmt_could_throw_p (first);
+              first = gsi_stmt (si))
+           {
+             gsi_remove (&si, false);
+             gimple_seq_add_stmt (&blist, first);
+           }
+
+         /* Extract the first remaining statement from LIST, this is
+            the EH statement if there is one.  */
+         gsi_remove (&si, false);
+
+         if (blist)
+           sra_insert_before (gsi, blist);
+
+         /* Replace the old statement with this new representative.  */
+         gsi_replace (gsi, first, true);
+
+         if (!gsi_end_p (si))
+           {
+             /* If any reference would trap, then they all would.  And more
+                to the point, the first would.  Therefore none of the rest
+                will trap since the first didn't.  Indicate this by
+                iterating over the remaining statements and set
+                TREE_THIS_NOTRAP in all INDIRECT_REFs.  */
+             do
+               {
+                 walk_gimple_stmt (&si, NULL, mark_notrap, NULL);
+                 gsi_next (&si);
+               }
+             while (!gsi_end_p (si));
+
+             insert_edge_copies_seq (seq, gsi_bb (*gsi));
+           }
+       }
+      else
+       sra_replace (gsi, seq);
+    }
+}
+
+/* Generate initializations for all scalarizable parameters.  */
+
+static void
+scalarize_parms (void)
+{
+  gimple_seq seq = NULL;
+  unsigned i;
+  bitmap_iterator bi;
+
+  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, &seq);
+    }
+
+  if (seq)
+    {
+      insert_edge_copies_seq (seq, ENTRY_BLOCK_PTR);
+      mark_all_v_defs_seq (seq);
+    }
+}
+
+/* Entry point to phase 4.  Update the function to match replacements.  */
+
+static void
+scalarize_function (void)
+{
+  static const struct sra_walk_fns fns = {
+    scalarize_use, scalarize_copy, scalarize_init, scalarize_ldst, false
+  };
+
+  sra_walk_function (&fns);
+  scalarize_parms ();
+  gsi_commit_edge_inserts ();
+}
+
+\f
+/* Debug helper function.  Print ELT in a nice human-readable format.  */
+
+static void
+dump_sra_elt_name (FILE *f, struct sra_elt *elt)
+{
+  if (elt->parent && TREE_CODE (elt->parent->type) == COMPLEX_TYPE)
+    {
+      fputs (elt->element == integer_zero_node ? "__real__ " : "__imag__ ", f);
+      dump_sra_elt_name (f, elt->parent);
+    }
+  else
+    {
+      if (elt->parent)
+        dump_sra_elt_name (f, elt->parent);
+      if (DECL_P (elt->element))
+       {
+         if (TREE_CODE (elt->element) == FIELD_DECL)
+           fputc ('.', f);
+         print_generic_expr (f, elt->element, dump_flags);
+       }
+      else if (TREE_CODE (elt->element) == BIT_FIELD_REF)
+       fprintf (f, "$B" HOST_WIDE_INT_PRINT_DEC "F" HOST_WIDE_INT_PRINT_DEC,
+                tree_low_cst (TREE_OPERAND (elt->element, 2), 1),
+                tree_low_cst (TREE_OPERAND (elt->element, 1), 1));
+      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));
+    }
+}
+
+/* Likewise, but callable from the debugger.  */
+
+void
+debug_sra_elt_name (struct sra_elt *elt)
+{
+  dump_sra_elt_name (stderr, 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 unsigned int
+tree_sra (void)
+{
+  /* Initialize local variables.  */
+  todoflags = 0;
+  gcc_obstack_init (&sra_obstack);
+  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.  */
+  if (find_candidates_for_sra ())
+    {
+      scan_function ();
+      decide_instantiations ();
+      scalarize_function ();
+      if (!bitmap_empty_p (sra_candidates))
+       todoflags |= TODO_rebuild_alias;
+    }
+
+  /* Free allocated memory.  */
+  htab_delete (sra_map);
+  sra_map = NULL;
+  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 unsigned int
+tree_sra_early (void)
+{
+  unsigned int ret;
+
+  early_sra = true;
+  ret = tree_sra ();
+  early_sra = false;
+
+  return ret & ~TODO_rebuild_alias;
+}
+
+static bool
+gate_sra (void)
+{
+  return flag_tree_sra != 0;
+}
+
+struct gimple_opt_pass pass_sra_early =
+{
+ {
+  GIMPLE_PASS,
+  "esra",                              /* name */
+  gate_sra,                            /* gate */
+  tree_sra_early,                      /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  TV_TREE_SRA,                         /* tv_id */
+  PROP_cfg | PROP_ssa,                 /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  TODO_dump_func
+  | TODO_update_ssa
+  | TODO_ggc_collect
+  | TODO_verify_ssa                    /* todo_flags_finish */
+ }
+};
+
+struct gimple_opt_pass pass_sra =
+{
+ {
+  GIMPLE_PASS,
+  "sra",                               /* name */
+  gate_sra,                            /* gate */
+  tree_sra,                            /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  TV_TREE_SRA,                         /* tv_id */
+  PROP_cfg | PROP_ssa,                 /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  TODO_dump_func
+  | TODO_update_ssa
+  | TODO_ggc_collect
+  | TODO_verify_ssa                    /* todo_flags_finish */
+ }
+};