OSDN Git Service

* common.opt (debug_struct_ordinary, debug_struct_generic): New
[pf3gnuchains/gcc-fork.git] / gcc / tree-sra.c
index a86a441..252fa06 100644 (file)
@@ -1,7 +1,7 @@
 /* Scalar Replacement of Aggregates (SRA) converts some structure
    references into scalar references, exposing them to the scalar
    optimizers.
-   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
    Contributed by Martin Jambor <mjambor@suse.cz>
 
 This file is part of GCC.
@@ -76,18 +76,22 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "alloc-pool.h"
 #include "tm.h"
+#include "toplev.h"
 #include "tree.h"
 #include "gimple.h"
 #include "cgraph.h"
 #include "tree-flow.h"
 #include "ipa-prop.h"
-#include "diagnostic.h"
+#include "tree-pretty-print.h"
 #include "statistics.h"
 #include "tree-dump.h"
 #include "timevar.h"
 #include "params.h"
 #include "target.h"
 #include "flags.h"
+#include "dbgcnt.h"
+#include "tree-inline.h"
+#include "gimple-pretty-print.h"
 
 /* Enumeration of all aggregate reductions we can do.  */
 enum sra_mode { SRA_MODE_EARLY_IPA,   /* early call regularization */
@@ -125,7 +129,9 @@ struct access
   HOST_WIDE_INT size;
   tree base;
 
-  /* Expression.  */
+  /* 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;
@@ -144,7 +150,9 @@ struct access
      points to the first one.  */
   struct access *first_child;
 
-  /* Pointer to the next sibling in the access tree as described above.  */
+  /* 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;
 
   /* Pointers to the first and last element in the linked list of assign
@@ -162,6 +170,10 @@ struct access
   /* Is this particular access write access? */
   unsigned write : 1;
 
+  /* Is this access an artificial one created to scalarize some record
+     entirely? */
+  unsigned total_scalarization : 1;
+
   /* Is this access currently in the work queue?  */
   unsigned grp_queued : 1;
 
@@ -173,6 +185,14 @@ struct access
      access tree.  */
   unsigned grp_read : 1;
 
+  /* Does this group contain a read access that comes from an assignment
+     statement?  This flag is propagated down the access tree.  */
+  unsigned grp_assignment_read : 1;
+
+  /* Does this group contain a write access that comes from an assignment
+     statement?  This flag is propagated down the access tree.  */
+  unsigned grp_assignment_write : 1;
+
   /* Other passes of the analysis use this bit to make function
      analyze_access_subtree create scalar replacements for this group if
      possible.  */
@@ -200,6 +220,9 @@ struct access
      cannot be called from within FOR_EACH_REFERENCED_VAR. */
   unsigned grp_to_be_replaced : 1;
 
+  /* Should TREE_NO_WARNING of a replacement be set?  */
+  unsigned grp_no_warning : 1;
+
   /* Is it possible that the group refers to data which might be (directly or
      otherwise) modified?  */
   unsigned grp_maybe_modified : 1;
@@ -240,6 +263,10 @@ static struct pointer_map_t *base_access_vec;
 /* Bitmap of candidates.  */
 static bitmap candidate_bitmap;
 
+/* Bitmap of candidates which we should try to entirely scalarize away and
+   those which cannot be (because they are and need be used as a whole).  */
+static bitmap should_scalarize_away_bitmap, cannot_scalarize_away_bitmap;
+
 /* Obstack for creation of fancy names.  */
 static struct obstack name_obstack;
 
@@ -254,6 +281,13 @@ static int func_param_count;
    __builtin_apply_args.  */
 static bool encountered_apply_args;
 
+/* Set by scan_function when it finds a recursive call.  */
+static bool encountered_recursive_call;
+
+/* Set by scan_function when it finds a recursive call with less actual
+   arguments than formal parameters..  */
+static bool encountered_unchangable_recursive_call;
+
 /* 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.  */
@@ -332,18 +366,24 @@ dump_access (FILE *f, struct access *access, bool grp)
   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_to_be_replaced = %d\n grp_maybe_modified = %d, "
+    fprintf (f, ", grp_write = %d, total_scalarization = %d, "
+            "grp_read = %d, grp_hint = %d, grp_assignment_read = %d,"
+            "grp_assignment_write = %d, grp_covered = %d, "
+            "grp_unscalarizable_region = %d, grp_unscalarized_data = %d, "
+            "grp_partial_lhs = %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_to_be_replaced, access->grp_maybe_modified,
+            access->grp_write, access->total_scalarization,
+            access->grp_read, access->grp_hint, access->grp_assignment_read,
+            access->grp_assignment_write, access->grp_covered,
+            access->grp_unscalarizable_region, access->grp_unscalarized_data,
+            access->grp_partial_lhs, 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,
+    fprintf (f, ", write = %d, total_scalarization = %d, "
+            "grp_partial_lhs = %d\n",
+            access->write, access->total_scalarization,
             access->grp_partial_lhs);
 }
 
@@ -535,12 +575,16 @@ static void
 sra_initialize (void)
 {
   candidate_bitmap = BITMAP_ALLOC (NULL);
+  should_scalarize_away_bitmap = BITMAP_ALLOC (NULL);
+  cannot_scalarize_away_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;
+  encountered_recursive_call = false;
+  encountered_unchangable_recursive_call = false;
 }
 
 /* Hook fed to pointer_map_traverse, deallocate stored vectors.  */
@@ -562,6 +606,8 @@ static void
 sra_deinitialize (void)
 {
   BITMAP_FREE (candidate_bitmap);
+  BITMAP_FREE (should_scalarize_away_bitmap);
+  BITMAP_FREE (cannot_scalarize_away_bitmap);
   free_alloc_pool (access_pool);
   free_alloc_pool (link_pool);
   obstack_free (&name_obstack, NULL);
@@ -599,7 +645,7 @@ type_internals_preclude_sra_p (tree type)
     case RECORD_TYPE:
     case UNION_TYPE:
     case QUAL_UNION_TYPE:
-      for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+      for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
        if (TREE_CODE (fld) == FIELD_DECL)
          {
            tree ft = TREE_TYPE (fld);
@@ -607,7 +653,8 @@ type_internals_preclude_sra_p (tree type)
            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))
+               || !host_integerp (DECL_SIZE (fld), 1)
+               || (DECL_BIT_FIELD (fld) && AGGREGATE_TYPE_P (ft)))
              return true;
 
            if (AGGREGATE_TYPE_P (ft)
@@ -662,7 +709,7 @@ mark_parm_dereference (tree base, HOST_WIDE_INT dist, gimple stmt)
 
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm && parm != base;
-       parm = TREE_CHAIN (parm))
+       parm = DECL_CHAIN (parm))
     parm_index++;
 
   gcc_assert (parm_index < func_param_count);
@@ -672,6 +719,37 @@ mark_parm_dereference (tree base, HOST_WIDE_INT dist, gimple stmt)
     bb_dereferences[idx] = dist;
 }
 
+/* Allocate an access structure for BASE, OFFSET and SIZE, clear it, fill in
+   the three fields.  Also add it to the vector of accesses corresponding to
+   the base.  Finally, return the new access.  */
+
+static struct access *
+create_access_1 (tree base, HOST_WIDE_INT offset, HOST_WIDE_INT size)
+{
+  VEC (access_p, heap) *vec;
+  struct access *access;
+  void **slot;
+
+  access = (struct access *) pool_alloc (access_pool);
+  memset (access, 0, sizeof (struct access));
+  access->base = base;
+  access->offset = offset;
+  access->size = size;
+
+  slot = pointer_map_contains (base_access_vec, base);
+  if (slot)
+    vec = (VEC (access_p, heap) *) *slot;
+  else
+    vec = VEC_alloc (access_p, heap, 32);
+
+  VEC_safe_push (access_p, heap, vec, access);
+
+  *((struct VEC (access_p,heap) **)
+       pointer_map_insert (base_access_vec, base)) = vec;
+
+  return access;
+}
+
 /* Create and insert access for EXPR. Return created access, or NULL if it is
    not possible.  */
 
@@ -679,15 +757,14 @@ 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;
 
   base = get_ref_base_and_extent (expr, &offset, &size, &max_size);
 
-  if (sra_mode == SRA_MODE_EARLY_IPA && INDIRECT_REF_P (base))
+  if (sra_mode == SRA_MODE_EARLY_IPA
+      && TREE_CODE (base) == MEM_REF)
     {
       base = get_ssa_base_param (TREE_OPERAND (base, 0));
       if (!base)
@@ -707,12 +784,13 @@ create_access (tree expr, gimple stmt, bool write)
          disqualify_candidate (base, "Encountered a variable sized access.");
          return NULL;
        }
-      if ((offset % BITS_PER_UNIT) != 0 || (size % BITS_PER_UNIT) != 0)
+      if (TREE_CODE (expr) == COMPONENT_REF
+         && DECL_BIT_FIELD (TREE_OPERAND (expr, 1)))
        {
-         disqualify_candidate (base,
-                               "Encountered an acces not aligned to a byte.");
+         disqualify_candidate (base, "Encountered a bit-field access.");
          return NULL;
        }
+      gcc_checking_assert ((offset % BITS_PER_UNIT) == 0);
 
       if (ptr)
        mark_parm_dereference (base, offset + size, stmt);
@@ -731,30 +809,80 @@ create_access (tree expr, gimple stmt, bool write)
        }
     }
 
-  access = (struct access *) pool_alloc (access_pool);
-  memset (access, 0, sizeof (struct access));
-
-  access->base = base;
-  access->offset = offset;
-  access->size = size;
+  access = create_access_1 (base, offset, size);
   access->expr = expr;
   access->type = TREE_TYPE (expr);
   access->write = write;
   access->grp_unscalarizable_region = unscalarizable_region;
   access->stmt = stmt;
 
-  slot = pointer_map_contains (base_access_vec, base);
-  if (slot)
-    vec = (VEC (access_p, heap) *) *slot;
-  else
-    vec = VEC_alloc (access_p, heap, 32);
+  return access;
+}
 
-  VEC_safe_push (access_p, heap, vec, access);
 
-  *((struct VEC (access_p,heap) **)
-       pointer_map_insert (base_access_vec, base)) = vec;
+/* Return true iff TYPE is a RECORD_TYPE with fields that are either of gimple
+   register types or (recursively) records with only these two kinds of fields.
+   It also returns false if any of these records contains a bit-field.  */
 
-  return access;
+static bool
+type_consists_of_records_p (tree type)
+{
+  tree fld;
+
+  if (TREE_CODE (type) != RECORD_TYPE)
+    return false;
+
+  for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
+    if (TREE_CODE (fld) == FIELD_DECL)
+      {
+       tree ft = TREE_TYPE (fld);
+
+       if (DECL_BIT_FIELD (fld))
+         return false;
+
+       if (!is_gimple_reg_type (ft)
+           && !type_consists_of_records_p (ft))
+         return false;
+      }
+
+  return true;
+}
+
+/* Create total_scalarization accesses for all scalar type fields in DECL that
+   must be of a RECORD_TYPE conforming to type_consists_of_records_p.  BASE
+   must be the top-most VAR_DECL representing the variable, OFFSET must be the
+   offset of DECL within BASE.  REF must be the memory reference expression for
+   the given decl.  */
+
+static void
+completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset,
+                            tree ref)
+{
+  tree fld, decl_type = TREE_TYPE (decl);
+
+  for (fld = TYPE_FIELDS (decl_type); fld; fld = DECL_CHAIN (fld))
+    if (TREE_CODE (fld) == FIELD_DECL)
+      {
+       HOST_WIDE_INT pos = offset + int_bit_position (fld);
+       tree ft = TREE_TYPE (fld);
+       tree nref = build3 (COMPONENT_REF, TREE_TYPE (fld), ref, fld,
+                           NULL_TREE);
+
+       if (is_gimple_reg_type (ft))
+         {
+           struct access *access;
+           HOST_WIDE_INT size;
+
+           size = tree_low_cst (DECL_SIZE (fld), 1);
+           access = create_access_1 (base, pos, size);
+           access->expr = nref;
+           access->type = ft;
+           access->total_scalarization = 1;
+           /* Accesses for intraprocedural SRA can have their stmt NULL.  */
+         }
+       else
+         completely_scalarize_record (base, fld, pos, nref);
+      }
 }
 
 
