OSDN Git Service

PR middle-end/22156
[pf3gnuchains/gcc-fork.git] / gcc / tree-sra.c
index 6cf5e8e..ff7a52c 100644 (file)
@@ -9,7 +9,7 @@ This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
+Free Software Foundation; either version 3, or (at your option) any
 later version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT
@@ -18,9 +18,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -209,6 +208,9 @@ extern void debug_sra_elt_name (struct sra_elt *);
 
 /* Forward declarations.  */
 static tree generate_element_ref (struct sra_elt *);
+static tree sra_build_assignment (tree dst, tree src);
+static void mark_all_v_defs (tree list);
+
 \f
 /* Return true if DECL is an SRA candidate.  */
 
@@ -225,6 +227,7 @@ is_sra_scalar_type (tree type)
 {
   enum tree_code code = TREE_CODE (type);
   return (code == INTEGER_TYPE || code == REAL_TYPE || code == VECTOR_TYPE
+         || code == FIXED_POINT_TYPE
          || code == ENUMERAL_TYPE || code == BOOLEAN_TYPE
          || code == POINTER_TYPE || code == OFFSET_TYPE
          || code == REFERENCE_TYPE);
@@ -662,10 +665,17 @@ maybe_lookup_element_for_expr (tree expr)
       break;
 
     case COMPONENT_REF:
-      /* Don't look through unions.  */
-      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) != RECORD_TYPE)
-       return NULL;
-      child = TREE_OPERAND (expr, 1);
+      {
+       tree type = TREE_TYPE (TREE_OPERAND (expr, 0));
+       /* Don't look through unions.  */
+       if (TREE_CODE (type) != RECORD_TYPE)
+         return NULL;
+       /* Neither through variable-sized records.  */
+       if (TYPE_SIZE (type) == NULL_TREE
+           || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+         return NULL;
+       child = TREE_OPERAND (expr, 1);
+      }
       break;
 
     case REALPART_EXPR:
@@ -697,9 +707,10 @@ struct sra_walk_fns
   /* Invoked when ELT is required as a unit.  Note that ELT might refer to
      a leaf node, in which case this is a simple scalar reference.  *EXPR_P
      points to the location of the expression.  IS_OUTPUT is true if this
-     is a left-hand-side reference.  */
+     is a left-hand-side reference.  USE_ALL is true if we saw something we
+     couldn't quite identify and had to force the use of the entire object.  */
   void (*use) (struct sra_elt *elt, tree *expr_p,
-              block_stmt_iterator *bsi, bool is_output);
+              block_stmt_iterator *bsi, bool is_output, bool use_all);
 
   /* Invoked when we have a copy between two scalarizable references.  */
   void (*copy) (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt,
@@ -753,6 +764,7 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
   tree expr = *expr_p;
   tree inner = expr;
   bool disable_scalarization = false;
+  bool use_all_p = false;
 
   /* We're looking to collect a reference expression between EXPR and INNER,
      such that INNER is a scalarizable decl and all other nodes through EXPR
@@ -773,7 +785,7 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
            if (disable_scalarization)
              elt->cannot_scalarize = true;
            else
-             fns->use (elt, expr_p, bsi, is_output);
+             fns->use (elt, expr_p, bsi, is_output, use_all_p);
          }
        return;
 
@@ -813,14 +825,17 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
        break;
 
       case COMPONENT_REF:
-       /* A reference to a union member constitutes a reference to the
-          entire union.  */
-       if (TREE_CODE (TREE_TYPE (TREE_OPERAND (inner, 0))) != RECORD_TYPE)
-         goto use_all;
-       /* ??? See above re non-constant stride.  */
-       if (TREE_OPERAND (inner, 2))
-         goto use_all;
-       inner = TREE_OPERAND (inner, 0);
+       {
+         tree type = TREE_TYPE (TREE_OPERAND (inner, 0));
+         /* Don't look through unions.  */
+         if (TREE_CODE (type) != RECORD_TYPE)
+           goto use_all;
+         /* Neither through variable-sized records.  */
+         if (TYPE_SIZE (type) == NULL_TREE
+             || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+           goto use_all;
+         inner = TREE_OPERAND (inner, 0);
+       }
        break;
 
       case REALPART_EXPR:
@@ -860,6 +875,7 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output,
       use_all:
         expr_p = &TREE_OPERAND (inner, 0);
        inner = expr = *expr_p;
+       use_all_p = true;
        break;
 
       default:
@@ -907,9 +923,6 @@ sra_walk_asm_expr (tree expr, block_stmt_iterator *bsi,
   sra_walk_tree_list (ASM_OUTPUTS (expr), bsi, true, fns);
 }
 
-static void sra_replace (block_stmt_iterator *bsi, tree list);
-static tree sra_build_elt_assignment (struct sra_elt *elt, tree src);
-
 /* Walk a GIMPLE_MODIFY_STMT and categorize the assignment appropriately.  */
 
 static void
@@ -937,7 +950,7 @@ sra_walk_gimple_modify_stmt (tree expr, block_stmt_iterator *bsi,
       if (!rhs_elt->is_scalar && !TREE_SIDE_EFFECTS (lhs))
        fns->ldst (rhs_elt, lhs, bsi, false);
       else
-       fns->use (rhs_elt, &GIMPLE_STMT_OPERAND (expr, 1), bsi, false);
+       fns->use (rhs_elt, &GIMPLE_STMT_OPERAND (expr, 1), bsi, false, false);
     }
 
   /* If it isn't scalarizable, there may be scalarizable variables within, so
@@ -984,9 +997,7 @@ sra_walk_gimple_modify_stmt (tree expr, block_stmt_iterator *bsi,
       /* Otherwise we're being used in some context that requires the
         aggregate to be seen as a whole.  Invoke USE.  */
       else
-       {
-         fns->use (lhs_elt, &GIMPLE_STMT_OPERAND (expr, 0), bsi, true);
-       }
+       fns->use (lhs_elt, &GIMPLE_STMT_OPERAND (expr, 0), bsi, true, false);
     }
 
   /* Similarly to above, LHS_ELT being null only means that the LHS as a
@@ -1097,7 +1108,7 @@ find_candidates_for_sra (void)
 static void
 scan_use (struct sra_elt *elt, tree *expr_p ATTRIBUTE_UNUSED,
          block_stmt_iterator *bsi ATTRIBUTE_UNUSED,
-         bool is_output ATTRIBUTE_UNUSED)
+         bool is_output ATTRIBUTE_UNUSED, bool use_all ATTRIBUTE_UNUSED)
 {
   elt->n_uses += 1;
 }
@@ -1250,12 +1261,30 @@ instantiate_element (struct sra_elt *elt)
 
   for (base_elt = elt; base_elt->parent; base_elt = base_elt->parent)
     if (!nowarn)
-      nowarn = base_elt->parent->n_uses
-       || TREE_NO_WARNING (base_elt->parent->element);
+      nowarn = TREE_NO_WARNING (base_elt->parent->element);
   base = base_elt->element;
 
   elt->replacement = var = make_rename_temp (elt->type, "SR");
 
+  if (DECL_P (elt->element)
+      && !tree_int_cst_equal (DECL_SIZE (var), DECL_SIZE (elt->element)))
+    {
+      DECL_SIZE (var) = DECL_SIZE (elt->element);
+      DECL_SIZE_UNIT (var) = DECL_SIZE_UNIT (elt->element);
+
+      elt->in_bitfld_block = 1;
+      elt->replacement = build3 (BIT_FIELD_REF, elt->type, var,
+                                DECL_SIZE (var),
+                                BITS_BIG_ENDIAN
+                                ? size_binop (MINUS_EXPR,
+                                              TYPE_SIZE (elt->type),
+                                              DECL_SIZE (var))
+                                : bitsize_int (0));
+      if (!INTEGRAL_TYPE_P (elt->type)
+         || TYPE_UNSIGNED (elt->type))
+       BIT_FIELD_REF_UNSIGNED (elt->replacement) = 1;
+    }
+
   /* For vectors, if used on the left hand side with BIT_FIELD_REF,
      they are not a gimple register.  */
   if (TREE_CODE (TREE_TYPE (var)) == VECTOR_TYPE && elt->is_vector_lhs)
@@ -1289,6 +1318,18 @@ instantiate_element (struct sra_elt *elt)
       TREE_NO_WARNING (var) = 1;
     }
 
