OSDN Git Service

2010-12-09 Martin Jambor <mjambor@suse.cz>
[pf3gnuchains/gcc-fork.git] / gcc / tree-sra.c
index 18cef67..4330a17 100644 (file)
@@ -89,6 +89,8 @@ along with GCC; see the file COPYING3.  If not see
 #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 */
@@ -186,6 +188,10 @@ struct access
      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.  */
@@ -213,6 +219,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;
@@ -358,15 +367,17 @@ dump_access (FILE *f, struct access *access, bool grp)
   if (grp)
     fprintf (f, ", grp_write = %d, total_scalarization = %d, "
             "grp_read = %d, grp_hint = %d, grp_assignment_read = %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_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->total_scalarization,
             access->grp_read, access->grp_hint, access->grp_assignment_read,
-            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_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, total_scalarization = %d, "
@@ -633,7 +644,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);
@@ -641,7 +652,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)
@@ -696,7 +708,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);
@@ -750,7 +762,8 @@ create_access (tree expr, gimple stmt, bool write)
 
   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)
@@ -770,12 +783,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);
@@ -807,73 +821,66 @@ create_access (tree expr, gimple stmt, bool write)
 
 /* 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 has a zero-size field as its
-   last field.  */
+   It also returns false if any of these records contains a bit-field.  */
 
 static bool
 type_consists_of_records_p (tree type)
 {
   tree fld;
-  bool last_fld_has_zero_size = false;
 
   if (TREE_CODE (type) != RECORD_TYPE)
     return false;
 
-  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);
 
+       if (DECL_BIT_FIELD (fld))
+         return false;
+
        if (!is_gimple_reg_type (ft)
            && !type_consists_of_records_p (ft))
          return false;
-
-       last_fld_has_zero_size = tree_low_cst (DECL_SIZE (fld), 1) == 0;
       }
 
-  if (last_fld_has_zero_size)
-    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.  */
+   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)
+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 = TREE_CHAIN (fld))
+  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;
-           tree expr;
-           bool ok;
 
            size = tree_low_cst (DECL_SIZE (fld), 1);
-           expr = base;
-           ok = build_ref_for_offset (&expr, TREE_TYPE (base), pos,
-                                      ft, false);
-           gcc_assert (ok);
-
            access = create_access_1 (base, pos, size);
-           access->expr = expr;
+           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);
+         completely_scalarize_record (base, fld, pos, nref);
       }
 }
 
@@ -884,15 +891,10 @@ completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset)
 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);
@@ -934,8 +936,9 @@ build_access_from_expr_1 (tree expr, 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:
@@ -1022,6 +1025,9 @@ build_accesses_from_assign (gimple stmt)
   racc = build_access_from_expr_1 (rhs, stmt, false);
   lacc = build_access_from_expr_1 (lhs, stmt, true);
 
+  if (lacc)
+    lacc->grp_assignment_write = 1;
+
   if (racc)
     {
       racc->grp_assignment_read = 1;
@@ -1284,7 +1290,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:
@@ -1307,11 +1327,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)
     {
@@ -1328,7 +1440,7 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
        case UNION_TYPE:
        case QUAL_UNION_TYPE:
        case RECORD_TYPE:
-         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;
@@ -1350,19 +1462,13 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
              else if (pos > offset || (pos + size) <= offset)
                continue;
 
-             if (res)
-               {
-                 expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld,
-                                NULL_TREE);
-                 expr_ptr = &expr;
-               }
-             else
-               expr_ptr = NULL;
-             if (build_ref_for_offset_1 (expr_ptr, TREE_TYPE (fld),
-                                         offset - pos, exp_type))
+             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;
                }
            }
@@ -1377,14 +1483,11 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
          minidx = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
          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;
@@ -1401,36 +1504,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
@@ -1504,8 +1577,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)
@@ -1514,6 +1586,7 @@ sort_and_splice_var_accesses (tree var)
       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;
@@ -1548,6 +1621,7 @@ sort_and_splice_var_accesses (tree var)
                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;
