OSDN Git Service

2010-01-01 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / tree-sra.c
index 83c5d8a..65d270f 100644 (file)
@@ -1,19 +1,18 @@
 /* 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
-     Free Software Foundation, Inc.
-   Contributed by Diego Novillo <dnovillo@redhat.com>
+   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+   Contributed by Martin Jambor <mjambor@suse.cz>
 
 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 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
+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.
 
@@ -21,3675 +20,3962 @@ 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/>.  */
 
+/* This file implements Scalar Reduction of Aggregates (SRA).  SRA is run
+   twice, once in the early stages of compilation (early SRA) and once in the
+   late stages (late SRA).  The aim of both is to turn references to scalar
+   parts of aggregates into uses of independent scalar variables.
+
+   The two passes are nearly identical, the only difference is that early SRA
+   does not scalarize unions which are used as the result in a GIMPLE_RETURN
+   statement because together with inlining this can lead to weird type
+   conversions.
+
+   Both passes operate in four stages:
+
+   1. The declarations that have properties which make them candidates for
+      scalarization are identified in function find_var_candidates().  The
+      candidates are stored in candidate_bitmap.
+
+   2. The function body is scanned.  In the process, declarations which are
+      used in a manner that prevent their scalarization are removed from the
+      candidate bitmap.  More importantly, for every access into an aggregate,
+      an access structure (struct access) is created by create_access() and
+      stored in a vector associated with the aggregate.  Among other
+      information, the aggregate declaration, the offset and size of the access
+      and its type are stored in the structure.
+
+      On a related note, assign_link structures are created for every assign
+      statement between candidate aggregates and attached to the related
+      accesses.
+
+   3. The vectors of accesses are analyzed.  They are first sorted according to
+      their offset and size and then scanned for partially overlapping accesses
+      (i.e. those which overlap but one is not entirely within another).  Such
+      an access disqualifies the whole aggregate from being scalarized.
+
+      If there is no such inhibiting overlap, a representative access structure
+      is chosen for every unique combination of offset and size.  Afterwards,
+      the pass builds a set of trees from these structures, in which children
+      of an access are within their parent (in terms of offset and size).
+
+      Then accesses  are propagated  whenever possible (i.e.  in cases  when it
+      does not create a partially overlapping access) across assign_links from
+      the right hand side to the left hand side.
+
+      Then the set of trees for each declaration is traversed again and those
+      accesses which should be replaced by a scalar are identified.
+
+   4. The function is traversed again, and for every reference into an
+      aggregate that has some component which is about to be scalarized,
+      statements are amended and new statements are created as necessary.
+      Finally, if a parameter got scalarized, the scalar replacements are
+      initialized with values from respective parameter aggregates.  */
+
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "alloc-pool.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 "gimple.h"
+#include "cgraph.h"
 #include "tree-flow.h"
-#include "tree-gimple.h"
+#include "ipa-prop.h"
+#include "diagnostic.h"
+#include "statistics.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"
+#include "target.h"
+#include "flags.h"
 
+/* Enumeration of all aggregate reductions we can do.  */
+enum sra_mode { SRA_MODE_EARLY_IPA,   /* early call regularization */
+               SRA_MODE_EARLY_INTRA, /* early intraprocedural SRA */
+               SRA_MODE_INTRA };     /* late intraprocedural SRA */
+
+/* Global variable describing which aggregate reduction we are performing at
+   the moment.  */
+static enum sra_mode sra_mode;
+
+struct assign_link;
+
+/* ACCESS represents each access to an aggregate variable (as a whole or a
+   part).  It can also represent a group of accesses that refer to exactly the
+   same fragment of an aggregate (i.e. those that have exactly the same offset
+   and size).  Such representatives for a single aggregate, once determined,
+   are linked in a linked list and have the group fields set.
+
+   Moreover, when doing intraprocedural SRA, a tree is built from those
+   representatives (by the means of first_child and next_sibling pointers), in
+   which all items in a subtree are "within" the root, i.e. their offset is
+   greater or equal to offset of the root and offset+size is smaller or equal
+   to offset+size of the root.  Children of an access are sorted by offset.
+
+   Note that accesses to parts of vector and complex number types always
+   represented by an access to the whole complex number or a vector.  It is a
+   duty of the modifying functions to replace them appropriately.  */
+
+struct access
+{
+  /* Values returned by  `get_ref_base_and_extent' for each component reference
+     If EXPR isn't a component reference  just set `BASE = EXPR', `OFFSET = 0',
+     `SIZE = TREE_SIZE (TREE_TYPE (expr))'.  */
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT size;
+  tree base;
+
+  /* Expression.  It is context dependent so do not use it to create new
+     expressions to access the original aggregate.  See PR 42154 for a
+     testcase.  */
+  tree expr;
+  /* Type.  */
+  tree type;
 
-/* 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 statement this access belongs to.  */
+  gimple stmt;
 
-   The optimization proceeds in phases:
+  /* Next group representative for this aggregate. */
+  struct access *next_grp;
 
-     (1) Identify variables that have types that are candidates for
-        decomposition.
+  /* Pointer to the group representative.  Pointer to itself if the struct is
+     the representative.  */
+  struct access *group_representative;
 
-     (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.
+  /* If this access has any children (in terms of the definition above), this
+     points to the first one.  */
+  struct access *first_child;
 
-     (3) Based on the usage profile, instantiate substitution variables.
+  /* In intraprocedural SRA, pointer to the next sibling in the access tree as
+     described above.  In IPA-SRA this is a pointer to the next access
+     belonging to the same group (having the same representative).  */
+  struct access *next_sibling;
 
-     (4) Scan the function making replacements.
-*/
+  /* Pointers to the first and last element in the linked list of assign
+     links.  */
+  struct assign_link *first_link, *last_link;
 
+  /* Pointer to the next access in the work queue.  */
+  struct access *next_queued;
 
-/* True if this is the "early" pass, before inlining.  */
-static bool early_sra;
+  /* Replacement variable for this access "region."  Never to be accessed
+     directly, always only by the means of get_access_replacement() and only
+     when grp_to_be_replaced flag is set.  */
+  tree replacement_decl;
 
-/* The set of todo flags to return from tree_sra.  */
-static unsigned int todoflags;
+  /* Is this particular access write access? */
+  unsigned write : 1;
 
-/* The set of aggregate variables that are candidates for scalarization.  */
-static bitmap sra_candidates;
+  /* Is this access currently in the work queue?  */
+  unsigned grp_queued : 1;
 
-/* Set of scalarizable PARM_DECLs that need copy-in operations at the
-   beginning of the function.  */
-static bitmap needs_copy_in;
+  /* Does this group contain a write access?  This flag is propagated down the
+     access tree.  */
+  unsigned grp_write : 1;
 
-/* Sets of bit pairs that cache type decomposition and instantiation.  */
-static bitmap sra_type_decomp_cache;
-static bitmap sra_type_inst_cache;
+  /* Does this group contain a read access?  This flag is propagated down the
+     access tree.  */
+  unsigned grp_read : 1;
 
-/* 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;
+  /* Other passes of the analysis use this bit to make function
+     analyze_access_subtree create scalar replacements for this group if
+     possible.  */
+  unsigned grp_hint : 1;
 
-  /* 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;
+  /* Is the subtree rooted in this access fully covered by scalar
+     replacements?  */
+  unsigned grp_covered : 1;
 
-  /* The type of the element.  */
-  tree type;
+  /* If set to true, this access and all below it in an access tree must not be
+     scalarized.  */
+  unsigned grp_unscalarizable_region : 1;
 
-  /* A VAR_DECL, for any sub-element we've decided to replace.  */
-  tree replacement;
+  /* Whether data have been written to parts of the aggregate covered by this
+     access which is not to be scalarized.  This flag is propagated up in the
+     access tree.  */
+  unsigned grp_unscalarized_data : 1;
 
-  /* 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;
+  /* Does this access and/or group contain a write access through a
+     BIT_FIELD_REF?  */
+  unsigned grp_partial_lhs : 1;
 
-  /* The number of times the element is copied to or from another
-     scalarizable element.  */
-  unsigned int n_copies;
+  /* Does this group contain accesses to different types? (I.e. through a union
+     or a similar mechanism).  */
+  unsigned grp_different_types : 1;
 
-  /* True if TYPE is scalar.  */
-  bool is_scalar;
+  /* Set when a scalar replacement should be created for this variable.  We do
+     the decision and creation at different places because create_tmp_var
+     cannot be called from within FOR_EACH_REFERENCED_VAR. */
+  unsigned grp_to_be_replaced : 1;
 
-  /* True if this element is a group of members of its parent.  */
-  bool is_group;
+  /* Is it possible that the group refers to data which might be (directly or
+     otherwise) modified?  */
+  unsigned grp_maybe_modified : 1;
 
-  /* True if we saw something about this element that prevents scalarization,
-     such as non-constant indexing.  */
-  bool cannot_scalarize;
+  /* Set when this is a representative of a pointer to scalar (i.e. by
+     reference) parameter which we consider for turning into a plain scalar
+     (i.e. a by value parameter).  */
+  unsigned grp_scalar_ptr : 1;
 
-  /* True if we've decided that structure-to-structure assignment
-     should happen via memcpy and not per-element.  */
-  bool use_block_copy;
+  /* Set when we discover that this pointer is not safe to dereference in the
+     caller.  */
+  unsigned grp_not_necessarilly_dereferenced : 1;
+};
 
-  /* True if everything under this element has been marked TREE_NO_WARNING.  */
-  bool all_no_warning;
+typedef struct access *access_p;
 
-  /* A flag for use with/after random access traversals.  */
-  bool visited;
+DEF_VEC_P (access_p);
+DEF_VEC_ALLOC_P (access_p, heap);
 
-  /* True if there is BIT_FIELD_REF on the lhs with a vector. */
-  bool is_vector_lhs;
+/* Alloc pool for allocating access structures.  */
+static alloc_pool access_pool;
 
-  /* 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;
+/* A structure linking lhs and rhs accesses from an aggregate assignment.  They
+   are used to propagate subaccesses from rhs to lhs as long as they don't
+   conflict with what is already there.  */
+struct assign_link
+{
+  struct access *lacc, *racc;
+  struct assign_link *next;
 };
 
-#define IS_ELEMENT_FOR_GROUP(ELEMENT) (TREE_CODE (ELEMENT) == RANGE_EXPR)
+/* Alloc pool for allocating assign link structures.  */
+static alloc_pool link_pool;
 
-#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)
+/* Base (tree) -> Vector (VEC(access_p,heap) *) map.  */
+static struct pointer_map_t *base_access_vec;
 
-/* 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);
+/* Bitmap of candidates.  */
+static bitmap candidate_bitmap;
 
-  /* Find the next child in the parent.  */
-  if (child)
-    child = child->sibling;
-  else
-    child = group->parent->children;
+/* Obstack for creation of fancy names.  */
+static struct obstack name_obstack;
 
-  /* 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 ();
+/* Head of a linked list of accesses that need to have its subaccesses
+   propagated to their assignment counterparts. */
+static struct access *work_queue_head;
 
-      child = child->sibling;
-    }
+/* Number of parameters of the analyzed function when doing early ipa SRA.  */
+static int func_param_count;
+
+/* scan_function sets the following to true if it encounters a call to
+   __builtin_apply_args.  */
+static bool encountered_apply_args;
+
+/* This is a table in which for each basic block and parameter there is a
+   distance (offset + size) in that parameter which is dereferenced and
+   accessed in that BB.  */
+static HOST_WIDE_INT *bb_dereferences;
+/* Bitmap of BBs that can cause the function to "stop" progressing by
+   returning, throwing externally, looping infinitely or calling a function
+   which might abort etc.. */
+static bitmap final_bbs;
 
-  return child;
+/* Representative of no accesses at all. */
+static struct access  no_accesses_representant;
+
+/* Predicate to test the special value.  */
+
+static inline bool
+no_accesses_p (struct access *access)
+{
+  return access == &no_accesses_representant;
 }
 
-/* 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;
+/* Dump contents of ACCESS to file F in a human friendly way.  If GRP is true,
+   representative fields are dumped, otherwise those which only describe the
+   individual access are.  */
+
+static struct
+{
+  /* Number of processed aggregates is readily available in
+     analyze_all_variable_accesses and so is not stored here.  */
 
-/* All structures are allocated out of the following obstack.  */
-static struct obstack sra_obstack;
+  /* Number of created scalar replacements.  */
+  int replacements;
 
-/* Debugging functions.  */
-static void dump_sra_elt_name (FILE *, struct sra_elt *);
-extern void debug_sra_elt_name (struct sra_elt *);
+  /* Number of times sra_modify_expr or sra_modify_assign themselves changed an
+     expression.  */
+  int exprs;
 
-/* Forward declarations.  */
-static tree generate_element_ref (struct sra_elt *);
-static tree sra_build_assignment (tree dst, tree src);
-static void mark_all_v_defs (tree list);
+  /* Number of statements created by generate_subtree_copies.  */
+  int subtree_copies;
 
-\f
-/* Return true if DECL is an SRA candidate.  */
+  /* Number of statements created by load_assign_lhs_subreplacements.  */
+  int subreplacements;
 
-static bool
-is_sra_candidate_decl (tree decl)
+  /* Number of times sra_modify_assign has deleted a statement.  */
+  int deleted;
+
+  /* Number of times sra_modify_assign has to deal with subaccesses of LHS and
+     RHS reparately due to type conversions or nonexistent matching
+     references.  */
+  int separate_lhs_rhs_handling;
+
+  /* Number of parameters that were removed because they were unused.  */
+  int deleted_unused_parameters;
+
+  /* Number of scalars passed as parameters by reference that have been
+     converted to be passed by value.  */
+  int scalar_by_ref_to_by_val;
+
+  /* Number of aggregate parameters that were replaced by one or more of their
+     components.  */
+  int aggregate_params_reduced;
+
+  /* Numbber of components created when splitting aggregate parameters.  */
+  int param_reductions_created;
+} sra_stats;
+
+static void
+dump_access (FILE *f, struct access *access, bool grp)
+{
+  fprintf (f, "access { ");
+  fprintf (f, "base = (%d)'", DECL_UID (access->base));
+  print_generic_expr (f, access->base, 0);
+  fprintf (f, "', offset = " HOST_WIDE_INT_PRINT_DEC, access->offset);
+  fprintf (f, ", size = " HOST_WIDE_INT_PRINT_DEC, access->size);
+  fprintf (f, ", expr = ");
+  print_generic_expr (f, access->expr, 0);
+  fprintf (f, ", type = ");
+  print_generic_expr (f, access->type, 0);
+  if (grp)
+    fprintf (f, ", grp_write = %d, grp_read = %d, grp_hint = %d, "
+            "grp_covered = %d, grp_unscalarizable_region = %d, "
+            "grp_unscalarized_data = %d, grp_partial_lhs = %d, "
+            "grp_different_types = %d, grp_to_be_replaced = %d, "
+            "grp_maybe_modified = %d, "
+            "grp_not_necessarilly_dereferenced = %d\n",
+            access->grp_write, access->grp_read, access->grp_hint,
+            access->grp_covered, access->grp_unscalarizable_region,
+            access->grp_unscalarized_data, access->grp_partial_lhs,
+            access->grp_different_types, access->grp_to_be_replaced,
+            access->grp_maybe_modified,
+            access->grp_not_necessarilly_dereferenced);
+  else
+    fprintf (f, ", write = %d, grp_partial_lhs = %d\n", access->write,
+            access->grp_partial_lhs);
+}
+
+/* Dump a subtree rooted in ACCESS to file F, indent by LEVEL.  */
+
+static void
+dump_access_tree_1 (FILE *f, struct access *access, int level)
 {
-  return DECL_P (decl) && bitmap_bit_p (sra_candidates, DECL_UID (decl));
+  do
+    {
+      int i;
+
+      for (i = 0; i < level; i++)
+       fputs ("* ", dump_file);
+
+      dump_access (f, access, true);
+
+      if (access->first_child)
+       dump_access_tree_1 (f, access->first_child, level + 1);
+
+      access = access->next_sibling;
+    }
+  while (access);
 }
 
-/* Return true if TYPE is a scalar type.  */
+/* Dump all access trees for a variable, given the pointer to the first root in
+   ACCESS.  */
 
-static bool
-is_sra_scalar_type (tree type)
+static void
+dump_access_tree (FILE *f, struct access *access)
 {
-  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);
+  for (; access; access = access->next_grp)
+    dump_access_tree_1 (f, access, 0);
 }
 
-/* Return true if TYPE can be decomposed into a set of independent variables.
+/* Return true iff ACC is non-NULL and has subaccesses.  */
+
+static inline bool
+access_has_children_p (struct access *acc)
+{
+  return acc && acc->first_child;
+}
 
-   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.  */
+/* Return a vector of pointers to accesses for the variable given in BASE or
+   NULL if there is none.  */
 
-bool
-sra_type_can_be_decomposed_p (tree type)
+static VEC (access_p, heap) *
+get_base_access_vector (tree base)
 {
-  unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
-  tree t;
+  void **slot;
 
-  /* 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;
+  slot = pointer_map_contains (base_access_vec, base);
+  if (!slot)
+    return NULL;
+  else
+    return *(VEC (access_p, heap) **) slot;
+}
 
-  /* 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;
+/* Find an access with required OFFSET and SIZE in a subtree of accesses rooted
+   in ACCESS.  Return NULL if it cannot be found.  */
 
-  /* The type must be a non-union aggregate.  */
-  switch (TREE_CODE (type))
+static struct access *
+find_access_in_subtree (struct access *access, HOST_WIDE_INT offset,
+                       HOST_WIDE_INT size)
+{
+  while (access && (access->offset != offset || access->size != size))
     {
-    case RECORD_TYPE:
-      {
-       bool saw_one_field = false;
+      struct access *child = access->first_child;
 
-       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)
-                 && (tree_low_cst (DECL_SIZE (t), 1)
-                     != TYPE_PRECISION (TREE_TYPE (t))))
-               goto fail;
+      while (child && (child->offset + child->size <= offset))
+       child = child->next_sibling;
+      access = child;
+    }
 
-             saw_one_field = true;
-           }
+  return access;
+}
 
-       /* Record types must have at least one field.  */
-       if (!saw_one_field)
-         goto fail;
-      }
-      break;
+/* Return the first group representative for DECL or NULL if none exists.  */
 
-    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;
+static struct access *
+get_first_repr_for_decl (tree base)
+{
+  VEC (access_p, heap) *access_vec;
 
-    case COMPLEX_TYPE:
-      break;
+  access_vec = get_base_access_vector (base);
+  if (!access_vec)
+    return NULL;
 
-    default:
-      goto fail;
-    }
+  return VEC_index (access_p, access_vec, 0);
+}
 
-  bitmap_set_bit (sra_type_decomp_cache, cache+0);
-  return true;
+/* Find an access representative for the variable BASE and given OFFSET and
+   SIZE.  Requires that access trees have already been built.  Return NULL if
+   it cannot be found.  */
 
- fail:
-  bitmap_set_bit (sra_type_decomp_cache, cache+1);
-  return false;
-}
+static struct access *
+get_var_base_offset_size_access (tree base, HOST_WIDE_INT offset,
+                                HOST_WIDE_INT size)
+{
+  struct access *access;
 
-/* Return true if DECL can be decomposed into a set of independent
-   (though not necessarily scalar) variables.  */
+  access = get_first_repr_for_decl (base);
+  while (access && (access->offset + access->size <= offset))
+    access = access->next_grp;
+  if (!access)
+    return NULL;
 
-static bool
-decl_can_be_decomposed_p (tree var)
+  return find_access_in_subtree (access, offset, size);
+}
+
+/* Add LINK to the linked list of assign links of RACC.  */
+static void
+add_link_to_rhs (struct access *racc, struct assign_link *link)
 {
-  /* Early out for scalars.  */
-  if (is_sra_scalar_type (TREE_TYPE (var)))
-    return false;
+  gcc_assert (link->racc == racc);
 
-  /* The variable must not be aliased.  */
-  if (!is_gimple_non_addressable (var))
+  if (!racc->first_link)
     {
-      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;
+      gcc_assert (!racc->last_link);
+      racc->first_link = link;
     }
+  else
+    racc->last_link->next = link;
 
-  /* The variable must not be volatile.  */
-  if (TREE_THIS_VOLATILE (var))
+  racc->last_link = link;
+  link->next = NULL;
+}
+
+/* Move all link structures in their linked list in OLD_RACC to the linked list
+   in NEW_RACC.  */
+static void
+relink_to_new_repr (struct access *new_racc, struct access *old_racc)
+{
+  if (!old_racc->first_link)
     {
-      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;
+      gcc_assert (!old_racc->last_link);
+      return;
     }
 
-  /* We must be able to decompose the variable's type.  */
-  if (!sra_type_can_be_decomposed_p (TREE_TYPE (var)))
+  if (new_racc->first_link)
     {
-      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;
-    }
+      gcc_assert (!new_racc->last_link->next);
+      gcc_assert (!old_racc->last_link || !old_racc->last_link->next);
 
-  /* 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 truely 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
-      && TYPE_MAIN_VARIANT (TREE_TYPE (var))
-        == TYPE_MAIN_VARIANT (va_list_type_node))
-    return false;
+      new_racc->last_link->next = old_racc->first_link;
+      new_racc->last_link = old_racc->last_link;
+    }
+  else
+    {
+      gcc_assert (!new_racc->last_link);
 
-  return true;
+      new_racc->first_link = old_racc->first_link;
+      new_racc->last_link = old_racc->last_link;
+    }
+  old_racc->first_link = old_racc->last_link = NULL;
 }
 
-/* Return true if TYPE can be *completely* decomposed into scalars.  */
+/* Add ACCESS to the work queue (which is actually a stack).  */
 
-static bool
-type_can_instantiate_all_elements (tree type)
+static void
+add_access_to_work_queue (struct access *access)
 {
-  if (is_sra_scalar_type (type))
-    return true;
-  if (!sra_type_can_be_decomposed_p (type))
-    return false;
-
-  switch (TREE_CODE (type))
+  if (!access->grp_queued)
     {
-    case RECORD_TYPE:
-      {
-       unsigned int cache = TYPE_UID (TYPE_MAIN_VARIANT (type)) * 2;
-       tree f;
+      gcc_assert (!access->next_queued);
+      access->next_queued = work_queue_head;
+      access->grp_queued = 1;
+      work_queue_head = access;
+    }
+}
 
-       if (bitmap_bit_p (sra_type_inst_cache, cache+0))
-         return true;
-       if (bitmap_bit_p (sra_type_inst_cache, cache+1))
-         return false;
+/* Pop an access from the work queue, and return it, assuming there is one.  */
 
-       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;
-               }
-           }
+static struct access *
+pop_access_from_work_queue (void)
+{
+  struct access *access = work_queue_head;
 
-       bitmap_set_bit (sra_type_inst_cache, cache+0);
-       return true;
-      }
+  work_queue_head = access->next_queued;
+  access->next_queued = NULL;
+  access->grp_queued = 0;
+  return access;
+}
 
-    case ARRAY_TYPE:
-      return type_can_instantiate_all_elements (TREE_TYPE (type));
 
-    case COMPLEX_TYPE:
-      return true;
+/* Allocate necessary structures.  */
 
-    default:
-      gcc_unreachable ();
-    }
+static void
+sra_initialize (void)
+{
+  candidate_bitmap = BITMAP_ALLOC (NULL);
+  gcc_obstack_init (&name_obstack);
+  access_pool = create_alloc_pool ("SRA accesses", sizeof (struct access), 16);
+  link_pool = create_alloc_pool ("SRA links", sizeof (struct assign_link), 16);
+  base_access_vec = pointer_map_create ();
+  memset (&sra_stats, 0, sizeof (sra_stats));
+  encountered_apply_args = false;
 }
 
-/* Test whether ELT or some sub-element cannot be scalarized.  */
+/* Hook fed to pointer_map_traverse, deallocate stored vectors.  */
 
 static bool
-can_completely_scalarize_p (struct sra_elt *elt)
+delete_base_accesses (const void *key ATTRIBUTE_UNUSED, void **value,
+                    void *data ATTRIBUTE_UNUSED)
 {
-  struct sra_elt *c;
+  VEC (access_p, heap) *access_vec;
+  access_vec = (VEC (access_p, heap) *) *value;
+  VEC_free (access_p, heap, access_vec);
 
-  if (elt->cannot_scalarize)
-    return false;
+  return true;
+}
 
-  for (c = elt->children; c; c = c->sibling)
-    if (!can_completely_scalarize_p (c))
-      return false;
+/* Deallocate all general structures.  */
 
