OSDN Git Service

2012-01-30 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / tree-sra.c
index fa567e5..ef26894 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, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
    Contributed by Martin Jambor <mjambor@suse.cz>
 
 This file is part of GCC.
@@ -91,6 +91,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dbgcnt.h"
 #include "tree-inline.h"
 #include "gimple-pretty-print.h"
+#include "ipa-inline.h"
 
 /* Enumeration of all aggregate reductions we can do.  */
 enum sra_mode { SRA_MODE_EARLY_IPA,   /* early call regularization */
@@ -169,9 +170,8 @@ 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 an access to a non-addressable field? */
+  unsigned non_addressable : 1;
 
   /* Is this access currently in the work queue?  */
   unsigned grp_queued : 1;
@@ -188,6 +188,22 @@ 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;
+
+  /* Does this group contain a read access through a scalar type?  This flag is
+     not propagated in the access tree in any direction.  */
+  unsigned grp_scalar_read : 1;
+
+  /* Does this group contain a write access through a scalar type?  This flag
+     is not propagated in the access tree in any direction.  */
+  unsigned grp_scalar_write : 1;
+
+  /* Is this access an artificial one created to scalarize some record
+     entirely? */
+  unsigned grp_total_scalarization : 1;
+
   /* Other passes of the analysis use this bit to make function
      analyze_access_subtree create scalar replacements for this group if
      possible.  */
@@ -215,6 +231,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,22 +377,26 @@ 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, 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, "
+    fprintf (f, ", grp_read = %d, grp_write = %d, grp_assignment_read = %d, "
+            "grp_assignment_write = %d, grp_scalar_read = %d, "
+            "grp_scalar_write = %d, grp_total_scalarization = %d, "
+            "grp_hint = %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_read, access->grp_write, access->grp_assignment_read,
+            access->grp_assignment_write, access->grp_scalar_read,
+            access->grp_scalar_write, access->grp_total_scalarization,
+            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_not_necessarilly_dereferenced);
   else
-    fprintf (f, ", write = %d, total_scalarization = %d, "
+    fprintf (f, ", write = %d, grp_total_scalarization = %d, "
             "grp_partial_lhs = %d\n",
-            access->write, access->total_scalarization,
+            access->write, access->grp_total_scalarization,
             access->grp_partial_lhs);
 }
 
@@ -625,7 +648,7 @@ disqualify_candidate (tree decl, const char *reason)
    scalarization.  */
 
 static bool
-type_internals_preclude_sra_p (tree type)
+type_internals_preclude_sra_p (tree type, const char **msg)
 {
   tree fld;
   tree et;
@@ -640,14 +663,39 @@ type_internals_preclude_sra_p (tree type)
          {
            tree ft = TREE_TYPE (fld);
 
-           if (TREE_THIS_VOLATILE (fld)
-               || !DECL_FIELD_OFFSET (fld) || !DECL_SIZE (fld)
-               || !host_integerp (DECL_FIELD_OFFSET (fld), 1)
-               || !host_integerp (DECL_SIZE (fld), 1))
-             return true;
-
+           if (TREE_THIS_VOLATILE (fld))
+             {
+               *msg = "volatile structure field";
+               return true;
+             }
+           if (!DECL_FIELD_OFFSET (fld))
+             {
+               *msg = "no structure field offset";
+               return true;
+             }
+           if (!DECL_SIZE (fld))
+             {
+               *msg = "zero structure field size";
+               return true;
+             }
+           if (!host_integerp (DECL_FIELD_OFFSET (fld), 1))
+             {
+               *msg = "structure field offset not fixed";
+               return true;
+             }
+           if (!host_integerp (DECL_SIZE (fld), 1))
+             {
+               *msg = "structure field size not fixed";
+               return true;
+             }       
            if (AGGREGATE_TYPE_P (ft)
-               && type_internals_preclude_sra_p (ft))
+                   && int_bit_position (fld) % BITS_PER_UNIT != 0)
+             {
+               *msg = "structure field is bit field";
+               return true;
+             }
+
+           if (AGGREGATE_TYPE_P (ft) && type_internals_preclude_sra_p (ft, msg))
              return true;
          }
 
@@ -656,10 +704,16 @@ type_internals_preclude_sra_p (tree type)
     case ARRAY_TYPE:
       et = TREE_TYPE (type);
 
-      if (AGGREGATE_TYPE_P (et))
-       return type_internals_preclude_sra_p (et);
-      else
-       return false;
+      if (TYPE_VOLATILE (et))
+       {
+         *msg = "element type is volatile";
+         return true;
+       }
+
+      if (AGGREGATE_TYPE_P (et) && type_internals_preclude_sra_p (et, msg))
+       return true;
+
+      return false;
 
     default:
       return false;
@@ -773,12 +827,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);
@@ -804,20 +859,22 @@ create_access (tree expr, gimple stmt, bool write)
   access->grp_unscalarizable_region = unscalarizable_region;
   access->stmt = stmt;
 
+  if (TREE_CODE (expr) == COMPONENT_REF
+      && DECL_NONADDRESSABLE_P (TREE_OPERAND (expr, 1)))
+    access->non_addressable = 1;
+
   return access;
 }
 
 
 /* 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;
@@ -827,26 +884,26 @@ type_consists_of_records_p (tree type)
       {
        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);
 
@@ -855,31 +912,43 @@ completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset)
       {
        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;
+           access->grp_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);
       }
 }
 
+/* Create total_scalarization accesses for all scalar type fields in VAR and
+   for VAR a a whole.  VAR must be of a RECORD_TYPE conforming to
+   type_consists_of_records_p.   */
+
+static void
+completely_scalarize_var (tree var)
+{
+  HOST_WIDE_INT size = tree_low_cst (DECL_SIZE (var), 1);
+  struct access *access;
+
+  access = create_access_1 (var, 0, size);
+  access->expr = var;
+  access->type = TREE_TYPE (var);
+  access->grp_total_scalarization = 1;
+
+  completely_scalarize_record (var, var, 0, var);
+}
 
 /* Search the given tree for a declaration by skipping handled components and
    exclude it from the candidates.  */