+  /* Zero-initialize bit-field scalarization variables, to avoid
+     triggering undefined behavior.  */
+  if (TREE_CODE (elt->element) == BIT_FIELD_REF
+      || (var != elt->replacement
+         && TREE_CODE (elt->replacement) == BIT_FIELD_REF))
+    {
+      tree init = sra_build_assignment (var, fold_convert (TREE_TYPE (var),
+                                                          integer_zero_node));
+      insert_edge_copies (init, ENTRY_BLOCK_PTR);
+      mark_all_v_defs (init);
+    }
+
   if (dump_file)
     {
       fputs ("  ", dump_file);
@@ -1417,7 +1458,8 @@ canon_type_for_field (tree f, tree element)
 static tree
 try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
 {
-  unsigned HOST_WIDE_INT align, oalign, word, bit, size, alchk;
+  int count;
+  unsigned HOST_WIDE_INT align, bit, size, alchk;
   enum machine_mode mode;
   tree first = f, prev;
   tree type, var;
@@ -1430,27 +1472,43 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
       || lookup_element (elt, f, NULL, NO_INSERT))
     return f;
 
-  /* Taking the alignment of elt->element is not enough, since it
-     might be just an array index or some such.  We shouldn't need to
-     initialize align here, but our optimizers don't always realize
-     that, if we leave the loop without initializing align, we'll fail
-     the assertion right after the loop.  */
-  align = (unsigned HOST_WIDE_INT)-1;
-  for (block = elt; block; block = block->parent)
-    if (DECL_P (block->element))
-      {
-       align = DECL_ALIGN (block->element);
-       break;
-      }
-  gcc_assert (block);
+  block = elt;
 
-  oalign = DECL_OFFSET_ALIGN (f);
-  word = tree_low_cst (DECL_FIELD_OFFSET (f), 1);
-  bit = tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
-  size = tree_low_cst (DECL_SIZE (f), 1);
+  /* For complex and array objects, there are going to be integer
+     literals as child elements.  In this case, we can't just take the
+     alignment and mode of the decl, so we instead rely on the element
+     type.
+
+     ??? We could try to infer additional alignment from the full
+     object declaration and the location of the sub-elements we're
+     accessing.  */
+  for (count = 0; !DECL_P (block->element); count++)
+    block = block->parent;
+
+  align = DECL_ALIGN (block->element);
+  alchk = GET_MODE_BITSIZE (DECL_MODE (block->element));
+
+  if (count)
+    {
+      type = TREE_TYPE (block->element);
+      while (count--)
+       type = TREE_TYPE (type);
+
+      align = TYPE_ALIGN (type);
+      alchk = GET_MODE_BITSIZE (TYPE_MODE (type));
+    }
+
+  if (align < alchk)
+    align = alchk;
 
-  if (align > oalign)
-    align = oalign;
+  /* Coalescing wider fields is probably pointless and
+     inefficient.  */
+  if (align > BITS_PER_WORD)
+    align = BITS_PER_WORD;
+
+  bit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+    + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+  size = tree_low_cst (DECL_SIZE (f), 1);
 
   alchk = align - 1;
   alchk = ~alchk;
@@ -1466,25 +1524,50 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
         && host_integerp (DECL_FIELD_OFFSET (f), 1)
         && host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)
         && host_integerp (DECL_SIZE (f), 1)
-        && (HOST_WIDE_INT)word == tree_low_cst (DECL_FIELD_OFFSET (f), 1)
         && !lookup_element (elt, f, NULL, NO_INSERT);
        prev = f, f = TREE_CHAIN (f))
     {
       unsigned HOST_WIDE_INT nbit, nsize;
 
-      nbit = tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+      nbit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+       + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
       nsize = tree_low_cst (DECL_SIZE (f), 1);
 
       if (bit + size == nbit)
        {
          if ((bit & alchk) != ((nbit + nsize - 1) & alchk))
-           break;
+           {
+             /* If we're at an alignment boundary, don't bother
+                growing alignment such that we can include this next
+                field.  */
+             if ((nbit & alchk)
+                 || GET_MODE_BITSIZE (DECL_MODE (f)) <= align)
+               break;
+
+             align = GET_MODE_BITSIZE (DECL_MODE (f));
+             alchk = align - 1;
+             alchk = ~alchk;
+
+             if ((bit & alchk) != ((nbit + nsize - 1) & alchk))
+               break;
+           }
          size += nsize;
        }
       else if (nbit + nsize == bit)
        {
          if ((nbit & alchk) != ((bit + size - 1) & alchk))
-           break;
+           {
+             if ((bit & alchk)
+                 || GET_MODE_BITSIZE (DECL_MODE (f)) <= align)
+               break;
+
+             align = GET_MODE_BITSIZE (DECL_MODE (f));
+             alchk = align - 1;
+             alchk = ~alchk;
+
+             if ((nbit & alchk) != ((bit + size - 1) & alchk))
+               break;
+           }
          bit = nbit;
          size += nsize;
        }
@@ -1509,7 +1592,7 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
       for (f = TYPE_FIELDS (elt->type);
           f; f = TREE_CHAIN (f))
        {
-         unsigned HOST_WIDE_INT fword, fbit, fsize;
+         unsigned HOST_WIDE_INT fbit, fsize;
 
          /* Skip the fields from first to prev.  */
          if (f == first)
@@ -1523,27 +1606,26 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
                && host_integerp (DECL_FIELD_BIT_OFFSET (f), 1)))
            continue;
 
-         fword = tree_low_cst (DECL_FIELD_OFFSET (f), 1);
+         fbit = tree_low_cst (DECL_FIELD_OFFSET (f), 1) * BITS_PER_UNIT
+           + tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
+
          /* If we're past the selected word, we're fine.  */
-         if (word < fword)
+         if ((bit & alchk) < (fbit & alchk))
            continue;
 
-         fbit = tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1);
-
          if (host_integerp (DECL_SIZE (f), 1))
            fsize = tree_low_cst (DECL_SIZE (f), 1);
          else
            /* Assume a variable-sized field takes up all space till
               the end of the word.  ??? Endianness issues?  */
-           fsize = align - fbit;
+           fsize = align - (fbit & alchk);
 