-  for (c = elt->groups; c; c = c->sibling)
-    if (!can_completely_scalarize_p (c))
-      return false;
+static void
+sra_deinitialize (void)
+{
+  BITMAP_FREE (candidate_bitmap);
+  free_alloc_pool (access_pool);
+  free_alloc_pool (link_pool);
+  obstack_free (&name_obstack, NULL);
 
-  return true;
+  pointer_map_traverse (base_access_vec, delete_base_accesses, NULL);
+  pointer_map_destroy (base_access_vec);
+}
+
+/* Remove DECL from candidates for SRA and write REASON to the dump file if
+   there is one.  */
+static void
+disqualify_candidate (tree decl, const char *reason)
+{
+  bitmap_clear_bit (candidate_bitmap, DECL_UID (decl));
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "! Disqualifying ");
+      print_generic_expr (dump_file, decl, 0);
+      fprintf (dump_file, " - %s\n", reason);
+    }
 }
 
-\f
-/* A simplified tree hashing algorithm that only handles the types of
-   trees we expect to find in sra_elt->element.  */
+/* Return true iff the type contains a field or an element which does not allow
+   scalarization.  */
 
-static hashval_t
-sra_hash_tree (tree t)
+static bool
+type_internals_preclude_sra_p (tree type)
 {
-  hashval_t h;
+  tree fld;
+  tree et;
 
-  switch (TREE_CODE (t))
+  switch (TREE_CODE (type))
     {
-    case VAR_DECL:
-    case PARM_DECL:
-    case RESULT_DECL:
-      h = DECL_UID (t);
-      break;
+    case RECORD_TYPE:
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+      for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+       if (TREE_CODE (fld) == FIELD_DECL)
+         {
+           tree ft = TREE_TYPE (fld);
 
-    case INTEGER_CST:
-      h = TREE_INT_CST_LOW (t) ^ TREE_INT_CST_HIGH (t);
-      break;
+           if (TREE_THIS_VOLATILE (fld)
+               || !DECL_FIELD_OFFSET (fld) || !DECL_SIZE (fld)
+               || !host_integerp (DECL_FIELD_OFFSET (fld), 1)
+               || !host_integerp (DECL_SIZE (fld), 1))
+             return true;
 
-    case RANGE_EXPR:
-      h = iterative_hash_expr (TREE_OPERAND (t, 0), 0);
-      h = iterative_hash_expr (TREE_OPERAND (t, 1), h);
-      break;
+           if (AGGREGATE_TYPE_P (ft)
+               && type_internals_preclude_sra_p (ft))
+             return true;
+         }
 
-    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;
+      return false;
 
-    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;
+    case ARRAY_TYPE:
+      et = TREE_TYPE (type);
+
+      if (AGGREGATE_TYPE_P (et))
+       return type_internals_preclude_sra_p (et);
+      else
+       return false;
 
     default:
-      gcc_unreachable ();
+      return false;
     }
-
-  return h;
 }
 
-/* Hash function for type SRA_PAIR.  */
+/* If T is an SSA_NAME, return NULL if it is not a default def or return its
+   base variable if it is.  Return T if it is not an SSA_NAME.  */
 
-static hashval_t
-sra_elt_hash (const void *x)
+static tree
+get_ssa_base_param (tree t)
 {
-  const struct sra_elt *e = x;
-  const struct sra_elt *p;
-  hashval_t h;
+  if (TREE_CODE (t) == SSA_NAME)
+    {
+      if (SSA_NAME_IS_DEFAULT_DEF (t))
+       return SSA_NAME_VAR (t);
+      else
+       return NULL_TREE;
+    }
+  return t;
+}
 
-  h = sra_hash_tree (e->element);
+/* Mark a dereference of BASE of distance DIST in a basic block tht STMT
+   belongs to, unless the BB has already been marked as a potentially
+   final.  */
 
-  /* 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);
+static void
+mark_parm_dereference (tree base, HOST_WIDE_INT dist, gimple stmt)
+{
+  basic_block bb = gimple_bb (stmt);
+  int idx, parm_index = 0;
+  tree parm;
 
-  return h;
-}
+  if (bitmap_bit_p (final_bbs, bb->index))
+    return;
 
-/* Equality function for type SRA_PAIR.  */
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm && parm != base;
+       parm = TREE_CHAIN (parm))
+    parm_index++;
 
-static int
-sra_elt_eq (const void *x, const void *y)
-{
-  const struct sra_elt *a = x;
-  const struct sra_elt *b = 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;
+  gcc_assert (parm_index < func_param_count);
 
-  ae = a->element;
-  be = b->element;
+  idx = bb->index * func_param_count + parm_index;
+  if (bb_dereferences[idx] < dist)
+    bb_dereferences[idx] = dist;
+}
 
-  if (ae == be)
-    return true;
-  if (TREE_CODE (ae) != TREE_CODE (be))
-    return false;
+/* Create and insert access for EXPR. Return created access, or NULL if it is
+   not possible.  */
 
-  switch (TREE_CODE (ae))
-    {
-    case VAR_DECL:
-    case PARM_DECL:
-    case RESULT_DECL:
-      /* These are all pointer unique.  */
-      return false;
+static struct access *
+create_access (tree expr, gimple stmt, bool write)
+{
+  struct access *access;
+  void **slot;
+  VEC (access_p,heap) *vec;
+  HOST_WIDE_INT offset, size, max_size;
+  tree base = expr;
+  bool ptr, unscalarizable_region = false;
 
-    case INTEGER_CST:
-      /* Integers are not pointer unique, so compare their values.  */
-      return tree_int_cst_equal (ae, be);
+  base = get_ref_base_and_extent (expr, &offset, &size, &max_size);
 
-    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));
+  if (sra_mode == SRA_MODE_EARLY_IPA && INDIRECT_REF_P (base))
+    {
+      base = get_ssa_base_param (TREE_OPERAND (base, 0));
+      if (!base)
+       return NULL;
+      ptr = true;
+    }
+  else
+    ptr = false;
 
-    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);
+  if (!DECL_P (base) || !bitmap_bit_p (candidate_bitmap, DECL_UID (base)))
+    return NULL;
 
-    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));
+  if (sra_mode == SRA_MODE_EARLY_IPA)
+    {
+      if (size < 0 || size != max_size)
+       {
+         disqualify_candidate (base, "Encountered a variable sized access.");
+         return NULL;
+       }
+      if ((offset % BITS_PER_UNIT) != 0 || (size % BITS_PER_UNIT) != 0)
+       {
+         disqualify_candidate (base,
+                               "Encountered an acces not aligned to a byte.");
+         return NULL;
+       }
 
-    default:
-      gcc_unreachable ();
+      if (ptr)
+       mark_parm_dereference (base, offset + size, stmt);
+    }
+  else
+    {
+      if (size != max_size)
+       {
+         size = max_size;
+         unscalarizable_region = true;
+       }
+      if (size < 0)
+       {
+         disqualify_candidate (base, "Encountered an unconstrained access.");
+         return NULL;
+       }
     }
-}
 
-/* Create or return the SRA_ELT structure for CHILD in PARENT.  PARENT
-   may be null, in which case CHILD must be a DECL.  */
+  access = (struct access *) pool_alloc (access_pool);
+  memset (access, 0, sizeof (struct access));
 
-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;
+  access->base = base;
+  access->offset = offset;
+  access->size = size;
+  access->expr = expr;
+  access->type = TREE_TYPE (expr);
+  access->write = write;
+  access->grp_unscalarizable_region = unscalarizable_region;
+  access->stmt = stmt;
 
-  if (parent)
-    dummy.parent = parent->is_group ? parent->parent : parent;
+  slot = pointer_map_contains (base_access_vec, base);
+  if (slot)
+    vec = (VEC (access_p, heap) *) *slot;
   else
-    dummy.parent = NULL;
-  dummy.element = child;
+    vec = VEC_alloc (access_p, heap, 32);
 
-  slot = (struct sra_elt **) htab_find_slot (sra_map, &dummy, insert);
-  if (!slot && insert == NO_INSERT)
-    return NULL;
+  VEC_safe_push (access_p, heap, vec, access);
 
-  elt = *slot;
-  if (!elt && insert == INSERT)
-    {
-      *slot = elt = obstack_alloc (&sra_obstack, sizeof (*elt));
-      memset (elt, 0, sizeof (*elt));
+  *((struct VEC (access_p,heap) **)
+       pointer_map_insert (base_access_vec, base)) = vec;
 
-      elt->parent = parent;
-      elt->element = child;
-      elt->type = type;
-      elt->is_scalar = is_sra_scalar_type (type);
+  return access;
+}
 
-      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));
-       }
+/* Search the given tree for a declaration by skipping handled components and
+   exclude it from the candidates.  */
+
+static void
+disqualify_base_of_expr (tree t, const char *reason)
+{
+  while (handled_component_p (t))
+    t = TREE_OPERAND (t, 0);
+
+  if (sra_mode == SRA_MODE_EARLY_IPA)
+    {
+      if (INDIRECT_REF_P (t))
+       t = TREE_OPERAND (t, 0);
+      t = get_ssa_base_param (t);
     }
 
-  return elt;
+  if (t && DECL_P (t))
+    disqualify_candidate (t, reason);
 }
 
-/* Create or return the SRA_ELT structure for EXPR if the expression
-   refers to a scalarizable variable.  */
+/* Scan expression EXPR and create access structures for all accesses to
+   candidates for scalarization.  Return the created access or NULL if none is
+   created.  */
 
-static struct sra_elt *
-maybe_lookup_element_for_expr (tree expr)
+static struct access *
+build_access_from_expr_1 (tree *expr_ptr, gimple stmt, bool write)
 {
-  struct sra_elt *elt;
-  tree child;
+  struct access *ret = NULL;
+  tree expr = *expr_ptr;
+  bool partial_ref;
+
+  if (TREE_CODE (expr) == BIT_FIELD_REF
+      || TREE_CODE (expr) == IMAGPART_EXPR
+      || TREE_CODE (expr) == REALPART_EXPR)
+    {
+      expr = TREE_OPERAND (expr, 0);
+      partial_ref = true;
+    }
+  else
+    partial_ref = false;
+
+  /* We need to dive through V_C_Es in order to get the size of its parameter
+     and not the result type.  Ada produces such statements.  We are also
+     capable of handling the topmost V_C_E but not any of those buried in other
+     handled components.  */
+  if (TREE_CODE (expr) == VIEW_CONVERT_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+
+  if (contains_view_convert_expr_p (expr))
+    {
+      disqualify_base_of_expr (expr, "V_C_E under a different handled "
+                              "component.");
+      return NULL;
+    }
 
   switch (TREE_CODE (expr))
     {
+    case INDIRECT_REF:
+      if (sra_mode != SRA_MODE_EARLY_IPA)
+       return NULL;
+      /* fall through */
     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 COMPONENT_REF:
     case ARRAY_REF:
-      /* We can't scalarize variable array indices.  */
-      if (in_array_bounds_p (expr))
-        child = TREE_OPERAND (expr, 1);
-      else
-       return NULL;
+    case ARRAY_RANGE_REF:
+      ret = create_access (expr, stmt, write);
       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;
+    default:
       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;
+  if (write && partial_ref && ret)
+    ret->grp_partial_lhs = 1;
 
-    case REALPART_EXPR:
-      child = integer_zero_node;
-      break;
-    case IMAGPART_EXPR:
-      child = integer_one_node;
-      break;
+  return ret;
+}
 
-    default:
-      return NULL;
-    }
+/* Callback of scan_function.  Scan expression EXPR and create access
+   structures for all accesses to candidates for scalarization.  Return true if
+   any access has been inserted.  */
 
-  elt = maybe_lookup_element_for_expr (TREE_OPERAND (expr, 0));
-  if (elt)
-    return lookup_element (elt, child, TREE_TYPE (expr), INSERT);
-  return NULL;
+static bool
+build_access_from_expr (tree *expr_ptr,
+                       gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED, bool write,
+                       void *data ATTRIBUTE_UNUSED)
+{
+  return build_access_from_expr_1 (expr_ptr, gsi_stmt (*gsi), write) != 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, *BSI 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,
-              block_stmt_iterator *bsi, bool is_output, bool use_all);
-
-  /* Invoked when we have a copy between two scalarizable references.  */
-  void (*copy) (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
-               block_stmt_iterator *bsi);
-
-  /* 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, block_stmt_iterator *bsi);
-
-  /* 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,
-               block_stmt_iterator *bsi, bool is_output);
-
-  /* True during phase 2, false during phase 4.  */
-  /* ??? This is a hack.  */
-  bool initial_scan;
-};
+/* Disqualify LHS and RHS for scalarization if STMT must end its basic block in
+   modes in which it matters, return true iff they have been disqualified.  RHS
+   may be NULL, in that case ignore it.  If we scalarize an aggregate in
+   intra-SRA we may need to add statements after each statement.  This is not
+   possible if a statement unconditionally has to end the basic block.  */
+static bool
+disqualify_ops_if_throwing_stmt (gimple stmt, tree lhs, tree rhs)
+{
+  if ((sra_mode == SRA_MODE_EARLY_INTRA || sra_mode == SRA_MODE_INTRA)
+      && (stmt_can_throw_internal (stmt) || stmt_ends_bb_p (stmt)))
+    {
+      disqualify_base_of_expr (lhs, "LHS of a throwing stmt.");
+      if (rhs)
+       disqualify_base_of_expr (rhs, "RHS of a throwing stmt.");
+      return true;
+    }
+  return false;
+}
 
-#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)
+/* Result code for scan_assign callback for scan_function.  */
+enum scan_assign_result { SRA_SA_NONE,       /* nothing done for the stmt */
+                         SRA_SA_PROCESSED,  /* stmt analyzed/changed */
+                         SRA_SA_REMOVED };  /* stmt redundant and eliminated */
+
+
+/* Callback of scan_function.  Scan expressions occuring in the statement
+   pointed to by STMT_EXPR, create access structures for all accesses to
+   candidates for scalarization and remove those candidates which occur in
+   statements or expressions that prevent them from being split apart.  Return
+   true if any access has been inserted.  */
+
+static enum scan_assign_result
+build_accesses_from_assign (gimple *stmt_ptr,
+                           gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
+                           void *data ATTRIBUTE_UNUSED)
 {
-  tree t = *tp;
-  enum tree_code code = TREE_CODE (t);
+  gimple stmt = *stmt_ptr;
+  tree *lhs_ptr, *rhs_ptr;
+  struct access *lacc, *racc;
 
-  if (code == VAR_DECL || code == PARM_DECL || code == RESULT_DECL)
+  if (!gimple_assign_single_p (stmt))
+    return SRA_SA_NONE;
+
+  lhs_ptr = gimple_assign_lhs_ptr (stmt);
+  rhs_ptr = gimple_assign_rhs1_ptr (stmt);
+
+  if (disqualify_ops_if_throwing_stmt (stmt, *lhs_ptr, *rhs_ptr))
+    return SRA_SA_NONE;
+
+  racc = build_access_from_expr_1 (rhs_ptr, stmt, false);
+  lacc = build_access_from_expr_1 (lhs_ptr, stmt, true);
+
+  if (lacc && racc
+      && (sra_mode == SRA_MODE_EARLY_INTRA || sra_mode == SRA_MODE_INTRA)
+      && !lacc->grp_unscalarizable_region
+      && !racc->grp_unscalarizable_region
+      && AGGREGATE_TYPE_P (TREE_TYPE (*lhs_ptr))
+      /* FIXME: Turn the following line into an assert after PR 40058 is
+        fixed.  */
+      && lacc->size == racc->size
+      && useless_type_conversion_p (lacc->type, racc->type))
     {
-      *walk_subtrees = 0;
-      if (is_sra_candidate_decl (t))
-       return t;
+      struct assign_link *link;
+
+      link = (struct assign_link *) pool_alloc (link_pool);
+      memset (link, 0, sizeof (struct assign_link));
+
+      link->lacc = lacc;
+      link->racc = racc;
+
+      add_link_to_rhs (racc, link);
     }
-  else if (TYPE_P (t))
-    *walk_subtrees = 0;
 
-  return NULL;
+  return (lacc || racc) ? SRA_SA_PROCESSED : SRA_SA_NONE;
 }
-#endif
 
-/* Walk most expressions looking for a scalarizable aggregate.
-   If we find one, invoke FNS->USE.  */
+/* Callback of walk_stmt_load_store_addr_ops visit_addr used to determine
+   GIMPLE_ASM operands with memory constrains which cannot be scalarized.  */
 
-static void
-sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
-              const struct sra_walk_fns *fns)
+static bool
+asm_visit_addr (gimple stmt ATTRIBUTE_UNUSED, tree op,
+               void *data ATTRIBUTE_UNUSED)
 {
-  tree expr = *expr_p;
-  tree inner = expr;
-  bool disable_scalarization = false;
-  bool use_all_p = false;
+  if (DECL_P (op))
+    disqualify_candidate (op, "Non-scalarizable GIMPLE_ASM operand.");
+
+  return 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, bsi, is_output, use_all_p);
-         }
-       return;
+/* Scan function and look for interesting statements. Return true if any has
+   been found or processed, as indicated by callbacks.  SCAN_EXPR is a callback
+   called on all expressions within statements except assign statements and
+   those deemed entirely unsuitable for some reason (all operands in such
+   statements and expression are removed from candidate_bitmap).  SCAN_ASSIGN
+   is a callback called on all assign statements, HANDLE_SSA_DEFS is a callback
+   called on assign statements and those call statements which have a lhs, it
+   can be NULL.  ANALYSIS_STAGE is true when running in the analysis stage of a
+   pass and thus no statement is being modified.  DATA is a pointer passed to
+   all callbacks.  If any single callback returns true, this function also
+   returns true, otherwise it returns false.  */
 
-      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;
+static bool
+scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
+              enum scan_assign_result (*scan_assign) (gimple *,
+                                                      gimple_stmt_iterator *,
+                                                      void *),
+              bool (*handle_ssa_defs)(gimple, void *),
+              bool analysis_stage, void *data)
+{
+  gimple_stmt_iterator gsi;
+  basic_block bb;
+  unsigned i;
+  tree *t;
+  bool ret = false;
 
-      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;
+  FOR_EACH_BB (bb)
+    {
+      bool bb_changed = false;
 
-      case COMPONENT_REF:
+      if (handle_ssa_defs)
+       for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+         ret |= handle_ssa_defs (gsi_stmt (gsi), data);
+
+      gsi = gsi_start_bb (bb);
+      while (!gsi_end_p (gsi))
        {
-         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;
+         gimple stmt = gsi_stmt (gsi);
+         enum scan_assign_result assign_result;
+         bool any = false, deleted = false;
 
-      case REALPART_EXPR:
-      case IMAGPART_EXPR:
-       inner = TREE_OPERAND (inner, 0);
-       break;
+         if (analysis_stage && final_bbs && stmt_can_throw_external (stmt))
+           bitmap_set_bit (final_bbs, bb->index);
+         switch (gimple_code (stmt))
+           {
+           case GIMPLE_RETURN:
+             t = gimple_return_retval_ptr (stmt);
+             if (*t != NULL_TREE)
+               any |= scan_expr (t, &gsi, false, data);
+             if (analysis_stage && final_bbs)
+               bitmap_set_bit (final_bbs, bb->index);
+             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
-          complete outer element, to which walk_tree will bring us next.  */
-         
-       goto use_all;
-
-      case VIEW_CONVERT_EXPR:
-      case NOP_EXPR:
-       /* Similarly, a view/nop explicitly wants to look at an object in a
-          type other than the one we've scalarized.  */
-       goto use_all;
-
-      case WITH_SIZE_EXPR:
-       /* This is a transparent wrapper.  The entire inner expression really
-          is being used.  */
-       goto use_all;
-
-      use_all:
-        expr_p = &TREE_OPERAND (inner, 0);
-       inner = expr = *expr_p;
-       use_all_p = true;
-       break;
+           case GIMPLE_ASSIGN:
+             assign_result = scan_assign (&stmt, &gsi, data);
+             any |= assign_result == SRA_SA_PROCESSED;
+             deleted = assign_result == SRA_SA_REMOVED;
+             if (handle_ssa_defs && assign_result != SRA_SA_REMOVED)
+               any |= handle_ssa_defs (stmt, data);
+             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;
-      }
-}
+           case GIMPLE_CALL:
+             /* Operands must be processed before the lhs.  */
+             for (i = 0; i < gimple_call_num_args (stmt); i++)
+               {
+                 tree *argp = gimple_call_arg_ptr (stmt, i);
+                 any |= scan_expr (argp, &gsi, false, data);
+               }
+
+             if (analysis_stage)
+               {
+                 tree dest = gimple_call_fndecl (stmt);
+                 int flags = gimple_call_flags (stmt);
 
-/* Walk a TREE_LIST of values looking for scalarizable aggregates.
-   If we find one, invoke FNS->USE.  */
+                 if (dest
+                     && DECL_BUILT_IN_CLASS (dest) == BUILT_IN_NORMAL
+                     && DECL_FUNCTION_CODE (dest) == BUILT_IN_APPLY_ARGS)
+                   encountered_apply_args = true;
 
-static void
-sra_walk_tree_list (tree list, block_stmt_iterator *bsi, bool is_output,
-                   const struct sra_walk_fns *fns)
-{
-  tree op;
-  for (op = list; op ; op = TREE_CHAIN (op))
-    sra_walk_expr (&TREE_VALUE (op), bsi, is_output, fns);
-}
+                 if (final_bbs
+                     && (flags & (ECF_CONST | ECF_PURE)) == 0)
+                   bitmap_set_bit (final_bbs, bb->index);
+               }
 
-/* Walk the arguments of a CALL_EXPR looking for scalarizable aggregates.
-   If we find one, invoke FNS->USE.  */
+             if (gimple_call_lhs (stmt))
+               {
+                 tree *lhs_ptr = gimple_call_lhs_ptr (stmt);
+                 if (!analysis_stage
+                     || !disqualify_ops_if_throwing_stmt (stmt,
+                                                          *lhs_ptr, NULL))
+                   {
+                     any |= scan_expr (lhs_ptr, &gsi, true, data);
+                     if (handle_ssa_defs)
+                       any |= handle_ssa_defs (stmt, data);
+                   }
+               }
+             break;
 
-static void
-sra_walk_call_expr (tree expr, block_stmt_iterator *bsi,
-                   const struct sra_walk_fns *fns)
-{
-  int i;
-  int nargs = call_expr_nargs (expr);
-  for (i = 0; i < nargs; i++)
-    sra_walk_expr (&CALL_EXPR_ARG (expr, i), bsi, false, fns);
+           case GIMPLE_ASM:
+             if (analysis_stage)
+               {
+                 walk_stmt_load_store_addr_ops (stmt, NULL, NULL, NULL,
+                                                asm_visit_addr);
+                 if (final_bbs)
+                   bitmap_set_bit (final_bbs, bb->index);
+               }
+             for (i = 0; i < gimple_asm_ninputs (stmt); i++)
+               {
+                 tree *op = &TREE_VALUE (gimple_asm_input_op (stmt, i));
+                 any |= scan_expr (op, &gsi, false, data);
+               }
+             for (i = 0; i < gimple_asm_noutputs (stmt); i++)
+               {
+                 tree *op = &TREE_VALUE (gimple_asm_output_op (stmt, i));
+                 any |= scan_expr (op, &gsi, true, data);
+               }
+             break;
+
+           default:
+             break;
+           }
+
+         if (any)
+           {
+             ret = true;
+
+             if (!analysis_stage)
+               {
+                 bb_changed = true;
+                 update_stmt (stmt);
+                 maybe_clean_eh_stmt (stmt);
+               }
+           }
+         if (deleted)
+           bb_changed = true;
+         else
+           {
+             gsi_next (&gsi);
+             ret = true;
+           }
+       }
+      if (!analysis_stage && bb_changed && sra_mode == SRA_MODE_EARLY_IPA)
+       gimple_purge_dead_eh_edges (bb);
+    }
+
+  return ret;
 }
 