@@ -998,6 +1067,53 @@ disqualify_ops_if_throwing_stmt (gimple stmt, tree lhs, tree rhs)
   return false;
 }
 
+/* Return true if EXP is a memory reference less aligned than ALIGN.  This is
+   invoked only on strict-alignment targets.  */
+
+static bool
+tree_non_aligned_mem_p (tree exp, unsigned int align)
+{
+  unsigned int exp_align;
+
+  if (TREE_CODE (exp) == VIEW_CONVERT_EXPR)
+    exp = TREE_OPERAND (exp, 0);
+
+  if (TREE_CODE (exp) == SSA_NAME || is_gimple_min_invariant (exp))
+    return false;
+
+  /* get_object_alignment will fall back to BITS_PER_UNIT if it cannot
+     compute an explicit alignment.  Pretend that dereferenced pointers
+     are always aligned on strict-alignment targets.  */
+  if (TREE_CODE (exp) == MEM_REF || TREE_CODE (exp) == TARGET_MEM_REF)
+    exp_align = get_object_or_type_alignment (exp);
+  else
+    exp_align = get_object_alignment (exp);
+
+  if (exp_align < align)
+    return true;
+
+  return false;
+}
+
+/* Return true if EXP is a memory reference less aligned than what the access
+   ACC would require.  This is invoked only on strict-alignment targets.  */
+
+static bool
+tree_non_aligned_mem_for_access_p (tree exp, struct access *acc)
+{
+  unsigned int acc_align;
+
+  /* The alignment of the access is that of its expression.  However, it may
+     have been artificially increased, e.g. by a local alignment promotion,
+     so we cap it to the alignment of the type of the base, on the grounds
+     that valid sub-accesses cannot be more aligned than that.  */
+  acc_align = get_object_alignment (acc->expr);
+  if (acc->base && acc_align > TYPE_ALIGN (TREE_TYPE (acc->base)))
+    acc_align = TYPE_ALIGN (TREE_TYPE (acc->base));
+
+  return tree_non_aligned_mem_p (exp, acc_align);
+}
+
 /* 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
@@ -1009,7 +1125,9 @@ build_accesses_from_assign (gimple stmt)
   tree lhs, rhs;
   struct access *lacc, *racc;
 
-  if (!gimple_assign_single_p (stmt))
+  if (!gimple_assign_single_p (stmt)
+      /* Scope clobbers don't influence scalarization.  */
+      || gimple_clobber_p (stmt))
     return false;
 
   lhs = gimple_assign_lhs (stmt);
@@ -1021,12 +1139,21 @@ 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 (STRICT_ALIGNMENT && tree_non_aligned_mem_for_access_p (rhs, lacc))
+        lacc->grp_unscalarizable_region = 1;
+    }
+
   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 (STRICT_ALIGNMENT && tree_non_aligned_mem_for_access_p (lhs, racc))