-         if (fword < word)
+         if ((fbit & alchk) < (bit & alchk))
            {
              /* A large field might start at a previous word and
                 extend into the selected word.  Exclude those
                 bits.  ??? Endianness issues? */
-             HOST_WIDE_INT diff = fbit + fsize
-               - (HOST_WIDE_INT)((word - fword) * BITS_PER_UNIT + mbit);
+             HOST_WIDE_INT diff = fbit + fsize - mbit;
 
              if (diff <= 0)
                continue;
@@ -1553,8 +1635,6 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
            }
          else
            {
-             gcc_assert (fword == word);
-
              /* Non-overlapping, great.  */
              if (fbit + fsize <= mbit
                  || mbit + msize <= fbit)
@@ -1601,7 +1681,7 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
   gcc_assert (type);
   var = build3 (BIT_FIELD_REF, type, NULL_TREE,
                bitsize_int (size),
-               bitsize_int (word * BITS_PER_UNIT + bit));
+               bitsize_int (bit));
   BIT_FIELD_REF_UNSIGNED (var) = 1;
 
   block = instantiate_missing_elements_1 (elt, var, type);
@@ -1609,16 +1689,14 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
 
   var = block->replacement;
 
-  if (((word * BITS_PER_UNIT + bit) & ~alchk)
+  if ((bit & ~alchk)
       || (HOST_WIDE_INT)size != tree_low_cst (DECL_SIZE (var), 1))
     {
       block->replacement = build3 (BIT_FIELD_REF,
                                   TREE_TYPE (block->element), var,
                                   bitsize_int (size),
-                                  bitsize_int ((word * BITS_PER_UNIT
-                                                + bit) & ~alchk));
+                                  bitsize_int (bit & ~alchk));
       BIT_FIELD_REF_UNSIGNED (block->replacement) = 1;
-      TREE_NO_WARNING (block->replacement) = 1;
     }
 
   block->in_bitfld_block = 2;
@@ -1636,12 +1714,12 @@ try_instantiate_multiple_fields (struct sra_elt *elt, tree f)
       fld->replacement = build3 (BIT_FIELD_REF, field_type, var,
                                 DECL_SIZE (f),
                                 bitsize_int
-                                ((word * BITS_PER_UNIT
+                                ((TREE_INT_CST_LOW (DECL_FIELD_OFFSET (f))
+                                  * BITS_PER_UNIT
                                   + (TREE_INT_CST_LOW
                                      (DECL_FIELD_BIT_OFFSET (f))))
                                  & ~alchk));
       BIT_FIELD_REF_UNSIGNED (fld->replacement) = TYPE_UNSIGNED (field_type);
-      TREE_NO_WARNING (block->replacement) = 1;
       fld->in_bitfld_block = 1;
     }
 
@@ -1775,8 +1853,8 @@ decide_block_copy (struct sra_elt *elt)
       return false;
     }
 
-  /* Don't decide if we've no uses.  */
-  if (elt->n_uses == 0 && elt->n_copies == 0)
+  /* Don't decide if we've no uses and no groups.  */
+  if (elt->n_uses == 0 && elt->n_copies == 0 && elt->groups == NULL)
     ;
 
   else if (!elt->is_scalar)
@@ -1988,7 +2066,7 @@ generate_one_element_ref (struct sra_elt *elt, tree base)
           yet.  */
        if (TREE_CODE (field) == BIT_FIELD_REF)
          {
-           tree ret = copy_node (field);
+           tree ret = unshare_expr (field);
            TREE_OPERAND (ret, 0) = base;
            return ret;
          }
@@ -2029,11 +2107,180 @@ generate_element_ref (struct sra_elt *elt)
     return elt->element;
 }
 
+/* Return true if BF is a bit-field that we can handle like a scalar.  */
+
+static bool
+scalar_bitfield_p (tree bf)
+{
+  return (TREE_CODE (bf) == BIT_FIELD_REF
+         && (is_gimple_reg (TREE_OPERAND (bf, 0))
+             || (TYPE_MODE (TREE_TYPE (TREE_OPERAND (bf, 0))) != BLKmode
+                 && (!TREE_SIDE_EFFECTS (TREE_OPERAND (bf, 0))
+                     || (GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE
+                                                      (TREE_OPERAND (bf, 0))))
+                         <= BITS_PER_WORD)))));
+}
+
 /* Create an assignment statement from SRC to DST.  */
 
 static tree
 sra_build_assignment (tree dst, tree src)
 {
+  /* Turning BIT_FIELD_REFs into bit operations enables other passes
+     to do a much better job at optimizing the code.  */
+  if (scalar_bitfield_p (src))
+    {
+      tree cst, cst2, mask, minshift, maxshift;
+      tree tmp, var, utype, stype;
+      tree list, stmt;
+      bool unsignedp = BIT_FIELD_REF_UNSIGNED (src);
+
+      var = TREE_OPERAND (src, 0);
+      cst = TREE_OPERAND (src, 2);
+      cst2 = size_binop (PLUS_EXPR, TREE_OPERAND (src, 1),
+                        TREE_OPERAND (src, 2));
+
+      if (BITS_BIG_ENDIAN)
+       {
+         maxshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst);
+         minshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst2);
+       }
+      else
+       {
+         maxshift = cst2;
+         minshift = cst;
+       }
+
+      stype = TREE_TYPE (var);
+      if (!INTEGRAL_TYPE_P (stype))
+       stype = lang_hooks.types.type_for_size (TREE_INT_CST_LOW
+                                               (TYPE_SIZE (stype)), 1);
+      else if (!TYPE_UNSIGNED (stype))
+       stype = unsigned_type_for (stype);
+
+      utype = TREE_TYPE (dst);
+      if (!INTEGRAL_TYPE_P (utype))
+       utype = lang_hooks.types.type_for_size (TREE_INT_CST_LOW
+                                               (TYPE_SIZE (utype)), 1);
+      else if (!TYPE_UNSIGNED (utype))
+       utype = unsigned_type_for (utype);
+
+      list = NULL;
+
+      cst2 = size_binop (MINUS_EXPR, maxshift, minshift);
+      if (tree_int_cst_equal (cst2, TYPE_SIZE (utype)))
+       {
+         unsignedp = true;
+         mask = NULL_TREE;
+       }
+      else
+       {
+         mask = build_int_cst_wide (utype, 1, 0);
+         cst = int_const_binop (LSHIFT_EXPR, mask, cst2, true);
+         mask = int_const_binop (MINUS_EXPR, cst, mask, true);
+       }
+
+      tmp = make_rename_temp (stype, "SR");
+      if (TYPE_MAIN_VARIANT (TREE_TYPE (var)) != TYPE_MAIN_VARIANT (stype))
+       {
+         if (INTEGRAL_TYPE_P (TREE_TYPE (var)))
+           stmt = build_gimple_modify_stmt (tmp,
+                                            fold_convert (stype, var));
+         else
+           stmt = build_gimple_modify_stmt (tmp,
+                                            fold_build1 (VIEW_CONVERT_EXPR,
+                                                         stype, var));
+         append_to_statement_list (stmt, &list);
+
+         var = tmp;
+       }
+
+      if (!integer_zerop (minshift))
+       {
+         tmp = make_rename_temp (stype, "SR");
+         stmt = build_gimple_modify_stmt (tmp,
+                                          fold_build2 (RSHIFT_EXPR, stype,
+                                                       var, minshift));
+         append_to_statement_list (stmt, &list);
+
+         var = tmp;
+       }
+
+      if (TYPE_MAIN_VARIANT (utype) != TYPE_MAIN_VARIANT (stype))
+       {
+         if (!mask && unsignedp
+             && (TYPE_MAIN_VARIANT (utype)
+                 == TYPE_MAIN_VARIANT (TREE_TYPE (dst))))
+           tmp = dst;
+         else
+           tmp = make_rename_temp (utype, "SR");
+
+         stmt = build_gimple_modify_stmt (tmp, fold_convert (utype, var));
+         append_to_statement_list (stmt, &list);
+
+         var = tmp;
+       }
+
+      if (mask)
+       {
+         if (!unsignedp
+             || (TYPE_MAIN_VARIANT (TREE_TYPE (dst))
+                 != TYPE_MAIN_VARIANT (utype)))
+           tmp = make_rename_temp (utype, "SR");
+         else
+           tmp = dst;
+
+         stmt = build_gimple_modify_stmt (tmp,
+                                          fold_build2 (BIT_AND_EXPR, utype,
+                                                       var, mask));
+         append_to_statement_list (stmt, &list);
+
+         var = tmp;
+       }
+
+      if (!unsignedp)
+       {
+         tree signbit = int_const_binop (LSHIFT_EXPR,
+                                         build_int_cst_wide (utype, 1, 0),
+                                         size_binop (MINUS_EXPR, cst2,
+                                                     bitsize_int (1)),
+                                         true);
+
+         tmp = make_rename_temp (utype, "SR");
+         stmt = build_gimple_modify_stmt (tmp,
+                                          fold_build2 (BIT_XOR_EXPR, utype,
+                                                       var, signbit));
+         append_to_statement_list (stmt, &list);
+
+         var = tmp;
+
+         if (TYPE_MAIN_VARIANT (TREE_TYPE (dst)) != TYPE_MAIN_VARIANT (utype))
+           tmp = make_rename_temp (utype, "SR");
+         else
+           tmp = dst;
+
+         stmt = build_gimple_modify_stmt (tmp,
+                                          fold_build2 (MINUS_EXPR, utype,
+                                                       var, signbit));
+         append_to_statement_list (stmt, &list);
+
+         var = tmp;
+       }
+
+      if (var != dst)
+       {
+         if (INTEGRAL_TYPE_P (TREE_TYPE (dst)))
+           var = fold_convert (TREE_TYPE (dst), var);
+         else
+           var = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (dst), var);
+
+         stmt = build_gimple_modify_stmt (dst, var);
+         append_to_statement_list (stmt, &list);
+       }
+
+      return list;
+    }
+
   /* It was hoped that we could perform some type sanity checking
      here, but since front-ends can emit accesses of fields in types
      different from their nominal types and copy structures containing
@@ -2047,101 +2294,136 @@ sra_build_assignment (tree dst, tree src)
 
 /* BIT_FIELD_REFs must not be shared.  sra_build_elt_assignment()
    takes care of assignments, but we must create copies for uses.  */