@@ -1567,6 +1641,7 @@ sort_and_splice_var_accesses (tree var)
       access->grp_write = grp_write;
       access->grp_read = grp_read;
       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;
@@ -1643,7 +1718,10 @@ create_access_replacement (struct access *access, bool rename)
          }
       SET_DECL_DEBUG_EXPR (repl, debug_expr);
       DECL_DEBUG_EXPR_IS_FROM (repl) = 1;
-      TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
+      if (access->grp_no_warning)
+       TREE_NO_WARNING (repl) = 1;
+      else
+       TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base);
     }
   else
     TREE_NO_WARNING (repl) = 1;
@@ -1752,17 +1830,50 @@ expr_with_var_bounded_array_refs_p (tree expr)
   return false;
 }
 
-enum mark_read_status { SRA_MR_NOT_READ, SRA_MR_READ, SRA_MR_ASSIGN_READ};
+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 set
    grp_read and grp_assign_read according to MARK_READ and grp_write when
-   MARK_WRITE is true.  */
+   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,
-                       enum mark_read_status 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;
@@ -1770,23 +1881,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 == SRA_MR_ASSIGN_READ)
+  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;
     }
-  if (mark_read == SRA_MR_READ)
+  else if (mark_read == SRA_MRRW_DIRECT)
     root->grp_read = 1;
-  else if (root->grp_assignment_read)
-    mark_read = SRA_MR_ASSIGN_READ;
   else if (root->grp_read)
-    mark_read = SRA_MR_READ;
+    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;
@@ -1811,13 +1930,8 @@ analyze_access_subtree (struct access *root, bool allow_replacements,
 
   if (allow_replacements && scalar && !root->first_child
       && (root->grp_hint
-         || (root->grp_write && (direct_read || root->grp_assignment_read)))
-      /* We must not ICE later on when trying to build an access to the
-        original data within the aggregate even when it is impossible to do in
-        a defined way like in the PR 42703 testcase.  Therefore we check
-        pre-emptively here that we will be able to do that.  */
-      && build_ref_for_offset (NULL, TREE_TYPE (root->base), root->offset,
-                              root->type, false))
+         || ((direct_write || root->grp_assignment_write)
+             && (direct_read || root->grp_assignment_read))))
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
@@ -1856,7 +1970,8 @@ analyze_access_trees (struct access *access)
 
   while (access)
     {
-      if (analyze_access_subtree (access, true, SRA_MR_NOT_READ, false))
+      if (analyze_access_subtree (access, true,
+                                 SRA_MRRW_NOTHING, SRA_MRRW_NOTHING))
        ret = true;
       access = access->next_grp;
     }
@@ -1902,16 +2017,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;
@@ -1952,11 +2071,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;
     }
@@ -1982,13 +2106,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)
@@ -2055,7 +2172,7 @@ analyze_all_variable_accesses (void)
                <= max_total_scalarization_size)
            && type_consists_of_records_p (TREE_TYPE (var)))
          {
-           completely_scalarize_record (var, var, 0);
+           completely_scalarize_record (var, var, 0, var);
            if (dump_file && (dump_flags & TDF_DETAILS))
              {
                fprintf (dump_file, "Will attempt to totally scalarize ");
@@ -2112,60 +2229,29 @@ analyze_all_variable_accesses (void)
     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.  */
-
-static bool
-ref_expr_for_all_replacements_p (struct access *access, tree agg,
-                                HOST_WIDE_INT top_offset)
-{
-  do
-    {
-      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;
-
-      access = access->next_sibling;
-    }
-  while (access);
-
-  return true;
-}
-
 /* Generate statements copying scalar replacements of accesses within a subtree
-   into or out of AGG.  ACCESS is the first child of the root of the subtree to
-   be processed.  AGG is an aggregate type expression (can be a declaration but
-   does not have to be, it can for example also be an indirect_ref).
-   TOP_OFFSET is the offset of the processed subtree which has to be subtracted
-   from offsets of individual accesses to get corresponding offsets for AGG.
-   If CHUNK_SIZE is non-null, copy only replacements in the interval
-   <start_offset, start_offset + chunk_size>, otherwise copy all.  GSI is a
-   statement iterator used to place the new statements.  WRITE should be true
-   when the statements should write from AGG to the replacement and false if
-   vice versa.  if INSERT_AFTER is true, new statements will be added after the
-   current statement in GSI, they will be added before the statement
-   otherwise.  */
+   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;
 
@@ -2173,14 +2259,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)
            {
@@ -2201,6 +2284,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);
@@ -2213,7 +2297,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;
     }