+        racc->grp_unscalarizable_region = 1;
     }
 
   if (lacc && racc
@@ -1320,15 +1447,164 @@ 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;
+  unsigned HOST_WIDE_INT misalign;
+  unsigned int align;
+
+  gcc_checking_assert (offset % BITS_PER_UNIT == 0);
 
-   FIXME: Eventually this should be rewritten to either re-use the
-   original access expression unshared (which is good for alias
-   analysis) or to build a MEM_REF expression.  */
+  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));
+      STRIP_USELESS_TYPE_CONVERSION (addr);
+      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);
+      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));
+    }
+
+  /* If prev_base were always an originally performed access
+     we can extract more optimistic alignment information
+     by looking at the access mode.  That would constrain the
+     alignment of base + base_offset which we would need to
+     adjust according to offset.
+     ???  But it is not at all clear that prev_base is an access
+     that was in the IL that way, so be conservative for now.  */
+  align = get_pointer_alignment_1 (base, &misalign);
+  misalign += (double_int_sext (tree_to_double_int (off),
+                               TYPE_PRECISION (TREE_TYPE (off))).low
+              * BITS_PER_UNIT);
+  misalign = misalign & (align - 1);
+  if (misalign != 0)
+    align = (misalign & -misalign);
+  if (align < TYPE_ALIGN (exp_type))
+    exp_type = build_aligned_type (exp_type, align);
+
+  return fold_build2_loc (loc, MEM_REF, exp_type, base, off);
+}
+
+DEF_VEC_ALLOC_P_STACK (tree);
+#define VEC_tree_stack_alloc(alloc) VEC_stack_alloc (tree, alloc)
+
+/* Construct a memory reference to a part of an aggregate BASE at the given
+   OFFSET and of the type of MODEL.  In case this is a chain of references
+   to component, the function will replicate the chain of COMPONENT_REFs of
+   the expression of MODEL 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)
+{
+  tree type = model->type, t;
+  VEC(tree,stack) *cr_stack = NULL;
+
+  if (TREE_CODE (model->expr) == COMPONENT_REF)
+    {
+      tree expr = model->expr;
+
+      /* Create a stack of the COMPONENT_REFs so later we can walk them in
+        order from inner to outer.  */
+      cr_stack = VEC_alloc (tree, stack, 6);
+
+      do {
+       tree field = TREE_OPERAND (expr, 1);
+       tree cr_offset = component_ref_field_offset (expr);
+       HOST_WIDE_INT bit_pos
+         = tree_low_cst (cr_offset, 1) * BITS_PER_UNIT
+             + TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
+
+       /* We can be called with a model different from the one associated
+          with BASE so we need to avoid going up the chain too far.  */
+       if (offset - bit_pos < 0)
+         break;
+
+       offset -= bit_pos;
+       VEC_safe_push (tree, stack, cr_stack, expr);
+
+       expr = TREE_OPERAND (expr, 0);
+       type = TREE_TYPE (expr);
+      } while (TREE_CODE (expr) == COMPONENT_REF);
+    }
+
+  t = build_ref_for_offset (loc, base, offset, type, gsi, insert_after);
+
+  if (TREE_CODE (model->expr) == COMPONENT_REF)
+    {
+      unsigned i;
+      tree expr;
+
+      /* Now replicate the chain of COMPONENT_REFs from inner to outer.  */
+      FOR_EACH_VEC_ELT_REVERSE (tree, cr_stack, i, expr)
+       {
+         tree field = TREE_OPERAND (expr, 1);
+         t = fold_build3_loc (loc, COMPONENT_REF, TREE_TYPE (field), t, field,
+                              TREE_OPERAND (expr, 2));
+       }
+
+      VEC_free (tree, stack, cr_stack);
+    }
+
+  return t;
+}
+
+/* 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)
     {
@@ -1367,19 +1643,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;
+             if (build_user_friendly_ref_for_offset (expr_ptr, TREE_TYPE (fld),
+                                                     offset - pos, exp_type))
                {
-                 expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld,
-                                NULL_TREE);
-                 expr_ptr = &expr;
-               }
-             else
-               expr_ptr = NULL;
-             if (build_ref_for_offset_1 (expr_ptr, TREE_TYPE (fld),
-                                         offset - pos, exp_type))
-               {
-                 if (res)
-                   *res = expr;
+                 *res = expr;
                  return true;
                }
            }
@@ -1394,14 +1664,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);
+         *res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index,
+                        NULL_TREE, NULL_TREE);
          offset = offset % el_size;
          type = TREE_TYPE (type);
          break;
@@ -1418,31 +1685,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.  */
-
-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 = build_simple_mem_ref_loc (loc, *expr);
-    }
-
-  return build_ref_for_offset_1 (expr, type, offset, exp_type);
-}
-
 /* Return true iff TYPE is stdarg va_list type.  */
 
 static inline bool
@@ -1451,6 +1693,19 @@ is_va_list_type (tree type)
   return TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (va_list_type_node);
 }
 
+/* Print message to dump file why a variable was rejected. */
+
+static void
+reject (tree var, const char *msg)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Rejected (%d): %s: ", DECL_UID (var), msg);
+      print_generic_expr (dump_file, var, 0);
+      fprintf (dump_file, "\n");
+    }
+}
+
 /* The very first phase of intraprocedural SRA.  It marks in candidate_bitmap
    those with type which is suitable for scalarization.  */
 
@@ -1460,26 +1715,58 @@ find_var_candidates (void)
   tree var, type;
   referenced_var_iterator rvi;
   bool ret = false;
+  const char *msg;
 