-#define REPLDUP(t) (TREE_CODE (t) != BIT_FIELD_REF ? (t) : copy_node (t))
+#define REPLDUP(t) (TREE_CODE (t) != BIT_FIELD_REF ? (t) : unshare_expr (t))
+
+/* Emit an assignment from SRC to DST, but if DST is a scalarizable
+   BIT_FIELD_REF, turn it into bit operations.  */
 
 static tree
-sra_build_elt_assignment (struct sra_elt *elt, tree src)
+sra_build_bf_assignment (tree dst, tree src)
 {
-  tree dst = elt->replacement;
-  tree var, type, tmp, tmp2, tmp3;
+  tree var, type, utype, tmp, tmp2, tmp3;
   tree list, stmt;
   tree cst, cst2, mask;
-  tree minshift = NULL, maxshift = NULL;
+  tree minshift, maxshift;
 
-  if (TREE_CODE (dst) != BIT_FIELD_REF
-      || !elt->in_bitfld_block)
-    return sra_build_assignment (REPLDUP (dst), src);
+  if (TREE_CODE (dst) != BIT_FIELD_REF)
+    return sra_build_assignment (dst, src);
 
   var = TREE_OPERAND (dst, 0);
 
-  /* Try to widen the assignment to the entire variable.
-     We need the source to be a BIT_FIELD_REF as well, such that, for
-     BIT_FIELD_REF<d,sz,dp> = BIT_FIELD_REF<s,sz,sp>,
-     if sp >= dp, we can turn it into
-     d = BIT_FIELD_REF<s,sp+sz,sp-dp>.  */
-  if (elt->in_bitfld_block == 2
-      && TREE_CODE (src) == BIT_FIELD_REF
-      && !tree_int_cst_lt (TREE_OPERAND (src, 2), TREE_OPERAND (dst, 2)))
-    {
-      src = fold_build3 (BIT_FIELD_REF, TREE_TYPE (var),
-                        TREE_OPERAND (src, 0),
-                        size_binop (PLUS_EXPR, TREE_OPERAND (src, 1),
-                                    TREE_OPERAND (dst, 2)),
-                        size_binop (MINUS_EXPR, TREE_OPERAND (src, 2),
-                                    TREE_OPERAND (dst, 2)));
-      BIT_FIELD_REF_UNSIGNED (src) = 1;
-
-      return sra_build_assignment (var, src);
-    }
-
-  if (!is_gimple_reg (var))
+  if (!scalar_bitfield_p (dst))
     return sra_build_assignment (REPLDUP (dst), src);
 
-  list = alloc_stmt_list ();
+  list = NULL;
+
+  cst = fold_convert (bitsizetype, TREE_OPERAND (dst, 2));
+  cst2 = size_binop (PLUS_EXPR,
+                    fold_convert (bitsizetype, TREE_OPERAND (dst, 1)),
+                    cst);
 
-  cst = TREE_OPERAND (dst, 2);
-  if (WORDS_BIG_ENDIAN)
+  if (BITS_BIG_ENDIAN)
     {
-      cst = size_binop (MINUS_EXPR, DECL_SIZE (var), cst);
-      maxshift = cst;
+      maxshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst);
+      minshift = size_binop (MINUS_EXPR, TYPE_SIZE (TREE_TYPE (var)), cst2);
     }
   else
-    minshift = cst;
-
-  cst2 = size_binop (PLUS_EXPR, TREE_OPERAND (dst, 1),
-                    TREE_OPERAND (dst, 2));
-  if (WORDS_BIG_ENDIAN)
     {
-      cst2 = size_binop (MINUS_EXPR, DECL_SIZE (var), cst2);
-      minshift = cst2;
+      maxshift = cst2;
+      minshift = cst;
     }
-  else
-    maxshift = cst2;
 
   type = TREE_TYPE (var);
+  if (!INTEGRAL_TYPE_P (type))
+    type = lang_hooks.types.type_for_size
+      (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (var))), 1);
+  if (TYPE_UNSIGNED (type))
+    utype = type;
+  else
+    utype = unsigned_type_for (type);
 
-  mask = build_int_cst_wide (type, 1, 0);
-  cst = int_const_binop (LSHIFT_EXPR, mask, maxshift, 1);
-  cst2 = int_const_binop (LSHIFT_EXPR, mask, minshift, 1);
-  mask = int_const_binop (MINUS_EXPR, cst, cst2, 1);
-  mask = fold_build1 (BIT_NOT_EXPR, type, mask);
+  mask = build_int_cst_wide (utype, 1, 0);
+  cst = int_const_binop (LSHIFT_EXPR, mask, maxshift, true);
+  cst2 = int_const_binop (LSHIFT_EXPR, mask, minshift, true);
+  mask = int_const_binop (MINUS_EXPR, cst, cst2, true);
+  mask = fold_build1 (BIT_NOT_EXPR, utype, mask);
 