-/* Walk the inputs and outputs of an ASM_EXPR looking for scalarizable
-   aggregates.  If we find one, invoke FNS->USE.  */
+/* Helper of QSORT function. There are pointers to accesses in the array.  An
+   access is considered smaller than another if it has smaller offset or if the
+   offsets are the same but is size is bigger. */
+
+static int
+compare_access_positions (const void *a, const void *b)
+{
+  const access_p *fp1 = (const access_p *) a;
+  const access_p *fp2 = (const access_p *) b;
+  const access_p f1 = *fp1;
+  const access_p f2 = *fp2;
+
+  if (f1->offset != f2->offset)
+    return f1->offset < f2->offset ? -1 : 1;
+
+  if (f1->size == f2->size)
+    {
+      /* Put any non-aggregate type before any aggregate type.  */
+      if (!is_gimple_reg_type (f1->type)
+         && is_gimple_reg_type (f2->type))
+       return 1;
+      else if (is_gimple_reg_type (f1->type)
+              && !is_gimple_reg_type (f2->type))
+       return -1;
+      /* Put any complex or vector type before any other scalar type.  */
+      else if (TREE_CODE (f1->type) != COMPLEX_TYPE
+              && TREE_CODE (f1->type) != VECTOR_TYPE
+              && (TREE_CODE (f2->type) == COMPLEX_TYPE
+                  || TREE_CODE (f2->type) == VECTOR_TYPE))
+       return 1;
+      else if ((TREE_CODE (f1->type) == COMPLEX_TYPE
+               || TREE_CODE (f1->type) == VECTOR_TYPE)
+              && TREE_CODE (f2->type) != COMPLEX_TYPE
+              && TREE_CODE (f2->type) != VECTOR_TYPE)
+       return -1;
+      /* Put the integral type with the bigger precision first.  */
+      else if (INTEGRAL_TYPE_P (f1->type)
+              && INTEGRAL_TYPE_P (f2->type))
+       return TYPE_PRECISION (f1->type) > TYPE_PRECISION (f2->type) ? -1 : 1;
+      /* Put any integral type with non-full precision last.  */
+      else if (INTEGRAL_TYPE_P (f1->type)
+              && (TREE_INT_CST_LOW (TYPE_SIZE (f1->type))
+                  != TYPE_PRECISION (f1->type)))
+       return 1;
+      else if (INTEGRAL_TYPE_P (f2->type)
+              && (TREE_INT_CST_LOW (TYPE_SIZE (f2->type))
+                  != TYPE_PRECISION (f2->type)))
+       return -1;
+      /* Stabilize the sort.  */
+      return TYPE_UID (f1->type) - TYPE_UID (f2->type);
+    }
+
+  /* We want the bigger accesses first, thus the opposite operator in the next
+     line: */
+  return f1->size > f2->size ? -1 : 1;
+}
+
+
+/* Append a name of the declaration to the name obstack.  A helper function for
+   make_fancy_name.  */
 
 static void
-sra_walk_asm_expr (tree expr, block_stmt_iterator *bsi,
-                  const struct sra_walk_fns *fns)
+make_fancy_decl_name (tree decl)
 {
-  sra_walk_tree_list (ASM_INPUTS (expr), bsi, false, fns);
-  sra_walk_tree_list (ASM_OUTPUTS (expr), bsi, true, fns);
+  char buffer[32];
+
+  tree name = DECL_NAME (decl);
+  if (name)
+    obstack_grow (&name_obstack, IDENTIFIER_POINTER (name),
+                 IDENTIFIER_LENGTH (name));
+  else
+    {
+      sprintf (buffer, "D%u", DECL_UID (decl));
+      obstack_grow (&name_obstack, buffer, strlen (buffer));
+    }
 }
 
-/* Walk a GIMPLE_MODIFY_STMT and categorize the assignment appropriately.  */
+/* Helper for make_fancy_name.  */
 
 static void
-sra_walk_gimple_modify_stmt (tree expr, block_stmt_iterator *bsi,
-                            const struct sra_walk_fns *fns)
+make_fancy_name_1 (tree expr)
 {
-  struct sra_elt *lhs_elt, *rhs_elt;
-  tree lhs, rhs;
-
-  lhs = GIMPLE_STMT_OPERAND (expr, 0);
-  rhs = GIMPLE_STMT_OPERAND (expr, 1);
-  lhs_elt = maybe_lookup_element_for_expr (lhs);
-  rhs_elt = maybe_lookup_element_for_expr (rhs);
+  char buffer[32];
+  tree index;
 
-  /* If both sides are scalarizable, this is a COPY operation.  */
-  if (lhs_elt && rhs_elt)
+  if (DECL_P (expr))
     {
-      fns->copy (lhs_elt, rhs_elt, bsi);
+      make_fancy_decl_name (expr);
       return;
     }
 
-  /* If the RHS is scalarizable, handle it.  There are only two cases.  */
-  if (rhs_elt)
+  switch (TREE_CODE (expr))
     {
-      if (!rhs_elt->is_scalar && !TREE_SIDE_EFFECTS (lhs))
-       fns->ldst (rhs_elt, lhs, bsi, false);
-      else
-       fns->use (rhs_elt, &GIMPLE_STMT_OPERAND (expr, 1), bsi, false, false);
-    }
+    case COMPONENT_REF:
+      make_fancy_name_1 (TREE_OPERAND (expr, 0));
+      obstack_1grow (&name_obstack, '$');
+      make_fancy_decl_name (TREE_OPERAND (expr, 1));
+      break;
 
-  /* If it isn't scalarizable, there may be scalarizable variables within, so
-     check for a call or else walk the RHS to see if we need to do any
-     copy-in operations.  We need to do it before the LHS is scalarized so
-     that the statements get inserted in the proper place, before any
-     copy-out operations.  */
-  else
-    {
-      tree call = get_call_expr_in (rhs);
-      if (call)
-       sra_walk_call_expr (call, bsi, fns);
-      else
-       sra_walk_expr (&GIMPLE_STMT_OPERAND (expr, 1), bsi, false, fns);
-    }
-
-  /* Likewise, handle the LHS being scalarizable.  We have cases similar
-     to those above, but also want to handle RHS being constant.  */
-  if (lhs_elt)
-    {
-      /* If this is an assignment from a constant, or constructor, then
-        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, bsi);
-
-      /* 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)
-              && TREE_READONLY (rhs)
-              && targetm.binds_local_p (rhs))
-       fns->init (lhs_elt, DECL_INITIAL (rhs), bsi);
-
-      /* If this is a copy from a non-scalarizable lvalue, invoke LDST.
-        The lvalue requirement prevents us from trying to directly scalarize
-        the result of a function call.  Which would result in trying to call
-        the function multiple times, and other evil things.  */
-      else if (!lhs_elt->is_scalar
-              && !TREE_SIDE_EFFECTS (rhs) && is_gimple_addressable (rhs))
-       fns->ldst (lhs_elt, rhs, bsi, true);
-
-      /* Otherwise we're being used in some context that requires the
-        aggregate to be seen as a whole.  Invoke USE.  */
-      else
-       fns->use (lhs_elt, &GIMPLE_STMT_OPERAND (expr, 0), bsi, true, false);
-    }
+    case ARRAY_REF:
+      make_fancy_name_1 (TREE_OPERAND (expr, 0));
+      obstack_1grow (&name_obstack, '$');
+      /* Arrays with only one element may not have a constant as their
+        index. */
+      index = TREE_OPERAND (expr, 1);
+      if (TREE_CODE (index) != INTEGER_CST)
+       break;
+      sprintf (buffer, HOST_WIDE_INT_PRINT_DEC, TREE_INT_CST_LOW (index));
+      obstack_grow (&name_obstack, buffer, strlen (buffer));
 
-  /* 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_STMT_OPERAND (expr, 0), bsi, true, fns);
+      break;
+
+    case BIT_FIELD_REF:
+    case REALPART_EXPR:
+    case IMAGPART_EXPR:
+      gcc_unreachable ();      /* we treat these as scalars.  */
+      break;
+    default:
+      break;
+    }
 }
 
-/* Entry point to the walk functions.  Search the entire function,
-   invoking the callbacks in FNS on each of the references to
-   scalarizable variables.  */
+/* Create a human readable name for replacement variable of ACCESS.  */
 
-static void
-sra_walk_function (const struct sra_walk_fns *fns)
+static char *
+make_fancy_name (tree expr)
 {
-  basic_block bb;
-  block_stmt_iterator si, ni;
+  make_fancy_name_1 (expr);
+  obstack_1grow (&name_obstack, '\0');
+  return XOBFINISH (&name_obstack, char *);
+}
 
-  /* ??? Phase 4 could derive some benefit to walking the function in
-     dominator tree order.  */
+/* Helper function for build_ref_for_offset.  */
 
-  FOR_EACH_BB (bb)
-    for (si = bsi_start (bb); !bsi_end_p (si); si = ni)
-      {
-       tree stmt, t;
-       stmt_ann_t ann;
+static bool
+build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
+                       tree exp_type)
+{
+  while (1)
+    {
+      tree fld;
+      tree tr_size, index, minidx;
+      HOST_WIDE_INT el_size;
 
-       stmt = bsi_stmt (si);
-       ann = stmt_ann (stmt);
+      if (offset == 0 && exp_type
+         && types_compatible_p (exp_type, type))
+       return true;
 
-       ni = si;
-       bsi_next (&ni);
+      switch (TREE_CODE (type))
+       {
+       case UNION_TYPE:
+       case QUAL_UNION_TYPE:
+       case RECORD_TYPE:
+         for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+           {
+             HOST_WIDE_INT pos, size;
+             tree expr, *expr_ptr;
 
-       /* 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;
+             if (TREE_CODE (fld) != FIELD_DECL)
+               continue;
 
-       switch (TREE_CODE (stmt))
-         {
-         case RETURN_EXPR:
-           /* 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 we have an embedded assignment, then <retval> is of
-              a type that gets returned in registers in this ABI, and
-              we do not wish to extend their lifetimes.  Treat this
-              as a USE of the variable on the RHS of this assignment.  */
-
-           t = TREE_OPERAND (stmt, 0);
-           if (t == NULL_TREE)
-             ;
-           else if (TREE_CODE (t) == GIMPLE_MODIFY_STMT)
-             sra_walk_expr (&GIMPLE_STMT_OPERAND (t, 1), &si, false, fns);
-           else
-             sra_walk_expr (&TREE_OPERAND (stmt, 0), &si, false, fns);
-           break;
+             pos = int_bit_position (fld);
+             gcc_assert (TREE_CODE (type) == RECORD_TYPE || pos == 0);
+             tr_size = DECL_SIZE (fld);
+             if (!tr_size || !host_integerp (tr_size, 1))
+               continue;
+             size = tree_low_cst (tr_size, 1);
+             if (pos > offset || (pos + size) <= offset)
+               continue;
 
-         case GIMPLE_MODIFY_STMT:
-           sra_walk_gimple_modify_stmt (stmt, &si, fns);
-           break;
-         case CALL_EXPR:
-           sra_walk_call_expr (stmt, &si, fns);
-           break;
-         case ASM_EXPR:
-           sra_walk_asm_expr (stmt, &si, fns);
-           break;
+             if (res)
+               {
+                 expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld,
+                                NULL_TREE);
+                 expr_ptr = &expr;
+               }
+             else
+               expr_ptr = NULL;
+             if (build_ref_for_offset_1 (expr_ptr, TREE_TYPE (fld),
+                                         offset - pos, exp_type))
+               {
+                 if (res)
+                   *res = expr;
+                 return true;
+               }
+           }
+         return false;
 
-         default:
-           break;
-         }
-      }
-}
-\f
-/* Phase One: Scan all referenced variables in the program looking for
-   structures that could be decomposed.  */
+       case ARRAY_TYPE:
+         tr_size = TYPE_SIZE (TREE_TYPE (type));
+         if (!tr_size || !host_integerp (tr_size, 1))
+           return false;
+         el_size = tree_low_cst (tr_size, 1);
 
-static bool
-find_candidates_for_sra (void)
-{
-  bool any_set = false;
-  tree var;
-  referenced_var_iterator rvi;
+         minidx = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
+         if (TREE_CODE (minidx) != INTEGER_CST)
+           return false;
+         if (res)
+           {
+             index = build_int_cst (TYPE_DOMAIN (type), offset / el_size);
+             if (!integer_zerop (minidx))
+               index = int_const_binop (PLUS_EXPR, index, minidx, 0);
+             *res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index,
+                            NULL_TREE, NULL_TREE);
+           }
+         offset = offset % el_size;
+         type = TREE_TYPE (type);
+         break;
 
-  FOR_EACH_REFERENCED_VAR (var, rvi)
-    {
-      if (decl_can_be_decomposed_p (var))
-        {
-          bitmap_set_bit (sra_candidates, DECL_UID (var));
-          any_set = true;
-        }
-    }
+       default:
+         if (offset != 0)
+           return false;
 
-  return any_set;
+         if (exp_type)
+           return false;
+         else
+           return true;
+       }
+    }
 }
 
-\f
-/* Phase Two: Scan all references to scalarizable variables.  Count the
-   number of times they are used or copied respectively.  */
+/* Construct an expression that would reference a part of aggregate *EXPR of
+   type TYPE at the given OFFSET of the type EXP_TYPE.  If EXPR is NULL, the
+   function only determines whether it can build such a reference without
+   actually doing it, otherwise, the tree it points to is unshared first and
+   then used as a base for furhter sub-references.
 
-/* 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.  */
+   FIXME: Eventually this should be replaced with
+   maybe_fold_offset_to_reference() from tree-ssa-ccp.c but that requires a
+   minor rewrite of fold_stmt.
+ */
 
-static void
-scan_use (struct sra_elt *elt, tree *expr_p ATTRIBUTE_UNUSED,
-         block_stmt_iterator *bsi ATTRIBUTE_UNUSED,
-         bool is_output ATTRIBUTE_UNUSED, bool use_all ATTRIBUTE_UNUSED)
+bool
+build_ref_for_offset (tree *expr, tree type, HOST_WIDE_INT offset,
+                     tree exp_type, bool allow_ptr)
 {
-  elt->n_uses += 1;
-}
+  location_t loc = expr ? EXPR_LOCATION (*expr) : UNKNOWN_LOCATION;
 
-static void
-scan_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
-          block_stmt_iterator *bsi ATTRIBUTE_UNUSED)
-{
-  lhs_elt->n_copies += 1;
-  rhs_elt->n_copies += 1;
-}
+  if (expr)
+    *expr = unshare_expr (*expr);
 
-static void
-scan_init (struct sra_elt *lhs_elt, tree rhs ATTRIBUTE_UNUSED,
-          block_stmt_iterator *bsi ATTRIBUTE_UNUSED)
-{
-  lhs_elt->n_copies += 1;
-}
+  if (allow_ptr && POINTER_TYPE_P (type))
+    {
+      type = TREE_TYPE (type);
+      if (expr)
+       *expr = fold_build1_loc (loc, INDIRECT_REF, type, *expr);
+    }
 
-static void
-scan_ldst (struct sra_elt *elt, tree other ATTRIBUTE_UNUSED,
-          block_stmt_iterator *bsi ATTRIBUTE_UNUSED,
-          bool is_output ATTRIBUTE_UNUSED)
-{
-  elt->n_copies += 1;
+  return build_ref_for_offset_1 (expr, type, offset, exp_type);
 }
 
-/* Dump the values we collected during the scanning phase.  */
+/* Return true iff TYPE is stdarg va_list type.  */
 
-static void
-scan_dump (struct sra_elt *elt)
+static inline bool
+is_va_list_type (tree type)
 {
-  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);
+  return TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (va_list_type_node);
 }
 
-/* Entry point to phase 2.  Scan the entire function, building up
-   scalarization data structures, recording copies and uses.  */
+/* The very first phase of intraprocedural SRA.  It marks in candidate_bitmap
+   those with type which is suitable for scalarization.  */
 
-static void
-scan_function (void)
+static bool
+find_var_candidates (void)
 {
-  static const struct sra_walk_fns fns = {
-    scan_use, scan_copy, scan_init, scan_ldst, true
-  };
-  bitmap_iterator bi;
-
-  sra_walk_function (&fns);
+  tree var, type;
+  referenced_var_iterator rvi;
+  bool ret = false;
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
+  FOR_EACH_REFERENCED_VAR (var, rvi)
     {
-      unsigned i;
+      if (TREE_CODE (var) != VAR_DECL && TREE_CODE (var) != PARM_DECL)
+        continue;
+      type = TREE_TYPE (var);
+
+      if (!AGGREGATE_TYPE_P (type)
+         || needs_to_live_in_memory (var)
+         || TREE_THIS_VOLATILE (var)
+         || !COMPLETE_TYPE_P (type)
+         || !host_integerp (TYPE_SIZE (type), 1)
+          || tree_low_cst (TYPE_SIZE (type), 1) == 0
+         || type_internals_preclude_sra_p (type)
+         /* Fix for PR 41089.  tree-stdarg.c needs to have va_lists intact but
+             we also want to schedule it rather late.  Thus we ignore it in
+             the early pass. */
+         || (sra_mode == SRA_MODE_EARLY_INTRA
+             && is_va_list_type (type)))
+       continue;
 
-      fputs ("\nScan results:\n", dump_file);
-      EXECUTE_IF_SET_IN_BITMAP (sra_candidates, 0, i, bi)
+      bitmap_set_bit (candidate_bitmap, DECL_UID (var));
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
        {
-         tree var = referenced_var (i);
-         struct sra_elt *elt = lookup_element (NULL, var, NULL, NO_INSERT);
-         if (elt)
-           scan_dump (elt);
+         fprintf (dump_file, "Candidate (%d): ", DECL_UID (var));
+         print_generic_expr (dump_file, var, 0);
+         fprintf (dump_file, "\n");
        }
-      fputc ('\n', dump_file);
+      ret = true;
     }
+
+  return ret;
 }
-\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.  */
+/* Sort all accesses for the given variable, check for partial overlaps and
+   return NULL if there are any.  If there are none, pick a representative for
+   each combination of offset and size and create a linked list out of them.
+   Return the pointer to the first representative and make sure it is the first
+   one in the vector of accesses.  */
 
-static void
-build_element_name_1 (struct sra_elt *elt)
+static struct access *
+sort_and_splice_var_accesses (tree var)
 {
-  tree t;
-  char buffer[32];
+  int i, j, access_count;
+  struct access *res, **prev_acc_ptr = &res;
+  VEC (access_p, heap) *access_vec;
+  bool first = true;
+  HOST_WIDE_INT low = -1, high = 0;
+
+  access_vec = get_base_access_vector (var);
+  if (!access_vec)
+    return NULL;
+  access_count = VEC_length (access_p, access_vec);
 
-  if (elt->parent)
+  /* Sort by <OFFSET, SIZE>.  */
+  qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p),
+        compare_access_positions);
+
+  i = 0;
+  while (i < access_count)
     {
-      build_element_name_1 (elt->parent);
-      obstack_1grow (&sra_obstack, '$');
+      struct access *access = VEC_index (access_p, access_vec, i);
+      bool grp_write = access->write;
+      bool grp_read = !access->write;
+      bool multiple_reads = false;
+      bool grp_partial_lhs = access->grp_partial_lhs;
+      bool grp_different_types = false;
+      bool first_scalar = is_gimple_reg_type (access->type);
+      bool unscalarizable_region = access->grp_unscalarizable_region;
 
-      if (TREE_CODE (elt->parent->type) == COMPLEX_TYPE)
+      if (first || access->offset >= high)
        {
-         if (elt->element == integer_zero_node)
-           obstack_grow (&sra_obstack, "real", 4);
-         else
-           obstack_grow (&sra_obstack, "imag", 4);
-         return;
+         first = false;
+         low = access->offset;
+         high = access->offset + access->size;
        }
-    }
-
-  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 if (access->offset > low && access->offset + access->size > high)
+       return NULL;
       else
+       gcc_assert (access->offset >= low
+                   && access->offset + access->size <= high);
+
+      j = i + 1;
+      while (j < access_count)
        {
-         sprintf (buffer, "D%u", DECL_UID (t));
-         obstack_grow (&sra_obstack, buffer, strlen (buffer));
+         struct access *ac2 = VEC_index (access_p, access_vec, j);
+         if (ac2->offset != access->offset || ac2->size != access->size)
+           break;
+         if (ac2->write)
+           grp_write = true;
+         else
+           {
+             if (grp_read)
+               multiple_reads = true;
+             else
+               grp_read = true;
+           }
+         grp_partial_lhs |= ac2->grp_partial_lhs;
+         grp_different_types |= !types_compatible_p (access->type, ac2->type);
+         unscalarizable_region |= ac2->grp_unscalarizable_region;
+         relink_to_new_repr (access, ac2);
+
+         /* If there are both aggregate-type and scalar-type accesses with
+            this combination of size and offset, the comparison function
+            should have put the scalars first.  */
+         gcc_assert (first_scalar || !is_gimple_reg_type (ac2->type));
+         ac2->group_representative = access;
+         j++;
        }
-    }
-}
-
-/* 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 *);
-}
+      i = j;
 
-/* Instantiate an element as an independent variable.  */
+      access->group_representative = access;
+      access->grp_write = grp_write;
+      access->grp_read = grp_read;
+      access->grp_hint = multiple_reads;
+      access->grp_partial_lhs = grp_partial_lhs;
+      access->grp_different_types = grp_different_types;
+      access->grp_unscalarizable_region = unscalarizable_region;
+      if (access->first_link)
+       add_access_to_work_queue (access);
 
-static void
-instantiate_element (struct sra_elt *elt)
-{
-  struct sra_elt *base_elt;
-  tree var, base;
-  bool nowarn = TREE_NO_WARNING (elt->element);
+      *prev_acc_ptr = access;
+      prev_acc_ptr = &access->next_grp;
+    }
 
-  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;
+  gcc_assert (res == VEC_index (access_p, access_vec, 0));
+  return res;
+}
 
-  elt->replacement = var = make_rename_temp (elt->type, "SR");
+/* Create a variable for the given ACCESS which determines the type, name and a
+   few other properties.  Return the variable declaration and store it also to
+   ACCESS->replacement.  */
 
-  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);
+static tree
+create_access_replacement (struct access *access)
+{
+  tree repl;
 
-      elt->in_bitfld_block = 1;
-      elt->replacement = 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));
-      if (!INTEGRAL_TYPE_P (elt->type)
-         || TYPE_UNSIGNED (elt->type))
-       BIT_FIELD_REF_UNSIGNED (elt->replacement) = 1;
-    }
+  repl = create_tmp_var (access->type, "SR");
+  get_var_ann (repl);
+  add_referenced_var (repl);
+  mark_sym_for_renaming (repl);
 
-  /* 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;
+  if (!access->grp_partial_lhs
+      && (TREE_CODE (access->type) == COMPLEX_TYPE
+         || TREE_CODE (access->type) == VECTOR_TYPE))
+    DECL_GIMPLE_REG_P (repl) = 1;
 
-  DECL_SOURCE_LOCATION (var) = DECL_SOURCE_LOCATION (base);
-  DECL_ARTIFICIAL (var) = 1;
+  DECL_SOURCE_LOCATION (repl) = DECL_SOURCE_LOCATION (access->base);
+  DECL_ARTIFICIAL (repl) = 1;
 
-  if (TREE_THIS_VOLATILE (elt->type))
+  if (DECL_NAME (access->base)
+      && !DECL_IGNORED_P (access->base)
+      && !DECL_ARTIFICIAL (access->base))
     {
-      TREE_THIS_VOLATILE (var) = 1;
-      TREE_SIDE_EFFECTS (var) = 1;
-    }
+      char *pretty_name = make_fancy_name (access->expr);
 
-  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);
+      DECL_NAME (repl) = get_identifier (pretty_name);
+      obstack_free (&name_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;
+      SET_DECL_DEBUG_EXPR (repl, access->expr);
+      DECL_DEBUG_EXPR_IS_FROM (repl) = 1;
+      DECL_IGNORED_P (repl) = 0;
     }
 
-  /* 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))
-    {
-      tree init = sra_build_assignment (var, fold_convert (TREE_TYPE (var),
-                                                          integer_zero_node));
-      insert_edge_copies (init, ENTRY_BLOCK_PTR);
-      mark_all_v_defs (init);
-    }
+  DECL_IGNORED_P (repl) = DECL_IGNORED_P (access->base);
+  TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
 
   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);
+      fprintf (dump_file, "Created a replacement for ");
+      print_generic_expr (dump_file, access->base, 0);
+      fprintf (dump_file, " offset: %u, size: %u: ",
+              (unsigned) access->offset, (unsigned) access->size);
+      print_generic_expr (dump_file, repl, 0);
+      fprintf (dump_file, "\n");
     }
+  sra_stats.replacements++;
+
+  return repl;
 }
 
-/* Make one pass across an element tree deciding whether or not it's
-   profitable to instantiate individual leaf scalars.
+/* Return ACCESS scalar replacement, create it if it does not exist yet.  */
 