-  FOR_EACH_REFERENCED_VAR (var, rvi)
+  FOR_EACH_REFERENCED_VAR (cfun, var, rvi)
     {
       if (TREE_CODE (var) != VAR_DECL && TREE_CODE (var) != PARM_DECL)
         continue;
       type = TREE_TYPE (var);
 
-      if (!AGGREGATE_TYPE_P (type)
-         || needs_to_live_in_memory (var)
-         || TREE_THIS_VOLATILE (var)
-         || !COMPLETE_TYPE_P (type)
-         || !host_integerp (TYPE_SIZE (type), 1)
-          || tree_low_cst (TYPE_SIZE (type), 1) == 0
-         || type_internals_preclude_sra_p (type)
-         /* Fix for PR 41089.  tree-stdarg.c needs to have va_lists intact but
+      if (!AGGREGATE_TYPE_P (type)) 
+        {
+          reject (var, "not aggregate");
+          continue;
+       }
+      if (needs_to_live_in_memory (var))
+        {
+          reject (var, "needs to live in memory");
+          continue;
+        }
+      if (TREE_THIS_VOLATILE (var))
+        {
+          reject (var, "is volatile");
+         continue;
+        }
+      if (!COMPLETE_TYPE_P (type))
+        {
+          reject (var, "has incomplete type");
+         continue;
+        }
+      if (!host_integerp (TYPE_SIZE (type), 1))
+        {
+          reject (var, "type size not fixed");
+         continue;
+        }
+      if (tree_low_cst (TYPE_SIZE (type), 1) == 0)
+        {
+          reject (var, "type size is zero");
+          continue;
+        }
+      if (type_internals_preclude_sra_p (type, &msg))
+       {
+         reject (var, msg);
+         continue;
+       }
+      if (/* Fix for PR 41089.  tree-stdarg.c needs to have va_lists intact but
              we also want to schedule it rather late.  Thus we ignore it in
              the early pass. */
-         || (sra_mode == SRA_MODE_EARLY_INTRA
+         (sra_mode == SRA_MODE_EARLY_INTRA
              && is_va_list_type (type)))
-       continue;
+        {
+         reject (var, "is va_list");
+         continue;
+       }
 
       bitmap_set_bit (candidate_bitmap, DECL_UID (var));
 
@@ -1516,8 +1803,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)
@@ -1525,9 +1811,14 @@ 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_scalar_write = access->write
+       && is_gimple_reg_type (access->type);
+      bool grp_scalar_read = !access->write
+       && is_gimple_reg_type (access->type);
       bool grp_assignment_read = access->grp_assignment_read;
-      bool multiple_reads = false;
-      bool total_scalarization = access->total_scalarization;
+      bool grp_assignment_write = access->grp_assignment_write;
+      bool multiple_scalar_reads = false;
+      bool total_scalarization = access->grp_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;
@@ -1551,18 +1842,27 @@ sort_and_splice_var_accesses (tree var)
          if (ac2->offset != access->offset || ac2->size != access->size)
            break;
          if (ac2->write)
-           grp_write = true;
+           {
+             grp_write = true;
+             grp_scalar_write = (grp_scalar_write
+                                 || is_gimple_reg_type (ac2->type));
+           }
          else
            {
-             if (grp_read)
-               multiple_reads = true;
-             else
-               grp_read = true;
+             grp_read = true;
+             if (is_gimple_reg_type (ac2->type))
+               {
+                 if (grp_scalar_read)
+                   multiple_scalar_reads = true;
+                 else
+                   grp_scalar_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;
+         total_scalarization |= ac2->grp_total_scalarization;
          relink_to_new_repr (access, ac2);
 
          /* If there are both aggregate-type and scalar-type accesses with
@@ -1578,8 +1878,12 @@ sort_and_splice_var_accesses (tree var)
       access->group_representative = access;
       access->grp_write = grp_write;
       access->grp_read = grp_read;
+      access->grp_scalar_read = grp_scalar_read;
+      access->grp_scalar_write = grp_scalar_write;
       access->grp_assignment_read = grp_assignment_read;
-      access->grp_hint = multiple_reads || total_scalarization;
+      access->grp_assignment_write = grp_assignment_write;
+      access->grp_hint = multiple_scalar_reads || total_scalarization;
+      access->grp_total_scalarization = total_scalarization;
       access->grp_partial_lhs = grp_partial_lhs;
       access->grp_unscalarizable_region = unscalarizable_region;
       if (access->first_link)
@@ -1603,7 +1907,6 @@ 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);
   if (rename)
     mark_sym_for_renaming (repl);
@@ -1655,7 +1958,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;
@@ -1764,41 +2070,67 @@ 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};
-
 /* 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 through a scalar type (once or more times)
+   |
+   |   Written to in an assignment statement
+   |   |
+   |   |       Access read as scalar _once_
+   |   |       |
+   |           |       |       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)
+analyze_access_subtree (struct access *root, struct access *parent,
+                       bool allow_replacements)
 {
   struct access *child;
   HOST_WIDE_INT limit = root->offset + root->size;
   HOST_WIDE_INT covered_to = root->offset;
   bool scalar = is_gimple_reg_type (root->type);
   bool hole = false, sth_created = false;
-  bool direct_read = root->grp_read;
 
-  if (mark_read == SRA_MR_ASSIGN_READ)
+  if (parent)
     {
-      root->grp_read = 1;
-      root->grp_assignment_read = 1;
+      if (parent->grp_read)
+       root->grp_read = 1;
+      if (parent->grp_assignment_read)
+       root->grp_assignment_read = 1;
+      if (parent->grp_write)
+       root->grp_write = 1;
+      if (parent->grp_assignment_write)
+       root->grp_assignment_write = 1;
+      if (parent->grp_total_scalarization)
+       root->grp_total_scalarization = 1;
     }
-  if (mark_read == SRA_MR_READ)
-    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;
-
-  if (mark_write)
-    root->grp_write = true;
-  else if (root->grp_write)
-    mark_write = true;
 
   if (root->grp_unscalarizable_region)
     allow_replacements = false;
@@ -1808,46 +2140,58 @@ analyze_access_subtree (struct access *root, bool allow_replacements,
 
   for (child = root->first_child; child; child = child->next_sibling)
     {
-      if (!hole && child->offset < covered_to)
-       hole = true;
-      else
-       covered_to += child->size;
-
-      sth_created |= analyze_access_subtree (child,
-                                            allow_replacements && !scalar,
-                                            mark_read, mark_write);
+      hole |= covered_to < child->offset;
+      sth_created |= analyze_access_subtree (child, root,
+                                            allow_replacements && !scalar);
 
       root->grp_unscalarized_data |= child->grp_unscalarized_data;
-      hole |= !child->grp_covered;
+      root->grp_total_scalarization &= child->grp_total_scalarization;
+      if (child->grp_covered)
+       covered_to += child->size;
+      else
+       hole = true;
     }
 
   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))
+         || ((root->grp_scalar_read || root->grp_assignment_read)
+             && (root->grp_scalar_write || root->grp_assignment_write))))
     {
+      bool new_integer_type;
+      if (TREE_CODE (root->type) == ENUMERAL_TYPE)
+       {
+         tree rt = root->type;
+         root->type = build_nonstandard_integer_type (TYPE_PRECISION (rt),
+                                                      TYPE_UNSIGNED (rt));
+         new_integer_type = true;
+       }
+      else
+       new_integer_type = false;
+
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
          fprintf (dump_file, "Marking ");
          print_generic_expr (dump_file, root->base, 0);
-         fprintf (dump_file, " offset: %u, size: %u: ",
+         fprintf (dump_file, " offset: %u, size: %u ",
                   (unsigned) root->offset, (unsigned) root->size);
-         fprintf (dump_file, " to be replaced.\n");
+         fprintf (dump_file, " to be replaced%s.\n",
+                  new_integer_type ? " with an integer": "");
        }
 
       root->grp_to_be_replaced = 1;
       sth_created = true;
       hole = false;
     }
-  else if (covered_to < limit)
-    hole = true;
+  else
+    {
+      if (covered_to < limit)
+       hole = true;
+      if (scalar)
+       root->grp_total_scalarization = 0;
+    }
 
-  if (sth_created && !hole)
+  if (sth_created
+      && (!hole || root->grp_total_scalarization))
     {
       root->grp_covered = 1;
       return true;
@@ -1868,7 +2212,7 @@ analyze_access_trees (struct access *access)
 
   while (access)
     {
-      if (analyze_access_subtree (access, true, SRA_MR_NOT_READ, false))
+      if (analyze_access_subtree (access, NULL, true))
        ret = true;
       access = access->next_grp;
     }
@@ -1914,16 +2258,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;
@@ -1959,16 +2307,23 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
       || racc->grp_unscalarizable_region)
     return false;
 
-  if (!lacc->first_child && !racc->first_child
-      && is_gimple_reg_type (racc->type))
+  if (is_gimple_reg_type (racc->type))
     {
-      tree t = lacc->base;
-
-      if (build_ref_for_offset (&t, TREE_TYPE (t), lacc->offset, racc->type,
-                               false))
+      if (!lacc->first_child && !racc->first_child)
        {
-         lacc->expr = t;
+         tree t = lacc->base;
+
          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 = build_ref_for_model (EXPR_LOCATION (lacc->base),
+                                               lacc->base, lacc->offset,
+                                               racc, NULL, false);
+             lacc->grp_no_warning = true;
+           }
        }
       return false;
     }
@@ -1994,13 +2349,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)
@@ -2063,16 +2411,24 @@ analyze_all_variable_accesses (void)
        tree var = referenced_var (i);
 
        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)))
          {
-           completely_scalarize_record (var, var, 0);
-           if (dump_file && (dump_flags & TDF_DETAILS))
+           if ((unsigned) tree_low_cst (TYPE_SIZE (TREE_TYPE (var)), 1)
+               <= max_total_scalarization_size)
+             {
+               completely_scalarize_var (var);
+               if (dump_file && (dump_flags & TDF_DETAILS))
+                 {
+                   fprintf (dump_file, "Will attempt to totally scalarize ");
+                   print_generic_expr (dump_file, var, 0);
+                   fprintf (dump_file, " (UID: %u): \n", DECL_UID (var));
+                 }
+             }
+           else if (dump_file && (dump_flags & TDF_DETAILS))
              {
-               fprintf (dump_file, "Will attempt to totally scalarize ");
+               fprintf (dump_file, "Too big to totally scalarize: ");
                print_generic_expr (dump_file, var, 0);
-               fprintf (dump_file, " (UID: %u)\n", DECL_UID (var));
+               fprintf (dump_file, " (UID: %u)\n", DECL_UID (var));
              }
          }
       }
@@ -2124,60 +2480,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;
 
@@ -2185,14 +2510,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)
            {
@@ -2213,6 +2535,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);
@@ -2225,7 +2548,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;
     }
@@ -2239,7 +2562,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;
@@ -2249,17 +2572,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
@@ -2296,6 +2619,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;
 
@@ -2314,6 +2638,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);
@@ -2329,12 +2654,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)
            {
@@ -2344,6 +2667,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
@@ -2354,6 +2678,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);
            }
        }
@@ -2377,7 +2702,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;
 }
@@ -2390,55 +2716,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.
-   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.  */
+/* 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;
 
@@ -2448,6 +2774,10 @@ load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
              rhs = get_access_replacement (racc);
              if (!useless_type_conversion_p (lacc->type, racc->type))
                rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, lacc->type, rhs);
+
+             if (racc->grp_partial_lhs && lacc->grp_partial_lhs)
+               rhs = force_gimple_operand_gsi (old_gsi, rhs, true, NULL_TREE,
+                                               true, GSI_SAME_STMT);
            }
          else
            {
@@ -2455,47 +2785,34 @@ 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);
+             if (lacc->grp_partial_lhs)
+               rhs = force_gimple_operand_gsi (new_gsi, rhs, true, NULL_TREE,
+                                               false, GSI_NEW_STMT);
            }
 
          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.  */
@@ -2513,11 +2830,27 @@ 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;
 
+  if (gimple_clobber_p (*stmt))
+    {
+      /* Remove clobbers of fully scalarized variables, otherwise
+        do nothing.  */
+      if (acc->grp_covered)
+       {
+         unlink_stmt_vdef (*stmt);
+         gsi_remove (gsi, true);
+         return SRA_AM_REMOVED;
+       }
+      else
+       return SRA_AM_NONE;
+    }
+
+  loc = gimple_location (*stmt);
   if (VEC_length (constructor_elt,
                  CONSTRUCTOR_ELTS (gimple_assign_rhs1 (*stmt))) > 0)
     {
@@ -2525,20 +2858,20 @@ 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;
     }
 }
@@ -2564,6 +2897,41 @@ get_repl_default_def_ssa_name (struct access *racc)
   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
    them with a scalare replacement if there is one and generate copying of
    replacements if scalarized aggregates have been used in the assignment.  GSI
@@ -2577,7 +2945,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))
@@ -2604,6 +2972,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);
@@ -2622,6 +2991,16 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
        force_gimple_rhs = true;
       sra_stats.exprs++;
     }