@@ -764,15 +892,10 @@ create_access (tree expr, gimple stmt, bool write)
 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);
-    }
+  t = get_base_address (t);
+  if (sra_mode == SRA_MODE_EARLY_IPA
+      && TREE_CODE (t) == MEM_REF)
+    t = get_ssa_base_param (TREE_OPERAND (t, 0));
 
   if (t && DECL_P (t))
     disqualify_candidate (t, reason);
@@ -783,10 +906,9 @@ disqualify_base_of_expr (tree t, const char *reason)
    created.  */
 
 static struct access *
-build_access_from_expr_1 (tree *expr_ptr, gimple stmt, bool write)
+build_access_from_expr_1 (tree expr, gimple stmt, bool write)
 {
   struct access *ret = NULL;
-  tree expr = *expr_ptr;
   bool partial_ref;
 
   if (TREE_CODE (expr) == BIT_FIELD_REF
@@ -815,8 +937,9 @@ build_access_from_expr_1 (tree *expr_ptr, gimple stmt, bool write)
 
   switch (TREE_CODE (expr))
     {
-    case INDIRECT_REF:
-      if (sra_mode != SRA_MODE_EARLY_IPA)
+    case MEM_REF:
+      if (TREE_CODE (TREE_OPERAND (expr, 0)) != ADDR_EXPR
+         && sra_mode != SRA_MODE_EARLY_IPA)
        return NULL;
       /* fall through */
     case VAR_DECL:
@@ -838,16 +961,27 @@ build_access_from_expr_1 (tree *expr_ptr, gimple stmt, bool write)
   return ret;
 }
 
-/* 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.  */
+/* Scan expression EXPR and create access structures for all accesses to
+   candidates for scalarization.  Return true if any access has been inserted.
+   STMT must be the statement from which the expression is taken, WRITE must be
+   true if the expression is a store and false otherwise. */
 
 static bool
-build_access_from_expr (tree *expr_ptr,
-                       gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED, bool write,
-                       void *data ATTRIBUTE_UNUSED)
+build_access_from_expr (tree expr, gimple stmt, bool write)
 {
-  return build_access_from_expr_1 (expr_ptr, gsi_stmt (*gsi), write) != NULL;
+  struct access *access;
+
+  access = build_access_from_expr_1 (expr, stmt, write);
+  if (access)
+    {
+      /* This means the aggregate is accesses as a whole in a way other than an
+        assign statement and thus cannot be removed even if we had a scalar
+        replacement for everything.  */
+      if (cannot_scalarize_away_bitmap)
+       bitmap_set_bit (cannot_scalarize_away_bitmap, DECL_UID (access->base));
+      return true;
+    }
+  return false;
 }
 
 /* Disqualify LHS and RHS for scalarization if STMT must end its basic block in
@@ -869,45 +1003,45 @@ disqualify_ops_if_throwing_stmt (gimple stmt, tree lhs, tree rhs)
   return false;
 }
 
-
-/* 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
+/* Scan expressions occuring in STMT, 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)
+static bool
+build_accesses_from_assign (gimple stmt)
 {
-  gimple stmt = *stmt_ptr;
-  tree *lhs_ptr, *rhs_ptr;
+  tree lhs, rhs;
   struct access *lacc, *racc;
 
   if (!gimple_assign_single_p (stmt))
-    return SRA_SA_NONE;
+    return false;
+
+  lhs = gimple_assign_lhs (stmt);
+  rhs = gimple_assign_rhs1 (stmt);
 
-  lhs_ptr = gimple_assign_lhs_ptr (stmt);
-  rhs_ptr = gimple_assign_rhs1_ptr (stmt);
+  if (disqualify_ops_if_throwing_stmt (stmt, lhs, rhs))
+    return false;
+
+  racc = build_access_from_expr_1 (rhs, stmt, false);
+  lacc = build_access_from_expr_1 (lhs, stmt, true);
 
-  if (disqualify_ops_if_throwing_stmt (stmt, *lhs_ptr, *rhs_ptr))
-    return SRA_SA_NONE;
+  if (lacc)
+    lacc->grp_assignment_write = 1;
 
-  racc = build_access_from_expr_1 (rhs_ptr, stmt, false);
-  lacc = build_access_from_expr_1 (lhs_ptr, stmt, true);
+  if (racc)
+    {
+      racc->grp_assignment_read = 1;
+      if (should_scalarize_away_bitmap && !gimple_has_volatile_ops (stmt)
+         && !is_gimple_reg_type (racc->type))
+       bitmap_set_bit (should_scalarize_away_bitmap, DECL_UID (racc->base));
+    }
 
   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))
+      && AGGREGATE_TYPE_P (TREE_TYPE (lhs))
       /* FIXME: Turn the following line into an assert after PR 40058 is
         fixed.  */
       && lacc->size == racc->size
@@ -924,7 +1058,7 @@ build_accesses_from_assign (gimple *stmt_ptr,
       add_link_to_rhs (racc, link);
     }
 
-  return (lacc || racc) ? SRA_SA_PROCESSED : SRA_SA_NONE;
+  return lacc || racc;
 }
 
 /* Callback of walk_stmt_load_store_addr_ops visit_addr used to determine
@@ -934,156 +1068,113 @@ static bool
 asm_visit_addr (gimple stmt ATTRIBUTE_UNUSED, tree op,
                void *data ATTRIBUTE_UNUSED)
 {
-  if (DECL_P (op))
+  op = get_base_address (op);
+  if (op
+      && DECL_P (op))
     disqualify_candidate (op, "Non-scalarizable GIMPLE_ASM operand.");
 
   return false;
 }
 
+/* Return true iff callsite CALL has at least as many actual arguments as there
+   are formal parameters of the function currently processed by IPA-SRA.  */
+
+static inline bool
+callsite_has_enough_arguments_p (gimple call)
+{
+  return gimple_call_num_args (call) >= (unsigned) func_param_count;
+}
 
-/* 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.  */
+/* Scan function and look for interesting expressions and create access
+   structures for them.  Return true iff any access is created.  */
 
 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)
+scan_function (void)
 {
-  gimple_stmt_iterator gsi;
   basic_block bb;
-  unsigned i;
-  tree *t;
   bool ret = false;
 
   FOR_EACH_BB (bb)
     {
-      bool bb_changed = false;
-
-      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))
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
        {
          gimple stmt = gsi_stmt (gsi);
-         enum scan_assign_result assign_result;
-         bool any = false, deleted = false;
+         tree t;
+         unsigned i;
 
-         if (analysis_stage && final_bbs && stmt_can_throw_external (stmt))
+         if (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)
+             t = gimple_return_retval (stmt);
+             if (t != NULL_TREE)
+               ret |= build_access_from_expr (t, stmt, false);
+             if (final_bbs)
                bitmap_set_bit (final_bbs, bb->index);
              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);
+             ret |= build_accesses_from_assign (stmt);
              break;
 
            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);
-               }
+               ret |= build_access_from_expr (gimple_call_arg (stmt, i),
+                                              stmt, false);
 
-             if (analysis_stage)
+             if (sra_mode == SRA_MODE_EARLY_IPA)
                {
                  tree dest = gimple_call_fndecl (stmt);
                  int flags = gimple_call_flags (stmt);
 
-                 if (dest
-                     && DECL_BUILT_IN_CLASS (dest) == BUILT_IN_NORMAL
-                     && DECL_FUNCTION_CODE (dest) == BUILT_IN_APPLY_ARGS)
-                   encountered_apply_args = true;
+                 if (dest)
+                   {
+                     if (DECL_BUILT_IN_CLASS (dest) == BUILT_IN_NORMAL
+                         && DECL_FUNCTION_CODE (dest) == BUILT_IN_APPLY_ARGS)
+                       encountered_apply_args = true;
+                     if (cgraph_get_node (dest)
+                         == cgraph_get_node (current_function_decl))
+                       {
+                         encountered_recursive_call = true;
+                         if (!callsite_has_enough_arguments_p (stmt))
+                           encountered_unchangable_recursive_call = true;
+                       }
+                   }
 
                  if (final_bbs
                      && (flags & (ECF_CONST | ECF_PURE)) == 0)
                    bitmap_set_bit (final_bbs, bb->index);
                }
 
-             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);
-                   }
-               }
+             t = gimple_call_lhs (stmt);
+             if (t && !disqualify_ops_if_throwing_stmt (stmt, t, NULL))
+               ret |= build_access_from_expr (t, stmt, true);
              break;
 
            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);
-               }
+             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);
+                 t = TREE_VALUE (gimple_asm_input_op (stmt, i));
+                 ret |= build_access_from_expr (t, stmt, false);
                }
              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);
+                 t = TREE_VALUE (gimple_asm_output_op (stmt, i));
+                 ret |= build_access_from_expr (t, stmt, true);
                }
              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;