-  if (!WORDS_BIG_ENDIAN)
-    cst2 = TREE_OPERAND (dst, 2);
+  if (TYPE_MAIN_VARIANT (utype) != TYPE_MAIN_VARIANT (TREE_TYPE (var))
+      && !integer_zerop (mask))
+    {
+      tmp = var;
+      if (!is_gimple_variable (tmp))
+       tmp = unshare_expr (var);
 
-  tmp = make_rename_temp (type, "SR");
-  stmt = build_gimple_modify_stmt (tmp,
-                                  fold_build2 (BIT_AND_EXPR, type,
-                                               var, mask));
-  append_to_statement_list (stmt, &list);
+      tmp2 = make_rename_temp (utype, "SR");
 
-  if (is_gimple_reg (src))
-    tmp2 = src;
+      if (INTEGRAL_TYPE_P (TREE_TYPE (var)))
+       stmt = build_gimple_modify_stmt (tmp2, fold_convert (utype, tmp));
+      else
+       stmt = build_gimple_modify_stmt (tmp2, fold_build1 (VIEW_CONVERT_EXPR,
+                                                           utype, tmp));
+      append_to_statement_list (stmt, &list);
+    }
   else
+    tmp2 = var;
+
+  if (!integer_zerop (mask))
+    {
+      tmp = make_rename_temp (utype, "SR");
+      stmt = build_gimple_modify_stmt (tmp,
+                                      fold_build2 (BIT_AND_EXPR, utype,
+                                                   tmp2, mask));
+      append_to_statement_list (stmt, &list);
+    }
+  else
+    tmp = mask;
+
+  if (is_gimple_reg (src) && INTEGRAL_TYPE_P (TREE_TYPE (src)))
+    tmp2 = src;
+  else if (INTEGRAL_TYPE_P (TREE_TYPE (src)))
     {
       tmp2 = make_rename_temp (TREE_TYPE (src), "SR");
       stmt = sra_build_assignment (tmp2, src);
       append_to_statement_list (stmt, &list);
     }
+  else
+    {
+      tmp2 = make_rename_temp
+       (lang_hooks.types.type_for_size
+        (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (src))),
+         1), "SR");
+      stmt = sra_build_assignment (tmp2, fold_build1 (VIEW_CONVERT_EXPR,
+                                                     TREE_TYPE (tmp2), src));
+      append_to_statement_list (stmt, &list);
+    }
 