+  else if (racc
+          && !access_has_children_p (racc)
+          && !racc->grp_to_be_replaced
+          && !racc->grp_unscalarized_data
+          && TREE_CODE (lhs) == SSA_NAME)
+    {
+      rhs = get_repl_default_def_ssa_name (racc);
+      modify_this_stmt = true;
+      sra_stats.exprs++;
+    }
 
   if (modify_this_stmt)
     {
@@ -2631,27 +3010,21 @@ 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_model (loc, lhs, 0, racc, 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_model (loc, rhs, 0, lacc, 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;
@@ -2692,21 +3065,33 @@ 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 (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)))
+  if (modify_this_stmt
+      || 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++;
+
+      /* 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;
     }
   else
     {
@@ -2716,13 +3101,12 @@ 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)
            {
              gsi_next (gsi);
@@ -2734,60 +3118,33 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
        }
       else
        {
-         if (racc)
+         if (access_has_children_p (racc)
+             && !racc->grp_unscalarized_data)
            {
-             if (!racc->grp_to_be_replaced && !racc->grp_unscalarized_data)
+             if (dump_file)
                {
-                 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);
-
-                     gcc_assert (*stmt == gsi_stmt (*gsi));
-                     unlink_stmt_vdef (*stmt);
-                     gsi_remove (gsi, true);
-                     sra_stats.deleted++;
-                     return SRA_AM_REMOVED;
-                   }
+                 fprintf (dump_file, "Removing load: ");
+                 print_gimple_stmt (dump_file, *stmt, 0, 0);
                }
-             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, false, loc);
+             gcc_assert (*stmt == gsi_stmt (*gsi));
+             unlink_stmt_vdef (*stmt);
+             gsi_remove (gsi, true);
+             sra_stats.deleted++;
+             return SRA_AM_REMOVED;
            }
+         if (access_has_children_p (racc))
+           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);
        }
-    }
 
-  /* 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)
-    {
-      gimple_assign_set_rhs_from_tree (&orig_gsi, rhs);
-      gcc_assert (*stmt == gsi_stmt (orig_gsi));
+      return SRA_AM_NONE;
     }
-
-  return modify_this_stmt ? SRA_AM_MODIFIED : SRA_AM_NONE;
 }
 
 /* Traverse the function body and all modifications as decided in
@@ -2904,7 +3261,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)
@@ -2990,8 +3348,7 @@ struct gimple_opt_pass pass_sra_early =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func
-  | TODO_update_ssa
+  TODO_update_ssa
   | TODO_ggc_collect
   | TODO_verify_ssa                    /* todo_flags_finish */
  }
@@ -3012,8 +3369,7 @@ struct gimple_opt_pass pass_sra =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   TODO_update_address_taken,           /* todo_flags_start */
-  TODO_dump_func
-  | TODO_update_ssa
+  TODO_update_ssa
   | TODO_ggc_collect
   | TODO_verify_ssa                    /* todo_flags_finish */
  }