@@ -1106,17 +1197,30 @@ compare_access_positions (const void *a, const void *b)
 
   if (f1->size == f2->size)
     {
+      if (f1->type == f2->type)
+       return 0;
       /* Put any non-aggregate type before any aggregate type.  */
-      if (!is_gimple_reg_type (f1->type)
-              && is_gimple_reg_type (f2->type))
+      else 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;
+              && INTEGRAL_TYPE_P (f2->type))
+       return TYPE_PRECISION (f2->type) - TYPE_PRECISION (f1->type);
       /* Put any integral type with non-full precision last.  */
       else if (INTEGRAL_TYPE_P (f1->type)
               && (TREE_INT_CST_LOW (TYPE_SIZE (f1->type))
@@ -1187,7 +1291,21 @@ make_fancy_name_1 (tree expr)
        break;
       sprintf (buffer, HOST_WIDE_INT_PRINT_DEC, TREE_INT_CST_LOW (index));
       obstack_grow (&name_obstack, buffer, strlen (buffer));
+      break;
 
+    case ADDR_EXPR:
+      make_fancy_name_1 (TREE_OPERAND (expr, 0));
+      break;
+
+    case MEM_REF:
+      make_fancy_name_1 (TREE_OPERAND (expr, 0));
+      if (!integer_zerop (TREE_OPERAND (expr, 1)))
+       {
+         obstack_1grow (&name_obstack, '$');
+         sprintf (buffer, HOST_WIDE_INT_PRINT_DEC,
+                  TREE_INT_CST_LOW (TREE_OPERAND (expr, 1)));
+         obstack_grow (&name_obstack, buffer, strlen (buffer));
+       }
       break;
 
     case BIT_FIELD_REF:
@@ -1210,11 +1328,103 @@ make_fancy_name (tree expr)
   return XOBFINISH (&name_obstack, char *);
 }
 
-/* Helper function for build_ref_for_offset.  */
+/* Construct a MEM_REF that would reference a part of aggregate BASE of type
+   EXP_TYPE at the given OFFSET.  If BASE is something for which
+   get_addr_base_and_unit_offset returns NULL, gsi must be non-NULL and is used
+   to insert new statements either before or below the current one as specified
+   by INSERT_AFTER.  This function is not capable of handling bitfields.  */
+
+tree
+build_ref_for_offset (location_t loc, tree base, HOST_WIDE_INT offset,
+                     tree exp_type, gimple_stmt_iterator *gsi,
+                     bool insert_after)
+{
+  tree prev_base = base;
+  tree off;
+  HOST_WIDE_INT base_offset;
+
+  gcc_checking_assert (offset % BITS_PER_UNIT == 0);
+
+  base = get_addr_base_and_unit_offset (base, &base_offset);
+
+  /* get_addr_base_and_unit_offset returns NULL for references with a variable
+     offset such as array[var_index].  */
+  if (!base)
+    {
+      gimple stmt;
+      tree tmp, addr;
+
+      gcc_checking_assert (gsi);
+      tmp = create_tmp_reg (build_pointer_type (TREE_TYPE (prev_base)), NULL);
+      add_referenced_var (tmp);
+      tmp = make_ssa_name (tmp, NULL);
+      addr = build_fold_addr_expr (unshare_expr (prev_base));
+      stmt = gimple_build_assign (tmp, addr);
+      gimple_set_location (stmt, loc);
+      SSA_NAME_DEF_STMT (tmp) = stmt;
+      if (insert_after)
+       gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
+      else
+       gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+      update_stmt (stmt);
+
+      off = build_int_cst (reference_alias_ptr_type (prev_base),
+                          offset / BITS_PER_UNIT);
+      base = tmp;
+    }
+  else if (TREE_CODE (base) == MEM_REF)
+    {
+      off = build_int_cst (TREE_TYPE (TREE_OPERAND (base, 1)),
+                          base_offset + offset / BITS_PER_UNIT);
+      off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), off, 0);
+      base = unshare_expr (TREE_OPERAND (base, 0));
+    }
+  else
+    {
+      off = build_int_cst (reference_alias_ptr_type (base),
+                          base_offset + offset / BITS_PER_UNIT);
+      base = build_fold_addr_expr (unshare_expr (base));
+    }
+
+  return fold_build2_loc (loc, MEM_REF, exp_type, base, off);
+}
+
+/* Construct a memory reference to a part of an aggregate BASE at the given
+   OFFSET and of the same type as MODEL.  In case this is a reference to a
+   component, the function will replicate the last COMPONENT_REF of model's
+   expr to access it.  GSI and INSERT_AFTER have the same meaning as in
+   build_ref_for_offset.  */
+
+static tree
+build_ref_for_model (location_t loc, tree base, HOST_WIDE_INT offset,
+                    struct access *model, gimple_stmt_iterator *gsi,
+                    bool insert_after)
+{
+  if (TREE_CODE (model->expr) == COMPONENT_REF)
+    {
+      tree t, exp_type;
+      offset -= int_bit_position (TREE_OPERAND (model->expr, 1));
+      exp_type = TREE_TYPE (TREE_OPERAND (model->expr, 0));
+      t = build_ref_for_offset (loc, base, offset, exp_type, gsi, insert_after);
+      return fold_build3_loc (loc, COMPONENT_REF, model->type, t,
+                             TREE_OPERAND (model->expr, 1), NULL_TREE);
+    }
+  else
+    return build_ref_for_offset (loc, base, offset, model->type,
+                                gsi, insert_after);
+}
+
+/* Construct a memory reference consisting of component_refs and array_refs to
+   a part of an aggregate *RES (which is of type TYPE).  The requested part
+   should have type EXP_TYPE at be the given OFFSET.  This function might not
+   succeed, it returns true when it does and only then *RES points to something
+   meaningful.  This function should be used only to build expressions that we
+   might need to present to user (e.g. in warnings).  In all other situations,
+   build_ref_for_model or build_ref_for_offset should be used instead.  */
 
 static bool
-build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
-                       tree exp_type)
+build_user_friendly_ref_for_offset (tree *res, tree type, HOST_WIDE_INT offset,
+                                   tree exp_type)
 {
   while (1)
     {
@@ -1231,9 +1441,7 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
        case UNION_TYPE:
        case QUAL_UNION_TYPE:
        case RECORD_TYPE:
-         /* ??? Some records used to be half-unions in Ada so the code treats
-            the 3 container types the same.  This has been fixed in Ada.  */
-         for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+         for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
            {
              HOST_WIDE_INT pos, size;
              tree expr, *expr_ptr;
@@ -1247,22 +1455,21 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
              if (!tr_size || !host_integerp (tr_size, 1))
                continue;
              size = tree_low_cst (tr_size, 1);
-             if (pos > offset || (pos + size) <= offset)
-               continue;
-
-             if (res)
+             if (size == 0)
                {
-                 expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld,
-                                NULL_TREE);
-                 expr_ptr = &expr;
+                 if (pos != offset)
+                   continue;
                }
-             else
-               expr_ptr = NULL;
-             if (build_ref_for_offset_1 (expr_ptr, TREE_TYPE (fld),
-                                         offset - pos, exp_type))
+             else if (pos > offset || (pos + size) <= offset)
+               continue;
+
+             expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld,
+                            NULL_TREE);
+             expr_ptr = &expr;
+             if (build_user_friendly_ref_for_offset (expr_ptr, TREE_TYPE (fld),
+                                                     offset - pos, exp_type))
                {
-                 if (res)
-                   *res = expr;
+                 *res = expr;
                  return true;
                }
            }
@@ -1275,16 +1482,13 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
          el_size = tree_low_cst (tr_size, 1);
 
          minidx = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
-         if (TREE_CODE (minidx) != INTEGER_CST)
+         if (TREE_CODE (minidx) != INTEGER_CST || el_size == 0)
            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);
-           }
+         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;
@@ -1301,36 +1505,6 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
     }
 }
 
-/* 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.
-
-   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.
- */
-
-bool
-build_ref_for_offset (tree *expr, tree type, HOST_WIDE_INT offset,
-                     tree exp_type, bool allow_ptr)
-{
-  location_t loc = expr ? EXPR_LOCATION (*expr) : UNKNOWN_LOCATION;
-
-  if (expr)
-    *expr = unshare_expr (*expr);
-
-  if (allow_ptr && POINTER_TYPE_P (type))
-    {
-      type = TREE_TYPE (type);
-      if (expr)
-       *expr = fold_build1_loc (loc, INDIRECT_REF, type, *expr);
-    }
-
-  return build_ref_for_offset_1 (expr, type, offset, exp_type);
-}
-
 /* Return true iff TYPE is stdarg va_list type.  */
 
 static inline bool
@@ -1404,8 +1578,7 @@ sort_and_splice_var_accesses (tree var)
   access_count = VEC_length (access_p, access_vec);
 
   /* Sort by <OFFSET, SIZE>.  */
-  qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p),
-        compare_access_positions);
+  VEC_qsort (access_p, access_vec, compare_access_positions);
 
   i = 0;
   while (i < access_count)
@@ -1413,7 +1586,10 @@ sort_and_splice_var_accesses (tree var)
       struct access *access = VEC_index (access_p, access_vec, i);
       bool grp_write = access->write;
       bool grp_read = !access->write;
+      bool grp_assignment_read = access->grp_assignment_read;
+      bool grp_assignment_write = access->grp_assignment_write;
       bool multiple_reads = false;
+      bool total_scalarization = access->total_scalarization;
       bool grp_partial_lhs = access->grp_partial_lhs;
       bool first_scalar = is_gimple_reg_type (access->type);
       bool unscalarizable_region = access->grp_unscalarizable_region;
@@ -1445,8 +1621,11 @@ sort_and_splice_var_accesses (tree var)
              else
                grp_read = true;
            }
+         grp_assignment_read |= ac2->grp_assignment_read;
+         grp_assignment_write |= ac2->grp_assignment_write;
          grp_partial_lhs |= ac2->grp_partial_lhs;
          unscalarizable_region |= ac2->grp_unscalarizable_region;
+         total_scalarization |= ac2->total_scalarization;
          relink_to_new_repr (access, ac2);
 
          /* If there are both aggregate-type and scalar-type accesses with
@@ -1462,7 +1641,9 @@ sort_and_splice_var_accesses (tree var)
       access->group_representative = access;
       access->grp_write = grp_write;
       access->grp_read = grp_read;
-      access->grp_hint = multiple_reads;
+      access->grp_assignment_read = grp_assignment_read;
+      access->grp_assignment_write = grp_assignment_write;
+      access->grp_hint = multiple_reads || total_scalarization;
       access->grp_partial_lhs = grp_partial_lhs;
       access->grp_unscalarizable_region = unscalarizable_region;
       if (access->first_link)
@@ -1481,14 +1662,15 @@ sort_and_splice_var_accesses (tree var)
    ACCESS->replacement.  */
 
 static tree