-   PARENT_USES and PARENT_COPIES are the sum of the N_USES and N_COPIES
-   fields all the way up the tree.  */
+static inline tree
+get_access_replacement (struct access *access)
+{
+  gcc_assert (access->grp_to_be_replaced);
+
+  if (!access->replacement_decl)
+    access->replacement_decl = create_access_replacement (access);
+  return access->replacement_decl;
+}
+
+/* Build a subtree of accesses rooted in *ACCESS, and move the pointer in the
+   linked list along the way.  Stop when *ACCESS is NULL or the access pointed
+   to it is not "within" the root.  */
 
 static void
-decide_instantiation_1 (struct sra_elt *elt, unsigned int parent_uses,
-                       unsigned int parent_copies)
+build_access_subtree (struct access **access)
 {
-  if (dump_file && !elt->parent)
-    {
-      fputs ("Initial instantiation for ", dump_file);
-      dump_sra_elt_name (dump_file, elt);
-      fputc ('\n', dump_file);
-    }
+  struct access *root = *access, *last_child = NULL;
+  HOST_WIDE_INT limit = root->offset + root->size;
 
-  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
+  *access = (*access)->next_grp;
+  while  (*access && (*access)->offset + (*access)->size <= limit)
     {
-      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;
-         }
+      if (!last_child)
+       root->first_child = *access;
+      else
+       last_child->next_sibling = *access;
+      last_child = *access;
 
-      for (c = elt->children; c ; c = c->sibling)
-       decide_instantiation_1 (c, this_uses, this_copies);
+      build_access_subtree (access);
     }
 }
 
-/* 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.  */
+/* Build a tree of access representatives, ACCESS is the pointer to the first
+   one, others are linked in a list by the next_grp field.  Decide about scalar
+   replacements on the way, return true iff any are to be created.  */
 
-static unsigned int
-sum_instantiated_sizes (struct sra_elt *elt, unsigned HOST_WIDE_INT *sizep)
+static void
+build_access_trees (struct access *access)
 {
-  if (elt->replacement)
+  while (access)
     {
-      *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);
+      struct access *root = access;
 
-      return count;
+      build_access_subtree (&access);
+      root->next_grp = access;
     }
 }
 
-/* Instantiate fields in ELT->TYPE that are not currently present as
-   children of ELT.  */
+/* Return true if expr contains some ARRAY_REFs into a variable bounded
+   array.  */
 
-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)
+static bool
+expr_with_var_bounded_array_refs_p (tree expr)
 {
-  struct sra_elt *sub = lookup_element (elt, child, type, INSERT);
-  if (sub->is_scalar)
+  while (handled_component_p (expr))
     {
-      if (sub->replacement == NULL)
-       instantiate_element (sub);
+      if (TREE_CODE (expr) == ARRAY_REF
+         && !host_integerp (array_ref_low_bound (expr), 0))
+       return true;
+      expr = TREE_OPERAND (expr, 0);
     }
-  else
-    instantiate_missing_elements (sub);
-  return sub;
+  return false;
 }
 
-/* Obtain the canonical type for field F of ELEMENT.  */
+/* Analyze the subtree of accesses rooted in ROOT, scheduling replacements when
+   both seeming beneficial and when ALLOW_REPLACEMENTS allows it.  Also set
+   all sorts of access flags appropriately along the way, notably always ser
+   grp_read when MARK_READ is true and grp_write when MARK_WRITE is true.  */
 
-static tree
-canon_type_for_field (tree f, tree element)
+static bool
+analyze_access_subtree (struct access *root, bool allow_replacements,
+                       bool mark_read, bool mark_write)
 {
-  tree field_type = TREE_TYPE (f);
+  struct access *child;
+  HOST_WIDE_INT limit = root->offset + root->size;
+  HOST_WIDE_INT covered_to = root->offset;
+  bool scalar = is_gimple_reg_type (root->type);
+  bool hole = false, sth_created = false;
+  bool direct_read = root->grp_read;
 
-  /* 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));
+  if (mark_read)
+    root->grp_read = true;
+  else if (root->grp_read)
+    mark_read = true;
 
-  return field_type;
-}
+  if (mark_write)
+    root->grp_write = true;
+  else if (root->grp_write)
+    mark_write = true;
 
-/* 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.  */
+  if (root->grp_unscalarizable_region)
+    allow_replacements = false;
 
-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;
-
-  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;
+  if (allow_replacements && expr_with_var_bounded_array_refs_p (root->expr))
+    allow_replacements = false;
 
-  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.
+  for (child = root->first_child; child; child = child->next_sibling)
+    {
+      if (!hole && child->offset < covered_to)
+       hole = true;
+      else
+       covered_to += child->size;
 
-     ??? 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;
+      sth_created |= analyze_access_subtree (child, allow_replacements,
+                                            mark_read, mark_write);
 
-  align = DECL_ALIGN (block->element);
-  alchk = GET_MODE_BITSIZE (DECL_MODE (block->element));
+      root->grp_unscalarized_data |= child->grp_unscalarized_data;
+      hole |= !child->grp_covered;
+    }
 
-  if (count)
+  if (allow_replacements && scalar && !root->first_child
+      && (root->grp_hint
+         || (direct_read && root->grp_write)))
     {
-      type = TREE_TYPE (block->element);
-      while (count--)
-       type = TREE_TYPE (type);
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Marking ");
+         print_generic_expr (dump_file, root->base, 0);
+         fprintf (dump_file, " offset: %u, size: %u: ",
+                  (unsigned) root->offset, (unsigned) root->size);
+         fprintf (dump_file, " to be replaced.\n");
+       }
 
-      align = TYPE_ALIGN (type);
-      alchk = GET_MODE_BITSIZE (TYPE_MODE (type));
+      root->grp_to_be_replaced = 1;
+      sth_created = true;
+      hole = false;
     }
+  else if (covered_to < limit)
+    hole = true;
 
-  if (align < alchk)
-    align = alchk;
+  if (sth_created && !hole)
+    {
+      root->grp_covered = 1;
+      return true;
+    }
+  if (root->grp_write || TREE_CODE (root->base) == PARM_DECL)
+    root->grp_unscalarized_data = 1; /* not covered and written to */
+  if (sth_created)
+    return true;
+  return false;
+}
 
-  /* Coalescing wider fields is probably pointless and
-     inefficient.  */
-  if (align > BITS_PER_WORD)
-    align = BITS_PER_WORD;
+/* Analyze all access trees linked by next_grp by the means of
+   analyze_access_subtree.  */
+static bool
+analyze_access_trees (struct access *access)
+{
+  bool ret = false;
 
-  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);
+  while (access)
+    {
+      if (analyze_access_subtree (access, true, false, false))
+       ret = true;
+      access = access->next_grp;
+    }
 
-  alchk = align - 1;
-  alchk = ~alchk;
+  return ret;
+}
 
-  if ((bit & alchk) != ((bit + size - 1) & alchk))
-    return f;
+/* Return true iff a potential new child of LACC at offset OFFSET and with size
+   SIZE would conflict with an already existing one.  If exactly such a child
+   already exists in LACC, store a pointer to it in EXACT_MATCH.  */
 
-  /* Find adjacent fields in the same alignment word.  */
+static bool
+child_would_conflict_in_lacc (struct access *lacc, HOST_WIDE_INT norm_offset,
+                             HOST_WIDE_INT size, struct access **exact_match)
+{
+  struct access *child;
 
-  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))
+  for (child = lacc->first_child; child; child = child->next_sibling)
     {
-      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 (child->offset == norm_offset && child->size == size)
        {
-         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;
+         *exact_match = child;
+         return true;
+       }
 
-             align = GET_MODE_BITSIZE (DECL_MODE (f));
-             alchk = align - 1;
-             alchk = ~alchk;
+      if (child->offset < norm_offset + size
+         && child->offset + child->size > norm_offset)
+       return true;
+    }
 
-             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;
+  return false;
+}
 
-             align = GET_MODE_BITSIZE (DECL_MODE (f));
-             alchk = align - 1;
-             alchk = ~alchk;
+/* Create a new child access of PARENT, with all properties just like MODEL
+   except for its offset and with its grp_write false and grp_read true.
+   Return the new access or NULL if it cannot be created.  Note that this access
+   is created long after all splicing and sorting, it's not located in any
+   access vector and is automatically a representative of its group.  */
 
-             if ((nbit & alchk) != ((bit + size - 1) & alchk))
-               break;
-           }
-         bit = nbit;
-         size += nsize;
-       }
-      else
-       break;
-    }
+static struct access *
+create_artificial_child_access (struct access *parent, struct access *model,
+                               HOST_WIDE_INT new_offset)
+{
+  struct access *access;
+  struct access **child;
+  tree expr = parent->base;;
 
-  f = prev;
+  gcc_assert (!model->grp_unscalarizable_region);
 
-  if (f == first)
-    return f;
+  if (!build_ref_for_offset (&expr, TREE_TYPE (expr), new_offset,
+                            model->type, false))
+    return NULL;
 
-  gcc_assert ((bit & alchk) == ((bit + size - 1) & alchk));
+  access = (struct access *) pool_alloc (access_pool);
+  memset (access, 0, sizeof (struct access));
+  access->base = parent->base;
+  access->expr = expr;
+  access->offset = new_offset;
+  access->size = model->size;
+  access->type = model->type;
+  access->grp_write = true;
+  access->grp_read = false;
 
-  /* Try to widen the bit range so as to cover padding bits as well.  */
+  child = &parent->first_child;
+  while (*child && (*child)->offset < new_offset)
+    child = &(*child)->next_sibling;
 
-  if ((bit & ~alchk) || size != align)
-    {
-      unsigned HOST_WIDE_INT mbit = bit & alchk;
-      unsigned HOST_WIDE_INT msize = align;
+  access->next_sibling = *child;
+  *child = access;
 
-      for (f = TYPE_FIELDS (elt->type);
-          f; f = TREE_CHAIN (f))
-       {
-         unsigned HOST_WIDE_INT fbit, fsize;
+  return access;
+}
 
-         /* 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;
+/* Propagate all subaccesses of RACC across an assignment link to LACC. Return
+   true if any new subaccess was created.  Additionally, if RACC is a scalar
+   access but LACC is not, change the type of the latter, if possible.  */
 
-         fbit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
-           + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+static bool
+propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
+{
+  struct access *rchild;
+  HOST_WIDE_INT norm_delta = lacc->offset - racc->offset;
+  bool ret = false;
 
-         /* If we're past the selected word, we're fine.  */
-         if ((bit & alchk) < (fbit & alchk))
-           continue;
+  if (is_gimple_reg_type (lacc->type)
+      || lacc->grp_unscalarizable_region
+      || racc->grp_unscalarizable_region)
+    return false;
 
-         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 (!lacc->first_child && !racc->first_child
+      && is_gimple_reg_type (racc->type))
+    {
+      tree t = lacc->base;
+
+      if (build_ref_for_offset (&t, TREE_TYPE (t), lacc->offset, racc->type,
+                               false))
+       {
+         lacc->expr = t;
+         lacc->type = racc->type;
+       }
+      return false;
+    }
 
-         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;
+  for (rchild = racc->first_child; rchild; rchild = rchild->next_sibling)
+    {
+      struct access *new_acc = NULL;
+      HOST_WIDE_INT norm_offset = rchild->offset + norm_delta;
 
-             if (diff <= 0)
-               continue;
+      if (rchild->grp_unscalarizable_region)
+       continue;
 
-             mbit += diff;
-             msize -= diff;
-           }
-         else
+      if (child_would_conflict_in_lacc (lacc, norm_offset, rchild->size,
+                                       &new_acc))
+       {
+         if (new_acc)
            {
-             /* 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 ();
+             rchild->grp_hint = 1;
+             new_acc->grp_hint |= new_acc->grp_read;
+             if (rchild->first_child)
+               ret |= propagate_subaccesses_across_link (new_acc, rchild);
            }
+         continue;
        }
 
-      bit = mbit;
-      size = msize;
+      /* If a (part of) a union field is on the RHS of an assignment, it can
+        have sub-accesses which do not make sense on the LHS (PR 40351).
+        Check that this is not the case.  */
+      if (!build_ref_for_offset (NULL, TREE_TYPE (lacc->base), norm_offset,
+                                rchild->type, false))
+       continue;
+
+      rchild->grp_hint = 1;
+      new_acc = create_artificial_child_access (lacc, rchild, norm_offset);
+      if (new_acc)
+       {
+         ret = true;
+         if (racc->first_child)
+           propagate_subaccesses_across_link (new_acc, rchild);
+       }
     }
 
-  /* Now we know the bit range we're interested in.  Find the smallest
-     machine mode we can use to access it.  */
+  return ret;
+}
+
+/* Propagate all subaccesses across assignment links.  */
 
-  for (mode = smallest_mode_for_size (size, MODE_INT);
-       ;
-       mode = GET_MODE_WIDER_MODE (mode))
+static void
+propagate_all_subaccesses (void)
+{
+  while (work_queue_head)
     {
-      gcc_assert (mode != VOIDmode);
+      struct access *racc = pop_access_from_work_queue ();
+      struct assign_link *link;
 
-      alchk = GET_MODE_PRECISION (mode) - 1;
-      alchk = ~alchk;
+      gcc_assert (racc->first_link);
 
-      if ((bit & alchk) == ((bit + size - 1) & alchk))
-       break;
+      for (link = racc->first_link; link; link = link->next)
+       {
+         struct access *lacc = link->lacc;
+
+         if (!bitmap_bit_p (candidate_bitmap, DECL_UID (lacc->base)))
+           continue;
+         lacc = lacc->group_representative;
+         if (propagate_subaccesses_across_link (lacc, racc)
+             && lacc->first_link)
+           add_access_to_work_queue (lacc);
+       }
     }
+}
 
-  gcc_assert (~alchk < align);
+/* Go through all accesses collected throughout the (intraprocedural) analysis
+   stage, exclude overlapping ones, identify representatives and build trees
+   out of them, making decisions about scalarization on the way.  Return true
+   iff there are any to-be-scalarized variables after this stage. */
 
-  /* Create the field group as a single variable.  */
+static bool
+analyze_all_variable_accesses (void)
+{
+  int res = 0;
+  bitmap tmp = BITMAP_ALLOC (NULL);
+  bitmap_iterator bi;
+  unsigned i;
 
-  type = lang_hooks.types.type_for_mode (mode, 1);
-  gcc_assert (type);
-  var = build3 (BIT_FIELD_REF, type, NULL_TREE,
-               bitsize_int (size),
-               bitsize_int (bit));
-  BIT_FIELD_REF_UNSIGNED (var) = 1;
+  bitmap_copy (tmp, candidate_bitmap);
+  EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, bi)
+    {
+      tree var = referenced_var (i);
+      struct access *access;
 
-  block = instantiate_missing_elements_1 (elt, var, type);
-  gcc_assert (block && block->is_scalar);
+      access = sort_and_splice_var_accesses (var);
+      if (access)
+       build_access_trees (access);
+      else
+       disqualify_candidate (var,
+                             "No or inhibitingly overlapping accesses.");
+    }
 
-  var = block->replacement;
+  propagate_all_subaccesses ();
 
-  if ((bit & ~alchk)
-      || (HOST_WIDE_INT)size != tree_low_cst (DECL_SIZE (var), 1))
+  bitmap_copy (tmp, candidate_bitmap);
+  EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, bi)
     {
-      block->replacement = build3 (BIT_FIELD_REF,
-                                  TREE_TYPE (block->element), var,
-                                  bitsize_int (size),
-                                  bitsize_int (bit & ~alchk));
-      BIT_FIELD_REF_UNSIGNED (block->replacement) = 1;
+      tree var = referenced_var (i);
+      struct access *access = get_first_repr_for_decl (var);
+
+      if (analyze_access_trees (access))
+       {
+         res++;
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "\nAccess trees for ");
+             print_generic_expr (dump_file, var, 0);
+             fprintf (dump_file, " (UID: %u): \n", DECL_UID (var));
+             dump_access_tree (dump_file, access);
+             fprintf (dump_file, "\n");
+           }
+       }
+      else
+       disqualify_candidate (var, "No scalar replacements to be created.");
     }
 
-  block->in_bitfld_block = 2;
+  BITMAP_FREE (tmp);
+
+  if (res)
+    {
+      statistics_counter_event (cfun, "Scalarized aggregates", res);
+      return true;
+    }
+  else
+    return false;
+}
 
-  /* Add the member fields to the group, such that they access
-     portions of the group variable.  */
+/* Return true iff a reference statement into aggregate AGG can be built for
+   every single to-be-replaced accesses that is a child of ACCESS, its sibling
+   or a child of its sibling. TOP_OFFSET is the offset from the processed
+   access subtree that has to be subtracted from offset of each access.  */
 
-  for (f = first; f != TREE_CHAIN (prev); f = TREE_CHAIN (f))
+static bool
+ref_expr_for_all_replacements_p (struct access *access, tree agg,
+                                HOST_WIDE_INT top_offset)
+{
+  do
     {
-      tree field_type = canon_type_for_field (f, elt->element);
-      struct sra_elt *fld = lookup_element (block, f, field_type, INSERT);
+      if (access->grp_to_be_replaced
+         && !build_ref_for_offset (NULL, TREE_TYPE (agg),
+                                   access->offset - top_offset,
+                                   access->type, false))
+       return false;
 
-      gcc_assert (fld && fld->is_scalar && !fld->replacement);
+      if (access->first_child
+         && !ref_expr_for_all_replacements_p (access->first_child, agg,
+                                              top_offset))
+       return false;
 
-      fld->replacement = build3 (BIT_FIELD_REF, field_type, var,
-                                DECL_SIZE (f),
-                                bitsize_int
-                                ((TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f))
-                                  * BITS_PER_UNIT
-                                  + (TREE_INT_CST_LOW
-                                     (DECL_FIELD_BIT_OFFSET (f))))
-                                 & ~alchk));
-      BIT_FIELD_REF_UNSIGNED (fld->replacement) = TYPE_UNSIGNED (field_type);
-      fld->in_bitfld_block = 1;
+      access = access->next_sibling;
     }
+  while (access);
 
-  return prev;
+  return true;
 }
 
+/* Generate statements copying scalar replacements of accesses within a subtree
+   into or out of AGG.  ACCESS is the first child of the root of the subtree to
+   be processed.  AGG is an aggregate type expression (can be a declaration but
+   does not have to be, it can for example also be an indirect_ref).
+   TOP_OFFSET is the offset of the processed subtree which has to be subtracted
+   from offsets of individual accesses to get corresponding offsets for AGG.
+   If CHUNK_SIZE is non-null, copy only replacements in the interval
+   <start_offset, start_offset + chunk_size>, otherwise copy all.  GSI is a
+   statement iterator used to place the new statements.  WRITE should be true
+   when the statements should write from AGG to the replacement and false if
+   vice versa.  if INSERT_AFTER is true, new statements will be added after the
+   current statement in GSI, they will be added before the statement
+   otherwise.  */
+
 static void
-instantiate_missing_elements (struct sra_elt *elt)
+generate_subtree_copies (struct access *access, tree agg,
+                        HOST_WIDE_INT top_offset,
+                        HOST_WIDE_INT start_offset, HOST_WIDE_INT chunk_size,
+                        gimple_stmt_iterator *gsi, bool write,
+                        bool insert_after)
 {
-  tree type = elt->type;
-
-  switch (TREE_CODE (type))
+  do
     {
-    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;
-               }
+      tree expr = agg;
 
-             instantiate_missing_elements_1 (elt, f,
-                                             canon_type_for_field
-                                             (f, elt->element));
-           }
-       break;
-      }
+      if (chunk_size && access->offset >= start_offset + chunk_size)
+       return;
 
-    case ARRAY_TYPE:
-      {
-       tree i, max, subtype;
+      if (access->grp_to_be_replaced
+         && (chunk_size == 0
+             || access->offset + access->size > start_offset))
+       {
+         tree repl = get_access_replacement (access);
+         bool ref_found;
+         gimple stmt;
 
-       i = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
-       max = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
-       subtype = TREE_TYPE (type);
+         ref_found = build_ref_for_offset (&expr, TREE_TYPE (agg),
+                                            access->offset - top_offset,
+                                            access->type, false);
+         gcc_assert (ref_found);
 
-       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);
-         }
+         if (write)
+           {
+             if (access->grp_partial_lhs)
+               expr = force_gimple_operand_gsi (gsi, expr, true, NULL_TREE,
+                                                !insert_after,
+                                                insert_after ? GSI_NEW_STMT
+                                                : GSI_SAME_STMT);
+             stmt = gimple_build_assign (repl, expr);
+           }
+         else
+           {
+             TREE_NO_WARNING (repl) = 1;
+             if (access->grp_partial_lhs)
+               repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE,
+                                                !insert_after,
+                                                insert_after ? GSI_NEW_STMT
+                                                : GSI_SAME_STMT);
+             stmt = gimple_build_assign (expr, repl);
+           }
 
-       break;
-      }
+         if (insert_after)
+           gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
+         else
+           gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+         update_stmt (stmt);
+         sra_stats.subtree_copies++;
+       }
 
-    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;
+      if (access->first_child)
+       generate_subtree_copies (access->first_child, agg, top_offset,
+                                start_offset, chunk_size, gsi,
+                                write, insert_after);
 
-    default:
-      gcc_unreachable ();
+      access = access->next_sibling;
     }
+  while (access);
 }
 
-/* Return true if there is only one non aggregate field in the record, TYPE.
-   Return false otherwise.  */
+/* Assign zero to all scalar replacements in an access subtree.  ACCESS is the
+   the root of the subtree to be processed.  GSI is the statement iterator used
+   for inserting statements which are added after the current statement if
+   INSERT_AFTER is true or before it otherwise.  */
+
+static void
+init_subtree_with_zero (struct access *access, gimple_stmt_iterator *gsi,
+                       bool insert_after)
 
-static bool
-single_scalar_field_in_record_p (tree type)
 {
-   int num_fields = 0;
-   tree field;
-   if (TREE_CODE (type) != RECORD_TYPE)
-     return false;
+  struct access *child;
 
-   for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
-     if (TREE_CODE (field) == FIELD_DECL)
-       {
-         num_fields++;
+  if (access->grp_to_be_replaced)
+    {
+      gimple stmt;
 
-         if (num_fields == 2)
-           return false;
-        
-         if (AGGREGATE_TYPE_P (TREE_TYPE (field)))
-           return false;
-       }
+      stmt = gimple_build_assign (get_access_replacement (access),
+                                 fold_convert (access->type,
+                                               integer_zero_node));
+      if (insert_after)
+       gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
+      else
+       gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+      update_stmt (stmt);
+    }
 
-   return true;
+  for (child = access->first_child; child; child = child->next_sibling)
+    init_subtree_with_zero (child, gsi, insert_after);
 }
 
-/* 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.  */
+/* Search for an access representative for the given expression EXPR and
+   return it or NULL if it cannot be found.  */
 
-static bool
-decide_block_copy (struct sra_elt *elt)
+static struct access *
+get_access_for_expr (tree expr)
 {
-  struct sra_elt *c;
-  bool any_inst;
+  HOST_WIDE_INT offset, size, max_size;
+  tree base;
 
-  /* 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;
+  /* FIXME: This should not be necessary but Ada produces V_C_Es with a type of
+     a different size than the size of its argument and we need the latter
+     one.  */
+  if (TREE_CODE (expr) == VIEW_CONVERT_EXPR)
+    expr = TREE_OPERAND (expr, 0);
 
-      if (dump_file)
-       {
-         fputs ("Scalarization disabled for ", dump_file);
-         dump_sra_elt_name (dump_file, elt);
-         fputc ('\n', dump_file);
-       }
+  base = get_ref_base_and_extent (expr, &offset, &size, &max_size);
+  if (max_size == -1 || !DECL_P (base))
+    return NULL;
 
-      /* Disable scalarization of sub-elements */
-      for (c = elt->children; c; c = c->sibling)
-       {
-         c->cannot_scalarize = 1;
-         decide_block_copy (c);
-       }
+  if (!bitmap_bit_p (candidate_bitmap, DECL_UID (base)))
+    return NULL;
 
-      /* Groups behave like their parent.  */
-      for (c = elt->groups; c; c = c->sibling)
-       {
-         c->cannot_scalarize = 1;
-         c->use_block_copy = 1;
-       }
+  return get_var_base_offset_size_access (base, offset, max_size);
+}
 
-      return false;
-    }
+/* Callback for scan_function.  Replace the expression EXPR with a scalar
+   replacement if there is one and generate other statements to do type
+   conversion or subtree copying if necessary.  GSI is used to place newly
+   created statements, WRITE is true if the expression is being written to (it
+   is on a LHS of a statement or output in an assembly statement).  */
 