-  if (!TYPE_UNSIGNED (TREE_TYPE (tmp2))
-      || TYPE_MAIN_VARIANT (TREE_TYPE (tmp2)) != TYPE_MAIN_VARIANT (type))
+  if (!TYPE_UNSIGNED (TREE_TYPE (tmp2)))
     {
-      tmp3 = make_rename_temp (type, "SR");
-      tmp2 = fold_build3 (BIT_FIELD_REF, type, tmp2, TREE_OPERAND (dst, 1),
-                         bitsize_int (0));
-      if (TREE_CODE (tmp2) == BIT_FIELD_REF)
-       BIT_FIELD_REF_UNSIGNED (tmp2) = 1;
+      tree ut = unsigned_type_for (TREE_TYPE (tmp2));
+      tmp3 = make_rename_temp (ut, "SR");
+      tmp2 = fold_convert (ut, tmp2);
+      stmt = sra_build_assignment (tmp3, tmp2);
+      append_to_statement_list (stmt, &list);
+
+      tmp2 = fold_build1 (BIT_NOT_EXPR, utype, mask);
+      tmp2 = int_const_binop (RSHIFT_EXPR, tmp2, minshift, true);
+      tmp2 = fold_convert (ut, tmp2);
+      tmp2 = fold_build2 (BIT_AND_EXPR, utype, tmp3, tmp2);
+
+      if (tmp3 != tmp2)
+       {
+         tmp3 = make_rename_temp (ut, "SR");
+         stmt = sra_build_assignment (tmp3, tmp2);
+         append_to_statement_list (stmt, &list);
+       }
+
+      tmp2 = tmp3;
+    }
+
+  if (TYPE_MAIN_VARIANT (TREE_TYPE (tmp2)) != TYPE_MAIN_VARIANT (utype))
+    {
+      tmp3 = make_rename_temp (utype, "SR");
+      tmp2 = fold_convert (utype, tmp2);
       stmt = sra_build_assignment (tmp3, tmp2);
       append_to_statement_list (stmt, &list);
       tmp2 = tmp3;
@@ -2149,22 +2431,117 @@ sra_build_elt_assignment (struct sra_elt *elt, tree src)
 
   if (!integer_zerop (minshift))
     {
-      tmp3 = make_rename_temp (type, "SR");
+      tmp3 = make_rename_temp (utype, "SR");
       stmt = build_gimple_modify_stmt (tmp3,
-                                      fold_build2 (LSHIFT_EXPR, type,
+                                      fold_build2 (LSHIFT_EXPR, utype,
                                                    tmp2, minshift));
       append_to_statement_list (stmt, &list);
       tmp2 = tmp3;
     }
 
-  stmt = build_gimple_modify_stmt (var,
-                                  fold_build2 (BIT_IOR_EXPR, type,
+  if (utype != TREE_TYPE (var))
+    tmp3 = make_rename_temp (utype, "SR");
+  else
+    tmp3 = var;
+  stmt = build_gimple_modify_stmt (tmp3,
+                                  fold_build2 (BIT_IOR_EXPR, utype,
                                                tmp, tmp2));
   append_to_statement_list (stmt, &list);
 
+  if (tmp3 != var)
+    {
+      if (TREE_TYPE (var) == type)
+       stmt = build_gimple_modify_stmt (var,
+                                        fold_convert (type, tmp3));
+      else
+       stmt = build_gimple_modify_stmt (var,
+                                        fold_build1 (VIEW_CONVERT_EXPR,
+                                                     TREE_TYPE (var), tmp3));
+      append_to_statement_list (stmt, &list);
+    }
+
   return list;
 }
 
+/* Expand an assignment of SRC to the scalarized representation of
+   ELT.  If it is a field group, try to widen the assignment to cover
+   the full variable.  */
+
+static tree
+sra_build_elt_assignment (struct sra_elt *elt, tree src)
+{
+  tree dst = elt->replacement;
+  tree var, tmp, cst, cst2, list, stmt;
+
+  if (TREE_CODE (dst) != BIT_FIELD_REF
+      || !elt->in_bitfld_block)
+    return sra_build_assignment (REPLDUP (dst), src);
+
+  var = TREE_OPERAND (dst, 0);
+
+  /* Try to widen the assignment to the entire variable.
+     We need the source to be a BIT_FIELD_REF as well, such that, for
+     BIT_FIELD_REF<d,sz,dp> = BIT_FIELD_REF<s,sz,sp>,
+     by design, conditions are met such that we can turn it into
+     d = BIT_FIELD_REF<s,dw,sp-dp>.  */
+  if (elt->in_bitfld_block == 2
+      && TREE_CODE (src) == BIT_FIELD_REF)
+    {
+      cst = TYPE_SIZE (TREE_TYPE (var));
+      cst2 = size_binop (MINUS_EXPR, TREE_OPERAND (src, 2),
+                        TREE_OPERAND (dst, 2));
+
+      src = TREE_OPERAND (src, 0);
+
+      /* Avoid full-width bit-fields.  */
+      if (integer_zerop (cst2)
+         && tree_int_cst_equal (cst, TYPE_SIZE (TREE_TYPE (src))))
+       {
+         if (INTEGRAL_TYPE_P (TREE_TYPE (src))
+             && !TYPE_UNSIGNED (TREE_TYPE (src)))
+           src = fold_convert (unsigned_type_for (TREE_TYPE (src)), src);
+
+         /* If a single conversion won't do, we'll need a statement
+            list.  */
+         if (TYPE_MAIN_VARIANT (TREE_TYPE (var))
+             != TYPE_MAIN_VARIANT (TREE_TYPE (src)))
+           {
+             list = NULL;
+
+             if (!INTEGRAL_TYPE_P (TREE_TYPE (src))
+                 || !TYPE_UNSIGNED (TREE_TYPE (src)))
+               src = fold_build1 (VIEW_CONVERT_EXPR,
+                                  lang_hooks.types.type_for_size
+                                  (TREE_INT_CST_LOW
+                                   (TYPE_SIZE (TREE_TYPE (src))),
+                                   1), src);
+
+             tmp = make_rename_temp (TREE_TYPE (src), "SR");
+             stmt = build_gimple_modify_stmt (tmp, src);
+             append_to_statement_list (stmt, &list);
+
+             stmt = sra_build_assignment (var,
+                                          fold_convert (TREE_TYPE (var),
+                                                        tmp));
+             append_to_statement_list (stmt, &list);
+
+             return list;
+           }
+
+         src = fold_convert (TREE_TYPE (var), src);
+       }
+      else
+       {
+         src = fold_build3 (BIT_FIELD_REF, TREE_TYPE (var), src, cst, cst2);
+         BIT_FIELD_REF_UNSIGNED (src) = 1;
+       }
+
+      return sra_build_assignment (var, src);
+    }
+
+  return sra_build_bf_assignment (dst, src);
+}
+
 /* Generate a set of assignment statements in *LIST_P to copy all
    instantiated elements under ELT to or from the equivalent structure
    rooted at EXPR.  COPY_OUT controls the direction of the copy, with
@@ -2188,7 +2565,7 @@ generate_copy_inout (struct sra_elt *elt, bool copy_out, tree expr,
       i = c->replacement;
 
       t = build2 (COMPLEX_EXPR, elt->type, r, i);
-      t = sra_build_assignment (expr, t);
+      t = sra_build_bf_assignment (expr, t);
       SSA_NAME_DEF_STMT (expr) = t;
       append_to_statement_list (t, list_p);
     }
@@ -2197,7 +2574,7 @@ generate_copy_inout (struct sra_elt *elt, bool copy_out, tree expr,
       if (copy_out)
        t = sra_build_elt_assignment (elt, expr);
       else
-       t = sra_build_assignment (expr, REPLDUP (elt->replacement));
+       t = sra_build_bf_assignment (expr, REPLDUP (elt->replacement));
       append_to_statement_list (t, list_p);
     }
   else
@@ -2471,50 +2848,384 @@ sra_replace (block_stmt_iterator *bsi, tree list)
     bsi_prev (bsi);
 }
 
+/* Data structure that bitfield_overlaps_p fills in with information
+   about the element passed in and how much of it overlaps with the
+   bit-range passed it to.  */
+
+struct bitfield_overlap_info
+{
+  /* The bit-length of an element.  */
+  tree field_len;
+
+  /* The bit-position of the element in its parent.  */
+  tree field_pos;
+
+  /* The number of bits of the element that overlap with the incoming
+     bit range.  */
+  tree overlap_len;
+
+  /* The first bit of the element that overlaps with the incoming bit
+     range.  */
+  tree overlap_pos;
+};
+
+/* Return true if a BIT_FIELD_REF<(FLD->parent), BLEN, BPOS>
+   expression (refereced as BF below) accesses any of the bits in FLD,
+   false if it doesn't.  If DATA is non-null, its field_len and
+   field_pos are filled in such that BIT_FIELD_REF<(FLD->parent),
+   field_len, field_pos> (referenced as BFLD below) represents the
+   entire field FLD->element, and BIT_FIELD_REF<BFLD, overlap_len,
+   overlap_pos> represents the portion of the entire field that
+   overlaps with BF.  */
+
+static bool
+bitfield_overlaps_p (tree blen, tree bpos, struct sra_elt *fld,
+                    struct bitfield_overlap_info *data)
+{
+  tree flen, fpos;
+  bool ret;
+
+  if (TREE_CODE (fld->element) == FIELD_DECL)
+    {
+      flen = fold_convert (bitsizetype, DECL_SIZE (fld->element));
+      fpos = fold_convert (bitsizetype, DECL_FIELD_OFFSET (fld->element));
+      fpos = size_binop (MULT_EXPR, fpos, bitsize_int (BITS_PER_UNIT));
+      fpos = size_binop (PLUS_EXPR, fpos, DECL_FIELD_BIT_OFFSET (fld->element));
+    }
+  else if (TREE_CODE (fld->element) == BIT_FIELD_REF)
+    {
+      flen = fold_convert (bitsizetype, TREE_OPERAND (fld->element, 1));
+      fpos = fold_convert (bitsizetype, TREE_OPERAND (fld->element, 2));
+    }
+  else
+    gcc_unreachable ();
+
+  gcc_assert (host_integerp (blen, 1)
+             && host_integerp (bpos, 1)
+             && host_integerp (flen, 1)
+             && host_integerp (fpos, 1));
+
+  ret = ((!tree_int_cst_lt (fpos, bpos)
+         && tree_int_cst_lt (size_binop (MINUS_EXPR, fpos, bpos),
+                             blen))
+        || (!tree_int_cst_lt (bpos, fpos)
+            && tree_int_cst_lt (size_binop (MINUS_EXPR, bpos, fpos),
+                                flen)));
+
+  if (!ret)
+    return ret;
+
+  if (data)
+    {
+      tree bend, fend;
+
+      data->field_len = flen;
+      data->field_pos = fpos;
+
+      fend = size_binop (PLUS_EXPR, fpos, flen);
+      bend = size_binop (PLUS_EXPR, bpos, blen);
+
+      if (tree_int_cst_lt (bend, fend))
+       data->overlap_len = size_binop (MINUS_EXPR, bend, fpos);
+      else
+       data->overlap_len = NULL;
+
+      if (tree_int_cst_lt (fpos, bpos))
+       {
+         data->overlap_pos = size_binop (MINUS_EXPR, bpos, fpos);
+         data->overlap_len = size_binop (MINUS_EXPR,
+                                         data->overlap_len
+                                         ? data->overlap_len
+                                         : data->field_len,
+                                         data->overlap_pos);
+       }
+      else
+       data->overlap_pos = NULL;
+    }
+
+  return ret;
+}
+
+/* Add to LISTP a sequence of statements that copies BLEN bits between
+   VAR and the scalarized elements of ELT, starting a bit VPOS of VAR
+   and at bit BPOS of ELT.  The direction of the copy is given by
+   TO_VAR.  */
+
+static void
+sra_explode_bitfield_assignment (tree var, tree vpos, bool to_var,
+                                tree *listp, tree blen, tree bpos,
+                                struct sra_elt *elt)
+{
+  struct sra_elt *fld;
+  struct bitfield_overlap_info flp;
+
+  FOR_EACH_ACTUAL_CHILD (fld, elt)
+    {
+      tree flen, fpos;
+
+      if (!bitfield_overlaps_p (blen, bpos, fld, &flp))
+       continue;
+
+      flen = flp.overlap_len ? flp.overlap_len : flp.field_len;
+      fpos = flp.overlap_pos ? flp.overlap_pos : bitsize_int (0);
+
+      if (fld->replacement)
+       {
+         tree infld, invar, st;
+
+         infld = fld->replacement;
+
+         if (TREE_CODE (infld) == BIT_FIELD_REF)
+           {
+             fpos = size_binop (PLUS_EXPR, fpos, TREE_OPERAND (infld, 2));
+             infld = TREE_OPERAND (infld, 0);
+           }
+         else if (BITS_BIG_ENDIAN && DECL_P (fld->element)
+                  && !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (infld)),
+                                          DECL_SIZE (fld->element)))
+           {
+             fpos = size_binop (PLUS_EXPR, fpos,
+                                TYPE_SIZE (TREE_TYPE (infld)));
+             fpos = size_binop (MINUS_EXPR, fpos,
+                                DECL_SIZE (fld->element));
+           }
+
+         infld = fold_build3 (BIT_FIELD_REF,
+                              lang_hooks.types.type_for_size
+                              (TREE_INT_CST_LOW (flen), 1),
+                              infld, flen, fpos);
+         BIT_FIELD_REF_UNSIGNED (infld) = 1;
+
+         invar = size_binop (MINUS_EXPR, flp.field_pos, bpos);
+         if (flp.overlap_pos)
+           invar = size_binop (PLUS_EXPR, invar, flp.overlap_pos);
+         invar = size_binop (PLUS_EXPR, invar, vpos);
+
+         invar = fold_build3 (BIT_FIELD_REF, TREE_TYPE (infld),
+                              var, flen, invar);
+         BIT_FIELD_REF_UNSIGNED (invar) = 1;
+
+         if (to_var)
+           st = sra_build_bf_assignment (invar, infld);
+         else
+           st = sra_build_bf_assignment (infld, invar);
+
+         append_to_statement_list (st, listp);
+       }
+      else
+       {
+         tree sub = size_binop (MINUS_EXPR, flp.field_pos, bpos);
+         sub = size_binop (PLUS_EXPR, vpos, sub);
+         if (flp.overlap_pos)
+           sub = size_binop (PLUS_EXPR, sub, flp.overlap_pos);
+
+         sra_explode_bitfield_assignment (var, sub, to_var, listp,
+                                          flen, fpos, fld);
+       }
+    }
+}
+
+/* Add to LISTBEFOREP statements that copy scalarized members of ELT
+   that overlap with BIT_FIELD_REF<(ELT->element), BLEN, BPOS> back
+   into the full variable, and to LISTAFTERP, if non-NULL, statements
+   that copy the (presumably modified) overlapping portions of the
+   full variable back to the scalarized variables.  */
+
+static void
+sra_sync_for_bitfield_assignment (tree *listbeforep, tree *listafterp,
+                                 tree blen, tree bpos,
+                                 struct sra_elt *elt)
+{
+  struct sra_elt *fld;
+  struct bitfield_overlap_info flp;
+
+  FOR_EACH_ACTUAL_CHILD (fld, elt)
+    if (bitfield_overlaps_p (blen, bpos, fld, &flp))
+      {
+       if (fld->replacement || (!flp.overlap_len && !flp.overlap_pos))
+         {
+           generate_copy_inout (fld, false, generate_element_ref (fld),
+                                listbeforep);
+           mark_no_warning (fld);
+           if (listafterp)
+             generate_copy_inout (fld, true, generate_element_ref (fld),
+                                  listafterp);
+         }
+       else
+         {
+           tree flen = flp.overlap_len ? flp.overlap_len : flp.field_len;
+           tree fpos = flp.overlap_pos ? flp.overlap_pos : bitsize_int (0);
+
+           sra_sync_for_bitfield_assignment (listbeforep, listafterp,
+                                             flen, fpos, fld);
+         }
+      }
+}
+
 /* Scalarize a USE.  To recap, this is either a simple reference to ELT,
    if elt is scalar, or some occurrence of ELT that requires a complete
    aggregate.  IS_OUTPUT is true if ELT is being modified.  */
 
 static void
 scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
-              bool is_output)
+              bool is_output, bool use_all)
 {
-  tree list = NULL, stmt = bsi_stmt (*bsi);
+  tree stmt = bsi_stmt (*bsi);
+  tree bfexpr;
 
   if (elt->replacement)
     {
+      tree replacement = elt->replacement;
+
       /* If we have a replacement, then updating the reference is as
         simple as modifying the existing statement in place.  */
-      if (is_output)
+      if (is_output
+         && TREE_CODE (elt->replacement) == BIT_FIELD_REF
+         && is_gimple_reg (TREE_OPERAND (elt->replacement, 0))
+         && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+         && &GIMPLE_STMT_OPERAND (stmt, 0) == expr_p)
        {
-         if (TREE_CODE (elt->replacement) == BIT_FIELD_REF
-             && is_gimple_reg (TREE_OPERAND (elt->replacement, 0))
-             && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
-             && &GIMPLE_STMT_OPERAND (stmt, 0) == expr_p)
+         tree newstmt = sra_build_elt_assignment
+           (elt, GIMPLE_STMT_OPERAND (stmt, 1));
+         if (TREE_CODE (newstmt) != STATEMENT_LIST)
            {
-             tree newstmt = sra_build_elt_assignment
-               (elt, GIMPLE_STMT_OPERAND (stmt, 1));
-             if (TREE_CODE (newstmt) != STATEMENT_LIST)
-               {
-                 tree list = alloc_stmt_list ();
-                 append_to_statement_list (newstmt, &list);
-                 newstmt = list;
-               }
-             sra_replace (bsi, newstmt);
-             return;
+             tree list = NULL;
+             append_to_statement_list (newstmt, &list);
+             newstmt = list;
            }
+         sra_replace (bsi, newstmt);
+         return;
+       }
+      else if (!is_output
+              && TREE_CODE (elt->replacement) == BIT_FIELD_REF
+              && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+              && &GIMPLE_STMT_OPERAND (stmt, 1) == expr_p)
+       {
+         tree tmp = make_rename_temp
+           (TREE_TYPE (GIMPLE_STMT_OPERAND (stmt, 0)), "SR");
+         tree newstmt = sra_build_assignment (tmp, REPLDUP (elt->replacement));
 
-         mark_all_v_defs (stmt);
+         if (TREE_CODE (newstmt) != STATEMENT_LIST)
+           {
+             tree list = NULL;
+             append_to_statement_list (newstmt, &list);
+             newstmt = list;
+           }
+         sra_insert_before (bsi, newstmt);
+         replacement = tmp;
        }
-      *expr_p = REPLDUP (elt->replacement);
+      if (is_output)
+         mark_all_v_defs (stmt);
+      *expr_p = REPLDUP (replacement);
       update_stmt (stmt);
     }
+  else if (use_all && is_output
+          && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+          && TREE_CODE (bfexpr
+                        = GIMPLE_STMT_OPERAND (stmt, 0)) == BIT_FIELD_REF
+          && &TREE_OPERAND (bfexpr, 0) == expr_p
+          && INTEGRAL_TYPE_P (TREE_TYPE (bfexpr))
+          && TREE_CODE (TREE_TYPE (*expr_p)) == RECORD_TYPE)
+    {
+      tree listbefore = NULL, listafter = NULL;
+      tree blen = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 1));
+      tree bpos = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 2));
+      bool update = false;
+
+      if (!elt->use_block_copy)
+       {
+         tree type = TREE_TYPE (bfexpr);
+         tree var = make_rename_temp (type, "SR"), tmp, st;
+
+         GIMPLE_STMT_OPERAND (stmt, 0) = var;
+         update = true;
+
+         if (!TYPE_UNSIGNED (type))
+           {
+             type = unsigned_type_for (type);
+             tmp = make_rename_temp (type, "SR");
+             st = build_gimple_modify_stmt (tmp,
+                                            fold_convert (type, var));
+             append_to_statement_list (st, &listafter);
+             var = tmp;
+           }
+
+         sra_explode_bitfield_assignment
+           (var, bitsize_int (0), false, &listafter, blen, bpos, elt);
+       }
+      else
+       sra_sync_for_bitfield_assignment
+         (&listbefore, &listafter, blen, bpos, elt);
+
+      if (listbefore)
+       {
+         mark_all_v_defs (listbefore);
+         sra_insert_before (bsi, listbefore);
+       }
+      if (listafter)
+       {
+         mark_all_v_defs (listafter);
+         sra_insert_after (bsi, listafter);
+       }
+
+      if (update)
+       update_stmt (stmt);
+    }
+  else if (use_all && !is_output
+          && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+          && TREE_CODE (bfexpr
+                        = GIMPLE_STMT_OPERAND (stmt, 1)) == BIT_FIELD_REF
+          && &TREE_OPERAND (GIMPLE_STMT_OPERAND (stmt, 1), 0) == expr_p
+          && INTEGRAL_TYPE_P (TREE_TYPE (bfexpr))
+          && TREE_CODE (TREE_TYPE (*expr_p)) == RECORD_TYPE)
+    {
+      tree list = NULL;
+      tree blen = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 1));
+      tree bpos = fold_convert (bitsizetype, TREE_OPERAND (bfexpr, 2));
+      bool update = false;
+
+      if (!elt->use_block_copy)
+       {
+         tree type = TREE_TYPE (bfexpr);
+         tree var;
+
+         if (!TYPE_UNSIGNED (type))
+           type = unsigned_type_for (type);
+
+         var = make_rename_temp (type, "SR");
+
+         append_to_statement_list (build_gimple_modify_stmt
+                                   (var, build_int_cst_wide (type, 0, 0)),
+                                   &list);
+
+         sra_explode_bitfield_assignment
+           (var, bitsize_int (0), true, &list, blen, bpos, elt);
+
+         GIMPLE_STMT_OPERAND (stmt, 1) = var;
+         update = true;
+       }
+      else
+       sra_sync_for_bitfield_assignment
+         (&list, NULL, blen, bpos, elt);
+
+      if (list)
+       {
+         mark_all_v_defs (list);
+         sra_insert_before (bsi, list);
+       }
+
+      if (update)
+       update_stmt (stmt);
+    }
   else
     {
-      /* Otherwise we need some copies.  If ELT is being read, then we want
-        to store all (modified) sub-elements back into the structure before
-        the reference takes place.  If ELT is being written, then we want to
-        load the changed values back into our shadow variables.  */
+      tree list = NULL;
+
+      /* Otherwise we need some copies.  If ELT is being read, then we
+        want to store all (modified) sub-elements back into the
+        structure before the reference takes place.  If ELT is being
+        written, then we want to load the changed values back into
+        our shadow variables.  */
       /* ??? We don't check modified for reads, we just always write all of
         the values.  We should be able to record the SSA number of the VOP
         for which the values were last read.  If that number matches the
@@ -2524,23 +3235,17 @@ scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi,
         This optimization would be most effective if sra_walk_function
         processed the blocks in dominator order.  */
 
-      generate_copy_inout (elt, false, generate_element_ref (elt), &list);
-      if (list)
-       {
-         mark_all_v_defs (list);
-         sra_insert_before (bsi, list);
-         mark_no_warning (elt);
-       }
-
+      generate_copy_inout (elt, is_output, generate_element_ref (elt), &list);
+      if (list == NULL)
+       return;
+      mark_all_v_defs (list);
       if (is_output)
+       sra_insert_after (bsi, list);
+      else
        {
-         list = NULL;
-         generate_copy_inout (elt, true, generate_element_ref (elt), &list);
-         if (list)
-           {
-             mark_all_v_defs (list);
-             sra_insert_after (bsi, list);
-           }
+         sra_insert_before (bsi, list);
+         if (use_all)
+           mark_no_warning (elt);
        }
     }
 }