-create_access_replacement (struct access *access)
+create_access_replacement (struct access *access, bool rename)
 {
   tree repl;
 
   repl = create_tmp_var (access->type, "SR");
   get_var_ann (repl);
   add_referenced_var (repl);
-  mark_sym_for_renaming (repl);
+  if (rename)
+    mark_sym_for_renaming (repl);
 
   if (!access->grp_partial_lhs
       && (TREE_CODE (access->type) == COMPLEX_TYPE
@@ -1497,23 +1679,53 @@ create_access_replacement (struct access *access)
 
   DECL_SOURCE_LOCATION (repl) = DECL_SOURCE_LOCATION (access->base);
   DECL_ARTIFICIAL (repl) = 1;
+  DECL_IGNORED_P (repl) = DECL_IGNORED_P (access->base);
 
   if (DECL_NAME (access->base)
       && !DECL_IGNORED_P (access->base)
       && !DECL_ARTIFICIAL (access->base))
     {
       char *pretty_name = make_fancy_name (access->expr);
+      tree debug_expr = unshare_expr (access->expr), d;
 
       DECL_NAME (repl) = get_identifier (pretty_name);
       obstack_free (&name_obstack, pretty_name);
 
-      SET_DECL_DEBUG_EXPR (repl, access->expr);
+      /* Get rid of any SSA_NAMEs embedded in debug_expr,
+        as DECL_DEBUG_EXPR isn't considered when looking for still
+        used SSA_NAMEs and thus they could be freed.  All debug info
+        generation cares is whether something is constant or variable
+        and that get_ref_base_and_extent works properly on the
+        expression.  */
+      for (d = debug_expr; handled_component_p (d); d = TREE_OPERAND (d, 0))
+       switch (TREE_CODE (d))
+         {
+         case ARRAY_REF:
+         case ARRAY_RANGE_REF:
+           if (TREE_OPERAND (d, 1)
+               && TREE_CODE (TREE_OPERAND (d, 1)) == SSA_NAME)
+             TREE_OPERAND (d, 1) = SSA_NAME_VAR (TREE_OPERAND (d, 1));
+           if (TREE_OPERAND (d, 3)
+               && TREE_CODE (TREE_OPERAND (d, 3)) == SSA_NAME)
+             TREE_OPERAND (d, 3) = SSA_NAME_VAR (TREE_OPERAND (d, 3));
+           /* FALLTHRU */
+         case COMPONENT_REF:
+           if (TREE_OPERAND (d, 2)
+               && TREE_CODE (TREE_OPERAND (d, 2)) == SSA_NAME)
+             TREE_OPERAND (d, 2) = SSA_NAME_VAR (TREE_OPERAND (d, 2));
+           break;
+         default:
+           break;
+         }
+      SET_DECL_DEBUG_EXPR (repl, debug_expr);
       DECL_DEBUG_EXPR_IS_FROM (repl) = 1;
-      DECL_IGNORED_P (repl) = 0;
+      if (access->grp_no_warning)
+       TREE_NO_WARNING (repl) = 1;
+      else
+       TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
     }
-
-  DECL_IGNORED_P (repl) = DECL_IGNORED_P (access->base);
-  TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
+  else
+    TREE_NO_WARNING (repl) = 1;
 
   if (dump_file)
     {
@@ -1537,15 +1749,30 @@ get_access_replacement (struct access *access)
   gcc_assert (access->grp_to_be_replaced);
 
   if (!access->replacement_decl)
-    access->replacement_decl = create_access_replacement (access);
+    access->replacement_decl = create_access_replacement (access, true);
+  return access->replacement_decl;
+}
+
+/* Return ACCESS scalar replacement, create it if it does not exist yet but do
+   not mark it for renaming.  */
+
+static inline tree
+get_unrenamed_access_replacement (struct access *access)
+{
+  gcc_assert (!access->grp_to_be_replaced);
+
+  if (!access->replacement_decl)
+    access->replacement_decl = create_access_replacement (access, false);
   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.  */
+   to it is not "within" the root.  Return false iff some accesses partially
+   overlap.  */
 
-static void
+static bool
 build_access_subtree (struct access **access)
 {
   struct access *root = *access, *last_child = NULL;
@@ -1560,24 +1787,32 @@ build_access_subtree (struct access **access)
        last_child->next_sibling = *access;
       last_child = *access;
 
-      build_access_subtree (access);
+      if (!build_access_subtree (access))
+       return false;
     }
+
+  if (*access && (*access)->offset < limit)
+    return false;
+
+  return true;
 }
 
 /* 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.  */
+   one, others are linked in a list by the next_grp field.  Return false iff
+   some accesses partially overlap.  */
 
-static void
+static bool
 build_access_trees (struct access *access)
 {
   while (access)
     {
       struct access *root = access;
 
-      build_access_subtree (&access);
+      if (!build_access_subtree (&access))
+       return false;
       root->next_grp = access;
     }
+  return true;
 }
 
 /* Return true if expr contains some ARRAY_REFs into a variable bounded
@@ -1596,14 +1831,50 @@ expr_with_var_bounded_array_refs_p (tree expr)
   return false;
 }
 
+enum mark_rw_status { SRA_MRRW_NOTHING, SRA_MRRW_DIRECT, SRA_MRRW_ASSIGN};
+
 /* 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.  */
+   both seeming beneficial and when ALLOW_REPLACEMENTS allows it.  Also set all
+   sorts of access flags appropriately along the way, notably always set
+   grp_read and grp_assign_read according to MARK_READ and grp_write when
+   MARK_WRITE is true.
+
+   Creating a replacement for a scalar access is considered beneficial if its
+   grp_hint is set (this means we are either attempting total scalarization or
+   there is more than one direct read access) or according to the following
+   table:
+
+   Access written to individually (once or more times)
+   |
+   |   Parent written to in an assignment statement
+   |   |
+   |   |       Access read individually _once_
+   |   |       |
+   |           |       |       Parent read in an assignment statement
+   |   |       |       |
+   |           |       |       |       Scalarize       Comment
+-----------------------------------------------------------------------------
+   0   0       0       0                       No access for the scalar
+   0   0       0       1                       No access for the scalar
+   0   0       1       0       No              Single read - won't help
+   0   0       1       1       No              The same case
+   0   1       0       0                       No access for the scalar
+   0   1       0       1                       No access for the scalar
+   0   1       1       0       Yes             s = *g; return s.i;
+   0   1       1       1       Yes             The same case as above
+   1   0       0       0       No              Won't help
+   1   0       0       1       Yes             s.i = 1; *g = s;
+   1   0       1       0       Yes             s.i = 5; g = s.i;
+   1   0       1       1       Yes             The same case as above
+   1   1       0       0       No              Won't help.
+   1   1       0       1       Yes             s.i = 1; *g = s;
+   1   1       1       0       Yes             s = *g; return s.i;
+   1   1       1       1       Yes             Any of the above yeses  */
 
 static bool
 analyze_access_subtree (struct access *root, bool allow_replacements,
-                       bool mark_read, bool mark_write)
+                       enum mark_rw_status mark_read,
+                       enum mark_rw_status mark_write)
 {
   struct access *child;
   HOST_WIDE_INT limit = root->offset + root->size;
@@ -1611,16 +1882,31 @@ analyze_access_subtree (struct access *root, bool allow_replacements,
   bool scalar = is_gimple_reg_type (root->type);
   bool hole = false, sth_created = false;
   bool direct_read = root->grp_read;
+  bool direct_write = root->grp_write;
 
-  if (mark_read)
-    root->grp_read = true;
+  if (root->grp_assignment_read)
+    mark_read = SRA_MRRW_ASSIGN;
+  else if (mark_read == SRA_MRRW_ASSIGN)
+    {
+      root->grp_read = 1;
+      root->grp_assignment_read = 1;
+    }
+  else if (mark_read == SRA_MRRW_DIRECT)
+    root->grp_read = 1;
   else if (root->grp_read)
-    mark_read = true;
+    mark_read = SRA_MRRW_DIRECT;
 
-  if (mark_write)
-    root->grp_write = true;
+  if (root->grp_assignment_write)
+    mark_write = SRA_MRRW_ASSIGN;
+  else if (mark_write == SRA_MRRW_ASSIGN)
+    {
+      root->grp_write = 1;
+      root->grp_assignment_write = 1;
+    }
+  else if (mark_write == SRA_MRRW_DIRECT)
+    root->grp_write = 1;
   else if (root->grp_write)
-    mark_write = true;
+    mark_write = SRA_MRRW_DIRECT;
 
   if (root->grp_unscalarizable_region)
     allow_replacements = false;
@@ -1635,7 +1921,8 @@ analyze_access_subtree (struct access *root, bool allow_replacements,
       else
        covered_to += child->size;
 
-      sth_created |= analyze_access_subtree (child, allow_replacements,
+      sth_created |= analyze_access_subtree (child,
+                                            allow_replacements && !scalar,
                                             mark_read, mark_write);
 
       root->grp_unscalarized_data |= child->grp_unscalarized_data;
@@ -1644,7 +1931,8 @@ analyze_access_subtree (struct access *root, bool allow_replacements,
 
   if (allow_replacements && scalar && !root->first_child
       && (root->grp_hint
-         || (direct_read && root->grp_write)))
+         || ((direct_write || root->grp_assignment_write)
+             && (direct_read || root->grp_assignment_read))))
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
@@ -1683,7 +1971,8 @@ analyze_access_trees (struct access *access)
 
   while (access)
     {
-      if (analyze_access_subtree (access, true, false, false))
+      if (analyze_access_subtree (access, true,
+                                 SRA_MRRW_NOTHING, SRA_MRRW_NOTHING))
        ret = true;
       access = access->next_grp;
     }
@@ -1729,16 +2018,20 @@ create_artificial_child_access (struct access *parent, struct access *model,
 {
   struct access *access;
   struct access **child;
-  tree expr = parent->base;;
+  tree expr = parent->base;
 
   gcc_assert (!model->grp_unscalarizable_region);
 
-  if (!build_ref_for_offset (&expr, TREE_TYPE (expr), new_offset,
-                            model->type, false))
-    return NULL;
-
   access = (struct access *) pool_alloc (access_pool);
   memset (access, 0, sizeof (struct access));
+  if (!build_user_friendly_ref_for_offset (&expr, TREE_TYPE (expr), new_offset,
+                                          model->type))
+    {
+      access->grp_no_warning = true;
+      expr = build_ref_for_model (EXPR_LOCATION (parent->base), parent->base,
+                                 new_offset, model, NULL, false);
+    }
+
   access->base = parent->base;
   access->expr = expr;
   access->offset = new_offset;
@@ -1779,11 +2072,16 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
     {
       tree t = lacc->base;
 
-      if (build_ref_for_offset (&t, TREE_TYPE (t), lacc->offset, racc->type,
-                               false))
+      lacc->type = racc->type;
+      if (build_user_friendly_ref_for_offset (&t, TREE_TYPE (t), lacc->offset,
+                                             racc->type))
+       lacc->expr = t;
+      else
        {
-         lacc->expr = t;
-         lacc->type = racc->type;
+         lacc->expr = build_ref_for_model (EXPR_LOCATION (lacc->base),
+                                           lacc->base, lacc->offset,
+                                           racc, NULL, false);
+         lacc->grp_no_warning = true;
        }
       return false;
     }
@@ -1809,13 +2107,6 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
          continue;
        }
 
-      /* 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)
@@ -1863,109 +2154,105 @@ propagate_all_subaccesses (void)
 static bool
 analyze_all_variable_accesses (void)
 {
-  tree var;
-  referenced_var_iterator rvi;
   int res = 0;
+  bitmap tmp = BITMAP_ALLOC (NULL);
+  bitmap_iterator bi;
+  unsigned i, max_total_scalarization_size;
 
-  FOR_EACH_REFERENCED_VAR (var, rvi)
-    if (bitmap_bit_p (candidate_bitmap, DECL_UID (var)))
-      {
-       struct access *access;
-
-       access = sort_and_splice_var_accesses (var);
-       if (access)
-         build_access_trees (access);
-       else
-         disqualify_candidate (var,
-                               "No or inhibitingly overlapping accesses.");
-      }
-
-  propagate_all_subaccesses ();
+  max_total_scalarization_size = UNITS_PER_WORD * BITS_PER_UNIT
+    * MOVE_RATIO (optimize_function_for_speed_p (cfun));
 
-  FOR_EACH_REFERENCED_VAR (var, rvi)
-    if (bitmap_bit_p (candidate_bitmap, DECL_UID (var)))
+  EXECUTE_IF_SET_IN_BITMAP (candidate_bitmap, 0, i, bi)
+    if (bitmap_bit_p (should_scalarize_away_bitmap, i)
+       && !bitmap_bit_p (cannot_scalarize_away_bitmap, i))
       {
-       struct access *access = get_first_repr_for_decl (var);
+       tree var = referenced_var (i);
 
-       if (analyze_access_trees (access))
+       if (TREE_CODE (var) == VAR_DECL
+           && ((unsigned) tree_low_cst (TYPE_SIZE (TREE_TYPE (var)), 1)
+               <= max_total_scalarization_size)
+           && type_consists_of_records_p (TREE_TYPE (var)))
          {
-           res++;
+           completely_scalarize_record (var, var, 0, var);
            if (dump_file && (dump_flags & TDF_DETAILS))
              {
-               fprintf (dump_file, "\nAccess trees for ");
+               fprintf (dump_file, "Will attempt to totally scalarize ");
                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.");
       }
 
-  if (res)
+  bitmap_copy (tmp, candidate_bitmap);
+  EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, bi)
     {
-      statistics_counter_event (cfun, "Scalarized aggregates", res);
-      return true;
+      tree var = referenced_var (i);
+      struct access *access;
+
+      access = sort_and_splice_var_accesses (var);
+      if (!access || !build_access_trees (access))
+       disqualify_candidate (var,
+                             "No or inhibitingly overlapping accesses.");
     }
-  else
-    return false;
-}
 
-/* 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.  */
+  propagate_all_subaccesses ();
 
-static bool
-ref_expr_for_all_replacements_p (struct access *access, tree agg,
-                                HOST_WIDE_INT top_offset)
-{
-  do
+  bitmap_copy (tmp, candidate_bitmap);
+  EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, bi)
     {
-      if (access->grp_to_be_replaced
-         && !build_ref_for_offset (NULL, TREE_TYPE (agg),
-                                   access->offset - top_offset,
-                                   access->type, false))
-       return false;
-
-      if (access->first_child
-         && !ref_expr_for_all_replacements_p (access->first_child, agg,
-                                              top_offset))
-       return false;
+      tree var = referenced_var (i);
+      struct access *access = get_first_repr_for_decl (var);
 
-      access = access->next_sibling;
+      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.");
     }
-  while (access);
 
-  return true;
+  BITMAP_FREE (tmp);
+
+  if (res)
+    {
+      statistics_counter_event (cfun, "Scalarized aggregates", res);
+      return true;
+    }
+  else
+    return false;
 }
 
 /* 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.  */
+   into or out of AGG.  ACCESS, all its children, siblings and their children
+   are 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 a mem_ref or
+   a series of handled components).  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
 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)
+                        bool insert_after, location_t loc)
 {
   do
     {
-      tree expr = agg;
-
       if (chunk_size && access->offset >= start_offset + chunk_size)
        return;
 
@@ -1973,14 +2260,11 @@ generate_subtree_copies (struct access *access, tree agg,
          && (chunk_size == 0
              || access->offset + access->size > start_offset))
        {
-         tree repl = get_access_replacement (access);
-         bool ref_found;
+         tree expr, repl = get_access_replacement (access);
          gimple stmt;
 
-         ref_found = build_ref_for_offset (&expr, TREE_TYPE (agg),
-                                            access->offset - top_offset,
-                                            access->type, false);
-         gcc_assert (ref_found);
+         expr = build_ref_for_model (loc, agg, access->offset - top_offset,
+                                     access, gsi, insert_after);
 
          if (write)
            {
@@ -2001,6 +2285,7 @@ generate_subtree_copies (struct access *access, tree agg,
                                                 : GSI_SAME_STMT);
              stmt = gimple_build_assign (expr, repl);
            }
+         gimple_set_location (stmt, loc);
 
          if (insert_after)
            gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
@@ -2013,7 +2298,7 @@ generate_subtree_copies (struct access *access, tree agg,
       if (access->first_child)
        generate_subtree_copies (access->first_child, agg, top_offset,
                                 start_offset, chunk_size, gsi,
-                                write, insert_after);
+                                write, insert_after, loc);
 
       access = access->next_sibling;
     }
@@ -2027,7 +2312,7 @@ generate_subtree_copies (struct access *access, tree agg,
 
 static void
 init_subtree_with_zero (struct access *access, gimple_stmt_iterator *gsi,
-                       bool insert_after)
+                       bool insert_after, location_t loc)
 
 {
   struct access *child;
@@ -2037,17 +2322,17 @@ init_subtree_with_zero (struct access *access, gimple_stmt_iterator *gsi,
       gimple stmt;
 
       stmt = gimple_build_assign (get_access_replacement (access),
-                                 fold_convert (access->type,
-                                               integer_zero_node));
+                                 build_zero_cst (access->type));
       if (insert_after)
        gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
       else
        gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
       update_stmt (stmt);
+      gimple_set_location (stmt, loc);
     }
 
   for (child = access->first_child; child; child = child->next_sibling)
-    init_subtree_with_zero (child, gsi, insert_after);
+    init_subtree_with_zero (child, gsi, insert_after, loc);
 }
 
 /* Search for an access representative for the given expression EXPR and
@@ -2075,16 +2360,16 @@ get_access_for_expr (tree expr)
   return get_var_base_offset_size_access (base, offset, max_size);
 }
 
-/* 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).  */
+/* 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).  */
 
 static bool
-sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write,
-                void *data ATTRIBUTE_UNUSED)
+sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
 {
+  location_t loc;
   struct access *access;
   tree type, bfr;
 
@@ -2103,6 +2388,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write,
     return false;
   type = TREE_TYPE (*expr);
 
+  loc = gimple_location (gsi_stmt (*gsi));
   if (access->grp_to_be_replaced)
     {
       tree repl = get_access_replacement (access);
@@ -2110,33 +2396,44 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write,
          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.  */
-      if (!is_gimple_reg_type (type))
+        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) and when scalarized unions are involved
+         in assembler statements (see PR42398).  */
+      if (!useless_type_conversion_p (type, access->type))
        {
-         gimple stmt;
+         tree ref;
+
+         ref = build_ref_for_model (loc, access->base, access->offset, access,
+                                    NULL, false);
+
          if (write)
            {
-             tree ref = unshare_expr (access->expr);
+             gimple stmt;
+
              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);
+             gimple_set_location (stmt, loc);
              gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
            }
          else
            {
+             gimple stmt;
+
              if (access->grp_partial_lhs)
                repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE,
                                                 true, GSI_SAME_STMT);
-             stmt = gimple_build_assign (unshare_expr (access->expr), repl);
+             stmt = gimple_build_assign (ref, repl);
+             gimple_set_location (stmt, loc);
              gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
            }
        }
       else
-       {
-         gcc_assert (useless_type_conversion_p (type, access->type));
-         *expr = repl;
-       }
+       *expr = repl;
       sra_stats.exprs++;
     }
 