-  /* Don't decide if we've no uses and no groups.  */
-  if (elt->n_uses == 0 && elt->n_copies == 0 && elt->groups == NULL)
-    ;
+static bool
+sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write,
+                void *data ATTRIBUTE_UNUSED)
+{
+  struct access *access;
+  tree type, bfr;
 
-  else if (!elt->is_scalar)
+  if (TREE_CODE (*expr) == BIT_FIELD_REF)
     {
-      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;
+      bfr = *expr;
+      expr = &TREE_OPERAND (*expr, 0);
+    }
+  else
+    bfr = NULL_TREE;
 
-      /* 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))
+  if (TREE_CODE (*expr) == REALPART_EXPR || TREE_CODE (*expr) == IMAGPART_EXPR)
+    expr = &TREE_OPERAND (*expr, 0);
+  access = get_access_for_expr (*expr);
+  if (!access)
+    return false;
+  type = TREE_TYPE (*expr);
+
+  if (access->grp_to_be_replaced)
+    {
+      tree repl = get_access_replacement (access);
+      /* If we replace a non-register typed access simply use the original
+         access expression to extract the scalar component afterwards.
+        This happens if scalarizing a function return value or parameter
+        like in gcc.c-torture/execute/20041124-1.c, 20050316-1.c and
+        gcc.c-torture/compile/20011217-1.c.
+
+         We also want to use this when accessing a complex or vector which can
+         be accessed as a different type too, potentially creating a need for
+         type conversion  (see PR42196).  */
+      if (!is_gimple_reg_type (type)
+         || (access->grp_different_types
+             && (TREE_CODE (type) == COMPLEX_TYPE
+                 || TREE_CODE (type) == VECTOR_TYPE)))
        {
-         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 * UNITS_PER_WORD;
-         max_count = SRA_MAX_STRUCTURE_COUNT
-           ? SRA_MAX_STRUCTURE_COUNT
-           : MOVE_RATIO;
-
-         full_size = tree_low_cst (size_tree, 1);
-         full_count = count_type_elements (elt->type, false);
-         inst_count = sum_instantiated_sizes (elt, &inst_size);
-
-         /* 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;
-       }
+         tree ref = access->base;
+         bool ok;
 
-      elt->use_block_copy = use_block_copy;
+         ok = build_ref_for_offset (&ref, TREE_TYPE (ref),
+                                    access->offset, access->type, false);
+         gcc_assert (ok);
 
-      /* Groups behave like their parent.  */
-      for (c = elt->groups; c; c = c->sibling)
-       c->use_block_copy = use_block_copy;
+         if (write)
+           {
+             gimple stmt;
 
-      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 (access->grp_partial_lhs)
+               ref = force_gimple_operand_gsi (gsi, ref, true, NULL_TREE,
+                                                false, GSI_NEW_STMT);
+             stmt = gimple_build_assign (repl, ref);
+             gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
+           }
+         else
+           {
+             gimple stmt;
 
-      if (!use_block_copy)
+             if (access->grp_partial_lhs)
+               repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE,
+                                                true, GSI_SAME_STMT);
+             stmt = gimple_build_assign (ref, repl);
+             gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+           }
+       }
+      else
        {
-         instantiate_missing_elements (elt);
-         return true;
+         gcc_assert (useless_type_conversion_p (type, access->type));
+         *expr = repl;
        }
+      sra_stats.exprs++;
     }
 
-  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)
+  if (access->first_child)
     {
-      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)
+      HOST_WIDE_INT start_offset, chunk_size;
+      if (bfr
+         && host_integerp (TREE_OPERAND (bfr, 1), 1)
+         && host_integerp (TREE_OPERAND (bfr, 2), 1))
        {
-         bitmap_set_bit (&done_head, i);
-         cleared_any = true;
+         chunk_size = tree_low_cst (TREE_OPERAND (bfr, 1), 1);
+         start_offset = access->offset
+           + tree_low_cst (TREE_OPERAND (bfr, 2), 1);
        }
-    }
+      else
+       start_offset = chunk_size = 0;
 
-  if (cleared_any)
-    {
-      bitmap_and_compl_into (sra_candidates, &done_head);
-      bitmap_and_compl_into (needs_copy_in, &done_head);
+      generate_subtree_copies (access->first_child, access->base, 0,
+                              start_offset, chunk_size, gsi, write, write);
     }
-  bitmap_clear (&done_head);
-  
-  mark_set_for_renaming (sra_candidates);
-
-  if (dump_file)
-    fputc ('\n', dump_file);
+  return true;
 }
 
-\f
-/* Phase Four: Update the function to match the replacements created.  */
+/* Where scalar replacements of the RHS have been written to when a replacement
+   of a LHS of an assigments cannot be direclty loaded from a replacement of
+   the RHS. */
+enum unscalarized_data_handling { SRA_UDH_NONE,  /* Nothing done so far. */
+                                 SRA_UDH_RIGHT, /* Data flushed to the RHS. */
+                                 SRA_UDH_LEFT }; /* Data flushed to the LHS. */
 
-/* Mark all the variables in VDEF/VUSE operators for STMT for
-   renaming. This becomes necessary when we modify all of a
-   non-scalar.  */
+/* Store all replacements in the access tree rooted in TOP_RACC either to their
+   base aggregate if there are unscalarized data or directly to LHS
+   otherwise.  */
 
-static void
-mark_all_v_defs_1 (tree stmt)
+static enum unscalarized_data_handling
+handle_unscalarized_data_in_subtree (struct access *top_racc, tree lhs,
+                                    gimple_stmt_iterator *gsi)
 {
-  tree sym;
-  ssa_op_iter iter;
-
-  update_stmt_if_modified (stmt);
-
-  FOR_EACH_SSA_TREE_OPERAND (sym, stmt, iter, SSA_OP_ALL_VIRTUALS)
+  if (top_racc->grp_unscalarized_data)
     {
-      if (TREE_CODE (sym) == SSA_NAME)
-       sym = SSA_NAME_VAR (sym);
-      mark_sym_for_renaming (sym);
+      generate_subtree_copies (top_racc->first_child, top_racc->base, 0, 0, 0,
+                              gsi, false, false);
+      return SRA_UDH_RIGHT;
     }
-}
-
-
-/* Mark all the variables in virtual operands in all the statements in
-   LIST for renaming.  */
-
-static void
-mark_all_v_defs (tree list)
-{
-  if (TREE_CODE (list) != STATEMENT_LIST)
-    mark_all_v_defs_1 (list);
   else
     {
-      tree_stmt_iterator i;
-      for (i = tsi_start (list); !tsi_end_p (i); tsi_next (&i))
-       mark_all_v_defs_1 (tsi_stmt (i));
+      generate_subtree_copies (top_racc->first_child, lhs, top_racc->offset,
+                              0, 0, gsi, false, false);
+      return SRA_UDH_LEFT;
     }
 }
 
 
-/* Mark every replacement under ELT with TREE_NO_WARNING.  */
+/* Try to generate statements to load all sub-replacements in an access
+   (sub)tree (LACC is the first child) from scalar replacements in the TOP_RACC
+   (sub)tree.  If that is not possible, refresh the TOP_RACC base aggregate and
+   load the accesses from it.  LEFT_OFFSET is the offset of the left whole
+   subtree being copied, RIGHT_OFFSET is the same thing for the right subtree.
+   GSI is stmt iterator used for statement insertions.  *REFRESHED is true iff
+   the rhs top aggregate has already been refreshed by contents of its scalar
+   reductions and is set to true if this function has to do it.  */
 
 static void
-mark_no_warning (struct sra_elt *elt)
-{
-  if (!elt->all_no_warning)
-    {
-      if (elt->replacement)
-       TREE_NO_WARNING (elt->replacement) = 1;
-      else
+load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
+                                HOST_WIDE_INT left_offset,
+                                HOST_WIDE_INT right_offset,
+                                gimple_stmt_iterator *old_gsi,
+                                gimple_stmt_iterator *new_gsi,
+                                enum unscalarized_data_handling *refreshed,
+                                tree lhs)
+{
+  location_t loc = EXPR_LOCATION (lacc->expr);
+  do
+    {
+      if (lacc->grp_to_be_replaced)
        {
-         struct sra_elt *c;
-         FOR_EACH_ACTUAL_CHILD (c, elt)
-           mark_no_warning (c);
-       }
-      elt->all_no_warning = true;
-    }
-}
+         struct access *racc;
+         HOST_WIDE_INT offset = lacc->offset - left_offset + right_offset;
+         gimple stmt;
+         tree rhs;
 
-/* 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_blk 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;
-         }
+         racc = find_access_in_subtree (top_racc, offset, lacc->size);
+         if (racc && racc->grp_to_be_replaced)
+           {
+             rhs = get_access_replacement (racc);
+             if (!useless_type_conversion_p (lacc->type, racc->type))
+               rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, lacc->type, rhs);
+           }
+         else
+           {
+             /* No suitable access on the right hand side, need to load from
+                the aggregate.  See if we have to update it first... */
+             if (*refreshed == SRA_UDH_NONE)
+               *refreshed = handle_unscalarized_data_in_subtree (top_racc,
+                                                                 lhs, old_gsi);
 
-       /* 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);
+             if (*refreshed == SRA_UDH_LEFT)
+               {
+                 bool repl_found;
 
-        return build3 (COMPONENT_REF, elt->type, base, field, NULL);
-      }
+                 rhs = lacc->base;
+                 repl_found = build_ref_for_offset (&rhs, TREE_TYPE (rhs),
+                                                    lacc->offset, lacc->type,
+                                                    false);
+                 gcc_assert (repl_found);
+               }
+             else
+               {
+                 bool repl_found;
 
-    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);
+                 rhs = top_racc->base;
+                 repl_found = build_ref_for_offset (&rhs,
+                                                    TREE_TYPE (top_racc->base),
+                                                    offset, lacc->type, false);
+                 gcc_assert (repl_found);
+               }
+           }
 
-    case COMPLEX_TYPE:
-      if (elt->element == integer_zero_node)
-       return build1 (REALPART_EXPR, elt->type, base);
-      else
-       return build1 (IMAGPART_EXPR, elt->type, base);
+         stmt = gimple_build_assign (get_access_replacement (lacc), rhs);
+         gsi_insert_after (new_gsi, stmt, GSI_NEW_STMT);
+         update_stmt (stmt);
+         sra_stats.subreplacements++;
+       }
+      else if (*refreshed == SRA_UDH_NONE
+              && lacc->grp_read && !lacc->grp_covered)
+       *refreshed = handle_unscalarized_data_in_subtree (top_racc, lhs,
+                                                         old_gsi);
 
-    default:
-      gcc_unreachable ();
+      if (lacc->first_child)
+       load_assign_lhs_subreplacements (lacc->first_child, top_racc,
+                                        left_offset, right_offset,
+                                        old_gsi, new_gsi, refreshed, lhs);
+      lacc = lacc->next_sibling;
     }
+  while (lacc);
 }
 
-/* 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.  */
+/* Modify assignments with a CONSTRUCTOR on their RHS.  STMT contains a pointer
+   to the assignment and GSI is the statement iterator pointing at it.  Returns
+   the same values as sra_modify_assign.  */
 
-static bool
-scalar_bitfield_p (tree bf)
+static enum scan_assign_result
+sra_modify_constructor_assign (gimple *stmt, gimple_stmt_iterator *gsi)
 {
-  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)))));
-}
+  tree lhs = gimple_assign_lhs (*stmt);
+  struct access *acc;
 
-/* Create an assignment statement from SRC to DST.  */
+  acc = get_access_for_expr (lhs);
+  if (!acc)
+    return SRA_SA_NONE;
 
-static tree
-sra_build_assignment (tree dst, tree src)
-{
-  /* Turning BIT_FIELD_REFs into bit operations enables other passes
-     to do a much better job at optimizing the code.  */
-  if (scalar_bitfield_p (src))
+  if (VEC_length (constructor_elt,
+                 CONSTRUCTOR_ELTS (gimple_assign_rhs1 (*stmt))) > 0)
     {
-      tree cst, cst2, mask, minshift, maxshift;
-      tree tmp, var, utype, stype;
-      tree list, stmt;
-      bool unsignedp = BIT_FIELD_REF_UNSIGNED (src);
+      /* I have never seen this code path trigger but if it can happen the
+        following should handle it gracefully.  */
+      if (access_has_children_p (acc))
+       generate_subtree_copies (acc->first_child, acc->base, 0, 0, 0, gsi,
+                                true, true);
+      return SRA_SA_PROCESSED;
+    }
 
-      var = TREE_OPERAND (src, 0);
-      cst = TREE_OPERAND (src, 2);
-      cst2 = size_binop (PLUS_EXPR, TREE_OPERAND (src, 1),
-                        TREE_OPERAND (src, 2));
+  if (acc->grp_covered)
+    {
+      init_subtree_with_zero (acc, gsi, false);
+      unlink_stmt_vdef (*stmt);
+      gsi_remove (gsi, true);
+      return SRA_SA_REMOVED;
+    }
+  else
+    {
+      init_subtree_with_zero (acc, gsi, true);
+      return SRA_SA_PROCESSED;
+    }
+}
 
-      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;
-       }
 
-      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);
+/* Callback of scan_function to process assign statements.  It examines both
+   sides of the statement, replaces them with a scalare replacement if there is
+   one and generating copying of replacements if scalarized aggregates have been
+   used in the assignment.  STMT is a pointer to the assign statement, GSI is
+   used to hold generated statements for type conversions and subtree
+   copying.  */
 
-      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);
+static enum scan_assign_result
+sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi,
+                  void *data ATTRIBUTE_UNUSED)
+{
+  struct access *lacc, *racc;
+  tree lhs, rhs;
+  bool modify_this_stmt = false;
+  bool force_gimple_rhs = false;
+  location_t loc = gimple_location (*stmt);
 
-      list = NULL;
+  if (!gimple_assign_single_p (*stmt))
+    return SRA_SA_NONE;
+  lhs = gimple_assign_lhs (*stmt);
+  rhs = gimple_assign_rhs1 (*stmt);
 
-      cst2 = size_binop (MINUS_EXPR, maxshift, minshift);
-      if (TREE_INT_CST_LOW (cst2) == TYPE_PRECISION (utype))
-       {
-         unsignedp = true;
-         mask = NULL_TREE;
-       }
-      else
-       {
-         mask = build_int_cst_wide (utype, 1, 0);
-         cst = int_const_binop (LSHIFT_EXPR, mask, cst2, true);
-         mask = int_const_binop (MINUS_EXPR, cst, mask, true);
-       }
+  if (TREE_CODE (rhs) == CONSTRUCTOR)
+    return sra_modify_constructor_assign (stmt, gsi);
 
-      tmp = make_rename_temp (stype, "SR");
-      if (TYPE_MAIN_VARIANT (TREE_TYPE (var)) != TYPE_MAIN_VARIANT (stype))
-       {
-         if (INTEGRAL_TYPE_P (TREE_TYPE (var)))
-           stmt = build_gimple_modify_stmt (tmp,
-                                            fold_convert (stype, var));
-         else
-           stmt = build_gimple_modify_stmt (tmp,
-                                            fold_build1 (VIEW_CONVERT_EXPR,
-                                                         stype, var));
-         append_to_statement_list (stmt, &list);
+  if (TREE_CODE (rhs) == REALPART_EXPR || TREE_CODE (lhs) == REALPART_EXPR
+      || TREE_CODE (rhs) == IMAGPART_EXPR || TREE_CODE (lhs) == IMAGPART_EXPR
+      || TREE_CODE (rhs) == BIT_FIELD_REF || TREE_CODE (lhs) == BIT_FIELD_REF)
+    {
+      modify_this_stmt = sra_modify_expr (gimple_assign_rhs1_ptr (*stmt),
+                                         gsi, false, data);
+      modify_this_stmt |= sra_modify_expr (gimple_assign_lhs_ptr (*stmt),
+                                          gsi, true, data);
+      return modify_this_stmt ? SRA_SA_PROCESSED : SRA_SA_NONE;
+    }
 
-         var = tmp;
-       }
+  lacc = get_access_for_expr (lhs);
+  racc = get_access_for_expr (rhs);
+  if (!lacc && !racc)
+    return SRA_SA_NONE;
 
-      if (!integer_zerop (minshift))
-       {
-         tmp = make_rename_temp (stype, "SR");
-         stmt = build_gimple_modify_stmt (tmp,
-                                          fold_build2 (RSHIFT_EXPR, stype,
-                                                       var, minshift));
-         append_to_statement_list (stmt, &list);
+  if (lacc && lacc->grp_to_be_replaced)
+    {
+      lhs = get_access_replacement (lacc);
+      gimple_assign_set_lhs (*stmt, lhs);
+      modify_this_stmt = true;
+      if (lacc->grp_partial_lhs)
+       force_gimple_rhs = true;
+      sra_stats.exprs++;
+    }
 
-         var = tmp;
-       }
+  if (racc && racc->grp_to_be_replaced)
+    {
+      rhs = get_access_replacement (racc);
+      modify_this_stmt = true;
+      if (racc->grp_partial_lhs)
+       force_gimple_rhs = true;
+      sra_stats.exprs++;
+    }
 
-      if (TYPE_MAIN_VARIANT (utype) != TYPE_MAIN_VARIANT (stype))
+  if (modify_this_stmt)
+    {
+      if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
        {
-         if (!mask && unsignedp
-             && (TYPE_MAIN_VARIANT (utype)
-                 == TYPE_MAIN_VARIANT (TREE_TYPE (dst))))
-           tmp = dst;
-         else
-           tmp = make_rename_temp (utype, "SR");
-
-         stmt = build_gimple_modify_stmt (tmp, fold_convert (utype, var));
-         append_to_statement_list (stmt, &list);
-
-         var = tmp;
+         /* If we can avoid creating a VIEW_CONVERT_EXPR do so.
+            ???  This should move to fold_stmt which we simply should
+            call after building a VIEW_CONVERT_EXPR here.  */
+         if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))
+             && !access_has_children_p (lacc))
+           {
+             tree expr = lhs;
+             if (build_ref_for_offset (&expr, TREE_TYPE (lhs), 0,
+                                       TREE_TYPE (rhs), false))
+               {
+                 lhs = expr;
+                 gimple_assign_set_lhs (*stmt, expr);
+               }
+           }
+         else if (AGGREGATE_TYPE_P (TREE_TYPE (rhs))
+                  && !access_has_children_p (racc))
+           {
+             tree expr = rhs;
+             if (build_ref_for_offset (&expr, TREE_TYPE (rhs), 0,
+                                       TREE_TYPE (lhs), false))
+               rhs = expr;
+           }
+         if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
+           {
+             rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (lhs), rhs);
+             if (is_gimple_reg_type (TREE_TYPE (lhs))
+                 && TREE_CODE (lhs) != SSA_NAME)
+               force_gimple_rhs = true;
+           }
        }
 
-      if (mask)
+      if (force_gimple_rhs)
+       rhs = force_gimple_operand_gsi (gsi, rhs, true, NULL_TREE,
+                                       true, GSI_SAME_STMT);
+      if (gimple_assign_rhs1 (*stmt) != rhs)
        {
-         if (!unsignedp
-             || (TYPE_MAIN_VARIANT (TREE_TYPE (dst))
-                 != TYPE_MAIN_VARIANT (utype)))
-           tmp = make_rename_temp (utype, "SR");
-         else
-           tmp = dst;
-
-         stmt = build_gimple_modify_stmt (tmp,
-                                          fold_build2 (BIT_AND_EXPR, utype,
-                                                       var, mask));
-         append_to_statement_list (stmt, &list);
-
-         var = tmp;
+         gimple_assign_set_rhs_from_tree (gsi, rhs);
+         gcc_assert (*stmt == gsi_stmt (*gsi));
        }
+    }
 
-      if (!unsignedp)
+  /* From this point on, the function deals with assignments in between
+     aggregates when at least one has scalar reductions of some of its
+     components.  There are three possible scenarios: Both the LHS and RHS have
+     to-be-scalarized components, 2) only the RHS has or 3) only the LHS has.
+
+     In the first case, we would like to load the LHS components from RHS
+     components whenever possible.  If that is not possible, we would like to
+     read it directly from the RHS (after updating it by storing in it its own
+     components).  If there are some necessary unscalarized data in the LHS,
+     those will be loaded by the original assignment too.  If neither of these
+     cases happen, the original statement can be removed.  Most of this is done
+     by load_assign_lhs_subreplacements.
+
+     In the second case, we would like to store all RHS scalarized components
+     directly into LHS and if they cover the aggregate completely, remove the
+     statement too.  In the third case, we want the LHS components to be loaded
+     directly from the RHS (DSE will remove the original statement if it
+     becomes redundant).
+
+     This is a bit complex but manageable when types match and when unions do
+     not cause confusion in a way that we cannot really load a component of LHS
+     from the RHS or vice versa (the access representing this level can have
+     subaccesses that are accessible only through a different union field at a
+     higher level - different from the one used in the examined expression).
+     Unions are fun.
+
+     Therefore, I specially handle a fourth case, happening when there is a
+     specific type cast or it is impossible to locate a scalarized subaccess on
+     the other side of the expression.  If that happens, I simply "refresh" the
+     RHS by storing in it is scalarized components leave the original statement
+     there to do the copying and then load the scalar replacements of the LHS.
+     This is what the first branch does.  */
+
+  if (contains_view_convert_expr_p (rhs) || contains_view_convert_expr_p (lhs)
+      || (access_has_children_p (racc)
+         && !ref_expr_for_all_replacements_p (racc, lhs, racc->offset))
+      || (access_has_children_p (lacc)
+         && !ref_expr_for_all_replacements_p (lacc, rhs, lacc->offset)))
+    {
+      if (access_has_children_p (racc))
+       generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,
+                                gsi, false, false);
+      if (access_has_children_p (lacc))
+       generate_subtree_copies (lacc->first_child, lacc->base, 0, 0, 0,
+                                gsi, true, true);
+      sra_stats.separate_lhs_rhs_handling++;
+    }
+  else
+    {
+      if (access_has_children_p (lacc) && access_has_children_p (racc))
        {
-         tree signbit = int_const_binop (LSHIFT_EXPR,
-                                         build_int_cst_wide (utype, 1, 0),
-                                         size_binop (MINUS_EXPR, cst2,
-                                                     bitsize_int (1)),
-                                         true);
-
-         tmp = make_rename_temp (utype, "SR");
-         stmt = build_gimple_modify_stmt (tmp,
-                                          fold_build2 (BIT_XOR_EXPR, utype,
-                                                       var, signbit));
-         append_to_statement_list (stmt, &list);
-
-         var = tmp;
-
-         if (TYPE_MAIN_VARIANT (TREE_TYPE (dst)) != TYPE_MAIN_VARIANT (utype))
-           tmp = make_rename_temp (utype, "SR");
+         gimple_stmt_iterator orig_gsi = *gsi;
+         enum unscalarized_data_handling refreshed;
+
+         if (lacc->grp_read && !lacc->grp_covered)
+           refreshed = handle_unscalarized_data_in_subtree (racc, lhs, gsi);
          else
-           tmp = dst;
+           refreshed = SRA_UDH_NONE;
 
-         stmt = build_gimple_modify_stmt (tmp,
-                                          fold_build2 (MINUS_EXPR, utype,
-                                                       var, signbit));
-         append_to_statement_list (stmt, &list);
+         load_assign_lhs_subreplacements (lacc->first_child, racc,
+                                          lacc->offset, racc->offset,
+                                          &orig_gsi, gsi, &refreshed, lhs);
+         if (refreshed != SRA_UDH_RIGHT)
+           {
+             if (*stmt == gsi_stmt (*gsi))
+               gsi_next (gsi);
 
-         var = tmp;
+             unlink_stmt_vdef (*stmt);
+             gsi_remove (&orig_gsi, true);
+             sra_stats.deleted++;
+             return SRA_SA_REMOVED;
+           }
        }
-
-      if (var != dst)
+      else
        {
-         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);
-
-         stmt = build_gimple_modify_stmt (dst, var);
-         append_to_statement_list (stmt, &list);
+         if (access_has_children_p (racc))
+           {
+             if (!racc->grp_unscalarized_data)
+               {
+                 generate_subtree_copies (racc->first_child, lhs,
+                                          racc->offset, 0, 0, gsi,
+                                          false, false);
+                 gcc_assert (*stmt == gsi_stmt (*gsi));
+                 unlink_stmt_vdef (*stmt);
+                 gsi_remove (gsi, true);
+                 sra_stats.deleted++;
+                 return SRA_SA_REMOVED;
+               }
+             else
+               generate_subtree_copies (racc->first_child, lhs,
+                                        racc->offset, 0, 0, gsi, false, true);
+           }
+         else if (access_has_children_p (lacc))
+           generate_subtree_copies (lacc->first_child, rhs, lacc->offset,
+                                    0, 0, gsi, true, true);
        }