@@ -3063,7 +3419,8 @@ ptr_parm_has_direct_uses (tree parm)
              && TREE_OPERAND (lhs, 0) == name
              && integer_zerop (TREE_OPERAND (lhs, 1))
              && types_compatible_p (TREE_TYPE (lhs),
-                                    TREE_TYPE (TREE_TYPE (name))))
+                                    TREE_TYPE (TREE_TYPE (name)))
+             && !TREE_THIS_VOLATILE (lhs))
            uses_ok++;
        }
       if (gimple_assign_single_p (stmt))
@@ -3075,7 +3432,8 @@ ptr_parm_has_direct_uses (tree parm)
              && TREE_OPERAND (rhs, 0) == name
              && integer_zerop (TREE_OPERAND (rhs, 1))
              && types_compatible_p (TREE_TYPE (rhs),
-                                    TREE_TYPE (TREE_TYPE (name))))
+                                    TREE_TYPE (TREE_TYPE (name)))
+             && !TREE_THIS_VOLATILE (rhs))
            uses_ok++;
        }
       else if (is_gimple_call (stmt))
@@ -3090,7 +3448,8 @@ ptr_parm_has_direct_uses (tree parm)
                  && TREE_OPERAND (arg, 0) == name
                  && integer_zerop (TREE_OPERAND (arg, 1))
                  && types_compatible_p (TREE_TYPE (arg),
-                                        TREE_TYPE (TREE_TYPE (name))))
+                                        TREE_TYPE (TREE_TYPE (name)))
+                 && !TREE_THIS_VOLATILE (arg))
                uses_ok++;
            }
        }
@@ -3121,6 +3480,7 @@ find_param_candidates (void)
   tree parm;
   int count = 0;
   bool ret = false;
+  const char *msg;
 
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm;
@@ -3147,6 +3507,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))
@@ -3159,7 +3521,7 @@ find_param_candidates (void)
          || !host_integerp (TYPE_SIZE (type), 1)
           || tree_low_cst (TYPE_SIZE (type), 1) == 0
          || (AGGREGATE_TYPE_P (type)
-             && type_internals_preclude_sra_p (type)))
+             && type_internals_preclude_sra_p (type, &msg)))
        continue;
 
       bitmap_set_bit (candidate_bitmap, DECL_UID (parm));
@@ -3439,6 +3801,10 @@ access_precludes_ipa_sra_p (struct access *access)
          || gimple_code (access->stmt) == GIMPLE_ASM))
     return true;
 