@@ -2155,7 +2452,8 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write,
        start_offset = chunk_size = 0;
 
       generate_subtree_copies (access->first_child, access->base, 0,
-                              start_offset, chunk_size, gsi, write, write);
+                              start_offset, chunk_size, gsi, write, write,
+                              loc);
     }
   return true;
 }
@@ -2168,53 +2466,55 @@ enum unscalarized_data_handling { SRA_UDH_NONE,  /* Nothing done so far. */
                                  SRA_UDH_LEFT }; /* Data flushed to the LHS. */
 
 /* 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.  */
+   base aggregate if there are unscalarized data or directly to LHS of the
+   statement that is pointed to by GSI otherwise.  */
 
 static enum unscalarized_data_handling
-handle_unscalarized_data_in_subtree (struct access *top_racc, tree lhs,
+handle_unscalarized_data_in_subtree (struct access *top_racc,
                                     gimple_stmt_iterator *gsi)
 {
   if (top_racc->grp_unscalarized_data)
     {
       generate_subtree_copies (top_racc->first_child, top_racc->base, 0, 0, 0,
-                              gsi, false, false);
+                              gsi, false, false,
+                              gimple_location (gsi_stmt (*gsi)));
       return SRA_UDH_RIGHT;
     }
   else
     {
+      tree lhs = gimple_assign_lhs (gsi_stmt (*gsi));
       generate_subtree_copies (top_racc->first_child, lhs, top_racc->offset,
-                              0, 0, gsi, false, false);
+                              0, 0, gsi, false, false,
+                              gimple_location (gsi_stmt (*gsi)));
       return SRA_UDH_LEFT;
     }
 }
 
 
-/* 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.  */
+/* Try to generate statements to load all sub-replacements in an access subtree
+   formed by children of LACC from scalar replacements in the TOP_RACC subtree.
+   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. NEW_GSI is stmt iterator used for statement insertions after the
+   original assignment, OLD_GSI is used to insert statements before the
+   assignment.  *REFRESHED keeps the information whether we have needed to
+   refresh replacements of the LHS and from which side of the assignments this
+   takes place.  */
 
 static void
 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)
+                                enum unscalarized_data_handling *refreshed)
 {
-  location_t loc = EXPR_LOCATION (lacc->expr);
-  do
+  location_t loc = gimple_location (gsi_stmt (*old_gsi));
+  for (lacc = lacc->first_child; lacc; lacc = lacc->next_sibling)
     {
       if (lacc->grp_to_be_replaced)
        {
          struct access *racc;
-         HOST_WIDE_INT offset = lacc->offset - left_offset + right_offset;
+         HOST_WIDE_INT offset = lacc->offset - left_offset + top_racc->offset;
          gimple stmt;
          tree rhs;
 
@@ -2227,59 +2527,59 @@ load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
            }
          else
            {
-             bool repl_found;
-
              /* 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);
+                                                                 old_gsi);
 
              if (*refreshed == SRA_UDH_LEFT)
-               rhs = unshare_expr (lacc->expr);
+               rhs = build_ref_for_model (loc, lacc->base, lacc->offset, lacc,
+                                           new_gsi, true);
              else
-               {
-                 rhs = top_racc->base;
-                 repl_found = build_ref_for_offset (&rhs,
-                                                    TREE_TYPE (top_racc->base),
-                                                    offset, lacc->type, false);
-                 gcc_assert (repl_found);
-               }
+               rhs = build_ref_for_model (loc, top_racc->base, offset, lacc,
+                                           new_gsi, true);
            }
 
          stmt = gimple_build_assign (get_access_replacement (lacc), rhs);
          gsi_insert_after (new_gsi, stmt, GSI_NEW_STMT);
+         gimple_set_location (stmt, loc);
          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,
+       *refreshed = handle_unscalarized_data_in_subtree (top_racc,
                                                          old_gsi);
 
       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;
+       load_assign_lhs_subreplacements (lacc, top_racc, left_offset,
+                                        old_gsi, new_gsi, refreshed);
     }
-  while (lacc);
 }
 
+/* Result code for SRA assignment modification.  */
+enum assignment_mod_result { SRA_AM_NONE,       /* nothing done for the stmt */
+                            SRA_AM_MODIFIED,  /* stmt changed but not
+                                                 removed */
+                            SRA_AM_REMOVED };  /* stmt eliminated */
+
 /* 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 enum scan_assign_result
+static enum assignment_mod_result
 sra_modify_constructor_assign (gimple *stmt, gimple_stmt_iterator *gsi)
 {
   tree lhs = gimple_assign_lhs (*stmt);
   struct access *acc;
+  location_t loc;
 
   acc = get_access_for_expr (lhs);
   if (!acc)
-    return SRA_SA_NONE;
+    return SRA_AM_NONE;
 
+  loc = gimple_location (*stmt);
   if (VEC_length (constructor_elt,
                  CONSTRUCTOR_ELTS (gimple_assign_rhs1 (*stmt))) > 0)
     {
@@ -2287,44 +2587,98 @@ sra_modify_constructor_assign (gimple *stmt, gimple_stmt_iterator *gsi)
         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;
+                                true, true, loc);
+      return SRA_AM_MODIFIED;
     }
 
   if (acc->grp_covered)
     {
-      init_subtree_with_zero (acc, gsi, false);
+      init_subtree_with_zero (acc, gsi, false, loc);
       unlink_stmt_vdef (*stmt);
       gsi_remove (gsi, true);
-      return SRA_SA_REMOVED;
+      return SRA_AM_REMOVED;
     }
   else
     {
-      init_subtree_with_zero (acc, gsi, true);
-      return SRA_SA_PROCESSED;
+      init_subtree_with_zero (acc, gsi, true, loc);
+      return SRA_AM_MODIFIED;
+    }
+}
+
+/* Create and return a new suitable default definition SSA_NAME for RACC which
+   is an access describing an uninitialized part of an aggregate that is being
+   loaded.  */
+
+static tree
+get_repl_default_def_ssa_name (struct access *racc)
+{
+  tree repl, decl;
+
+  decl = get_unrenamed_access_replacement (racc);
+
+  repl = gimple_default_def (cfun, decl);
+  if (!repl)
+    {
+      repl = make_ssa_name (decl, gimple_build_nop ());
+      set_default_def (decl, repl);
+    }
+
+  return repl;
+}
+
+/* Return true if REF has a COMPONENT_REF with a bit-field field declaration
+   somewhere in it.  */
+
+static inline bool
+contains_bitfld_comp_ref_p (const_tree ref)
+{
+  while (handled_component_p (ref))
+    {
+      if (TREE_CODE (ref) == COMPONENT_REF
+          && DECL_BIT_FIELD (TREE_OPERAND (ref, 1)))
+        return true;
+      ref = TREE_OPERAND (ref, 0);
     }
+
+  return false;
 }
 
+/* Return true if REF has an VIEW_CONVERT_EXPR or a COMPONENT_REF with a
+   bit-field field declaration somewhere in it.  */
+
+static inline bool
+contains_vce_or_bfcref_p (const_tree ref)
+{
+  while (handled_component_p (ref))
+    {
+      if (TREE_CODE (ref) == VIEW_CONVERT_EXPR
+         || (TREE_CODE (ref) == COMPONENT_REF
+             && DECL_BIT_FIELD (TREE_OPERAND (ref, 1))))
+       return true;
+      ref = TREE_OPERAND (ref, 0);
+    }
+
+  return false;
+}
 
-/* 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
+/* Examine both sides of the assignment statement pointed to by STMT, replace
+   them with a scalare replacement if there is one and generate copying of
+   replacements if scalarized aggregates have been used in the assignment.  GSI
+   is used to hold generated statements for type conversions and subtree
    copying.  */
 
-static enum scan_assign_result
-sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi,
-                  void *data ATTRIBUTE_UNUSED)
+static enum assignment_mod_result
+sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
 {
   struct access *lacc, *racc;
   tree lhs, rhs;
   bool modify_this_stmt = false;
   bool force_gimple_rhs = false;
-  location_t loc = gimple_location (*stmt);
+  location_t loc;
+  gimple_stmt_iterator orig_gsi = *gsi;
 
   if (!gimple_assign_single_p (*stmt))
-    return SRA_SA_NONE;
+    return SRA_AM_NONE;
   lhs = gimple_assign_lhs (*stmt);
   rhs = gimple_assign_rhs1 (*stmt);
 
@@ -2336,17 +2690,18 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi,
       || 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);
+                                         gsi, false);
       modify_this_stmt |= sra_modify_expr (gimple_assign_lhs_ptr (*stmt),
-                                          gsi, true, data);
-      return modify_this_stmt ? SRA_SA_PROCESSED : SRA_SA_NONE;
+                                          gsi, true);
+      return modify_this_stmt ? SRA_AM_MODIFIED : SRA_AM_NONE;
     }
 
   lacc = get_access_for_expr (lhs);
   racc = get_access_for_expr (rhs);
   if (!lacc && !racc)