@@ -2227,7 +2311,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;
@@ -2237,17 +2321,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
@@ -2284,6 +2368,7 @@ get_access_for_expr (tree expr)
 static bool
 sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
 {
+  location_t loc;
   struct access *access;
   tree type, bfr;
 
@@ -2302,6 +2387,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);
@@ -2317,12 +2403,10 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
          in assembler statements (see PR42398).  */
       if (!useless_type_conversion_p (type, access->type))
        {
-         tree ref = access->base;
-         bool ok;
+         tree ref;
 
-         ok = build_ref_for_offset (&ref, TREE_TYPE (ref),
-                                    access->offset, access->type, false);
-         gcc_assert (ok);
+         ref = build_ref_for_model (loc, access->base, access->offset, access,
+                                    NULL, false);
 
          if (write)
            {
@@ -2332,6 +2416,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
                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
@@ -2342,6 +2427,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
                repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE,
                                                 true, GSI_SAME_STMT);
              stmt = gimple_build_assign (ref, repl);
+             gimple_set_location (stmt, loc);
              gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
            }
        }
@@ -2365,7 +2451,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;
 }
@@ -2378,53 +2465,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;
 
@@ -2441,47 +2530,31 @@ load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
                 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)
-               {
-                 bool repl_found;
-
-                 rhs = lacc->base;
-                 repl_found = build_ref_for_offset (&rhs, TREE_TYPE (rhs),
-                                                    lacc->offset, lacc->type,
-                                                    false);
-                 gcc_assert (repl_found);
-               }
+               rhs = build_ref_for_model (loc, lacc->base, lacc->offset, lacc,
+                                           new_gsi, true);
              else
-               {
-                 bool repl_found;
-
-                 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.  */
@@ -2499,11 +2572,13 @@ 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_AM_NONE;
 
+  loc = gimple_location (*stmt);
   if (VEC_length (constructor_elt,
                  CONSTRUCTOR_ELTS (gimple_assign_rhs1 (*stmt))) > 0)
     {
@@ -2511,30 +2586,30 @@ 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);
+                                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_AM_REMOVED;
     }
   else
     {
-      init_subtree_with_zero (acc, gsi, true);
+      init_subtree_with_zero (acc, gsi, true, loc);
       return SRA_AM_MODIFIED;
     }
 }
 