-
-      return list;
     }
-
-  /* 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.  */
-  return build_gimple_modify_stmt (dst, src);
+  return modify_this_stmt ? SRA_SA_PROCESSED : SRA_SA_NONE;
 }
 
-/* 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))
+/* Generate statements initializing scalar replacements of parts of function
+   parameters.  */
 
-/* Emit an assignment from SRC to DST, but if DST is a scalarizable
-   BIT_FIELD_REF, turn it into bit operations.  */
-
-static tree
-sra_build_bf_assignment (tree dst, tree src)
+static void
+initialize_parameter_reductions (void)
 {
-  tree var, type, utype, tmp, tmp2, tmp3;
-  tree list, stmt;
-  tree cst, cst2, mask;
-  tree minshift, maxshift;
-
-  if (TREE_CODE (dst) != BIT_FIELD_REF)
-    return sra_build_assignment (dst, src);
+  gimple_stmt_iterator gsi;
+  gimple_seq seq = NULL;
+  tree parm;
 
-  var = TREE_OPERAND (dst, 0);
-
-  if (!scalar_bitfield_p (dst))
-    return sra_build_assignment (REPLDUP (dst), src);
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm;
+       parm = TREE_CHAIN (parm))
+    {
+      VEC (access_p, heap) *access_vec;
+      struct access *access;
 
-  list = NULL;
+      if (!bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
+       continue;
+      access_vec = get_base_access_vector (parm);
+      if (!access_vec)
+       continue;
 
-  cst = fold_convert (bitsizetype, TREE_OPERAND (dst, 2));
-  cst2 = size_binop (PLUS_EXPR,
-                    fold_convert (bitsizetype, TREE_OPERAND (dst, 1)),
-                    cst);
+      if (!seq)
+       {
+         seq = gimple_seq_alloc ();
+         gsi = gsi_start (seq);
+       }
 
-  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;
+      for (access = VEC_index (access_p, access_vec, 0);
+          access;
+          access = access->next_grp)
+       generate_subtree_copies (access, parm, 0, 0, 0, &gsi, true, true);
     }
 
-  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);
+  if (seq)
+    gsi_insert_seq_on_edge_immediate (single_succ_edge (ENTRY_BLOCK_PTR), seq);
+}
 
-  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);
+/* The "main" function of intraprocedural SRA passes.  Runs the analysis and if
+   it reveals there are components of some aggregates to be scalarized, it runs
+   the required transformations.  */
+static unsigned int
+perform_intra_sra (void)
+{
+  int ret = 0;
+  sra_initialize ();
 
-  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);
+  if (!find_var_candidates ())
+    goto out;
 
-      tmp2 = make_rename_temp (utype, "SR");
+  if (!scan_function (build_access_from_expr, build_accesses_from_assign, NULL,
+                     true, NULL))
+    goto out;
 
-      if (INTEGRAL_TYPE_P (TREE_TYPE (var)))
-       stmt = build_gimple_modify_stmt (tmp2, fold_convert (utype, tmp));
-      else
-       stmt = build_gimple_modify_stmt (tmp2, fold_build1 (VIEW_CONVERT_EXPR,
-                                                           utype, tmp));
-      append_to_statement_list (stmt, &list);
-    }
-  else
-    tmp2 = var;
+  if (!analyze_all_variable_accesses ())
+    goto out;
 
-  if (!integer_zerop (mask))
-    {
-      tmp = make_rename_temp (utype, "SR");
-      stmt = build_gimple_modify_stmt (tmp,
-                                      fold_build2 (BIT_AND_EXPR, utype,
-                                                   tmp2, mask));
-      append_to_statement_list (stmt, &list);
-    }
-  else
-    tmp = mask;
+  scan_function (sra_modify_expr, sra_modify_assign, NULL, false, NULL);
+  initialize_parameter_reductions ();
 
-  if (is_gimple_reg (src) && INTEGRAL_TYPE_P (TREE_TYPE (src)))
-    tmp2 = src;
-  else if (INTEGRAL_TYPE_P (TREE_TYPE (src)))
-    {
-      tmp2 = make_rename_temp (TREE_TYPE (src), "SR");
-      stmt = sra_build_assignment (tmp2, src);
-      append_to_statement_list (stmt, &list);
-    }
-  else
-    {
-      tmp2 = make_rename_temp
-       (lang_hooks.types.type_for_size
-        (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (src))),
-         1), "SR");
-      stmt = sra_build_assignment (tmp2, fold_build1 (VIEW_CONVERT_EXPR,
-                                                     TREE_TYPE (tmp2), src));
-      append_to_statement_list (stmt, &list);
-    }
+  statistics_counter_event (cfun, "Scalar replacements created",
+                           sra_stats.replacements);
+  statistics_counter_event (cfun, "Modified expressions", sra_stats.exprs);
+  statistics_counter_event (cfun, "Subtree copy stmts",
+                           sra_stats.subtree_copies);
+  statistics_counter_event (cfun, "Subreplacement stmts",
+                           sra_stats.subreplacements);
+  statistics_counter_event (cfun, "Deleted stmts", sra_stats.deleted);
+  statistics_counter_event (cfun, "Separate LHS and RHS handling",
+                           sra_stats.separate_lhs_rhs_handling);
 
-  if (!TYPE_UNSIGNED (TREE_TYPE (tmp2)))
-    {
-      tree ut = unsigned_type_for (TREE_TYPE (tmp2));
-      tmp3 = make_rename_temp (ut, "SR");
-      tmp2 = fold_convert (ut, tmp2);
-      stmt = sra_build_assignment (tmp3, tmp2);
-      append_to_statement_list (stmt, &list);
+  ret = TODO_update_ssa;
 
-      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);
+ out:
+  sra_deinitialize ();
+  return ret;
+}
 
-      if (tmp3 != tmp2)
-       {
-         tmp3 = make_rename_temp (ut, "SR");
-         stmt = sra_build_assignment (tmp3, tmp2);
-         append_to_statement_list (stmt, &list);
-       }
+/* Perform early intraprocedural SRA.  */
+static unsigned int
+early_intra_sra (void)
+{
+  sra_mode = SRA_MODE_EARLY_INTRA;
+  return perform_intra_sra ();
+}
 
-      tmp2 = tmp3;
-    }
+/* Perform "late" intraprocedural SRA.  */
+static unsigned int
+late_intra_sra (void)
+{
+  sra_mode = SRA_MODE_INTRA;
+  return perform_intra_sra ();
+}
 
-  if (TYPE_MAIN_VARIANT (TREE_TYPE (tmp2)) != TYPE_MAIN_VARIANT (utype))
-    {
-      tmp3 = make_rename_temp (utype, "SR");
-      tmp2 = fold_convert (utype, tmp2);
-      stmt = sra_build_assignment (tmp3, tmp2);
-      append_to_statement_list (stmt, &list);
-      tmp2 = tmp3;
-    }
 
-  if (!integer_zerop (minshift))
-    {
-      tmp3 = make_rename_temp (utype, "SR");
-      stmt = build_gimple_modify_stmt (tmp3,
-                                      fold_build2 (LSHIFT_EXPR, utype,
-                                                   tmp2, minshift));
-      append_to_statement_list (stmt, &list);
-      tmp2 = tmp3;
-    }
+static bool
+gate_intra_sra (void)
+{
+  return flag_tree_sra != 0;
+}
 
-  if (utype != TREE_TYPE (var))
-    tmp3 = make_rename_temp (utype, "SR");
-  else
-    tmp3 = var;
-  stmt = build_gimple_modify_stmt (tmp3,
-                                  fold_build2 (BIT_IOR_EXPR, utype,
-                                               tmp, tmp2));
-  append_to_statement_list (stmt, &list);
 
-  if (tmp3 != var)
-    {
-      if (TREE_TYPE (var) == type)
-       stmt = build_gimple_modify_stmt (var,
-                                        fold_convert (type, tmp3));
-      else
-       stmt = build_gimple_modify_stmt (var,
-                                        fold_build1 (VIEW_CONVERT_EXPR,
-                                                     TREE_TYPE (var), tmp3));
-      append_to_statement_list (stmt, &list);
-    }
+struct gimple_opt_pass pass_sra_early =
+{
+ {
+  GIMPLE_PASS,
+  "esra",                              /* name */
+  gate_intra_sra,                      /* gate */
+  early_intra_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 */
+ }
+};
 
-  return list;
-}
+struct gimple_opt_pass pass_sra =
+{
+ {
+  GIMPLE_PASS,
+  "sra",                               /* name */
+  gate_intra_sra,                      /* gate */
+  late_intra_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 */
+  TODO_update_address_taken,           /* todo_flags_start */
+  TODO_dump_func
+  | TODO_update_ssa
+  | TODO_ggc_collect
+  | TODO_verify_ssa                    /* todo_flags_finish */
+ }
+};
 
-/* 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 tree
-sra_build_elt_assignment (struct sra_elt *elt, tree src)
+/* Return true iff PARM (which must be a parm_decl) is an unused scalar
+   parameter.  */
+
+static bool
+is_unused_scalar_param (tree parm)
 {
-  tree dst = elt->replacement;
-  tree var, tmp, cst, cst2, list, stmt;
+  tree name;
+  return (is_gimple_reg (parm)
+         && (!(name = gimple_default_def (cfun, parm))
+             || has_zero_uses (name)));
+}
 
-  if (TREE_CODE (dst) != BIT_FIELD_REF
-      || !elt->in_bitfld_block)
-    return sra_build_assignment (REPLDUP (dst), src);
+/* Scan immediate uses of a default definition SSA name of a parameter PARM and
+   examine whether there are any direct or otherwise infeasible ones.  If so,
+   return true, otherwise return false.  PARM must be a gimple register with a
+   non-NULL default definition.  */
 
-  var = TREE_OPERAND (dst, 0);
+static bool
+ptr_parm_has_direct_uses (tree parm)
+{
+  imm_use_iterator ui;
+  gimple stmt;
+  tree name = gimple_default_def (cfun, parm);
+  bool ret = false;
 
-  /* 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)
+  FOR_EACH_IMM_USE_STMT (stmt, ui, name)
     {
-      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 (gimple_assign_single_p (stmt))
        {
-         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)))
+         tree rhs = gimple_assign_rhs1 (stmt);
+         if (rhs == name)
+           ret = true;
+         else if (TREE_CODE (rhs) == ADDR_EXPR)
            {
-             list = 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 = build_gimple_modify_stmt (tmp, src);
-             append_to_statement_list (stmt, &list);
-
-             stmt = sra_build_assignment (var,
-                                          fold_convert (TREE_TYPE (var),
-                                                        tmp));
-             append_to_statement_list (stmt, &list);
-
-             return list;
+             do
+               {
+                 rhs = TREE_OPERAND (rhs, 0);
+               }
+             while (handled_component_p (rhs));
+             if (INDIRECT_REF_P (rhs) && TREE_OPERAND (rhs, 0) == name)
+               ret = true;
            }
-
-         src = fold_convert (TREE_TYPE (var), src);
        }
-      else
+      else if (gimple_code (stmt) == GIMPLE_RETURN)
+       {
+         tree t = gimple_return_retval (stmt);
+         if (t == name)
+           ret = true;
+       }
+      else if (is_gimple_call (stmt))
        {
-         src = fold_build3 (BIT_FIELD_REF, TREE_TYPE (var), src, cst, cst2);
-         BIT_FIELD_REF_UNSIGNED (src) = 1;
+         unsigned i;
+         for (i = 0; i < gimple_call_num_args (stmt); i++)
+           {
+             tree arg = gimple_call_arg (stmt, i);
+             if (arg == name)
+               {
+                 ret = true;
+                 break;
+               }
+           }
        }
+      else if (!is_gimple_debug (stmt))
+       ret = true;
 
-      return sra_build_assignment (var, src);
+      if (ret)
+       BREAK_FROM_IMM_USE_STMT (ui);
     }
 
-  return sra_build_bf_assignment (dst, src);
+  return ret;
 }
 
-/* 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.  */
+/* Identify candidates for reduction for IPA-SRA based on their type and mark
+   them in candidate_bitmap.  Note that these do not necessarily include
+   parameter which are unused and thus can be removed.  Return true iff any
+   such candidate has been found.  */
 
-static void
-generate_copy_inout (struct sra_elt *elt, bool copy_out, tree expr,
-                    tree *list_p)
+static bool
+find_param_candidates (void)
 {
-  struct sra_elt *c;
-  tree t;
+  tree parm;
+  int count = 0;
+  bool ret = false;
 
-  if (!copy_out && TREE_CODE (expr) == SSA_NAME
-      && TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm;
+       parm = TREE_CHAIN (parm))
     {
-      tree r, i;
+      tree type = TREE_TYPE (parm);
 
-      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;
+      count++;
 
-      t = build2 (COMPLEX_EXPR, elt->type, r, i);
-      t = sra_build_bf_assignment (expr, t);
-      SSA_NAME_DEF_STMT (expr) = t;
-      append_to_statement_list (t, list_p);
-    }
-  else if (elt->replacement)
-    {
-      if (copy_out)
-       t = sra_build_elt_assignment (elt, expr);
-      else
-       t = sra_build_bf_assignment (expr, REPLDUP (elt->replacement));
-      append_to_statement_list (t, list_p);
-    }
-  else
-    {
-      FOR_EACH_ACTUAL_CHILD (c, elt)
+      if (TREE_THIS_VOLATILE (parm)
+         || TREE_ADDRESSABLE (parm)
+         || is_va_list_type (type))
+       continue;
+
+      if (is_unused_scalar_param (parm))
        {
-         t = generate_one_element_ref (c, unshare_expr (expr));
-         generate_copy_inout (c, copy_out, t, list_p);
+         ret = true;
+         continue;
        }
-    }
-}
-
-/* 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, tree *list_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)
+      if (POINTER_TYPE_P (type))
        {
-         struct sra_elt *dcs;
+         type = TREE_TYPE (type);
 
-         FOR_EACH_ACTUAL_CHILD (dcs, dc)
-           {
-             sc = lookup_element (src, dcs->element, NULL, NO_INSERT);
-             gcc_assert (sc);
-             generate_element_copy (dcs, sc, list_p);
-           }
+         if (TREE_CODE (type) == FUNCTION_TYPE
+             || TYPE_VOLATILE (type)
+             || !is_gimple_reg (parm)
+             || is_va_list_type (type)
+             || ptr_parm_has_direct_uses (parm))
+           continue;
+       }
+      else if (!AGGREGATE_TYPE_P (type))
+       continue;
 
-         continue;
+      if (!COMPLETE_TYPE_P (type)
+         || !host_integerp (TYPE_SIZE (type), 1)
+          || tree_low_cst (TYPE_SIZE (type), 1) == 0
+         || (AGGREGATE_TYPE_P (type)
+             && type_internals_preclude_sra_p (type)))
+       continue;
+
+      bitmap_set_bit (candidate_bitmap, DECL_UID (parm));
+      ret = true;
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Candidate (%d): ", DECL_UID (parm));
+         print_generic_expr (dump_file, parm, 0);
+         fprintf (dump_file, "\n");
        }
-      gcc_assert (sc);
-      generate_element_copy (dc, sc, list_p);
     }
 
-  if (dst->replacement)
-    {
-      tree t;
+  func_param_count = count;
+  return ret;
+}
 
-      gcc_assert (src->replacement);
+/* Callback of walk_aliased_vdefs, marks the access passed as DATA as
+   maybe_modified. */
 
-      t = sra_build_elt_assignment (dst, REPLDUP (src->replacement));
-      append_to_statement_list (t, list_p);
-    }
+static bool
+mark_maybe_modified (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef ATTRIBUTE_UNUSED,
+                    void *data)
+{
+  struct access *repr = (struct access *) data;
+
+  repr->grp_maybe_modified = 1;
+  return true;
 }
 
-/* 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.  */
+/* Analyze what representatives (in linked lists accessible from
+   REPRESENTATIVES) can be modified by side effects of statements in the
+   current function.  */
 
 static void
-generate_element_zero (struct sra_elt *elt, tree *list_p)
+analyze_modified_params (VEC (access_p, heap) *representatives)
 {
-  struct sra_elt *c;
+  int i;
 
-  if (elt->visited)
+  for (i = 0; i < func_param_count; i++)
     {
-      elt->visited = false;
-      return;
-    }
-
-  if (!elt->in_bitfld_block)
-    FOR_EACH_ACTUAL_CHILD (c, elt)
-      generate_element_zero (c, list_p);
+      struct access *repr;
 
-  if (elt->replacement)
-    {
-      tree t;
+      for (repr = VEC_index (access_p, representatives, i);
+          repr;
+          repr = repr->next_grp)
+       {
+         struct access *access;
+         bitmap visited;
+         ao_ref ar;
 
-      gcc_assert (elt->is_scalar);
-      t = fold_convert (elt->type, integer_zero_node);
+         if (no_accesses_p (repr))
+           continue;
+         if (!POINTER_TYPE_P (TREE_TYPE (repr->base))
+             || repr->grp_maybe_modified)
+           continue;
 
-      t = sra_build_elt_assignment (elt, t);
-      append_to_statement_list (t, list_p);
+         ao_ref_init (&ar, repr->expr);
+         visited = BITMAP_ALLOC (NULL);
+         for (access = repr; access; access = access->next_sibling)
+           {
+             /* All accesses are read ones, otherwise grp_maybe_modified would
+                be trivially set.  */
+             walk_aliased_vdefs (&ar, gimple_vuse (access->stmt),
+                                 mark_maybe_modified, repr, &visited);
+             if (repr->grp_maybe_modified)
+               break;
+           }
+         BITMAP_FREE (visited);
+       }
     }
 }
 
-/* Generate an assignment VAR = INIT, where INIT may need gimplification.
-   Add the result to *LIST_P.  */
+/* Propagate distances in bb_dereferences in the opposite direction than the
+   control flow edges, in each step storing the maximum of the current value
+   and the minimum of all successors.  These steps are repeated until the table
+   stabilizes.  Note that BBs which might terminate the functions (according to
+   final_bbs bitmap) never updated in this way.  */
 
 static void
-generate_one_element_init (struct sra_elt *elt, tree init, tree *list_p)
-{
-  /* The replacement can be almost arbitrarily complex.  Gimplify.  */
-  tree stmt = sra_build_elt_assignment (elt, init);
-  gimplify_and_add (stmt, list_p);
-}
-
-/* Generate a set of assignment statements in *LIST_P to set all instantiated
-   elements under ELT with the contents of the initializer INIT.  In addition,
-   mark all assigned elements VISITED; this allows easy coordination with
-   generate_element_zero.  Return false if we found a case we couldn't
-   handle.  */
-
-static bool
-generate_element_init_1 (struct sra_elt *elt, tree init, tree *list_p)
+propagate_dereference_distances (void)
 {
-  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);
+  VEC (basic_block, heap) *queue;
+  basic_block bb;
 
-  if (elt->is_scalar)
+  queue = VEC_alloc (basic_block, heap, last_basic_block_for_function (cfun));
+  VEC_quick_push (basic_block, queue, ENTRY_BLOCK_PTR);
+  FOR_EACH_BB (bb)
     {
-      if (elt->replacement)
-       {
-         generate_one_element_init (elt, init, list_p);
-         elt->visited = true;
-       }
-      return result;
+      VEC_quick_push (basic_block, queue, bb);
+      bb->aux = bb;
     }
 
-  switch (init_code)
+  while (!VEC_empty (basic_block, queue))
     {
-    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, list_p);
-       }
-      break;
+      edge_iterator ei;
+      edge e;
+      bool change = false;
+      int i;
 
-    case CONSTRUCTOR:
-      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, purpose, value)
-       {
-         if (TREE_CODE (purpose) == RANGE_EXPR)
-           {
-             tree lower = TREE_OPERAND (purpose, 0);
-             tree upper = TREE_OPERAND (purpose, 1);
+      bb = VEC_pop (basic_block, queue);
+      bb->aux = NULL;
 
-             while (1)
-               {
-                 sub = lookup_element (elt, lower, NULL, NO_INSERT);
-                 if (sub != NULL)
-                   result &= generate_element_init_1 (sub, value, list_p);
-                 if (tree_int_cst_equal (lower, upper))
-                   break;
-                 lower = int_const_binop (PLUS_EXPR, lower,
-                                          integer_one_node, true);
-               }
-           }
-         else
-           {
-             sub = lookup_element (elt, purpose, NULL, NO_INSERT);
-             if (sub != NULL)
-               result &= generate_element_init_1 (sub, value, list_p);
-           }
-       }
-      break;
+      if (bitmap_bit_p (final_bbs, bb->index))
+       continue;
 
-    default:
-      elt->visited = true;
-      result = false;
-    }
+      for (i = 0; i < func_param_count; i++)
+       {
+         int idx = bb->index * func_param_count + i;
+         bool first = true;
+         HOST_WIDE_INT inh = 0;
 
-  return result;
-}
+         FOR_EACH_EDGE (e, ei, bb->succs)
+         {
+           int succ_idx = e->dest->index * func_param_count + i;
 
-/* A wrapper function for generate_element_init_1 that handles cleanup after
-   gimplification.  */
+           if (e->src == EXIT_BLOCK_PTR)
+             continue;
 
-static bool
-generate_element_init (struct sra_elt *elt, tree init, tree *list_p)
-{
-  bool ret;
+           if (first)
+             {
+               first = false;
+               inh = bb_dereferences [succ_idx];
+             }
+           else if (bb_dereferences [succ_idx] < inh)
+             inh = bb_dereferences [succ_idx];
+         }
 
-  push_gimplify_context ();
-  ret = generate_element_init_1 (elt, init, list_p);
-  pop_gimplify_context (NULL);
+         if (!first && bb_dereferences[idx] < inh)
+           {
+             bb_dereferences[idx] = inh;
+             change = true;
+           }
+       }
 
-  /* The replacement can expose previously unreferenced variables.  */
-  if (ret && *list_p)
-    {
-      tree_stmt_iterator i;
+      if (change && !bitmap_bit_p (final_bbs, bb->index))
+       FOR_EACH_EDGE (e, ei, bb->preds)
+         {
+           if (e->src->aux)
+             continue;
 
-      for (i = tsi_start (*list_p); !tsi_end_p (i); tsi_next (&i))
-       find_new_referenced_vars (tsi_stmt_ptr (i));
+           e->src->aux = e->src;
+           VEC_quick_push (basic_block, queue, e->src);
+         }
     }
 
-  return ret;
+  VEC_free (basic_block, heap, queue);
 }
 
-/* Insert STMT 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.  */
+/* Dump a dereferences TABLE with heading STR to file F.  */
 
-void
-insert_edge_copies (tree stmt, basic_block bb)
+static void
+dump_dereferences_table (FILE *f, const char *str, HOST_WIDE_INT *table)
 {
-  edge e;
-  edge_iterator ei;
-  bool first_copy;
+  basic_block bb;
 
-  first_copy = true;
-  FOR_EACH_EDGE (e, ei, bb->succs)
+  fprintf (dump_file, str);
+  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
     {
-      /* We don't need to insert copies on abnormal edges.  The
-        value of the scalar replacement is not guaranteed to
-        be valid through an abnormal edge.  */
-      if (!(e->flags & EDGE_ABNORMAL))
+      fprintf (f, "%4i  %i   ", bb->index, bitmap_bit_p (final_bbs, bb->index));
+      if (bb != EXIT_BLOCK_PTR)
        {
-         if (first_copy)
+         int i;
+         for (i = 0; i < func_param_count; i++)
            {
-             bsi_insert_on_edge (e, stmt);
-             first_copy = false;
+             int idx = bb->index * func_param_count + i;
+             fprintf (f, " %4" HOST_WIDE_INT_PRINT "d", table[idx]);
            }
-         else
-           bsi_insert_on_edge (e, unsave_expr_now (stmt));
        }
+      fprintf (f, "\n");
     }
+  fprintf (dump_file, "\n");
 }
 