-    return SRA_SA_NONE;
+    return SRA_AM_NONE;
 
+  loc = gimple_location (*stmt);
   if (lacc && lacc->grp_to_be_replaced)
     {
       lhs = get_access_replacement (lacc);
@@ -2374,40 +2729,28 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi,
             ???  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))
+             && !contains_bitfld_comp_ref_p (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);
-               }
+             lhs = build_ref_for_offset (loc, lhs, 0, TREE_TYPE (rhs),
+                                         gsi, false);
+             gimple_assign_set_lhs (*stmt, lhs);
            }
          else if (AGGREGATE_TYPE_P (TREE_TYPE (rhs))
+                  && !contains_vce_or_bfcref_p (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;
-           }
+           rhs = build_ref_for_offset (loc, rhs, 0, TREE_TYPE (lhs),
+                                       gsi, false);
+
          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 (lhs))
+             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 (force_gimple_rhs)
-       rhs = force_gimple_operand_gsi (gsi, rhs, true, NULL_TREE,
-                                       true, GSI_SAME_STMT);
-      if (gimple_assign_rhs1 (*stmt) != rhs)
-       {
-         gimple_assign_set_rhs_from_tree (gsi, rhs);
-         gcc_assert (*stmt == gsi_stmt (*gsi));
-       }
     }
 
   /* From this point on, the function deals with assignments in between
@@ -2443,18 +2786,16 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi,
      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 (gimple_has_volatile_ops (*stmt)
+      || contains_vce_or_bfcref_p (rhs)
+      || contains_vce_or_bfcref_p (lhs))
     {
       if (access_has_children_p (racc))
        generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,
-                                gsi, false, false);
+                                gsi, false, false, loc);
       if (access_has_children_p (lacc))
        generate_subtree_copies (lacc->first_child, lacc->base, 0, 0, 0,
-                                gsi, true, true);
+                                gsi, true, true, loc);
       sra_stats.separate_lhs_rhs_handling++;
     }
   else
@@ -2465,49 +2806,160 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi,
          enum unscalarized_data_handling refreshed;
 
          if (lacc->grp_read && !lacc->grp_covered)
-           refreshed = handle_unscalarized_data_in_subtree (racc, lhs, gsi);
+           refreshed = handle_unscalarized_data_in_subtree (racc, gsi);
          else
            refreshed = SRA_UDH_NONE;
 
-         load_assign_lhs_subreplacements (lacc->first_child, racc,
-                                          lacc->offset, racc->offset,
-                                          &orig_gsi, gsi, &refreshed, lhs);
+         load_assign_lhs_subreplacements (lacc, racc, lacc->offset,
+                                          &orig_gsi, gsi, &refreshed);
          if (refreshed != SRA_UDH_RIGHT)
            {
-             if (*stmt == gsi_stmt (*gsi))
-               gsi_next (gsi);
-
+             gsi_next (gsi);
              unlink_stmt_vdef (*stmt);
              gsi_remove (&orig_gsi, true);
              sra_stats.deleted++;
-             return SRA_SA_REMOVED;
+             return SRA_AM_REMOVED;
            }
        }
       else
        {
-         if (access_has_children_p (racc))
+         if (racc)
            {
-             if (!racc->grp_unscalarized_data)
+             if (!racc->grp_to_be_replaced && !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;
+                 if (dump_file)
+                   {
+                     fprintf (dump_file, "Removing load: ");
+                     print_gimple_stmt (dump_file, *stmt, 0, 0);
+                   }
+
+                 if (TREE_CODE (lhs) == SSA_NAME)
+                   {
+                     rhs = get_repl_default_def_ssa_name (racc);
+                     if (!useless_type_conversion_p (TREE_TYPE (lhs),
+                                                     TREE_TYPE (rhs)))
+                       rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR,
+                                              TREE_TYPE (lhs), rhs);
+                   }
+                 else
+                   {
+                     if (racc->first_child)
+                       generate_subtree_copies (racc->first_child, lhs,
+                                                racc->offset, 0, 0, gsi,
+                                                false, false, loc);
+
+                     gcc_assert (*stmt == gsi_stmt (*gsi));
+                     unlink_stmt_vdef (*stmt);
+                     gsi_remove (gsi, true);
+                     sra_stats.deleted++;
+                     return SRA_AM_REMOVED;
+                   }
                }
-             else
-               generate_subtree_copies (racc->first_child, lhs,
-                                        racc->offset, 0, 0, gsi, false, true);
+             else if (racc->first_child)
+               generate_subtree_copies (racc->first_child, lhs, racc->offset,
+                                        0, 0, gsi, false, true, loc);
            }
-         else if (access_has_children_p (lacc))
+         if (access_has_children_p (lacc))
            generate_subtree_copies (lacc->first_child, rhs, lacc->offset,
-                                    0, 0, gsi, true, true);
+                                    0, 0, gsi, true, true, loc);
        }
     }
-  return modify_this_stmt ? SRA_SA_PROCESSED : SRA_SA_NONE;
+
+  /* This gimplification must be done after generate_subtree_copies, lest we
+     insert the subtree copies in the middle of the gimplified sequence.  */
+  if (force_gimple_rhs)
+    rhs = force_gimple_operand_gsi (&orig_gsi, rhs, true, NULL_TREE,
+                                   true, GSI_SAME_STMT);
+  if (gimple_assign_rhs1 (*stmt) != rhs)
+    {
+      modify_this_stmt = true;
+      gimple_assign_set_rhs_from_tree (&orig_gsi, rhs);
+      gcc_assert (*stmt == gsi_stmt (orig_gsi));
+    }
+
+  return modify_this_stmt ? SRA_AM_MODIFIED : SRA_AM_NONE;
+}
+
+/* Traverse the function body and all modifications as decided in
+   analyze_all_variable_accesses.  Return true iff the CFG has been
+   changed.  */
+
+static bool
+sra_modify_function_body (void)
+{
+  bool cfg_changed = false;
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      gimple_stmt_iterator gsi = gsi_start_bb (bb);
+      while (!gsi_end_p (gsi))
+       {
+         gimple stmt = gsi_stmt (gsi);
+         enum assignment_mod_result assign_result;
+         bool modified = false, deleted = false;
+         tree *t;
+         unsigned i;
+
+         switch (gimple_code (stmt))
+           {
+           case GIMPLE_RETURN:
+             t = gimple_return_retval_ptr (stmt);
+             if (*t != NULL_TREE)
+               modified |= sra_modify_expr (t, &gsi, false);
+             break;
+
+           case GIMPLE_ASSIGN:
+             assign_result = sra_modify_assign (&stmt, &gsi);
+             modified |= assign_result == SRA_AM_MODIFIED;
+             deleted = assign_result == SRA_AM_REMOVED;
+             break;
+
+           case GIMPLE_CALL:
+             /* Operands must be processed before the lhs.  */
+             for (i = 0; i < gimple_call_num_args (stmt); i++)
+               {
+                 t = gimple_call_arg_ptr (stmt, i);
+                 modified |= sra_modify_expr (t, &gsi, false);
+               }
+
+             if (gimple_call_lhs (stmt))
+               {
+                 t = gimple_call_lhs_ptr (stmt);
+                 modified |= sra_modify_expr (t, &gsi, true);
+               }
+             break;
+
+           case GIMPLE_ASM:
+             for (i = 0; i < gimple_asm_ninputs (stmt); i++)
+               {
+                 t = &TREE_VALUE (gimple_asm_input_op (stmt, i));
+                 modified |= sra_modify_expr (t, &gsi, false);
+               }
+             for (i = 0; i < gimple_asm_noutputs (stmt); i++)
+               {
+                 t = &TREE_VALUE (gimple_asm_output_op (stmt, i));
+                 modified |= sra_modify_expr (t, &gsi, true);
+               }
+             break;
+
+           default:
+             break;
+           }
+
+         if (modified)
+           {
+             update_stmt (stmt);
+             if (maybe_clean_eh_stmt (stmt)
+                 && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+               cfg_changed = true;
+           }
+         if (!deleted)
+           gsi_next (&gsi);
+       }
+    }
+
+  return cfg_changed;
 }
 
 /* Generate statements initializing scalar replacements of parts of function
@@ -2522,7 +2974,7 @@ initialize_parameter_reductions (void)
 
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm;
-       parm = TREE_CHAIN (parm))
+       parm = DECL_CHAIN (parm))
     {
       VEC (access_p, heap) *access_vec;
       struct access *access;
@@ -2542,7 +2994,8 @@ initialize_parameter_reductions (void)
       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);
+       generate_subtree_copies (access, parm, 0, 0, 0, &gsi, true, true,
+                                EXPR_LOCATION (parm));
     }
 
   if (seq)
@@ -2561,14 +3014,16 @@ perform_intra_sra (void)
   if (!find_var_candidates ())
     goto out;
 
-  if (!scan_function (build_access_from_expr, build_accesses_from_assign, NULL,
-                     true, NULL))
+  if (!scan_function ())
     goto out;
 
   if (!analyze_all_variable_accesses ())
     goto out;
 
-  scan_function (sra_modify_expr, sra_modify_assign, NULL, false, NULL);
+  if (sra_modify_function_body ())
+    ret = TODO_update_ssa | TODO_cleanup_cfg;
+  else
+    ret = TODO_update_ssa;
   initialize_parameter_reductions ();
 
   statistics_counter_event (cfun, "Scalar replacements created",
@@ -2582,8 +3037,6 @@ perform_intra_sra (void)
   statistics_counter_event (cfun, "Separate LHS and RHS handling",
                            sra_stats.separate_lhs_rhs_handling);
 
-  ret = TODO_update_ssa;
-
  out:
   sra_deinitialize ();
   return ret;
@@ -2609,7 +3062,7 @@ late_intra_sra (void)
 static bool
 gate_intra_sra (void)
 {
-  return flag_tree_sra != 0;
+  return flag_tree_sra != 0 && dbg_cnt (tree_sra);
 }
 
 
@@ -2685,42 +3138,60 @@ ptr_parm_has_direct_uses (tree parm)
 
   FOR_EACH_IMM_USE_STMT (stmt, ui, name)
     {
-      if (gimple_assign_single_p (stmt))
+      int uses_ok = 0;
+      use_operand_p use_p;
+
+      if (is_gimple_debug (stmt))
+       continue;
+
+      /* Valid uses include dereferences on the lhs and the rhs.  */
+      if (gimple_has_lhs (stmt))
        {
-         tree rhs = gimple_assign_rhs1 (stmt);
-         if (rhs == name)
-           ret = true;
-         else if (TREE_CODE (rhs) == ADDR_EXPR)
-           {
-             do
-               {
-                 rhs = TREE_OPERAND (rhs, 0);
-               }
-             while (handled_component_p (rhs));
-             if (INDIRECT_REF_P (rhs) && TREE_OPERAND (rhs, 0) == name)
-               ret = true;
-           }
+         tree lhs = gimple_get_lhs (stmt);
+         while (handled_component_p (lhs))
+           lhs = TREE_OPERAND (lhs, 0);
+         if (TREE_CODE (lhs) == MEM_REF
+             && TREE_OPERAND (lhs, 0) == name
+             && integer_zerop (TREE_OPERAND (lhs, 1))
+             && types_compatible_p (TREE_TYPE (lhs),
+                                    TREE_TYPE (TREE_TYPE (name))))
+           uses_ok++;
        }