-/* Create a new suitable default definition SSA_NAME and replace all uses of
-   SSA with it, RACC is access describing the uninitialized part of an
-   aggregate that is being loaded.  */
+/* 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 void
-replace_uses_with_default_def_ssa_name (tree ssa, struct access *racc)
+static tree
+get_repl_default_def_ssa_name (struct access *racc)
 {
   tree repl, decl;
 
@@ -2547,7 +2622,42 @@ replace_uses_with_default_def_ssa_name (tree ssa, struct access *racc)
       set_default_def (decl, repl);
     }
 
-  replace_uses_by (ssa, 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;
 }
 
 /* Examine both sides of the assignment statement pointed to by STMT, replace
@@ -2563,7 +2673,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
   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))
@@ -2590,6 +2700,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
   if (!lacc && !racc)
     return SRA_AM_NONE;
 
+  loc = gimple_location (*stmt);
   if (lacc && lacc->grp_to_be_replaced)
     {
       lhs = get_access_replacement (lacc);
@@ -2617,27 +2728,23 @@ 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);
+             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;
@@ -2679,19 +2786,15 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
      This is what the first branch does.  */
 
   if (gimple_has_volatile_ops (*stmt)
-      || 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)))
+      || 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
@@ -2702,18 +2805,15 @@ 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++;
@@ -2726,26 +2826,41 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
            {
              if (!racc->grp_to_be_replaced && !racc->grp_unscalarized_data)
                {
-                 if (racc->first_child)
-                   generate_subtree_copies (racc->first_child, lhs,
-                                            racc->offset, 0, 0, gsi,
-                                            false, false);
-                 gcc_assert (*stmt == gsi_stmt (*gsi));
-                 if (TREE_CODE (lhs) == SSA_NAME)
-                   replace_uses_with_default_def_ssa_name (lhs, racc);
+                 if (dump_file)
+                   {
+                     fprintf (dump_file, "Removing load: ");
+                     print_gimple_stmt (dump_file, *stmt, 0, 0);
+                   }
 
-                 unlink_stmt_vdef (*stmt);
-                 gsi_remove (gsi, true);
-                 sra_stats.deleted++;
-                 return SRA_AM_REMOVED;
+                 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 if (racc->first_child)
-               generate_subtree_copies (racc->first_child, lhs,
-                                        racc->offset, 0, 0, gsi, false, true);
+               generate_subtree_copies (racc->first_child, lhs, racc->offset,
+                                        0, 0, gsi, false, true, loc);
            }
          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);
        }
     }
 
@@ -2756,6 +2871,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
                                    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));
     }
@@ -2764,11 +2880,13 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
 }
 
 /* Traverse the function body and all modifications as decided in
-   analyze_all_variable_accesses.  */
+   analyze_all_variable_accesses.  Return true iff the CFG has been
+   changed.  */
 
-static void
+static bool
 sra_modify_function_body (void)
 {
+  bool cfg_changed = false;
   basic_block bb;
 
   FOR_EACH_BB (bb)
@@ -2831,12 +2949,16 @@ sra_modify_function_body (void)
          if (modified)
            {
              update_stmt (stmt);
-             maybe_clean_eh_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
@@ -2851,7 +2973,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;
@@ -2871,7 +2993,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)
@@ -2896,7 +3019,10 @@ perform_intra_sra (void)
   if (!analyze_all_variable_accesses ())
     goto out;
 
-  sra_modify_function_body ();
+  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",
@@ -2910,8 +3036,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;
@@ -3025,8 +3149,11 @@ ptr_parm_has_direct_uses (tree parm)
          tree lhs = gimple_get_lhs (stmt);
          while (handled_component_p (lhs))
            lhs = TREE_OPERAND (lhs, 0);
-         if (INDIRECT_REF_P (lhs)
-             && TREE_OPERAND (lhs, 0) == name)
+         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++;
        }
       if (gimple_assign_single_p (stmt))
@@ -3034,8 +3161,11 @@ ptr_parm_has_direct_uses (tree parm)
          tree rhs = gimple_assign_rhs1 (stmt);
          while (handled_component_p (rhs))
            rhs = TREE_OPERAND (rhs, 0);
-         if (INDIRECT_REF_P (rhs)
-             && TREE_OPERAND (rhs, 0) == name)
+         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))
@@ -3046,8 +3176,11 @@ ptr_parm_has_direct_uses (tree parm)
              tree arg = gimple_call_arg (stmt, i);
              while (handled_component_p (arg))
                arg = TREE_OPERAND (arg, 0);
-             if (INDIRECT_REF_P (arg)
-                 && TREE_OPERAND (arg, 0) == name)
+             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++;
            }
        }
@@ -3081,7 +3214,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);
 
@@ -3104,6 +3237,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))
@@ -3420,8 +3555,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;
@@ -3453,7 +3587,10 @@ splice_param_accesses (tree parm, bool *ro_grp)
          else if (ac2->size != access->size)
            return NULL;
 
-         if (access_precludes_ipa_sra_p (ac2))
+         if (access_precludes_ipa_sra_p (ac2)
+             || (ac2->type != access->type
+                 && (TREE_ADDRESSABLE (ac2->type)
+                     || TREE_ADDRESSABLE (access->type))))
            return NULL;
 
          modification |= ac2->write;