+  if (STRICT_ALIGNMENT
+      && tree_non_aligned_mem_p (access->expr, TYPE_ALIGN (access->type)))
+    return true;
+
   return false;
 }
 
@@ -3463,8 +3829,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;
@@ -3472,10 +3837,12 @@ splice_param_accesses (tree parm, bool *ro_grp)
   while (i < access_count)
     {
       bool modification;
+      tree a1_alias_type;
       access = VEC_index (access_p, access_vec, i);
       modification = access->write;
       if (access_precludes_ipa_sra_p (access))
        return NULL;
+      a1_alias_type = reference_alias_ptr_type (access->expr);
 
       /* Access is about to become group representative unless we find some
         nasty overlap which would preclude us from breaking this parameter
@@ -3496,7 +3863,11 @@ 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)))
+             || (reference_alias_ptr_type (ac2->expr) != a1_alias_type))
            return NULL;
 
          modification |= ac2->write;
@@ -3568,13 +3939,25 @@ decide_one_param_reduction (struct access *repr)
   for (; repr; repr = repr->next_grp)
     {
       gcc_assert (parm == repr->base);
-      new_param_count++;
+
+      /* Taking the address of a non-addressable field is verboten.  */
+      if (by_ref && repr->non_addressable)
+       return 0;
+
+      /* Do not decompose a non-BLKmode param in a way that would
+         create BLKmode params.  Especially for by-reference passing
+        (thus, pointer-type param) this is hardly worthwhile.  */
+      if (DECL_MODE (parm) != BLKmode
+         && TYPE_MODE (repr->type) == BLKmode)
+       return 0;
 
       if (!by_ref || (!repr->grp_maybe_modified
                      && !repr->grp_not_necessarilly_dereferenced))
        total_size += repr->size;
       else
        total_size += cur_parm_size;
+
+      new_param_count++;
     }
 
   gcc_assert (new_param_count > 0);
@@ -3731,6 +4114,7 @@ turn_representatives_into_adjustments (VEC (access_p, heap) *representatives,
              adj->base_index = index;
              adj->base = repr->base;
              adj->type = repr->type;
+             adj->alias_ptr_type = reference_alias_ptr_type (repr->expr);
              adj->offset = repr->offset;
              adj->by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
                             && (repr->grp_maybe_modified
@@ -3843,7 +4227,6 @@ get_replaced_param_substitute (struct ipa_parm_adjustment *adj)
       DECL_NAME (repl) = get_identifier (pretty_name);
       obstack_free (&name_obstack, pretty_name);
 
-      get_var_ann (repl);
       add_referenced_var (repl);
       adj->new_ssa_base = repl;
     }
@@ -4039,7 +4422,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);
            }