-      else if (gimple_code (stmt) == GIMPLE_RETURN)
+      if (gimple_assign_single_p (stmt))
        {
-         tree t = gimple_return_retval (stmt);
-         if (t == name)
-           ret = true;
+         tree rhs = gimple_assign_rhs1 (stmt);
+         while (handled_component_p (rhs))
+           rhs = TREE_OPERAND (rhs, 0);
+         if (TREE_CODE (rhs) == MEM_REF
+             && TREE_OPERAND (rhs, 0) == name
+             && integer_zerop (TREE_OPERAND (rhs, 1))
+             && types_compatible_p (TREE_TYPE (rhs),
+                                    TREE_TYPE (TREE_TYPE (name))))
+           uses_ok++;
        }
       else if (is_gimple_call (stmt))
        {
          unsigned i;
-         for (i = 0; i < gimple_call_num_args (stmt); i++)
+         for (i = 0; i < gimple_call_num_args (stmt); ++i)
            {
              tree arg = gimple_call_arg (stmt, i);
-             if (arg == name)
-               {
-                 ret = true;
-                 break;
-               }
+             while (handled_component_p (arg))
+               arg = TREE_OPERAND (arg, 0);
+             if (TREE_CODE (arg) == MEM_REF
+                 && TREE_OPERAND (arg, 0) == name
+                 && integer_zerop (TREE_OPERAND (arg, 1))
+                 && types_compatible_p (TREE_TYPE (arg),
+                                        TREE_TYPE (TREE_TYPE (name))))
+               uses_ok++;
            }
        }
-      else if (!is_gimple_debug (stmt))
+
+      /* If the number of valid uses does not match the number of
+         uses in this stmt there is an unhandled use.  */
+      FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
+       --uses_ok;
+
+      if (uses_ok != 0)
        ret = true;
 
       if (ret)
@@ -2744,7 +3215,7 @@ find_param_candidates (void)
 
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm;
-       parm = TREE_CHAIN (parm))
+       parm = DECL_CHAIN (parm))
     {
       tree type = TREE_TYPE (parm);
 
@@ -2752,7 +3223,7 @@ find_param_candidates (void)
 
       if (TREE_THIS_VOLATILE (parm)
          || TREE_ADDRESSABLE (parm)
-         || is_va_list_type (type))
+         || (!is_gimple_reg_type (type) && is_va_list_type (type)))
        continue;
 
       if (is_unused_scalar_param (parm))
@@ -2767,6 +3238,8 @@ find_param_candidates (void)
 
          if (TREE_CODE (type) == FUNCTION_TYPE
              || TYPE_VOLATILE (type)
+             || (TREE_CODE (type) == ARRAY_TYPE
+                 && TYPE_NONALIASED_COMPONENT (type))
              || !is_gimple_reg (parm)
              || is_va_list_type (type)
              || ptr_parm_has_direct_uses (parm))
@@ -2820,33 +3293,34 @@ analyze_modified_params (VEC (access_p, heap) *representatives)
 
   for (i = 0; i < func_param_count; i++)
     {
-      struct access *repr = VEC_index (access_p, representatives, i);
-      VEC (access_p, heap) *access_vec;
-      int j, access_count;
-      tree parm;
-
-      if (!repr || no_accesses_p (repr))
-       continue;
-      parm = repr->base;
-      if (!POINTER_TYPE_P (TREE_TYPE (parm))
-         || repr->grp_maybe_modified)
-       continue;
+      struct access *repr;
 
-      access_vec = get_base_access_vector (parm);
-      access_count = VEC_length (access_p, access_vec);
-      for (j = 0; j < access_count; j++)
+      for (repr = VEC_index (access_p, representatives, i);
+          repr;
+          repr = repr->next_grp)
        {
          struct access *access;
+         bitmap visited;
          ao_ref ar;
 
-         /* All accesses are read ones, otherwise grp_maybe_modified would be
-            trivially set.  */
-         access = VEC_index (access_p, access_vec, j);
-         ao_ref_init (&ar, access->expr);
-         walk_aliased_vdefs (&ar, gimple_vuse (access->stmt),
-                             mark_maybe_modified, repr, NULL);
-         if (repr->grp_maybe_modified)
-           break;
+         if (no_accesses_p (repr))
+           continue;
+         if (!POINTER_TYPE_P (TREE_TYPE (repr->base))
+             || repr->grp_maybe_modified)
+           continue;
+
+         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);
        }
     }
 }
@@ -3015,26 +3489,53 @@ static struct access *
 unmodified_by_ref_scalar_representative (tree parm)
 {
   int i, access_count;
-  struct access *access;
+  struct access *repr;
   VEC (access_p, heap) *access_vec;
 
   access_vec = get_base_access_vector (parm);
   gcc_assert (access_vec);
-  access_count = VEC_length (access_p, access_vec);
+  repr = VEC_index (access_p, access_vec, 0);
+  if (repr->write)
+    return NULL;
+  repr->group_representative = repr;
 
-  for (i = 0; i < access_count; i++)
+  access_count = VEC_length (access_p, access_vec);
+  for (i = 1; i < access_count; i++)
     {
-      access = VEC_index (access_p, access_vec, 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;
     }
 
-  access = VEC_index (access_p, access_vec, 0);
-  access->grp_read = 1;
-  access->grp_scalar_ptr = 1;
-  return access;
+  repr->grp_read = 1;
+  repr->grp_scalar_ptr = 1;
+  return repr;
 }
 
+/* Return true iff this access precludes IPA-SRA of the parameter it is
+   associated with. */
+
+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;
+}
+
+
 /* 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
@@ -3055,8 +3556,7 @@ splice_param_accesses (tree parm, bool *ro_grp)
     return &no_accesses_representant;
   access_count = VEC_length (access_p, access_vec);
 
-  qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p),
-        compare_access_positions);
+  VEC_qsort (access_p, access_vec, compare_access_positions);
 
   i = 0;
   total_size = 0;
@@ -3066,6 +3566,8 @@ splice_param_accesses (tree parm, bool *ro_grp)
       bool modification;
       access = VEC_index (access_p, access_vec, i);
       modification = access->write;
+      if (access_precludes_ipa_sra_p (access))
+       return NULL;
 
       /* Access is about to become group representative unless we find some
         nasty overlap which would preclude us from breaking this parameter
@@ -3086,7 +3588,13 @@ splice_param_accesses (tree parm, bool *ro_grp)
          else if (ac2->size != access->size)
            return NULL;
 
+         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++;
        }
 
@@ -3200,7 +3708,7 @@ splice_all_param_accesses (VEC (access_p, heap) **representatives)
 
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm;
-       parm = TREE_CHAIN (parm))
+       parm = DECL_CHAIN (parm))
     {
       if (is_unused_scalar_param (parm))
        {
@@ -3285,7 +3793,7 @@ turn_representatives_into_adjustments (VEC (access_p, heap) *representatives,
   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))
+  for (i = 0; i < func_param_count; i++, parm = DECL_CHAIN (parm))
     {
       struct access *repr = VEC_index (access_p, representatives, i);
 
@@ -3423,7 +3931,7 @@ get_replaced_param_substitute (struct ipa_parm_adjustment *adj)
     {
       char *pretty_name = make_fancy_name (adj->base);
 
-      repl = make_rename_temp (TREE_TYPE (adj->base), "ISR");
+      repl = create_tmp_reg (TREE_TYPE (adj->base), "ISR");
       DECL_NAME (repl) = get_identifier (pretty_name);
       obstack_free (&name_obstack, pretty_name);
 
@@ -3458,19 +3966,18 @@ get_adjustment_for_base (ipa_parm_adjustment_vec adjustments, tree base)
   return NULL;
 }
 
-/* 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.  DATA is a pointer to an adjustments vector.  */
+/* 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 together all of its uses and return true.
+   ADJUSTMENTS is a pointer to an adjustments vector.  */
 
 static bool
-replace_removed_params_ssa_names (gimple stmt, void *data)
+replace_removed_params_ssa_names (gimple stmt,
+                                 ipa_parm_adjustment_vec adjustments)
 {
-  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))
@@ -3510,40 +4017,45 @@ replace_removed_params_ssa_names (gimple stmt, void *data)
     gimple_phi_set_result (stmt, name);
 
   replace_uses_by (lhs, name);
+  release_ssa_name (lhs);
   return true;
 }
 
-/* Callback for scan_function.  If the expression *EXPR should be replaced by a
-   reduction of a parameter, do so.  DATA is a pointer to a vector of
-   adjustments.  */
+/* If the expression *EXPR should be replaced by a reduction of a parameter, do
+   so.  ADJUSTMENTS is a pointer to a vector of adjustments.  CONVERT
+   specifies whether the function should care about type incompatibility the
+   current and new expressions.  If it is false, the function will leave
+   incompatibility issues to the caller.  Return true iff the expression
+   was modified. */
 
 static bool
-sra_ipa_modify_expr (tree *expr, gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
-                    bool write ATTRIBUTE_UNUSED, void *data)
+sra_ipa_modify_expr (tree *expr, bool convert,
+                    ipa_parm_adjustment_vec adjustments)
 {
-  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);
 
   if (TREE_CODE (*expr) == BIT_FIELD_REF
       || TREE_CODE (*expr) == IMAGPART_EXPR
       || TREE_CODE (*expr) == REALPART_EXPR)
-    expr = &TREE_OPERAND (*expr, 0);
-  while (TREE_CODE (*expr) == NOP_EXPR
-        || TREE_CODE (*expr) == VIEW_CONVERT_EXPR)
-    expr = &TREE_OPERAND (*expr, 0);
+    {
+      expr = &TREE_OPERAND (*expr, 0);
+      convert = true;
+    }
 
   base = get_ref_base_and_extent (*expr, &offset, &size, &max_size);
   if (!base || size == -1 || max_size == -1)
     return false;
 
-  if (INDIRECT_REF_P (base))
-    base = TREE_OPERAND (base, 0);
+  if (TREE_CODE (base) == MEM_REF)
+    {
+      offset += mem_ref_offset (base).low * BITS_PER_UNIT;
+      base = TREE_OPERAND (base, 0);
+    }
 
   base = get_ssa_base_param (base);
   if (!base || TREE_CODE (base) != PARM_DECL)
@@ -3564,14 +4076,7 @@ sra_ipa_modify_expr (tree *expr, gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
     return false;
 
   if (cand->by_ref)
-    {
-      tree folded;
-      src = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (cand->reduction)),
-                   cand->reduction);
-      folded = gimple_fold_indirect_ref (src);
-      if (folded)
-        src = folded;
-    }
+    src = build_simple_mem_ref (cand->reduction);
   else
     src = cand->reduction;
 