-/* Helper function to insert LIST before BSI, and set up line number info.  */
+/* Determine what (parts of) parameters passed by reference that are not
+   assigned to are not certainly dereferenced in this function and thus the
+   dereferencing cannot be safely moved to the caller without potentially
+   introducing a segfault.  Mark such REPRESENTATIVES as
+   grp_not_necessarilly_dereferenced.
+
+   The dereferenced maximum "distance," i.e. the offset + size of the accessed
+   part is calculated rather than simple booleans are calculated for each
+   pointer parameter to handle cases when only a fraction of the whole
+   aggregate is allocated (see testsuite/gcc.c-torture/execute/ipa-sra-2.c for
+   an example).
+
+   The maximum dereference distances for each pointer parameter and BB are
+   already stored in bb_dereference.  This routine simply propagates these
+   values upwards by propagate_dereference_distances and then compares the
+   distances of individual parameters in the ENTRY BB to the equivalent
+   distances of each representative of a (fraction of a) parameter.  */
 
-void
-sra_insert_before (block_stmt_iterator *bsi, tree list)
+static void
+analyze_caller_dereference_legality (VEC (access_p, heap) *representatives)
 {
-  tree stmt = bsi_stmt (*bsi);
+  int i;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    dump_dereferences_table (dump_file,
+                            "Dereference table before propagation:\n",
+                            bb_dereferences);
 
-  if (EXPR_HAS_LOCATION (stmt))
-    annotate_all_with_locus (&list, EXPR_LOCATION (stmt));
-  bsi_insert_before (bsi, list, BSI_SAME_STMT);
+  propagate_dereference_distances ();
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    dump_dereferences_table (dump_file,
+                            "Dereference table after propagation:\n",
+                            bb_dereferences);
+
+  for (i = 0; i < func_param_count; i++)
+    {
+      struct access *repr = VEC_index (access_p, representatives, i);
+      int idx = ENTRY_BLOCK_PTR->index * func_param_count + i;
+
+      if (!repr || no_accesses_p (repr))
+       continue;
+
+      do
+       {
+         if ((repr->offset + repr->size) > bb_dereferences[idx])
+           repr->grp_not_necessarilly_dereferenced = 1;
+         repr = repr->next_grp;
+       }
+      while (repr);
+    }
 }
 
-/* Similarly, but insert after BSI.  Handles insertion onto edges as well.  */
+/* Return the representative access for the parameter declaration PARM if it is
+   a scalar passed by reference which is not written to and the pointer value
+   is not used directly.  Thus, if it is legal to dereference it in the caller
+   and we can rule out modifications through aliases, such parameter should be
+   turned into one passed by value.  Return NULL otherwise.  */
 
-void
-sra_insert_after (block_stmt_iterator *bsi, tree list)
+static struct access *
+unmodified_by_ref_scalar_representative (tree parm)
 {
-  tree stmt = bsi_stmt (*bsi);
+  int i, access_count;
+  struct access *repr;
+  VEC (access_p, heap) *access_vec;
 
-  if (EXPR_HAS_LOCATION (stmt))
-    annotate_all_with_locus (&list, EXPR_LOCATION (stmt));
+  access_vec = get_base_access_vector (parm);
+  gcc_assert (access_vec);
+  repr = VEC_index (access_p, access_vec, 0);
+  if (repr->write)
+    return NULL;
+  repr->group_representative = repr;
 
-  if (stmt_ends_bb_p (stmt))
-    insert_edge_copies (list, bsi->bb);
-  else
-    bsi_insert_after (bsi, list, BSI_SAME_STMT);
+  access_count = VEC_length (access_p, access_vec);
+  for (i = 1; i < access_count; i++)
+    {
+      struct access *access = VEC_index (access_p, access_vec, i);
+      if (access->write)
+       return NULL;
+      access->group_representative = repr;
+      access->next_sibling = repr->next_sibling;
+      repr->next_sibling = access;
+    }
+
+  repr->grp_read = 1;
+  repr->grp_scalar_ptr = 1;
+  return repr;
 }
 
-/* Similarly, but replace the statement at BSI.  */
+/* Return true iff this access precludes IPA-SRA of the parameter it is
+   associated with. */
 
-static void
-sra_replace (block_stmt_iterator *bsi, tree list)
-{
-  sra_insert_before (bsi, list);
-  bsi_remove (bsi, false);
-  if (bsi_end_p (*bsi))
-    *bsi = bsi_last (bsi->bb);
-  else
-    bsi_prev (bsi);
+static bool
+access_precludes_ipa_sra_p (struct access *access)
+{
+  /* Avoid issues such as the second simple testcase in PR 42025.  The problem
+     is incompatible assign in a call statement (and possibly even in asm
+     statements).  This can be relaxed by using a new temporary but only for
+     non-TREE_ADDRESSABLE types and is probably not worth the complexity. (In
+     intraprocedural SRA we deal with this by keeping the old aggregate around,
+     something we cannot do in IPA-SRA.)  */
+  if (access->write
+      && (is_gimple_call (access->stmt)
+         || gimple_code (access->stmt) == GIMPLE_ASM))
+    return true;
+
+  return false;
 }
 
-/* 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
+/* Sort collected accesses for parameter PARM, identify representatives for
+   each accessed region and link them together.  Return NULL if there are
+   different but overlapping accesses, return the special ptr value meaning
+   there are no accesses for this parameter if that is the case and return the
+   first representative otherwise.  Set *RO_GRP if there is a group of accesses
+   with only read (i.e. no write) accesses.  */
+
+static struct access *
+splice_param_accesses (tree parm, bool *ro_grp)
 {
-  /* The bit-length of an element.  */
-  tree field_len;
+  int i, j, access_count, group_count;
+  int agg_size, total_size = 0;
+  struct access *access, *res, **prev_acc_ptr = &res;
+  VEC (access_p, heap) *access_vec;
 
-  /* The bit-position of the element in its parent.  */
-  tree field_pos;
+  access_vec = get_base_access_vector (parm);
+  if (!access_vec)
+    return &no_accesses_representant;
+  access_count = VEC_length (access_p, access_vec);
 
-  /* The number of bits of the element that overlap with the incoming
-     bit range.  */
-  tree overlap_len;
+  qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p),
+        compare_access_positions);
 
-  /* The first bit of the element that overlaps with the incoming bit
-     range.  */
-  tree overlap_pos;
-};
+  i = 0;
+  total_size = 0;
+  group_count = 0;
+  while (i < access_count)
+    {
+      bool modification;
+      access = VEC_index (access_p, access_vec, i);
+      modification = access->write;
+      if (access_precludes_ipa_sra_p (access))
+       return NULL;
 
-/* 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.  */
+      /* Access is about to become group representative unless we find some
+        nasty overlap which would preclude us from breaking this parameter
+        apart. */
 
-static bool
-bitfield_overlaps_p (tree blen, tree bpos, struct sra_elt *fld,
-                    struct bitfield_overlap_info *data)
-{
-  tree flen, fpos;
-  bool ret;
+      j = i + 1;
+      while (j < access_count)
+       {
+         struct access *ac2 = VEC_index (access_p, access_vec, j);
+         if (ac2->offset != access->offset)
+           {
+             /* All or nothing law for parameters. */
+             if (access->offset + access->size > ac2->offset)
+               return NULL;
+             else
+               break;
+           }
+         else if (ac2->size != access->size)
+           return NULL;
 
-  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)
-    {
-      flen = fold_convert (bitsizetype, TYPE_SIZE (fld->type));
-      fpos = fold_convert (bitsizetype, fld->element);
-      fpos = size_binop (MULT_EXPR, flen, fpos);
+         if (access_precludes_ipa_sra_p (ac2))
+           return NULL;
+
+         modification |= ac2->write;
+         ac2->group_representative = access;
+         ac2->next_sibling = access->next_sibling;
+         access->next_sibling = ac2;
+         j++;
+       }
+
+      group_count++;
+      access->grp_maybe_modified = modification;
+      if (!modification)
+       *ro_grp = true;
+      *prev_acc_ptr = access;
+      prev_acc_ptr = &access->next_grp;
+      total_size += access->size;
+      i = j;
     }
+
+  if (POINTER_TYPE_P (TREE_TYPE (parm)))
+    agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (parm))), 1);
   else
-    gcc_unreachable ();
+    agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (parm)), 1);
+  if (total_size >= agg_size)
+    return NULL;
+
+  gcc_assert (group_count > 0);
+  return res;
+}
 
-  gcc_assert (host_integerp (blen, 1)
-             && host_integerp (bpos, 1)
-             && host_integerp (flen, 1)
-             && host_integerp (fpos, 1));
+/* Decide whether parameters with representative accesses given by REPR should
+   be reduced into components.  */
 
-  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)));
+static int
+decide_one_param_reduction (struct access *repr)
+{
+  int total_size, cur_parm_size, agg_size, new_param_count, parm_size_limit;
+  bool by_ref;
+  tree parm;
 
-  if (!ret)
-    return ret;
+  parm = repr->base;
+  cur_parm_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (parm)), 1);
+  gcc_assert (cur_parm_size > 0);
 
-  if (data)
+  if (POINTER_TYPE_P (TREE_TYPE (parm)))
+    {
+      by_ref = true;
+      agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (parm))), 1);
+    }
+  else
     {
-      tree bend, fend;
+      by_ref = false;
+      agg_size = cur_parm_size;
+    }
 
-      data->field_len = flen;
-      data->field_pos = fpos;
+  if (dump_file)
+    {
+      struct access *acc;
+      fprintf (dump_file, "Evaluating PARAM group sizes for ");
+      print_generic_expr (dump_file, parm, 0);
+      fprintf (dump_file, " (UID: %u): \n", DECL_UID (parm));
+      for (acc = repr; acc; acc = acc->next_grp)
+       dump_access (dump_file, acc, true);
+    }
 
-      fend = size_binop (PLUS_EXPR, fpos, flen);
-      bend = size_binop (PLUS_EXPR, bpos, blen);
+  total_size = 0;
+  new_param_count = 0;
 
-      if (tree_int_cst_lt (bend, fend))
-       data->overlap_len = size_binop (MINUS_EXPR, bend, fpos);
-      else
-       data->overlap_len = NULL;
+  for (; repr; repr = repr->next_grp)
+    {
+      gcc_assert (parm == repr->base);
+      new_param_count++;
 
-      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);
-       }
+      if (!by_ref || (!repr->grp_maybe_modified
+                     && !repr->grp_not_necessarilly_dereferenced))
+       total_size += repr->size;
       else
-       data->overlap_pos = NULL;
+       total_size += cur_parm_size;
     }
 
-  return ret;
-}
+  gcc_assert (new_param_count > 0);
 
-/* 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,
-                                tree *listp, tree blen, tree bpos,
-                                struct sra_elt *elt)
-{
-  struct sra_elt *fld;
-  struct bitfield_overlap_info flp;
+  if (optimize_function_for_size_p (cfun))
+    parm_size_limit = cur_parm_size;
+  else
+    parm_size_limit = (PARAM_VALUE (PARAM_IPA_SRA_PTR_GROWTH_FACTOR)
+                       * cur_parm_size);
 
-  FOR_EACH_ACTUAL_CHILD (fld, elt)
+  if (total_size < agg_size
+      && total_size <= parm_size_limit)
     {
-      tree flen, fpos;
+      if (dump_file)
+       fprintf (dump_file, "    ....will be split into %i components\n",
+                new_param_count);
+      return new_param_count;
+    }
+  else
+    return 0;
+}
 
-      if (!bitfield_overlaps_p (blen, bpos, fld, &flp))
-       continue;
+/* The order of the following enums is important, we need to do extra work for
+   UNUSED_PARAMS, BY_VAL_ACCESSES and UNMODIF_BY_REF_ACCESSES.  */
+enum ipa_splicing_result { NO_GOOD_ACCESS, UNUSED_PARAMS, BY_VAL_ACCESSES,
+                         MODIF_BY_REF_ACCESSES, UNMODIF_BY_REF_ACCESSES };
 
-      flen = flp.overlap_len ? flp.overlap_len : flp.field_len;
-      fpos = flp.overlap_pos ? flp.overlap_pos : bitsize_int (0);
+/* Identify representatives of all accesses to all candidate parameters for
+   IPA-SRA.  Return result based on what representatives have been found. */
 
-      if (fld->replacement)
-       {
-         tree infld, invar, st, type;
+static enum ipa_splicing_result
+splice_all_param_accesses (VEC (access_p, heap) **representatives)
+{
+  enum ipa_splicing_result result = NO_GOOD_ACCESS;
+  tree parm;
+  struct access *repr;
 
-         infld = fld->replacement;
+  *representatives = VEC_alloc (access_p, heap, func_param_count);
 
-         type = TREE_TYPE (infld);
-         if (TYPE_PRECISION (type) != TREE_INT_CST_LOW (flen))
-           type = lang_hooks.types.type_for_size (TREE_INT_CST_LOW (flen), 1);
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm;
+       parm = TREE_CHAIN (parm))
+    {
+      if (is_unused_scalar_param (parm))
+       {
+         VEC_quick_push (access_p, *representatives,
+                         &no_accesses_representant);
+         if (result == NO_GOOD_ACCESS)
+           result = UNUSED_PARAMS;
+       }
+      else if (POINTER_TYPE_P (TREE_TYPE (parm))
+              && is_gimple_reg_type (TREE_TYPE (TREE_TYPE (parm)))
+              && bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
+       {
+         repr = unmodified_by_ref_scalar_representative (parm);
+         VEC_quick_push (access_p, *representatives, repr);
+         if (repr)
+           result = UNMODIF_BY_REF_ACCESSES;
+       }
+      else if (bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
+       {
+         bool ro_grp = false;
+         repr = splice_param_accesses (parm, &ro_grp);
+         VEC_quick_push (access_p, *representatives, repr);
 
-         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)))
+         if (repr && !no_accesses_p (repr))
            {
-             fpos = size_binop (PLUS_EXPR, fpos,
-                                TYPE_SIZE (TREE_TYPE (infld)));
-             fpos = size_binop (MINUS_EXPR, fpos,
-                                DECL_SIZE (fld->element));
+             if (POINTER_TYPE_P (TREE_TYPE (parm)))
+               {
+                 if (ro_grp)
+                   result = UNMODIF_BY_REF_ACCESSES;
+                 else if (result < MODIF_BY_REF_ACCESSES)
+                   result = MODIF_BY_REF_ACCESSES;
+               }
+             else if (result < BY_VAL_ACCESSES)
+               result = BY_VAL_ACCESSES;
            }
-
-         infld = fold_build3 (BIT_FIELD_REF, type, infld, flen, fpos);
-         BIT_FIELD_REF_UNSIGNED (infld) = 1;
-
-         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);
-         BIT_FIELD_REF_UNSIGNED (invar) = 1;
-
-         if (to_var)
-           st = sra_build_bf_assignment (invar, infld);
-         else
-           st = sra_build_bf_assignment (infld, invar);
-
-         append_to_statement_list (st, listp);
+         else if (no_accesses_p (repr) && (result == NO_GOOD_ACCESS))
+           result = UNUSED_PARAMS;
        }
       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);
+       VEC_quick_push (access_p, *representatives, NULL);
+    }
 
-         sra_explode_bitfield_assignment (var, sub, to_var, listp,
-                                          flen, fpos, fld);
-       }
+  if (result == NO_GOOD_ACCESS)
+    {
+      VEC_free (access_p, heap, *representatives);
+      *representatives = NULL;
+      return NO_GOOD_ACCESS;
     }
+
+  return result;
 }
 
-/* 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.  */
+/* Return the index of BASE in PARMS.  Abort if it is not found.  */
 
-static void
-sra_sync_for_bitfield_assignment (tree *listbeforep, tree *listafterp,
-                                 tree blen, tree bpos,
-                                 struct sra_elt *elt)
+static inline int
+get_param_index (tree base, VEC(tree, heap) *parms)
 {
-  struct sra_elt *fld;
-  struct bitfield_overlap_info flp;
+  int i, len;
 
-  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),
-                                listbeforep);
-           mark_no_warning (fld);
-           if (listafterp)
-             generate_copy_inout (fld, true, generate_element_ref (fld),
-                                  listafterp);
-         }
-       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 (listbeforep, listafterp,
-                                             flen, fpos, fld);
-         }
-      }
+  len = VEC_length (tree, parms);
+  for (i = 0; i < len; i++)
+    if (VEC_index (tree, parms, i) == base)
+      return i;
+  gcc_unreachable ();
 }
 
-/* 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.  */
+/* Convert the decisions made at the representative level into compact
+   parameter adjustments.  REPRESENTATIVES are pointers to first
+   representatives of each param accesses, ADJUSTMENTS_COUNT is the expected
+   final number of adjustments.  */
 