@@ -4162,57 +4545,115 @@ static void
 sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
 {
   int i, len;
+  gimple_stmt_iterator *gsip = NULL, gsi;
 
+  if (MAY_HAVE_DEBUG_STMTS && single_succ_p (ENTRY_BLOCK_PTR))
+    {
+      gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR));
+      gsip = &gsi;
+    }
   len = VEC_length (ipa_parm_adjustment_t, adjustments);
   for (i = 0; i < len; i++)
     {
       struct ipa_parm_adjustment *adj;
       imm_use_iterator ui;
-      gimple stmt;
-      tree name;
+      gimple stmt, def_temp;
+      tree name, vexpr, copy = NULL_TREE;
+      use_operand_p use_p;
 
       adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
       if (adj->copy_param || !is_gimple_reg (adj->base))
        continue;
       name = gimple_default_def (cfun, adj->base);
-      if (!name)
-       continue;
-      FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+      vexpr = NULL;
+      if (name)
+       FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+         {
+           /* All other users must have been removed by
+              ipa_sra_modify_function_body.  */
+           gcc_assert (is_gimple_debug (stmt));
+           if (vexpr == NULL && gsip != NULL)
+             {
+               gcc_assert (TREE_CODE (adj->base) == PARM_DECL);
+               vexpr = make_node (DEBUG_EXPR_DECL);
+               def_temp = gimple_build_debug_source_bind (vexpr, adj->base,
+                                                          NULL);
+               DECL_ARTIFICIAL (vexpr) = 1;
+               TREE_TYPE (vexpr) = TREE_TYPE (name);
+               DECL_MODE (vexpr) = DECL_MODE (adj->base);
+               gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
+             }
+           if (vexpr)
+             {
+               FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
+                 SET_USE (use_p, vexpr);
+             }
+           else
+             gimple_debug_bind_reset_value (stmt);
+           update_stmt (stmt);
+         }
+      /* Create a VAR_DECL for debug info purposes.  */
+      if (!DECL_IGNORED_P (adj->base))
        {
-         /* 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);
+         copy = build_decl (DECL_SOURCE_LOCATION (current_function_decl),
+                            VAR_DECL, DECL_NAME (adj->base),
+                            TREE_TYPE (adj->base));
+         if (DECL_PT_UID_SET_P (adj->base))
+           SET_DECL_PT_UID (copy, DECL_PT_UID (adj->base));
+         TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (adj->base);
+         TREE_READONLY (copy) = TREE_READONLY (adj->base);
+         TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (adj->base);
+         DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (adj->base);
+         DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (adj->base);
+         DECL_IGNORED_P (copy) = DECL_IGNORED_P (adj->base);
+         DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (adj->base);
+         DECL_SEEN_IN_BIND_EXPR_P (copy) = 1;
+         SET_DECL_RTL (copy, 0);
+         TREE_USED (copy) = 1;
+         DECL_CONTEXT (copy) = current_function_decl;
+         add_referenced_var (copy);
+         add_local_decl (cfun, copy);
+         DECL_CHAIN (copy) =
+           BLOCK_VARS (DECL_INITIAL (current_function_decl));
+         BLOCK_VARS (DECL_INITIAL (current_function_decl)) = copy;
+       }
+      if (gsip != NULL && copy && target_for_debug_bind (adj->base))
+       {
+         gcc_assert (TREE_CODE (adj->base) == PARM_DECL);
+         if (vexpr)
+           def_temp = gimple_build_debug_bind (copy, vexpr, NULL);
+         else
+           def_temp = gimple_build_debug_source_bind (copy, adj->base,
+                                                      NULL);
+         gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
        }
     }
 }
 
-/* Return true iff all callers have at least as many actual arguments as there
+/* Return false 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)
+not_all_callers_have_enough_arguments_p (struct cgraph_node *node,
+                                        void *data ATTRIBUTE_UNUSED)
 {
   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;
 
-  return true;
+  return false;
 }
 
+/* Convert all callers of NODE.  */
 
-/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS.  */
-
-static void
-convert_callers (struct cgraph_node *node, tree old_decl,
-                ipa_parm_adjustment_vec adjustments)
+static bool
+convert_callers_for_node (struct cgraph_node *node,
+                         void *data)
 {
-  tree old_cur_fndecl = current_function_decl;
-  struct cgraph_edge *cs;
-  basic_block this_block;
+  ipa_parm_adjustment_vec adjustments = (ipa_parm_adjustment_vec)data;
   bitmap recomputed_callers = BITMAP_ALLOC (NULL);
+  struct cgraph_edge *cs;
 
   for (cs = node->callers; cs; cs = cs->next_caller)
     {
@@ -4231,13 +4672,26 @@ convert_callers (struct cgraph_node *node, tree old_decl,
     }
 
   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)
+       && gimple_in_ssa_p (DECL_STRUCT_FUNCTION (cs->caller->decl)))
+      compute_inline_parameters (cs->caller, true);
   BITMAP_FREE (recomputed_callers);
 
+  return true;
+}
+
+/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS.  */
+
+static void
+convert_callers (struct cgraph_node *node, tree old_decl,
+                ipa_parm_adjustment_vec adjustments)
+{
+  tree old_cur_fndecl = current_function_decl;
+  basic_block this_block;
+
+  cgraph_for_node_and_aliases (node, convert_callers_for_node,
+                              adjustments, false);
+
   current_function_decl = old_cur_fndecl;
 
   if (!encountered_recursive_call)
@@ -4274,24 +4728,16 @@ 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);
+  VEC (cgraph_edge_p, heap) * redirect_callers = collect_callers_of_node (node);
 
   rebuild_cgraph_edges ();
+  free_dominance_info (CDI_DOMINATORS);
   pop_cfun ();
   current_function_decl = NULL_TREE;
 
   new_node = cgraph_function_versioning (node, redirect_callers, NULL, NULL,
-                                        NULL, NULL, "isra");
+                                        false, NULL, NULL, "isra");
   current_function_decl = new_node->decl;
   push_cfun (DECL_STRUCT_FUNCTION (new_node->decl));
 
@@ -4310,17 +4756,17 @@ modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
 static bool
 ipa_sra_preliminary_function_checks (struct cgraph_node *node)
 {
-  if (!tree_versionable_function_p (current_function_decl))
+  if (!cgraph_node_can_be_local_p (node))
     {
       if (dump_file)
-       fprintf (dump_file, "Function isn't allowed to be versioned.\n");
+       fprintf (dump_file, "Function not local to this compilation unit.\n");
       return false;
     }
 
-  if (!cgraph_node_can_be_local_p (node))
+  if (!node->local.can_change_signature)
     {
       if (dump_file)
-       fprintf (dump_file, "Function not local to this compilation unit.\n");
+       fprintf (dump_file, "Function can not change signature.\n");
       return false;
     }
 
@@ -4339,7 +4785,7 @@ ipa_sra_preliminary_function_checks (struct cgraph_node *node)
     }
 
   if ((DECL_COMDAT (node->decl) || DECL_EXTERNAL (node->decl))
-      && node->global.size >= MAX_INLINE_INSNS_AUTO)
+      && inline_summary(node)->size >= MAX_INLINE_INSNS_AUTO)
     {
       if (dump_file)
        fprintf (dump_file, "Function too big to be made truly local.\n");
@@ -4372,7 +4818,7 @@ ipa_sra_preliminary_function_checks (struct cgraph_node *node)
 static unsigned int
 ipa_early_sra (void)
 {
-  struct cgraph_node *node = cgraph_node (current_function_decl);
+  struct cgraph_node *node = cgraph_get_node (current_function_decl);
   ipa_parm_adjustment_vec adjustments;
   int ret = 0;
 
@@ -4389,7 +4835,8 @@ ipa_early_sra (void)
       goto simple_out;
     }
 
-  if (!all_callers_have_enough_arguments_p (node))
+  if (cgraph_for_node_and_aliases (node, not_all_callers_have_enough_arguments_p,
+                                  NULL, true))
     {
       if (dump_file)
        fprintf (dump_file, "There are callers with insufficient number of "
@@ -4451,7 +4898,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 =
@@ -4469,8 +4916,6 @@ struct gimple_opt_pass pass_early_ipa_sra =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func | TODO_dump_cgraph    /* todo_flags_finish */
+  TODO_dump_cgraph                     /* todo_flags_finish */
  }
 };
-
-