@@ -3584,34 +4089,162 @@ sra_ipa_modify_expr (tree *expr, gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
       fprintf (dump_file, "\n");
     }
 
-  if (!useless_type_conversion_p (TREE_TYPE (*expr), cand->type))
+  if (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;
+  else
+    *expr = src;
   return true;
 }
 
-/* Callback for scan_function to process assign statements.  Performs
-   essentially the same function like sra_ipa_modify_expr.  */
+/* If the statement pointed to by STMT_PTR contains any expressions that need
+   to replaced with a different one as noted by ADJUSTMENTS, do so.  Handle any
+   potential type incompatibilities (GSI is used to accommodate conversion
+   statements and must point to the statement).  Return true iff the statement
+   was modified.  */
 
-static enum scan_assign_result
-sra_ipa_modify_assign (gimple *stmt_ptr,
-                      gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED, void *data)
+static bool
+sra_ipa_modify_assign (gimple *stmt_ptr, gimple_stmt_iterator *gsi,
+                      ipa_parm_adjustment_vec adjustments)
 {
   gimple stmt = *stmt_ptr;
-  bool any = false;
+  tree *lhs_p, *rhs_p;
+  bool any;
 
   if (!gimple_assign_single_p (stmt))
-    return SRA_SA_NONE;
+    return false;
+
+  rhs_p = gimple_assign_rhs1_ptr (stmt);
+  lhs_p = gimple_assign_lhs_ptr (stmt);
+
+  any = sra_ipa_modify_expr (rhs_p, false, adjustments);
+  any |= sra_ipa_modify_expr (lhs_p, false, adjustments);
+  if (any)
+    {
+      tree new_rhs = NULL_TREE;
+
+      if (!useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p)))
+       {
+         if (TREE_CODE (*rhs_p) == CONSTRUCTOR)
+           {
+             /* V_C_Es of constructors can cause trouble (PR 42714).  */
+             if (is_gimple_reg_type (TREE_TYPE (*lhs_p)))
+               *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p));
+             else
+               *rhs_p = build_constructor (TREE_TYPE (*lhs_p), 0);
+           }
+         else
+           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;
+
+      if (new_rhs)
+       {
+         tree tmp = force_gimple_operand_gsi (gsi, new_rhs, true, NULL_TREE,
+                                              true, GSI_SAME_STMT);
 
-  any |= sra_ipa_modify_expr (gimple_assign_rhs1_ptr (stmt), gsi, false,
-                             data);
-  any |= sra_ipa_modify_expr (gimple_assign_lhs_ptr (stmt), gsi, true, data);
+         gimple_assign_set_rhs_from_tree (gsi, tmp);
+       }
+
+      return true;
+    }
 
-  return any ? SRA_SA_PROCESSED : SRA_SA_NONE;
+  return false;
+}
+
+/* Traverse the function body and all modifications as described in
+   ADJUSTMENTS.  Return true iff the CFG has been changed.  */
+
+static bool
+ipa_sra_modify_function_body (ipa_parm_adjustment_vec adjustments)
+{
+  bool cfg_changed = false;
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      gimple_stmt_iterator gsi;
+
+      for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+       replace_removed_params_ssa_names (gsi_stmt (gsi), adjustments);
+
+      gsi = gsi_start_bb (bb);
+      while (!gsi_end_p (gsi))
+       {
+         gimple stmt = gsi_stmt (gsi);
+         bool modified = false;
+         tree *t;
+         unsigned i;
+
+         switch (gimple_code (stmt))
+           {
+           case GIMPLE_RETURN:
+             t = gimple_return_retval_ptr (stmt);
+             if (*t != NULL_TREE)
+               modified |= sra_ipa_modify_expr (t, true, adjustments);
+             break;
+
+           case GIMPLE_ASSIGN:
+             modified |= sra_ipa_modify_assign (&stmt, &gsi, adjustments);
+             modified |= replace_removed_params_ssa_names (stmt, adjustments);
+             break;
+
+           case GIMPLE_CALL:
+             /* Operands must be processed before the lhs.  */
+             for (i = 0; i < gimple_call_num_args (stmt); i++)
+               {
+                 t = gimple_call_arg_ptr (stmt, i);
+                 modified |= sra_ipa_modify_expr (t, true, adjustments);
+               }
+
+             if (gimple_call_lhs (stmt))
+               {
+                 t = gimple_call_lhs_ptr (stmt);
+                 modified |= sra_ipa_modify_expr (t, false, adjustments);
+                 modified |= replace_removed_params_ssa_names (stmt,
+                                                               adjustments);
+               }
+             break;
+
+           case GIMPLE_ASM:
+             for (i = 0; i < gimple_asm_ninputs (stmt); i++)
+               {
+                 t = &TREE_VALUE (gimple_asm_input_op (stmt, i));
+                 modified |= sra_ipa_modify_expr (t, true, adjustments);
+               }
+             for (i = 0; i < gimple_asm_noutputs (stmt); i++)
+               {
+                 t = &TREE_VALUE (gimple_asm_output_op (stmt, i));
+                 modified |= sra_ipa_modify_expr (t, false, adjustments);
+               }
+             break;
+
+           default:
+             break;
+           }
+
+         if (modified)
+           {
+             update_stmt (stmt);
+             if (maybe_clean_eh_stmt (stmt)
+                 && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+               cfg_changed = true;
+           }
+         gsi_next (&gsi);
+       }
+    }
+
+  return cfg_changed;
 }
 
 /* Call gimple_debug_bind_reset_value on all debug statements describing
@@ -3638,7 +4271,8 @@ sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
        continue;
       FOR_EACH_IMM_USE_STMT (stmt, ui, name)
        {
-         /* All other users must have been removed by scan_function.  */
+         /* All other users must have been removed by
+            ipa_sra_modify_function_body.  */
          gcc_assert (is_gimple_debug (stmt));
          gimple_debug_bind_reset_value (stmt);
          update_stmt (stmt);
@@ -3646,10 +4280,26 @@ sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
     }
 }
 
+/* Return true iff all callers have at least as many actual arguments as there
+   are formal parameters in the current function.  */
+
+static bool
+all_callers_have_enough_arguments_p (struct cgraph_node *node)
+{
+  struct cgraph_edge *cs;
+  for (cs = node->callers; cs; cs = cs->next_caller)
+    if (!callsite_has_enough_arguments_p (cs->call_stmt))
+      return false;
+
+  return true;
+}
+
+
 /* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS.  */
 
 static void
-convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
+convert_callers (struct cgraph_node *node, tree old_decl,
+                ipa_parm_adjustment_vec adjustments)
 {
   tree old_cur_fndecl = current_function_decl;
   struct cgraph_edge *cs;
@@ -3673,14 +4323,15 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
     }
 
   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);
-      }
+    if (bitmap_set_bit (recomputed_callers, cs->caller->uid))
+      compute_inline_parameters (cs->caller);
   BITMAP_FREE (recomputed_callers);
 
   current_function_decl = old_cur_fndecl;
+
+  if (!encountered_recursive_call)
+    return;
+
   FOR_EACH_BB (this_block)
     {
       gimple_stmt_iterator gsi;
@@ -3688,11 +4339,15 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
       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)
+         tree call_fndecl;
+         if (gimple_code (stmt) != GIMPLE_CALL)
+           continue;
+         call_fndecl = gimple_call_fndecl (stmt);
+         if (call_fndecl == old_decl)
            {
              if (dump_file)
                fprintf (dump_file, "Adjusting recursive call");
+             gimple_call_set_fndecl (stmt, node->decl);
              ipa_modify_call_arguments (NULL, stmt, adjustments);
            }
        }
@@ -3702,18 +4357,39 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
 }
 
 /* Perform all the modification required in IPA-SRA for NODE to have parameters
-   as given in ADJUSTMENTS.  */
+   as given in ADJUSTMENTS.  Return true iff the CFG has been changed.  */
 
-static void
+static bool
 modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
 {
+  struct cgraph_node *new_node;
+  struct cgraph_edge *cs;
+  bool cfg_changed;
+  VEC (cgraph_edge_p, heap) * redirect_callers;
+  int node_callers;
+
+  node_callers = 0;
+  for (cs = node->callers; cs != NULL; cs = cs->next_caller)
+    node_callers++;
+  redirect_callers = VEC_alloc (cgraph_edge_p, heap, node_callers);
+  for (cs = node->callers; cs != NULL; cs = cs->next_caller)
+    VEC_quick_push (cgraph_edge_p, redirect_callers, cs);
+
+  rebuild_cgraph_edges ();
+  pop_cfun ();
+  current_function_decl = NULL_TREE;
+
+  new_node = cgraph_function_versioning (node, redirect_callers, NULL, NULL,
+                                        NULL, NULL, "isra");
+  current_function_decl = new_node->decl;
+  push_cfun (DECL_STRUCT_FUNCTION (new_node->decl));
+
   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);
+  cfg_changed = ipa_sra_modify_function_body (adjustments);
   sra_ipa_reset_debug_stmts (adjustments);
-  convert_callers (node, adjustments);
-  cgraph_make_node_local (node);
-  return;
+  convert_callers (new_node, node->decl, adjustments);
+  cgraph_make_node_local (new_node);
+  return cfg_changed;
 }
 
 /* Return false the function is apparently unsuitable for IPA-SRA based on it's
@@ -3730,6 +4406,13 @@ ipa_sra_preliminary_function_checks (struct cgraph_node *node)
       return false;
     }
 
+  if (!tree_versionable_function_p (node->decl))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function is not versionable.\n");
+      return false;
+    }
+
   if (DECL_VIRTUAL_P (current_function_decl))
     {
       if (dump_file)
@@ -3760,6 +4443,9 @@ ipa_sra_preliminary_function_checks (struct cgraph_node *node)
       return false;
     }
 
+  if (TYPE_ATTRIBUTES (TREE_TYPE (node->decl)))
+    return false;
+
   return true;
 }
 
@@ -3785,13 +4471,20 @@ ipa_early_sra (void)
       goto simple_out;
     }
 
+  if (!all_callers_have_enough_arguments_p (node))
+    {
+      if (dump_file)
+       fprintf (dump_file, "There are callers with insufficient number of "
+                "arguments.\n");
+      goto simple_out;
+    }
+
   bb_dereferences = XCNEWVEC (HOST_WIDE_INT,
                                 func_param_count
                                 * last_basic_block_for_function (cfun));
   final_bbs = BITMAP_ALLOC (NULL);
 
-  scan_function (build_access_from_expr, build_accesses_from_assign,
-                NULL, true, NULL);
+  scan_function ();
   if (encountered_apply_args)
     {
       if (dump_file)
@@ -3799,15 +4492,25 @@ ipa_early_sra (void)
       goto out;
     }
 
+  if (encountered_unchangable_recursive_call)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function calls itself with insufficient "
+                "number of arguments.\n");
+      goto out;
+    }
+
   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);
+  if (modify_function (node, adjustments))
+    ret = TODO_update_ssa | TODO_cleanup_cfg;
+  else
+    ret = TODO_update_ssa;
   VEC_free (ipa_parm_adjustment_t, heap, adjustments);
-  ret = TODO_update_ssa;
 
   statistics_counter_event (cfun, "Unused parameters deleted",
                            sra_stats.deleted_unused_parameters);
@@ -3830,7 +4533,7 @@ ipa_early_sra (void)
 static bool
 ipa_early_sra_gate (void)
 {
-  return flag_ipa_sra;
+  return flag_ipa_sra && dbg_cnt (eipa_sra);
 }
 
 struct gimple_opt_pass pass_early_ipa_sra =