-static void
-scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
-              bool is_output, bool use_all)
+static ipa_parm_adjustment_vec
+turn_representatives_into_adjustments (VEC (access_p, heap) *representatives,
+                                      int adjustments_count)
 {
-  tree stmt = bsi_stmt (*bsi);
-  tree bfexpr;
+  VEC (tree, heap) *parms;
+  ipa_parm_adjustment_vec adjustments;
+  tree parm;
+  int i;
 
-  if (elt->replacement)
+  gcc_assert (adjustments_count > 0);
+  parms = ipa_get_vector_of_formal_parms (current_function_decl);
+  adjustments = VEC_alloc (ipa_parm_adjustment_t, heap, adjustments_count);
+  parm = DECL_ARGUMENTS (current_function_decl);
+  for (i = 0; i < func_param_count; i++, parm = TREE_CHAIN (parm))
     {
-      tree replacement = elt->replacement;
+      struct access *repr = VEC_index (access_p, representatives, i);
 
-      /* 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))
-         && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
-         && &GIMPLE_STMT_OPERAND (stmt, 0) == expr_p)
+      if (!repr || no_accesses_p (repr))
        {
-         tree newstmt = sra_build_elt_assignment
-           (elt, GIMPLE_STMT_OPERAND (stmt, 1));
-         if (TREE_CODE (newstmt) != STATEMENT_LIST)
-           {
-             tree list = NULL;
-             append_to_statement_list (newstmt, &list);
-             newstmt = list;
-           }
-         sra_replace (bsi, newstmt);
-         return;
+         struct ipa_parm_adjustment *adj;
+
+         adj = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL);
+         memset (adj, 0, sizeof (*adj));
+         adj->base_index = get_param_index (parm, parms);
+         adj->base = parm;
+         if (!repr)
+           adj->copy_param = 1;
+         else
+           adj->remove_param = 1;
        }
-      else if (!is_output
-              && TREE_CODE (elt->replacement) == BIT_FIELD_REF
-              && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
-              && &GIMPLE_STMT_OPERAND (stmt, 1) == expr_p)
+      else
        {
-         tree tmp = make_rename_temp
-           (TREE_TYPE (GIMPLE_STMT_OPERAND (stmt, 0)), "SR");
-         tree newstmt = sra_build_assignment (tmp, REPLDUP (elt->replacement));
+         struct ipa_parm_adjustment *adj;
+         int index = get_param_index (parm, parms);
 
-         if (TREE_CODE (newstmt) != STATEMENT_LIST)
+         for (; repr; repr = repr->next_grp)
            {
-             tree list = NULL;
-             append_to_statement_list (newstmt, &list);
-             newstmt = list;
+             adj = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL);
+             memset (adj, 0, sizeof (*adj));
+             gcc_assert (repr->base == parm);
+             adj->base_index = index;
+             adj->base = repr->base;
+             adj->type = repr->type;
+             adj->offset = repr->offset;
+             adj->by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
+                            && (repr->grp_maybe_modified
+                                || repr->grp_not_necessarilly_dereferenced));
+
            }
-         sra_insert_before (bsi, newstmt);
-         replacement = tmp;
        }
-      if (is_output)
-         mark_all_v_defs (stmt);
-      *expr_p = REPLDUP (replacement);
-      update_stmt (stmt);
     }
-  else if (use_all && is_output
-          && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
-          && TREE_CODE (bfexpr
-                        = GIMPLE_STMT_OPERAND (stmt, 0)) == BIT_FIELD_REF
-          && &TREE_OPERAND (bfexpr, 0) == expr_p
-          && INTEGRAL_TYPE_P (TREE_TYPE (bfexpr))
-          && TREE_CODE (TREE_TYPE (*expr_p)) == RECORD_TYPE)
-    {
-      tree listbefore = NULL, listafter = NULL;
-      tree blen = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 1));
-      tree bpos = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 2));
-      bool update = false;
+  VEC_free (tree, heap, parms);
+  return adjustments;
+}
 
-      if (!elt->use_block_copy)
-       {
-         tree type = TREE_TYPE (bfexpr);
-         tree var = make_rename_temp (type, "SR"), tmp, st, vpos;
+/* Analyze the collected accesses and produce a plan what to do with the
+   parameters in the form of adjustments, NULL meaning nothing.  */
+
+static ipa_parm_adjustment_vec
+analyze_all_param_acesses (void)
+{
+  enum ipa_splicing_result repr_state;
+  bool proceed = false;
+  int i, adjustments_count = 0;
+  VEC (access_p, heap) *representatives;
+  ipa_parm_adjustment_vec adjustments;
+
+  repr_state = splice_all_param_accesses (&representatives);
+  if (repr_state == NO_GOOD_ACCESS)
+    return NULL;
+
+  /* If there are any parameters passed by reference which are not modified
+     directly, we need to check whether they can be modified indirectly.  */
+  if (repr_state == UNMODIF_BY_REF_ACCESSES)
+    {
+      analyze_caller_dereference_legality (representatives);
+      analyze_modified_params (representatives);
+    }
 
-         GIMPLE_STMT_OPERAND (stmt, 0) = var;
-         update = true;
+  for (i = 0; i < func_param_count; i++)
+    {
+      struct access *repr = VEC_index (access_p, representatives, i);
 
-         if (!TYPE_UNSIGNED (type))
+      if (repr && !no_accesses_p (repr))
+       {
+         if (repr->grp_scalar_ptr)
            {
-             type = unsigned_type_for (type);
-             tmp = make_rename_temp (type, "SR");
-             st = build_gimple_modify_stmt (tmp,
-                                            fold_convert (type, var));
-             append_to_statement_list (st, &listafter);
-             var = tmp;
+             adjustments_count++;
+             if (repr->grp_not_necessarilly_dereferenced
+                 || repr->grp_maybe_modified)
+               VEC_replace (access_p, representatives, i, NULL);
+             else
+               {
+                 proceed = true;
+                 sra_stats.scalar_by_ref_to_by_val++;
+               }
            }
-
-         /* 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, &listafter, blen, bpos, elt);
-       }
-      else
-       sra_sync_for_bitfield_assignment
-         (&listbefore, &listafter, blen, bpos, elt);
+           {
+             int new_components = decide_one_param_reduction (repr);
 
-      if (listbefore)
-       {
-         mark_all_v_defs (listbefore);
-         sra_insert_before (bsi, listbefore);
+             if (new_components == 0)
+               {
+                 VEC_replace (access_p, representatives, i, NULL);
+                 adjustments_count++;
+               }
+             else
+               {
+                 adjustments_count += new_components;
+                 sra_stats.aggregate_params_reduced++;
+                 sra_stats.param_reductions_created += new_components;
+                 proceed = true;
+               }
+           }
        }
-      if (listafter)
+      else
        {
-         mark_all_v_defs (listafter);
-         sra_insert_after (bsi, listafter);
+         if (no_accesses_p (repr))
+           {
+             proceed = true;
+             sra_stats.deleted_unused_parameters++;
+           }
+         adjustments_count++;
        }
-
-      if (update)
-       update_stmt (stmt);
     }
-  else if (use_all && !is_output
-          && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
-          && TREE_CODE (bfexpr
-                        = GIMPLE_STMT_OPERAND (stmt, 1)) == BIT_FIELD_REF
-          && &TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt, 1), 0) == expr_p
-          && INTEGRAL_TYPE_P (TREE_TYPE (bfexpr))
-          && TREE_CODE (TREE_TYPE (*expr_p)) == RECORD_TYPE)
-    {
-      tree list = 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, vpos;
 
-         if (!TYPE_UNSIGNED (type))
-           type = unsigned_type_for (type);
+  if (!proceed && dump_file)
+    fprintf (dump_file, "NOT proceeding to change params.\n");
 
-         var = make_rename_temp (type, "SR");
+  if (proceed)
+    adjustments = turn_representatives_into_adjustments (representatives,
+                                                        adjustments_count);
+  else
+    adjustments = NULL;
 
-         append_to_statement_list (build_gimple_modify_stmt
-                                   (var, build_int_cst_wide (type, 0, 0)),
-                                   &list);
+  VEC_free (access_p, heap, representatives);
+  return adjustments;
+}
 
-         /* 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, &list, blen, bpos, elt);
+/* If a parameter replacement identified by ADJ does not yet exist in the form
+   of declaration, create it and record it, otherwise return the previously
+   created one.  */
 
-         GIMPLE_STMT_OPERAND (stmt, 1) = var;
-         update = true;
-       }
-      else
-       sra_sync_for_bitfield_assignment
-         (&list, NULL, blen, bpos, elt);
+static tree
+get_replaced_param_substitute (struct ipa_parm_adjustment *adj)
+{
+  tree repl;
+  if (!adj->new_ssa_base)
+    {
+      char *pretty_name = make_fancy_name (adj->base);
 
-      if (list)
-       {
-         mark_all_v_defs (list);
-         sra_insert_before (bsi, list);
-       }
+      repl = create_tmp_var (TREE_TYPE (adj->base), "ISR");
+      if (TREE_CODE (TREE_TYPE (repl)) == COMPLEX_TYPE
+         || TREE_CODE (TREE_TYPE (repl)) == VECTOR_TYPE)
+       DECL_GIMPLE_REG_P (repl) = 1;
+      DECL_NAME (repl) = get_identifier (pretty_name);
+      obstack_free (&name_obstack, pretty_name);
 
-      if (update)
-       update_stmt (stmt);
+      get_var_ann (repl);
+      add_referenced_var (repl);
+      adj->new_ssa_base = repl;
     }
   else
-    {
-      tree list = 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), &list);
-      if (list == NULL)
-       return;
-      mark_all_v_defs (list);
-      if (is_output)
-       sra_insert_after (bsi, list);
-      else
-       {
-         sra_insert_before (bsi, list);
-         if (use_all)
-           mark_no_warning (elt);
-       }
-    }
+    repl = adj->new_ssa_base;
+  return repl;
 }
 
-/* Scalarize a COPY.  To recap, this is an assignment statement between
-   two scalarizable references, LHS_ELT and RHS_ELT.  */
+/* Find the first adjustment for a particular parameter BASE in a vector of
+   ADJUSTMENTS which is not a copy_param.  Return NULL if there is no such
+   adjustment. */
 
-static void
-scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
-               block_stmt_iterator *bsi)
+static struct ipa_parm_adjustment *
+get_adjustment_for_base (ipa_parm_adjustment_vec adjustments, tree base)
 {
-  tree list, stmt;
+  int i, len;
 
-  if (lhs_elt->replacement && rhs_elt->replacement)
+  len = VEC_length (ipa_parm_adjustment_t, adjustments);
+  for (i = 0; i < len; i++)
     {
-      /* If we have two scalar operands, modify the existing statement.  */
-      stmt = bsi_stmt (*bsi);
-
-      /* See the commentary in sra_walk_function concerning
-        RETURN_EXPR, and why we should never see one here.  */
-      gcc_assert (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT);
+      struct ipa_parm_adjustment *adj;
 
-      GIMPLE_STMT_OPERAND (stmt, 0) = lhs_elt->replacement;
-      GIMPLE_STMT_OPERAND (stmt, 1) = REPLDUP (rhs_elt->replacement);
-      update_stmt (stmt);
+      adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+      if (!adj->copy_param && adj->base == base)
+       return adj;
     }
-  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.  */
 
-      list = NULL;
-      generate_copy_inout (rhs_elt, false,
-                          generate_element_ref (rhs_elt), &list);
-      if (list)
-       {
-         mark_all_v_defs (list);
-         sra_insert_before (bsi, list);
-       }
+  return NULL;
+}
 
-      list = NULL;
-      generate_copy_inout (lhs_elt, true,
-                          generate_element_ref (lhs_elt), &list);
-      if (list)
-       {
-         mark_all_v_defs (list);
-         sra_insert_after (bsi, list);
-       }
-    }
+/* Callback for scan_function.  If the statement STMT defines an SSA_NAME of a
+   parameter which is to be removed because its value is not used, replace the
+   SSA_NAME with a one relating to a created VAR_DECL and replace all of its
+   uses too and return true (update_stmt is then issued for the statement by
+   the caller).  DATA is a pointer to an adjustments vector.  */
+
+static bool
+replace_removed_params_ssa_names (gimple stmt, void *data)
+{
+  VEC (ipa_parm_adjustment_t, heap) *adjustments;
+  struct ipa_parm_adjustment *adj;
+  tree lhs, decl, repl, name;
+
+  adjustments = (VEC (ipa_parm_adjustment_t, heap) *) data;
+  if (gimple_code (stmt) == GIMPLE_PHI)
+    lhs = gimple_phi_result (stmt);
+  else if (is_gimple_assign (stmt))
+    lhs = gimple_assign_lhs (stmt);
+  else if (is_gimple_call (stmt))
+    lhs = gimple_call_lhs (stmt);
   else
-    {
-      /* Otherwise both sides must be fully instantiated.  In which
-        case perform pair-wise element assignments and replace the
-        original block copy statement.  */
+    gcc_unreachable ();
+
+  if (TREE_CODE (lhs) != SSA_NAME)
+    return false;
+  decl = SSA_NAME_VAR (lhs);
+  if (TREE_CODE (decl) != PARM_DECL)
+    return false;
+
+  adj = get_adjustment_for_base (adjustments, decl);
+  if (!adj)
+    return false;
 
-      stmt = bsi_stmt (*bsi);
-      mark_all_v_defs (stmt);
+  repl = get_replaced_param_substitute (adj);
+  name = make_ssa_name (repl, stmt);
 
-      list = NULL;
-      generate_element_copy (lhs_elt, rhs_elt, &list);
-      gcc_assert (list);
-      mark_all_v_defs (list);
-      sra_replace (bsi, list);
+  if (dump_file)
+    {
+      fprintf (dump_file, "replacing an SSA name of a removed param ");
+      print_generic_expr (dump_file, lhs, 0);
+      fprintf (dump_file, " with ");
+      print_generic_expr (dump_file, name, 0);
+      fprintf (dump_file, "\n");
     }
+
+  if (is_gimple_assign (stmt))
+    gimple_assign_set_lhs (stmt, name);
+  else if (is_gimple_call (stmt))
+    gimple_call_set_lhs (stmt, name);
+  else
+    gimple_phi_set_result (stmt, name);
+
+  replace_uses_by (lhs, name);
+  return true;
 }
 
-/* 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.  */
+/* Callback for scan_function and helper to sra_ipa_modify_assign.  If the
+   expression *EXPR should be replaced by a reduction of a parameter, do so.
+   DATA is a pointer to a vector of adjustments.  DONT_CONVERT specifies
+   whether the function should care about type incompatibility the current and
+   new expressions.  If it is true, the function will leave incompatibility
+   issues to the caller.
 
-static void
-scalarize_init (struct sra_elt *lhs_elt, tree rhs, block_stmt_iterator *bsi)
+   When called directly by scan_function, DONT_CONVERT is true when the EXPR is
+   a write (LHS) expression.  */
+
+static bool
+sra_ipa_modify_expr (tree *expr, gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
+                    bool dont_convert, void *data)
 {
-  bool result = true;
-  tree list = NULL;
+  ipa_parm_adjustment_vec adjustments;
+  int i, len;
+  struct ipa_parm_adjustment *adj, *cand = NULL;
+  HOST_WIDE_INT offset, size, max_size;
+  tree base, src;
+
+  adjustments = (VEC (ipa_parm_adjustment_t, heap) *) data;
+  len = VEC_length (ipa_parm_adjustment_t, adjustments);
 
-  /* Generate initialization statements for all members extant in the RHS.  */
-  if (rhs)
+  if (TREE_CODE (*expr) == BIT_FIELD_REF
+      || TREE_CODE (*expr) == IMAGPART_EXPR
+      || TREE_CODE (*expr) == REALPART_EXPR)
     {
-      /* Unshare the expression just in case this is from a decl's initial.  */
-      rhs = unshare_expr (rhs);
-      result = generate_element_init (lhs_elt, rhs, &list);
+      expr = &TREE_OPERAND (*expr, 0);
+      dont_convert = false;
     }
 
-  /* CONSTRUCTOR is defined such that any member not mentioned is assigned
-     a zero value.  Initialize the rest of the instantiated elements.  */
-  generate_element_zero (lhs_elt, &list);
+  base = get_ref_base_and_extent (*expr, &offset, &size, &max_size);
+  if (!base || size == -1 || max_size == -1)
+    return false;
 
-  if (!result)
-    {
-      /* If we failed to convert the entire initializer, then we must
-        leave the structure assignment in place and must load values
-        from the structure into the slots for which we did not find
-        constants.  The easiest way to do this is to generate a complete
-        copy-out, and then follow that with the constant assignments
-        that we were able to build.  DCE will clean things up.  */
-      tree list0 = NULL;
-      generate_copy_inout (lhs_elt, true, generate_element_ref (lhs_elt),
-                          &list0);
-      append_to_statement_list (list, &list0);
-      list = list0;
-    }
+  if (INDIRECT_REF_P (base))
+    base = TREE_OPERAND (base, 0);
+
+  base = get_ssa_base_param (base);
+  if (!base || TREE_CODE (base) != PARM_DECL)
+    return false;
 
-  if (lhs_elt->use_block_copy || !result)
+  for (i = 0; i < len; i++)
     {
-      /* 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 (list)
+      adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+
+      if (adj->base == base &&
+         (adj->offset == offset || adj->remove_param))
        {
-         mark_all_v_defs (list);
-         sra_insert_after (bsi, list);
+         cand = adj;
+         break;
        }
     }
-  else
+  if (!cand || cand->copy_param || cand->remove_param)
+    return false;
+
+  if (cand->by_ref)
     {
-      /* The LHS is fully instantiated.  The list of initializations
-        replaces the original structure assignment.  */
-      gcc_assert (list);
-      mark_all_v_defs (bsi_stmt (*bsi));
-      mark_all_v_defs (list);
-      sra_replace (bsi, list);
+      tree folded;
+      src = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (cand->reduction)),
+                   cand->reduction);
+      folded = gimple_fold_indirect_ref (src);
+      if (folded)
+        src = folded;
     }
-}
-
-/* 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;
+  else
+    src = cand->reduction;
 
-  if (TREE_CODE (t) == INDIRECT_REF)
+  if (dump_file && (dump_flags & TDF_DETAILS))
     {
-      TREE_THIS_NOTRAP (t) = 1;
-      *walk_subtrees = 0;
+      fprintf (dump_file, "About to replace expr ");
+      print_generic_expr (dump_file, *expr, 0);
+      fprintf (dump_file, " with ");
+      print_generic_expr (dump_file, src, 0);
+      fprintf (dump_file, "\n");
     }
-  else if (IS_TYPE_OR_DECL_P (t))
-    *walk_subtrees = 0;
 
-  return NULL;
+  if (!dont_convert
+      && !useless_type_conversion_p (TREE_TYPE (*expr), cand->type))
+    {
+      tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src);
+      *expr = vce;
+    }
+  else
+    *expr = src;
+  return true;
 }
 
-/* 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.  */
+/* Callback for scan_function to process assign statements.  Performs
+   essentially the same function like sra_ipa_modify_expr.  */
 
-static void
-scalarize_ldst (struct sra_elt *elt, tree other,
-               block_stmt_iterator *bsi, bool is_output)
+static enum scan_assign_result
+sra_ipa_modify_assign (gimple *stmt_ptr, gimple_stmt_iterator *gsi, void *data)
 {
-  /* Shouldn't have gotten called for a scalar.  */
-  gcc_assert (!elt->replacement);
+  gimple stmt = *stmt_ptr;
+  tree *lhs_p, *rhs_p;
+  bool any;
 
-  if (elt->use_block_copy)
-    {
-      /* Since ELT is not fully instantiated, we have to leave the
-        block copy in place.  Treat this as a USE.  */
-      scalarize_use (elt, NULL, bsi, is_output, 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.  */
+  if (!gimple_assign_single_p (stmt))
+    return SRA_SA_NONE;
 
-      tree list = NULL, stmt = bsi_stmt (*bsi);
+  rhs_p = gimple_assign_rhs1_ptr (stmt);
+  lhs_p = gimple_assign_lhs_ptr (stmt);
 
-      mark_all_v_defs (stmt);
-      generate_copy_inout (elt, is_output, other, &list);
-      gcc_assert (list);
-      mark_all_v_defs (list);
+  any = sra_ipa_modify_expr (rhs_p, gsi, true, data);
+  any |= sra_ipa_modify_expr (lhs_p, gsi, true, data);
+  if (any)
+    {
+      tree new_rhs = NULL_TREE;
+
+      if (!useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p)))
+       new_rhs = fold_build1_loc (gimple_location (stmt), VIEW_CONVERT_EXPR,
+                                  TREE_TYPE (*lhs_p), *rhs_p);
+      else if (REFERENCE_CLASS_P (*rhs_p)
+              && is_gimple_reg_type (TREE_TYPE (*lhs_p))
+              && !is_gimple_reg (*lhs_p))
+       /* This can happen when an assignment in between two single field
+          structures is turned into an assignment in between two pointers to
+          scalars (PR 42237).  */
+       new_rhs = *rhs_p;
 
-      /* Preserve EH semantics.  */
-      if (stmt_ends_bb_p (stmt))
+      if (new_rhs)
        {
-         tree_stmt_iterator tsi;
-         tree first, blist = NULL;
-         bool thr = tree_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.  */
-         tsi = tsi_start (list);
-         for (first = tsi_stmt (tsi);
-              thr && !tsi_end_p (tsi) && !tree_could_throw_p (first);
-              first = tsi_stmt (tsi))
-           {
-             tsi_delink (&tsi);
-             append_to_statement_list (first, &blist);
-           }
+         tree tmp = force_gimple_operand_gsi (gsi, new_rhs, true, NULL_TREE,
+                                              true, GSI_SAME_STMT);
 
-         /* Extract the first remaining statement from LIST, this is
-            the EH statement if there is one.  */
-         tsi_delink (&tsi);
+         gimple_assign_set_rhs_from_tree (gsi, tmp);
+       }
 
-         if (blist)
-           sra_insert_before (bsi, blist);
+      return SRA_SA_PROCESSED;
+    }
 
-         /* Replace the old statement with this new representative.  */
-         bsi_replace (bsi, first, true);
+  return SRA_SA_NONE;
+}
 
-         if (!tsi_end_p (tsi))
-           {
-             /* 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_tree (tsi_stmt_ptr (tsi), mark_notrap, NULL, NULL);
-                 tsi_next (&tsi);
-               }
-             while (!tsi_end_p (tsi));
+/* Call gimple_debug_bind_reset_value on all debug statements describing
+   gimple register parameters that are being removed or replaced.  */
 
-             insert_edge_copies (list, bsi->bb);
-           }
+static void
+sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
+{
+  int i, len;
+
+  len = VEC_length (ipa_parm_adjustment_t, adjustments);
+  for (i = 0; i < len; i++)
+    {
+      struct ipa_parm_adjustment *adj;
+      imm_use_iterator ui;
+      gimple stmt;
+      tree name;
+
+      adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+      if (adj->copy_param || !is_gimple_reg (adj->base))
+       continue;
+      name = gimple_default_def (cfun, adj->base);
+      if (!name)
+       continue;
+      FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+       {
+         /* All other users must have been removed by scan_function.  */
+         gcc_assert (is_gimple_debug (stmt));
+         gimple_debug_bind_reset_value (stmt);
+         update_stmt (stmt);
        }
-      else
-       sra_replace (bsi, list);
     }
 }
 
-/* Generate initializations for all scalarizable parameters.  */
+/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS.  */
 
 static void
-scalarize_parms (void)
+convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
 {
-  tree list = NULL;
-  unsigned i;
-  bitmap_iterator bi;
+  tree old_cur_fndecl = current_function_decl;
+  struct cgraph_edge *cs;
+  basic_block this_block;
+  bitmap recomputed_callers = BITMAP_ALLOC (NULL);
 
-  EXECUTE_IF_SET_IN_BITMAP (needs_copy_in, 0, i, bi)
+  for (cs = node->callers; cs; cs = cs->next_caller)
     {
-      tree var = referenced_var (i);
-      struct sra_elt *elt = lookup_element (NULL, var, NULL, NO_INSERT);
-      generate_copy_inout (elt, true, var, &list);
+      current_function_decl = cs->caller->decl;
+      push_cfun (DECL_STRUCT_FUNCTION (cs->caller->decl));
+
+      if (dump_file)
+       fprintf (dump_file, "Adjusting call (%i -> %i) %s -> %s\n",
+                cs->caller->uid, cs->callee->uid,
+                cgraph_node_name (cs->caller),
+                cgraph_node_name (cs->callee));
+
+      ipa_modify_call_arguments (cs, cs->call_stmt, adjustments);
+
+      pop_cfun ();
     }
 
-  if (list)
+  for (cs = node->callers; cs; cs = cs->next_caller)
+    if (!bitmap_bit_p (recomputed_callers, cs->caller->uid))
+      {
+       compute_inline_parameters (cs->caller);
+       bitmap_set_bit (recomputed_callers, cs->caller->uid);
+      }
+  BITMAP_FREE (recomputed_callers);
+
+  current_function_decl = old_cur_fndecl;
+  FOR_EACH_BB (this_block)
     {
-      insert_edge_copies (list, ENTRY_BLOCK_PTR);
-      mark_all_v_defs (list);
+      gimple_stmt_iterator gsi;
+
+      for (gsi = gsi_start_bb (this_block); !gsi_end_p (gsi); gsi_next (&gsi))
+        {
+         gimple stmt = gsi_stmt (gsi);
+         if (gimple_code (stmt) == GIMPLE_CALL
+             && gimple_call_fndecl (stmt) == node->decl)
+           {
+             if (dump_file)
+               fprintf (dump_file, "Adjusting recursive call");
+             ipa_modify_call_arguments (NULL, stmt, adjustments);
+           }
+       }
     }
+
+  return;
 }
 
-/* Entry point to phase 4.  Update the function to match replacements.  */
+/* Perform all the modification required in IPA-SRA for NODE to have parameters
+   as given in ADJUSTMENTS.  */
 
 static void
-scalarize_function (void)
+modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
 {
-  static const struct sra_walk_fns fns = {
-    scalarize_use, scalarize_copy, scalarize_init, scalarize_ldst, false
-  };
-
-  sra_walk_function (&fns);
-  scalarize_parms ();
-  bsi_commit_edge_inserts ();
+  ipa_modify_formal_parameters (current_function_decl, adjustments, "ISRA");
+  scan_function (sra_ipa_modify_expr, sra_ipa_modify_assign,
+                replace_removed_params_ssa_names, false, adjustments);
+  sra_ipa_reset_debug_stmts (adjustments);
+  convert_callers (node, adjustments);
+  cgraph_make_node_local (node);
+  return;
 }
 
-\f
-/* Debug helper function.  Print ELT in a nice human-readable format.  */
+/* Return false the function is apparently unsuitable for IPA-SRA based on it's
+   attributes, return true otherwise.  NODE is the cgraph node of the current
+   function.  */
 
-static void
-dump_sra_elt_name (FILE *f, struct sra_elt *elt)
+static bool
+ipa_sra_preliminary_function_checks (struct cgraph_node *node)
 {
-  if (elt->parent && TREE_CODE (elt->parent->type) == COMPLEX_TYPE)
+  if (!cgraph_node_can_be_local_p (node))
     {
-      fputs (elt->element == integer_zero_node ? "__real__ " : "__imag__ ", f);
-      dump_sra_elt_name (f, elt->parent);
+      if (dump_file)
+       fprintf (dump_file, "Function not local to this compilation unit.\n");
+      return false;
     }
-  else
+
+  if (DECL_VIRTUAL_P (current_function_decl))
     {
-      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));
+      if (dump_file)
+       fprintf (dump_file, "Function is a virtual method.\n");
+      return false;
     }
-}
 
-/* Likewise, but callable from the debugger.  */
+  if ((DECL_COMDAT (node->decl) || DECL_EXTERNAL (node->decl))
+      && node->global.size >= MAX_INLINE_INSNS_AUTO)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function too big to be made truly local.\n");
+      return false;
+    }
 
-void
-debug_sra_elt_name (struct sra_elt *elt)
-{
-  dump_sra_elt_name (stderr, elt);
-  fputc ('\n', stderr);
-}
+  if (!node->callers)
+    {
+      if (dump_file)
+       fprintf (dump_file,
+                "Function has no callers in this compilation unit.\n");
+      return false;
+    }
 
-void 
-sra_init_cache (void)
-{
-  if (sra_type_decomp_cache) 
-    return;
+  if (cfun->stdarg)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function uses stdarg. \n");
+      return false;
+    }
 
-  sra_type_decomp_cache = BITMAP_ALLOC (NULL);
-  sra_type_inst_cache = BITMAP_ALLOC (NULL);
+  return true;
 }
 
-/* Main entry point.  */
+/* Perform early interprocedural SRA.  */
 
 static unsigned int
-tree_sra (void)
+ipa_early_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);
+  struct cgraph_node *node = cgraph_node (current_function_decl);
+  ipa_parm_adjustment_vec adjustments;
+  int ret = 0;
+
+  if (!ipa_sra_preliminary_function_checks (node))
+    return 0;
 
-  /* Scan.  If we find anything, instantiate and scalarize.  */
-  if (find_candidates_for_sra ())
+  sra_initialize ();
+  sra_mode = SRA_MODE_EARLY_IPA;
+
+  if (!find_param_candidates ())
     {
-      scan_function ();
-      decide_instantiations ();
-      scalarize_function ();
-      if (!bitmap_empty_p (sra_candidates))
-       todoflags |= TODO_rebuild_alias;
+      if (dump_file)
+       fprintf (dump_file, "Function has no IPA-SRA candidates.\n");
+      goto simple_out;
     }
 
-  /* 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;
+  bb_dereferences = XCNEWVEC (HOST_WIDE_INT,
+                                func_param_count
+                                * last_basic_block_for_function (cfun));
+  final_bbs = BITMAP_ALLOC (NULL);
 
-  early_sra = true;
-  ret = tree_sra ();
-  early_sra = false;
+  scan_function (build_access_from_expr, build_accesses_from_assign,
+                NULL, true, NULL);
+  if (encountered_apply_args)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function calls  __builtin_apply_args().\n");
+      goto out;
+    }
 
-  return ret & ~TODO_rebuild_alias;
+  adjustments = analyze_all_param_acesses ();
+  if (!adjustments)
+    goto out;
+  if (dump_file)
+    ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl);
+
+  modify_function (node, adjustments);
+  VEC_free (ipa_parm_adjustment_t, heap, adjustments);
+  ret = TODO_update_ssa;
+
+  statistics_counter_event (cfun, "Unused parameters deleted",
+                           sra_stats.deleted_unused_parameters);
+  statistics_counter_event (cfun, "Scalar parameters converted to by-value",
+                           sra_stats.scalar_by_ref_to_by_val);
+  statistics_counter_event (cfun, "Aggregate parameters broken up",
+                           sra_stats.aggregate_params_reduced);
+  statistics_counter_event (cfun, "Aggregate parameter components created",
+                           sra_stats.param_reductions_created);
+
+ out:
+  BITMAP_FREE (final_bbs);
+  free (bb_dereferences);
+ simple_out:
+  sra_deinitialize ();
+  return ret;
 }
 
+/* Return if early ipa sra shall be performed.  */
 static bool
-gate_sra (void)
+ipa_early_sra_gate (void)
 {
-  return flag_tree_sra != 0;
+  return flag_ipa_sra;
 }
 
-struct tree_opt_pass pass_sra_early =
+struct gimple_opt_pass pass_early_ipa_sra =
 {
-  "esra",                              /* name */
-  gate_sra,                            /* gate */
-  tree_sra_early,                      /* execute */
+ {
+  GIMPLE_PASS,
+  "eipa_sra",                          /* name */
+  ipa_early_sra_gate,                  /* gate */
+  ipa_early_sra,                       /* execute */
   NULL,                                        /* sub */
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
-  TV_TREE_SRA,                         /* tv_id */
-  PROP_cfg | PROP_ssa,                 /* properties_required */
+  TV_IPA_SRA,                          /* tv_id */
+  0,                                   /* properties_required */
   0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
+  0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func
-  | TODO_update_ssa
-  | TODO_ggc_collect
-  | TODO_verify_ssa,                   /* todo_flags_finish */
-  0                                    /* letter */
+  TODO_dump_func | TODO_dump_cgraph    /* todo_flags_finish */
+ }
 };
 
-struct tree_opt_pass pass_sra =
-{
-  "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 */
-  0                                    /* letter */
-};
+