@@ -2706,7 +3411,7 @@ scalarize_ldst (struct sra_elt *elt, tree other,
     {
       /* Since ELT is not fully instantiated, we have to leave the
         block copy in place.  Treat this as a USE.  */
-      scalarize_use (elt, NULL, bsi, is_output);
+      scalarize_use (elt, NULL, bsi, is_output, false);
     }
   else
     {
@@ -2725,13 +3430,34 @@ scalarize_ldst (struct sra_elt *elt, tree other,
       if (stmt_ends_bb_p (stmt))
        {
          tree_stmt_iterator tsi;
-         tree first;
-
-         /* Extract the first statement from LIST.  */
+         tree first, blist = NULL;
+         bool thr = (bsi->bb->flags & EDGE_COMPLEX) != 0;
+
+         /* If the last statement of this BB created an EH edge
+            before scalarization, we have to locate the first
+            statement that can throw in the new statement list and
+            use that as the last statement of this BB, such that EH
+            semantics is preserved.  All statements up to this one
+            are added to the same BB.  All other statements in the
+            list will be added to normal outgoing edges of the same
+            BB.  If they access any memory, it's the same memory, so
+            we can assume they won't throw.  */
          tsi = tsi_start (list);
-         first = tsi_stmt (tsi);
+         for (first = tsi_stmt (tsi);
+              thr && !tsi_end_p (tsi) && !tree_could_throw_p (first);
+              first = tsi_stmt (tsi))
+           {
+             tsi_delink (&tsi);
+             append_to_statement_list (first, &blist);
+           }
+
+         /* Extract the first remaining statement from LIST, this is
+            the EH statement if there is one.  */
          tsi_delink (&tsi);
 
+         if (blist)
+           sra_insert_before (bsi, blist);
+
          /* Replace the old statement with this new representative.  */
          bsi_replace (bsi, first, true);
 
@@ -2867,6 +3593,8 @@ tree_sra (void)
       scan_function ();
       decide_instantiations ();
       scalarize_function ();
+      if (!bitmap_empty_p (sra_candidates))
+       todoflags |= TODO_rebuild_alias;
     }
 
   /* Free allocated memory.  */
@@ -2889,7 +3617,7 @@ tree_sra_early (void)
   ret = tree_sra ();
   early_sra = false;
 
-  return ret;
+  return ret & ~TODO_rebuild_alias;
 }
 
 static bool