@@ -3573,7 +3710,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))
        {
@@ -3658,7 +3795,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);
 
@@ -3916,8 +4053,11 @@ sra_ipa_modify_expr (tree *expr, bool convert,
   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)
@@ -3938,14 +4078,7 @@ sra_ipa_modify_expr (tree *expr, bool convert,
     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;
 
@@ -4000,7 +4133,7 @@ sra_ipa_modify_assign (gimple *stmt_ptr, gimple_stmt_iterator *gsi,
            {
              /* V_C_Es of constructors can cause trouble (PR 42714).  */
              if (is_gimple_reg_type (TREE_TYPE (*lhs_p)))
-               *rhs_p = fold_convert (TREE_TYPE (*lhs_p), integer_zero_node);
+               *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p));
              else
                *rhs_p = build_constructor (TREE_TYPE (*lhs_p), 0);
            }
@@ -4032,17 +4165,17 @@ sra_ipa_modify_assign (gimple *stmt_ptr, gimple_stmt_iterator *gsi,
 }
 
 /* Traverse the function body and all modifications as described in
-   ADJUSTMENTS.  */
+   ADJUSTMENTS.  Return true iff the CFG has been changed.  */
 
-static void
+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;
-      bool bb_changed = false;
 
       for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
        replace_removed_params_ssa_names (gsi_stmt (gsi), adjustments);
@@ -4104,15 +4237,16 @@ ipa_sra_modify_function_body (ipa_parm_adjustment_vec adjustments)
 
          if (modified)
            {
-             bb_changed = true;
              update_stmt (stmt);
-             maybe_clean_eh_stmt (stmt);
+             if (maybe_clean_eh_stmt (stmt)
+                 && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+               cfg_changed = true;
            }
          gsi_next (&gsi);
        }
-      if (bb_changed)
-       gimple_purge_dead_eh_edges (bb);
     }
+
+  return cfg_changed;
 }
 
 /* Call gimple_debug_bind_reset_value on all debug statements describing
@@ -4166,7 +4300,8 @@ all_callers_have_enough_arguments_p (struct cgraph_node *node)
 /* 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;
@@ -4190,11 +4325,8 @@ 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;
@@ -4213,10 +4345,11 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
          if (gimple_code (stmt) != GIMPLE_CALL)
            continue;
          call_fndecl = gimple_call_fndecl (stmt);
-         if (call_fndecl && cgraph_get_node (call_fndecl) == node)
+         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);
            }
        }
@@ -4225,44 +4358,40 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
   return;
 }
 
-/* Create an abstract origin declaration for OLD_DECL and make it an abstract
-   origin of the provided decl so that there are preserved parameters for debug
-   information.  */
-
-static void
-create_abstract_origin (tree old_decl)
-{
-  if (!DECL_ABSTRACT_ORIGIN (old_decl))
-    {
-      tree new_decl = copy_node (old_decl);
-
-      DECL_ABSTRACT (new_decl) = 1;
-      SET_DECL_ASSEMBLER_NAME (new_decl, NULL_TREE);
-      SET_DECL_RTL (new_decl, NULL);
-      DECL_STRUCT_FUNCTION (new_decl) = NULL;
-      DECL_ARTIFICIAL (old_decl) = 1;
-      DECL_ABSTRACT_ORIGIN (old_decl) = new_decl;
-    }
-}
-
 /* 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 *alias;
-  for (alias = node->same_body; alias; alias = alias->next)
-    ipa_modify_formal_parameters (alias->decl, adjustments, "ISRA");
-  /* current_function_decl must be handled last, after same_body aliases,
-     as following functions will use what it computed.  */
-  create_abstract_origin (current_function_decl);
+  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");
-  ipa_sra_modify_function_body (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
@@ -4279,6 +4408,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)
@@ -4372,9 +4508,11 @@ ipa_early_sra (void)
   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);
@@ -4397,7 +4535,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 =