]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/expr.c
* config/i386/i386.md (and<mode>3): Generate zero-extends for
[thirdparty/gcc.git] / gcc / expr.c
index cf5b63172f1cddcb0c1ea4ba6bd488c2e3b817df..c78bc74c0d9f9005f284741863652852a295125b 100644 (file)
@@ -1,5 +1,5 @@
 /* Convert tree expression to rtl instructions, for GNU compiler.
-   Copyright (C) 1988-2018 Free Software Foundation, Inc.
+   Copyright (C) 1988-2019 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -54,13 +54,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "reload.h"
 #include "langhooks.h"
 #include "common/common-target.h"
+#include "tree-dfa.h"
 #include "tree-ssa-live.h"
 #include "tree-outof-ssa.h"
 #include "tree-ssa-address.h"
 #include "builtins.h"
-#include "tree-chkp.h"
-#include "rtl-chkp.h"
 #include "ccmp.h"
+#include "gimple-fold.h"
 #include "rtx-vector-builder.h"
 
 
@@ -233,7 +233,10 @@ convert_move (rtx to, rtx from, int unsignedp)
       && (GET_MODE_PRECISION (subreg_promoted_mode (from))
          >= GET_MODE_PRECISION (to_int_mode))
       && SUBREG_CHECK_PROMOTED_SIGN (from, unsignedp))
-    from = gen_lowpart (to_int_mode, from), from_mode = to_int_mode;
+    {
+      from = gen_lowpart (to_int_mode, SUBREG_REG (from));
+      from_mode = to_int_mode;
+    }
 
   gcc_assert (GET_CODE (to) != SUBREG || !SUBREG_PROMOTED_VAR_P (to));
 
@@ -1143,7 +1146,7 @@ class move_by_pieces_d : public op_by_pieces_d
     : op_by_pieces_d (to, false, from, true, NULL, NULL, len, align)
   {
   }
-  rtx finish_endp (int);
+  rtx finish_retmode (memop_ret);
 };
 
 /* Return true if MODE can be used for a set of copies, given an
@@ -1179,15 +1182,14 @@ move_by_pieces_d::generate (rtx op0, rtx op1,
 }
 
 /* Perform the final adjustment at the end of a string to obtain the
-   correct return value for the block operation.  If ENDP is 1 return
-   memory at the end ala mempcpy, and if ENDP is 2 return memory the
-   end minus one byte ala stpcpy.  */
+   correct return value for the block operation.
+   Return value is based on RETMODE argument.  */
 
 rtx
-move_by_pieces_d::finish_endp (int endp)
+move_by_pieces_d::finish_retmode (memop_ret retmode)
 {
   gcc_assert (!m_reverse);
-  if (endp == 2)
+  if (retmode == RETURN_END_MINUS_ONE)
     {
       m_to.maybe_postinc (-1);
       --m_offset;
@@ -1203,13 +1205,11 @@ move_by_pieces_d::finish_endp (int endp)
 
    ALIGN is maximum stack alignment we can assume.
 
-   If ENDP is 0 return to, if ENDP is 1 return memory at the end ala
-   mempcpy, and if ENDP is 2 return memory the end minus one byte ala
-   stpcpy.  */
+   Return value is based on RETMODE argument.  */
 
 rtx
 move_by_pieces (rtx to, rtx from, unsigned HOST_WIDE_INT len,
-               unsigned int align, int endp)
+               unsigned int align, memop_ret retmode)
 {
 #ifndef PUSH_ROUNDING
   if (to == NULL)
@@ -1220,8 +1220,8 @@ move_by_pieces (rtx to, rtx from, unsigned HOST_WIDE_INT len,
 
   data.run ();
 
-  if (endp)
-    return data.finish_endp (endp);
+  if (retmode != RETURN_BEGIN)
+    return data.finish_retmode (retmode);
   else
     return to;
 }
@@ -1241,7 +1241,7 @@ class store_by_pieces_d : public op_by_pieces_d
     : op_by_pieces_d (to, false, NULL_RTX, true, cfn, cfn_data, len, align)
   {
   }
-  rtx finish_endp (int);
+  rtx finish_retmode (memop_ret);
 };
 
 /* Return true if MODE can be used for a set of stores, given an
@@ -1269,15 +1269,14 @@ store_by_pieces_d::generate (rtx op0, rtx op1, machine_mode)
 }
 
 /* Perform the final adjustment at the end of a string to obtain the
-   correct return value for the block operation.  If ENDP is 1 return
-   memory at the end ala mempcpy, and if ENDP is 2 return memory the
-   end minus one byte ala stpcpy.  */
+   correct return value for the block operation.
+   Return value is based on RETMODE argument.  */
 
 rtx
-store_by_pieces_d::finish_endp (int endp)
+store_by_pieces_d::finish_retmode (memop_ret retmode)
 {
   gcc_assert (!m_reverse);
-  if (endp == 2)
+  if (retmode == RETURN_END_MINUS_ONE)
     {
       m_to.maybe_postinc (-1);
       --m_offset;
@@ -1367,18 +1366,17 @@ can_store_by_pieces (unsigned HOST_WIDE_INT len,
    pointer which will be passed as argument in every CONSTFUN call.
    ALIGN is maximum alignment we can assume.  MEMSETP is true if this is
    a memset operation and false if it's a copy of a constant string.
-   If ENDP is 0 return to, if ENDP is 1 return memory at the end ala
-   mempcpy, and if ENDP is 2 return memory the end minus one byte ala
-   stpcpy.  */
+   Return value is based on RETMODE argument.  */
 
 rtx
 store_by_pieces (rtx to, unsigned HOST_WIDE_INT len,
                 rtx (*constfun) (void *, HOST_WIDE_INT, scalar_int_mode),
-                void *constfundata, unsigned int align, bool memsetp, int endp)
+                void *constfundata, unsigned int align, bool memsetp,
+                memop_ret retmode)
 {
   if (len == 0)
     {
-      gcc_assert (endp != 2);
+      gcc_assert (retmode != RETURN_END_MINUS_ONE);
       return to;
     }
 
@@ -1390,8 +1388,8 @@ store_by_pieces (rtx to, unsigned HOST_WIDE_INT len,
   store_by_pieces_d data (to, constfun, constfundata, len, align);
   data.run ();
 
-  if (endp)
-    return data.finish_endp (endp);
+  if (retmode != RETURN_BEGIN)
+    return data.finish_retmode (retmode);
   else
     return to;
 }
@@ -1552,7 +1550,7 @@ compare_by_pieces (rtx arg0, rtx arg1, unsigned HOST_WIDE_INT len,
    ALIGN is the maximum alignment we can assume they have.
    METHOD describes what kind of copy this is, and what mechanisms may be used.
    MIN_SIZE is the minimal size of block to move
-   MAX_SIZE is the maximal size of block to move, if it can not be represented
+   MAX_SIZE is the maximal size of block to move, if it cannot be represented
    in unsigned HOST_WIDE_INT, than it is mask of all ones.
 
    Return the address of the new block, if memcpy is called and returns it,
@@ -1563,12 +1561,16 @@ emit_block_move_hints (rtx x, rtx y, rtx size, enum block_op_methods method,
                       unsigned int expected_align, HOST_WIDE_INT expected_size,
                       unsigned HOST_WIDE_INT min_size,
                       unsigned HOST_WIDE_INT max_size,
-                      unsigned HOST_WIDE_INT probable_max_size)
+                      unsigned HOST_WIDE_INT probable_max_size,
+                      bool bail_out_libcall, bool *is_move_done)
 {
   int may_use_call;
   rtx retval = 0;
   unsigned int align;
 
+  if (is_move_done)
+    *is_move_done = true;
+
   gcc_assert (size);
   if (CONST_INT_P (size) && INTVAL (size) == 0)
     return 0;
@@ -1611,16 +1613,17 @@ emit_block_move_hints (rtx x, rtx y, rtx size, enum block_op_methods method,
 
   /* Set MEM_SIZE as appropriate for this block copy.  The main place this
      can be incorrect is coming from __builtin_memcpy.  */
-  if (CONST_INT_P (size))
+  poly_int64 const_size;
+  if (poly_int_rtx_p (size, &const_size))
     {
       x = shallow_copy_rtx (x);
       y = shallow_copy_rtx (y);
-      set_mem_size (x, INTVAL (size));
-      set_mem_size (y, INTVAL (size));
+      set_mem_size (x, const_size);
+      set_mem_size (y, const_size);
     }
 
   if (CONST_INT_P (size) && can_move_by_pieces (INTVAL (size), align))
-    move_by_pieces (x, y, INTVAL (size), align, 0);
+    move_by_pieces (x, y, INTVAL (size), align, RETURN_BEGIN);
   else if (emit_block_move_via_movmem (x, y, size, align,
                                       expected_align, expected_size,
                                       min_size, max_size, probable_max_size))
@@ -1629,17 +1632,16 @@ emit_block_move_hints (rtx x, rtx y, rtx size, enum block_op_methods method,
           && ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x))
           && ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (y)))
     {
+      if (bail_out_libcall)
+       {
+         if (is_move_done)
+           *is_move_done = false;
+         return retval;
+       }
+
       if (may_use_call < 0)
        return pc_rtx;
 
-      /* Since x and y are passed to a libcall, mark the corresponding
-        tree EXPR as addressable.  */
-      tree y_expr = MEM_EXPR (y);
-      tree x_expr = MEM_EXPR (x);
-      if (y_expr)
-       mark_addressable (y_expr);
-      if (x_expr)
-       mark_addressable (x_expr);
       retval = emit_block_copy_via_libcall (x, y, size,
                                            method == BLOCK_OP_TAILCALL);
     }
@@ -1790,7 +1792,7 @@ emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align,
          if (nops >= 8)
            {
              create_integer_operand (&ops[6], min_size);
-             /* If we can not represent the maximal size,
+             /* If we cannot represent the maximal size,
                 make parameter NULL.  */
              if ((HOST_WIDE_INT) max_size != -1)
                create_integer_operand (&ops[7], max_size);
@@ -1799,7 +1801,7 @@ emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align,
            }
          if (nops == 9)
            {
-             /* If we can not represent the maximal size,
+             /* If we cannot represent the maximal size,
                 make parameter NULL.  */
              if ((HOST_WIDE_INT) probable_max_size != -1)
                create_integer_operand (&ops[8], probable_max_size);
@@ -1885,6 +1887,15 @@ emit_block_op_via_libcall (enum built_in_function fncode, rtx dst, rtx src,
   tree call_expr, dst_tree, src_tree, size_tree;
   machine_mode size_mode;
 
+  /* Since dst and src are passed to a libcall, mark the corresponding
+     tree EXPR as addressable.  */
+  tree dst_expr = MEM_EXPR (dst);
+  tree src_expr = MEM_EXPR (src);
+  if (dst_expr)
+    mark_addressable (dst_expr);
+  if (src_expr)
+    mark_addressable (src_expr);
+
   dst_addr = copy_addr_to_reg (XEXP (dst, 0));
   dst_addr = convert_memory_address (ptr_mode, dst_addr);
   dst_tree = make_tree (ptr_type_node, dst_addr);
@@ -2145,7 +2156,7 @@ emit_group_load_1 (rtx *tmps, rtx dst, rtx orig_src, tree type,
   for (i = start; i < XVECLEN (dst, 0); i++)
     {
       machine_mode mode = GET_MODE (XEXP (XVECEXP (dst, 0, i), 0));
-      poly_int64 bytepos = INTVAL (XEXP (XVECEXP (dst, 0, i), 1));
+      poly_int64 bytepos = rtx_to_poly_int64 (XEXP (XVECEXP (dst, 0, i), 1));
       poly_int64 bytelen = GET_MODE_SIZE (mode);
       poly_int64 shift = 0;
 
@@ -2476,7 +2487,8 @@ emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED,
        {
          inner = GET_MODE (tmps[start]);
          bytepos = subreg_lowpart_offset (inner, outer);
-         if (known_eq (INTVAL (XEXP (XVECEXP (src, 0, start), 1)), bytepos))
+         if (known_eq (rtx_to_poly_int64 (XEXP (XVECEXP (src, 0, start), 1)),
+                       bytepos))
            {
              temp = simplify_gen_subreg (outer, tmps[start],
                                          inner, 0);
@@ -2495,7 +2507,8 @@ emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED,
        {
          inner = GET_MODE (tmps[finish - 1]);
          bytepos = subreg_lowpart_offset (inner, outer);
-         if (known_eq (INTVAL (XEXP (XVECEXP (src, 0, finish - 1), 1)),
+         if (known_eq (rtx_to_poly_int64 (XEXP (XVECEXP (src, 0,
+                                                         finish - 1), 1)),
                        bytepos))
            {
              temp = simplify_gen_subreg (outer, tmps[finish - 1],
@@ -2517,7 +2530,7 @@ emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED,
   /* Process the pieces.  */
   for (i = start; i < finish; i++)
     {
-      poly_int64 bytepos = INTVAL (XEXP (XVECEXP (src, 0, i), 1));
+      poly_int64 bytepos = rtx_to_poly_int64 (XEXP (XVECEXP (src, 0, i), 1));
       machine_mode mode = GET_MODE (tmps[i]);
       poly_int64 bytelen = GET_MODE_SIZE (mode);
       poly_uint64 adj_bytelen;
@@ -2761,6 +2774,7 @@ copy_blkmode_to_reg (machine_mode mode_in, tree src)
   /* No current ABI uses variable-sized modes to pass a BLKmnode type.  */
   fixed_size_mode mode = as_a <fixed_size_mode> (mode_in);
   fixed_size_mode dst_mode;
+  scalar_int_mode min_mode;
 
   gcc_assert (TYPE_MODE (TREE_TYPE (src)) == BLKmode);
 
@@ -2790,6 +2804,7 @@ copy_blkmode_to_reg (machine_mode mode_in, tree src)
   n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
   dst_words = XALLOCAVEC (rtx, n_regs);
   bitsize = MIN (TYPE_ALIGN (TREE_TYPE (src)), BITS_PER_WORD);
+  min_mode = smallest_int_mode_for_size (bitsize);
 
   /* Copy the structure BITSIZE bits at a time.  */
   for (bitpos = 0, xbitpos = padding_correction;
@@ -2810,6 +2825,25 @@ copy_blkmode_to_reg (machine_mode mode_in, tree src)
          emit_move_insn (dst_word, CONST0_RTX (word_mode));
        }
 
+      /* Find the largest integer mode that can be used to copy all or as
+        many bits as possible of the structure if the target supports larger
+        copies.  There are too many corner cases here w.r.t to alignments on
+        the read/writes.  So if there is any padding just use single byte
+        operations.  */
+      opt_scalar_int_mode mode_iter;
+      if (padding_correction == 0 && !STRICT_ALIGNMENT)
+       {
+         FOR_EACH_MODE_FROM (mode_iter, min_mode)
+           {
+             unsigned int msize = GET_MODE_BITSIZE (mode_iter.require ());
+             if (msize <= ((bytes * BITS_PER_UNIT) - bitpos)
+                 && msize <= BITS_PER_WORD)
+               bitsize = msize;
+             else
+               break;
+           }
+       }
+
       /* We need a new source operand each time bitpos is on a word
         boundary.  */
       if (bitpos % BITS_PER_WORD == 0)
@@ -2973,9 +3007,10 @@ clear_storage_hints (rtx object, rtx size, enum block_op_methods method,
 
   /* If OBJECT is not BLKmode and SIZE is the same size as its mode,
      just move a zero.  Otherwise, do this a piece at a time.  */
+  poly_int64 size_val;
   if (mode != BLKmode
-      && CONST_INT_P (size)
-      && known_eq (INTVAL (size), GET_MODE_SIZE (mode)))
+      && poly_int_rtx_p (size, &size_val)
+      && known_eq (size_val, GET_MODE_SIZE (mode)))
     {
       rtx zero = CONST0_RTX (mode);
       if (zero != NULL)
@@ -3121,7 +3156,7 @@ set_storage_via_setmem (rtx object, rtx size, rtx val, unsigned int align,
          if (nops >= 8)
            {
              create_integer_operand (&ops[6], min_size);
-             /* If we can not represent the maximal size,
+             /* If we cannot represent the maximal size,
                 make parameter NULL.  */
              if ((HOST_WIDE_INT) max_size != -1)
                create_integer_operand (&ops[7], max_size);
@@ -3130,7 +3165,7 @@ set_storage_via_setmem (rtx object, rtx size, rtx val, unsigned int align,
            }
          if (nops == 9)
            {
-             /* If we can not represent the maximal size,
+             /* If we cannot represent the maximal size,
                 make parameter NULL.  */
              if ((HOST_WIDE_INT) probable_max_size != -1)
                create_integer_operand (&ops[8], probable_max_size);
@@ -3911,9 +3946,10 @@ push_block (rtx size, poly_int64 extra, int below)
     }
   else
     {
-      if (CONST_INT_P (size))
+      poly_int64 csize;
+      if (poly_int_rtx_p (size, &csize))
        temp = plus_constant (Pmode, virtual_outgoing_args_rtx,
-                             -INTVAL (size) - (below ? 0 : extra));
+                             -csize - (below ? 0 : extra));
       else if (maybe_ne (extra, 0) && !below)
        temp = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
                             negate_rtx (Pmode, plus_constant (Pmode, size,
@@ -4033,11 +4069,10 @@ find_args_size_adjust (rtx_insn *insn)
       /* Look for a trivial adjustment, otherwise assume nothing.  */
       /* Note that the SPU restore_stack_block pattern refers to
         the stack pointer in V4SImode.  Consider that non-trivial.  */
+      poly_int64 offset;
       if (SCALAR_INT_MODE_P (GET_MODE (dest))
-         && GET_CODE (SET_SRC (set)) == PLUS
-         && XEXP (SET_SRC (set), 0) == stack_pointer_rtx
-         && CONST_INT_P (XEXP (SET_SRC (set), 1)))
-       return INTVAL (XEXP (SET_SRC (set), 1));
+         && strip_offset (SET_SRC (set), &offset) == stack_pointer_rtx)
+       return offset;
       /* ??? Reload can generate no-op moves, which will be cleaned
         up later.  Recognize it and continue searching.  */
       else if (rtx_equal_p (dest, SET_SRC (set)))
@@ -4075,8 +4110,7 @@ find_args_size_adjust (rtx_insn *insn)
          addr = XEXP (addr, 1);
          gcc_assert (GET_CODE (addr) == PLUS);
          gcc_assert (XEXP (addr, 0) == stack_pointer_rtx);
-         gcc_assert (CONST_INT_P (XEXP (addr, 1)));
-         return INTVAL (XEXP (addr, 1));
+         return rtx_to_poly_int64 (XEXP (addr, 1));
        default:
          gcc_unreachable ();
        }
@@ -4394,7 +4428,8 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
              && where_pad != stack_direction)
            anti_adjust_stack (gen_int_mode (extra, Pmode));
 
-         move_by_pieces (NULL, xinner, INTVAL (size) - used, align, 0);
+         move_by_pieces (NULL, xinner, INTVAL (size) - used, align,
+                         RETURN_BEGIN);
        }
       else
 #endif /* PUSH_ROUNDING  */
@@ -4418,15 +4453,16 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
          /* Get the address of the stack space.
             In this case, we do not deal with EXTRA separately.
             A single stack adjust will do.  */
+         poly_int64 offset;
          if (! args_addr)
            {
              temp = push_block (size, extra, where_pad == PAD_DOWNWARD);
              extra = 0;
            }
-         else if (CONST_INT_P (args_so_far))
+         else if (poly_int_rtx_p (args_so_far, &offset))
            temp = memory_address (BLKmode,
                                   plus_constant (Pmode, args_addr,
-                                                 skip + INTVAL (args_so_far)));
+                                                 skip + offset));
          else
            temp = memory_address (BLKmode,
                                   plus_constant (Pmode,
@@ -4910,7 +4946,7 @@ get_bit_range (poly_uint64_pod *bitstart, poly_uint64_pod *bitend, tree exp,
   else
     *bitstart = *bitpos - bitoffset;
 
-  *bitend = *bitstart + tree_to_uhwi (DECL_SIZE (repr)) - 1;
+  *bitend = *bitstart + tree_to_poly_uint64 (DECL_SIZE (repr)) - 1;
 }
 
 /* Returns true if ADDR is an ADDR_EXPR of a DECL that does not reside
@@ -5145,12 +5181,12 @@ expand_assignment (tree to, tree from, bool nontemporal)
          gcc_checking_assert (COMPLEX_MODE_P (to_mode));
          poly_int64 mode_bitsize = GET_MODE_BITSIZE (to_mode);
          unsigned short inner_bitsize = GET_MODE_UNIT_BITSIZE (to_mode);
-         if (TYPE_MODE (TREE_TYPE (from)) == GET_MODE (to_rtx)
-             && COMPLEX_MODE_P (GET_MODE (to_rtx))
+         if (TYPE_MODE (TREE_TYPE (from)) == to_mode
              && known_eq (bitpos, 0)
              && known_eq (bitsize, mode_bitsize))
            result = store_expr (from, to_rtx, false, nontemporal, reversep);
-         else if (known_eq (bitsize, inner_bitsize)
+         else if (TYPE_MODE (TREE_TYPE (from)) == GET_MODE_INNER (to_mode)
+                  && known_eq (bitsize, inner_bitsize)
                   && (known_eq (bitpos, 0)
                       || known_eq (bitpos, inner_bitsize)))
            result = store_expr (from, XEXP (to_rtx, maybe_ne (bitpos, 0)),
@@ -5186,9 +5222,13 @@ expand_assignment (tree to, tree from, bool nontemporal)
                }
              else
                {
-                 rtx from_rtx
-                   = simplify_gen_subreg (to_mode, result,
-                                          TYPE_MODE (TREE_TYPE (from)), 0);
+                 rtx from_rtx;
+                 if (MEM_P (result))
+                   from_rtx = change_address (result, to_mode, NULL_RTX);
+                 else
+                   from_rtx
+                     = simplify_gen_subreg (to_mode, result,
+                                            TYPE_MODE (TREE_TYPE (from)), 0);
                  if (from_rtx)
                    {
                      emit_move_insn (XEXP (to_rtx, 0),
@@ -5230,6 +5270,21 @@ expand_assignment (tree to, tree from, bool nontemporal)
              emit_move_insn (XEXP (to_rtx, 1), read_complex_part (temp, true));
            }
        }
+      /* For calls to functions returning variable length structures, if TO_RTX
+        is not a MEM, go through a MEM because we must not create temporaries
+        of the VLA type.  */
+      else if (!MEM_P (to_rtx)
+              && TREE_CODE (from) == CALL_EXPR
+              && COMPLETE_TYPE_P (TREE_TYPE (from))
+              && TREE_CODE (TYPE_SIZE (TREE_TYPE (from))) != INTEGER_CST)
+       {
+         rtx temp = assign_stack_temp (GET_MODE (to_rtx),
+                                       GET_MODE_SIZE (GET_MODE (to_rtx)));
+         result = store_field (temp, bitsize, bitpos, bitregion_start,
+                               bitregion_end, mode1, from, get_alias_set (to),
+                               nontemporal, reversep);
+         emit_move_insn (to_rtx, temp);
+       }
       else
        {
          if (MEM_P (to_rtx))
@@ -5242,6 +5297,7 @@ expand_assignment (tree to, tree from, bool nontemporal)
                MEM_VOLATILE_P (to_rtx) = 1;
            }
 
+         gcc_checking_assert (known_ge (bitpos, 0));
          if (optimize_bitfield_assignment_op (bitsize, bitpos,
                                               bitregion_start, bitregion_end,
                                               mode1, to_rtx, to, from,
@@ -5280,14 +5336,10 @@ expand_assignment (tree to, tree from, bool nontemporal)
            || TREE_CODE (to) == SSA_NAME))
     {
       rtx value;
-      rtx bounds;
 
       push_temp_slots ();
       value = expand_normal (from);
 
-      /* Split value and bounds to store them separately.  */
-      chkp_split_slot (value, &value, &bounds);
-
       if (to_rtx == 0)
        to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);
 
@@ -5322,14 +5374,6 @@ expand_assignment (tree to, tree from, bool nontemporal)
          emit_move_insn (to_rtx, value);
        }
 
-      /* Store bounds if required.  */
-      if (bounds
-         && (BOUNDED_P (to) || chkp_type_has_pointer (TREE_TYPE (to))))
-       {
-         gcc_assert (MEM_P (to_rtx));
-         chkp_emit_bounds_store (bounds, value, to_rtx);
-       }
-
       preserve_temp_slots (to_rtx);
       pop_temp_slots ();
       return;
@@ -5400,7 +5444,7 @@ expand_assignment (tree to, tree from, bool nontemporal)
   /* Compute FROM and store the value in the rtx we got.  */
 
   push_temp_slots ();
-  result = store_expr_with_bounds (from, to_rtx, 0, nontemporal, false, to);
+  result = store_expr (from, to_rtx, 0, nontemporal, false);
   preserve_temp_slots (result);
   pop_temp_slots ();
   return;
@@ -5424,6 +5468,30 @@ emit_storent_insn (rtx to, rtx from)
   return maybe_expand_insn (code, 2, ops);
 }
 
+/* Helper function for store_expr storing of STRING_CST.  */
+
+static rtx
+string_cst_read_str (void *data, HOST_WIDE_INT offset, scalar_int_mode mode)
+{
+  tree str = (tree) data;
+
+  gcc_assert (offset >= 0);
+  if (offset >= TREE_STRING_LENGTH (str))
+    return const0_rtx;
+
+  if ((unsigned HOST_WIDE_INT) offset + GET_MODE_SIZE (mode)
+      > (unsigned HOST_WIDE_INT) TREE_STRING_LENGTH (str))
+    {
+      char *p = XALLOCAVEC (char, GET_MODE_SIZE (mode));
+      size_t l = TREE_STRING_LENGTH (str) - offset;
+      memcpy (p, TREE_STRING_POINTER (str) + offset, l);
+      memset (p + l, '\0', GET_MODE_SIZE (mode) - l);
+      return c_readstr (p, mode, false);
+    }
+
+  return c_readstr (TREE_STRING_POINTER (str) + offset, mode, false);
+}
+
 /* Generate code for computing expression EXP,
    and storing the value into TARGET.
 
@@ -5439,14 +5507,11 @@ emit_storent_insn (rtx to, rtx from)
 
    If NONTEMPORAL is true, try using a nontemporal store instruction.
 
-   If REVERSE is true, the store is to be done in reverse order.
-
-   If BTARGET is not NULL then computed bounds of EXP are
-   associated with BTARGET.  */
+   If REVERSE is true, the store is to be done in reverse order.  */
 
 rtx
-store_expr_with_bounds (tree exp, rtx target, int call_param_p,
-                       bool nontemporal, bool reverse, tree btarget)
+store_expr (tree exp, rtx target, int call_param_p,
+           bool nontemporal, bool reverse)
 {
   rtx temp;
   rtx alt_rtl = NULL_RTX;
@@ -5467,9 +5532,8 @@ store_expr_with_bounds (tree exp, rtx target, int call_param_p,
         part.  */
       expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
                   call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
-      return store_expr_with_bounds (TREE_OPERAND (exp, 1), target,
-                                    call_param_p, nontemporal, reverse,
-                                    btarget);
+      return store_expr (TREE_OPERAND (exp, 1), target,
+                                    call_param_p, nontemporal, reverse);
     }
   else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
     {
@@ -5484,13 +5548,13 @@ store_expr_with_bounds (tree exp, rtx target, int call_param_p,
       NO_DEFER_POP;
       jumpifnot (TREE_OPERAND (exp, 0), lab1,
                 profile_probability::uninitialized ());
-      store_expr_with_bounds (TREE_OPERAND (exp, 1), target, call_param_p,
-                             nontemporal, reverse, btarget);
+      store_expr (TREE_OPERAND (exp, 1), target, call_param_p,
+                 nontemporal, reverse);
       emit_jump_insn (targetm.gen_jump (lab2));
       emit_barrier ();
       emit_label (lab1);
-      store_expr_with_bounds (TREE_OPERAND (exp, 2), target, call_param_p,
-                             nontemporal, reverse, btarget);
+      store_expr (TREE_OPERAND (exp, 2), target, call_param_p,
+                 nontemporal, reverse);
       emit_label (lab2);
       OK_DEFER_POP;
 
@@ -5543,18 +5607,6 @@ store_expr_with_bounds (tree exp, rtx target, int call_param_p,
       temp = expand_expr (exp, inner_target, VOIDmode,
                          call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
 
-      /* Handle bounds returned by call.  */
-      if (TREE_CODE (exp) == CALL_EXPR)
-       {
-         rtx bounds;
-         chkp_split_slot (temp, &temp, &bounds);
-         if (bounds && btarget)
-           {
-             gcc_assert (TREE_CODE (btarget) == SSA_NAME);
-             rtx tmp = targetm.calls.load_returned_bounds (bounds);
-             chkp_set_rtl_bounds (btarget, tmp);
-           }
-       }
 
       /* If TEMP is a VOIDmode constant, use convert_modes to make
         sure that we properly convert it.  */
@@ -5593,35 +5645,33 @@ store_expr_with_bounds (tree exp, rtx target, int call_param_p,
       if (TREE_STRING_LENGTH (str) <= 0)
        goto normal_expr;
 
-      str_copy_len = strlen (TREE_STRING_POINTER (str));
-      if (str_copy_len < TREE_STRING_LENGTH (str) - 1)
-       goto normal_expr;
+      if (can_store_by_pieces (exp_len, string_cst_read_str, (void *) str,
+                              MEM_ALIGN (target), false))
+       {
+         store_by_pieces (target, exp_len, string_cst_read_str, (void *) str,
+                          MEM_ALIGN (target), false, RETURN_BEGIN);
+         return NULL_RTX;
+       }
 
       str_copy_len = TREE_STRING_LENGTH (str);
-      if ((STORE_MAX_PIECES & (STORE_MAX_PIECES - 1)) == 0
-         && TREE_STRING_POINTER (str)[TREE_STRING_LENGTH (str) - 1] == '\0')
+      if ((STORE_MAX_PIECES & (STORE_MAX_PIECES - 1)) == 0)
        {
          str_copy_len += STORE_MAX_PIECES - 1;
          str_copy_len &= ~(STORE_MAX_PIECES - 1);
        }
-      str_copy_len = MIN (str_copy_len, exp_len);
-      if (!can_store_by_pieces (str_copy_len, builtin_strncpy_read_str,
-                               CONST_CAST (char *, TREE_STRING_POINTER (str)),
-                               MEM_ALIGN (target), false))
+      if (str_copy_len >= exp_len)
        goto normal_expr;
 
-      dest_mem = target;
-
-      dest_mem = store_by_pieces (dest_mem,
-                                 str_copy_len, builtin_strncpy_read_str,
-                                 CONST_CAST (char *,
-                                             TREE_STRING_POINTER (str)),
-                                 MEM_ALIGN (target), false,
-                                 exp_len > str_copy_len ? 1 : 0);
-      if (exp_len > str_copy_len)
-       clear_storage (adjust_address (dest_mem, BLKmode, 0),
-                      GEN_INT (exp_len - str_copy_len),
-                      BLOCK_OP_NORMAL);
+      if (!can_store_by_pieces (str_copy_len, string_cst_read_str,
+                               (void *) str, MEM_ALIGN (target), false))
+       goto normal_expr;
+
+      dest_mem = store_by_pieces (target, str_copy_len, string_cst_read_str,
+                                 (void *) str, MEM_ALIGN (target), false,
+                                 RETURN_END);
+      clear_storage (adjust_address_1 (dest_mem, BLKmode, 0, 1, 1, 0,
+                                      exp_len - str_copy_len),
+                    GEN_INT (exp_len - str_copy_len), BLOCK_OP_NORMAL);
       return NULL_RTX;
     }
   else
@@ -5636,19 +5686,6 @@ store_expr_with_bounds (tree exp, rtx target, int call_param_p,
                               (call_param_p
                                ? EXPAND_STACK_PARM : EXPAND_NORMAL),
                               &alt_rtl, false);
-
-      /* Handle bounds returned by call.  */
-      if (TREE_CODE (exp) == CALL_EXPR)
-       {
-         rtx bounds;
-         chkp_split_slot (temp, &temp, &bounds);
-         if (bounds && btarget)
-           {
-             gcc_assert (TREE_CODE (btarget) == SSA_NAME);
-             rtx tmp = targetm.calls.load_returned_bounds (bounds);
-             chkp_set_rtl_bounds (btarget, tmp);
-           }
-       }
     }
 
   /* If TEMP is a VOIDmode constant and the mode of the type of EXP is not
@@ -5764,12 +5801,11 @@ store_expr_with_bounds (tree exp, rtx target, int call_param_p,
 
              /* Figure out how much is left in TARGET that we have to clear.
                 Do all calculations in pointer_mode.  */
-             if (CONST_INT_P (copy_size_rtx))
+             poly_int64 const_copy_size;
+             if (poly_int_rtx_p (copy_size_rtx, &const_copy_size))
                {
-                 size = plus_constant (address_mode, size,
-                                       -INTVAL (copy_size_rtx));
-                 target = adjust_address (target, BLKmode,
-                                          INTVAL (copy_size_rtx));
+                 size = plus_constant (address_mode, size, -const_copy_size);
+                 target = adjust_address (target, BLKmode, const_copy_size);
                }
              else
                {
@@ -5828,15 +5864,6 @@ store_expr_with_bounds (tree exp, rtx target, int call_param_p,
 
   return NULL_RTX;
 }
-
-/* Same as store_expr_with_bounds but ignoring bounds of EXP.  */
-rtx
-store_expr (tree exp, rtx target, int call_param_p, bool nontemporal,
-           bool reverse)
-{
-  return store_expr_with_bounds (exp, target, call_param_p, nontemporal,
-                                reverse, NULL);
-}
 \f
 /* Return true if field F of structure TYPE is a flexible array.  */
 
@@ -5970,10 +5997,11 @@ count_type_elements (const_tree type, bool for_ctor_p)
 
 static bool
 categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
+                           HOST_WIDE_INT *p_unique_nz_elts,
                            HOST_WIDE_INT *p_init_elts, bool *p_complete)
 {
   unsigned HOST_WIDE_INT idx;
-  HOST_WIDE_INT nz_elts, init_elts, num_fields;
+  HOST_WIDE_INT nz_elts, unique_nz_elts, init_elts, num_fields;
   tree value, purpose, elt_type;
 
   /* Whether CTOR is a valid constant initializer, in accordance with what
@@ -5983,6 +6011,7 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
   bool const_p = const_from_elts_p ? true : TREE_STATIC (ctor);
 
   nz_elts = 0;
+  unique_nz_elts = 0;
   init_elts = 0;
   num_fields = 0;
   elt_type = NULL_TREE;
@@ -6007,12 +6036,13 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
        {
        case CONSTRUCTOR:
          {
-           HOST_WIDE_INT nz = 0, ic = 0;
+           HOST_WIDE_INT nz = 0, unz = 0, ic = 0;
 
-           bool const_elt_p = categorize_ctor_elements_1 (value, &nz, &ic,
-                                                          p_complete);
+           bool const_elt_p = categorize_ctor_elements_1 (value, &nz, &unz,
+                                                          &ic, p_complete);
 
            nz_elts += mult * nz;
+           unique_nz_elts += unz;
            init_elts += mult * ic;
 
            if (const_from_elts_p && const_p)
@@ -6024,21 +6054,31 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
        case REAL_CST:
        case FIXED_CST:
          if (!initializer_zerop (value))
-           nz_elts += mult;
+           {
+             nz_elts += mult;
+             unique_nz_elts++;
+           }
          init_elts += mult;
          break;
 
        case STRING_CST:
          nz_elts += mult * TREE_STRING_LENGTH (value);
+         unique_nz_elts += TREE_STRING_LENGTH (value);
          init_elts += mult * TREE_STRING_LENGTH (value);
          break;
 
        case COMPLEX_CST:
          if (!initializer_zerop (TREE_REALPART (value)))
-           nz_elts += mult;
+           {
+             nz_elts += mult;
+             unique_nz_elts++;
+           }
          if (!initializer_zerop (TREE_IMAGPART (value)))
-           nz_elts += mult;
-         init_elts += mult;
+           {
+             nz_elts += mult;
+             unique_nz_elts++;
+           }
+         init_elts += 2 * mult;
          break;
 
        case VECTOR_CST:
@@ -6050,7 +6090,10 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
              {
                tree v = VECTOR_CST_ELT (value, i);
                if (!initializer_zerop (v))
-                 nz_elts += mult;
+                 {
+                   nz_elts += mult;
+                   unique_nz_elts++;
+                 }
                init_elts += mult;
              }
          }
@@ -6060,6 +6103,7 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
          {
            HOST_WIDE_INT tc = count_type_elements (elt_type, false);
            nz_elts += mult * tc;
+           unique_nz_elts += tc;
            init_elts += mult * tc;
 
            if (const_from_elts_p && const_p)
@@ -6079,6 +6123,7 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
     *p_complete = false;
 
   *p_nz_elts += nz_elts;
+  *p_unique_nz_elts += unique_nz_elts;
   *p_init_elts += init_elts;
 
   return const_p;
@@ -6087,6 +6132,11 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
 /* Examine CTOR to discover:
    * how many scalar fields are set to nonzero values,
      and place it in *P_NZ_ELTS;
+   * the same, but counting RANGE_EXPRs as multiplier of 1 instead of
+     high - low + 1 (this can be useful for callers to determine ctors
+     that could be cheaply initialized with - perhaps nested - loops
+     compared to copied from huge read-only data),
+     and place it in *P_UNIQUE_NZ_ELTS;
    * how many scalar fields in total are in CTOR,
      and place it in *P_ELT_COUNT.
    * whether the constructor is complete -- in the sense that every
@@ -6098,13 +6148,16 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
 
 bool
 categorize_ctor_elements (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
+                         HOST_WIDE_INT *p_unique_nz_elts,
                          HOST_WIDE_INT *p_init_elts, bool *p_complete)
 {
   *p_nz_elts = 0;
+  *p_unique_nz_elts = 0;
   *p_init_elts = 0;
   *p_complete = true;
 
-  return categorize_ctor_elements_1 (ctor, p_nz_elts, p_init_elts, p_complete);
+  return categorize_ctor_elements_1 (ctor, p_nz_elts, p_unique_nz_elts,
+                                    p_init_elts, p_complete);
 }
 
 /* TYPE is initialized by a constructor with NUM_ELTS elements, the last
@@ -6135,17 +6188,18 @@ complete_ctor_at_level_p (const_tree type, HOST_WIDE_INT num_elts,
   return count_type_elements (type, true) == num_elts;
 }
 
-/* Return 1 if EXP contains mostly (3/4)  zeros.  */
+/* Return 1 if EXP contains mostly (3/4) zeros.  */
 
 static int
 mostly_zeros_p (const_tree exp)
 {
   if (TREE_CODE (exp) == CONSTRUCTOR)
     {
-      HOST_WIDE_INT nz_elts, init_elts;
+      HOST_WIDE_INT nz_elts, unz_elts, init_elts;
       bool complete_p;
 
-      categorize_ctor_elements (exp, &nz_elts, &init_elts, &complete_p);
+      categorize_ctor_elements (exp, &nz_elts, &unz_elts, &init_elts,
+                               &complete_p);
       return !complete_p || nz_elts < init_elts / 4;
     }
 
@@ -6159,10 +6213,11 @@ all_zeros_p (const_tree exp)
 {
   if (TREE_CODE (exp) == CONSTRUCTOR)
     {
-      HOST_WIDE_INT nz_elts, init_elts;
+      HOST_WIDE_INT nz_elts, unz_elts, init_elts;
       bool complete_p;
 
-      categorize_ctor_elements (exp, &nz_elts, &init_elts, &complete_p);
+      categorize_ctor_elements (exp, &nz_elts, &unz_elts, &init_elts,
+                               &complete_p);
       return nz_elts == 0;
     }
 
@@ -6518,12 +6573,10 @@ store_constructor (tree exp, rtx target, int cleared, poly_int64 size,
              continue;
 
            mode = TYPE_MODE (elttype);
-           if (mode == BLKmode)
-             bitsize = (tree_fits_uhwi_p (TYPE_SIZE (elttype))
-                        ? tree_to_uhwi (TYPE_SIZE (elttype))
-                        : -1);
-           else
+           if (mode != BLKmode)
              bitsize = GET_MODE_BITSIZE (mode);
+           else if (!poly_int_tree_p (TYPE_SIZE (elttype), &bitsize))
+             bitsize = -1;
 
            if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
              {
@@ -6989,8 +7042,9 @@ store_field (rtx target, poly_int64 bitsize, poly_int64 bitpos,
       if (GET_CODE (temp) == PARALLEL)
        {
          HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp));
-         scalar_int_mode temp_mode
-           = smallest_int_mode_for_size (size * BITS_PER_UNIT);
+         machine_mode temp_mode = GET_MODE (temp);
+         if (temp_mode == BLKmode || temp_mode == VOIDmode)
+           temp_mode = smallest_int_mode_for_size (size * BITS_PER_UNIT);
          rtx temp_target = gen_reg_rtx (temp_mode);
          emit_group_store (temp_target, temp, TREE_TYPE (exp), size);
          temp = temp_target;
@@ -7070,6 +7124,7 @@ store_field (rtx target, poly_int64 bitsize, poly_int64 bitpos,
        }
 
       /* Store the value in the bitfield.  */
+      gcc_checking_assert (known_ge (bitpos, 0));
       store_bit_field (target, bitsize, bitpos,
                       bitregion_start, bitregion_end,
                       mode, temp, reverse);
@@ -8116,12 +8171,12 @@ expand_constructor (tree exp, rtx target, enum expand_modifier modifier,
   if ((TREE_STATIC (exp)
        && ((mode == BLKmode
            && ! (target != 0 && safe_from_p (target, exp, 1)))
-                 || TREE_ADDRESSABLE (exp)
-                 || (tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
-                     && (! can_move_by_pieces
-                                    (tree_to_uhwi (TYPE_SIZE_UNIT (type)),
-                                     TYPE_ALIGN (type)))
-                     && ! mostly_zeros_p (exp))))
+          || TREE_ADDRESSABLE (exp)
+          || (tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
+              && (! can_move_by_pieces
+                  (tree_to_uhwi (TYPE_SIZE_UNIT (type)),
+                   TYPE_ALIGN (type)))
+              && ! mostly_zeros_p (exp))))
       || ((modifier == EXPAND_INITIALIZER || modifier == EXPAND_CONST_ADDRESS)
          && TREE_CONSTANT (exp)))
     {
@@ -8772,8 +8827,8 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
                 != INTEGER_CST check.  Handle it.  */
              if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
                {
-                 op0 = convert_modes (innermode, mode, op0, true);
-                 op1 = convert_modes (innermode, mode, op1, false);
+                 op0 = convert_modes (mode, innermode, op0, true);
+                 op1 = convert_modes (mode, innermode, op1, false);
                  return REDUCE_BIT_FIELD (expand_mult (mode, op0, op1,
                                                        target, unsignedp));
                }
@@ -8795,7 +8850,7 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
          if (TREE_CODE (treeop0) != INTEGER_CST)
            {
              if (find_widening_optab_handler (this_optab, mode, innermode)
-                   != CODE_FOR_nothing)
+                 != CODE_FOR_nothing)
                {
                  expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1,
                                   EXPAND_NORMAL);
@@ -8804,9 +8859,9 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
                  if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
                    {
                     widen_mult_const:
-                     op0 = convert_modes (innermode, mode, op0, zextend_p);
+                     op0 = convert_modes (mode, innermode, op0, zextend_p);
                      op1
-                       = convert_modes (innermode, mode, op1,
+                       = convert_modes (mode, innermode, op1,
                                         TYPE_UNSIGNED (TREE_TYPE (treeop1)));
                      return REDUCE_BIT_FIELD (expand_mult (mode, op0, op1,
                                                            target,
@@ -8817,21 +8872,19 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
                  return REDUCE_BIT_FIELD (temp);
                }
              if (find_widening_optab_handler (other_optab, mode, innermode)
-                   != CODE_FOR_nothing
+                 != CODE_FOR_nothing
                  && innermode == word_mode)
                {
                  rtx htem, hipart;
                  op0 = expand_normal (treeop0);
-                 if (TREE_CODE (treeop1) == INTEGER_CST)
-                   op1 = convert_modes (word_mode, mode,
-                                        expand_normal (treeop1),
-                                        TYPE_UNSIGNED (TREE_TYPE (treeop1)));
-                 else
-                   op1 = expand_normal (treeop1);
-                 /* op0 and op1 might still be constant, despite the above
+                 op1 = expand_normal (treeop1);
+                 /* op0 and op1 might be constants, despite the above
                     != INTEGER_CST check.  Handle it.  */
                  if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
                    goto widen_mult_const;
+                 if (TREE_CODE (treeop1) == INTEGER_CST)
+                   op1 = convert_modes (mode, word_mode, op1,
+                                        TYPE_UNSIGNED (TREE_TYPE (treeop1)));
                  temp = expand_binop (mode, other_optab, op0, op1, target,
                                       unsignedp, OPTAB_LIB_WIDEN);
                  hipart = gen_highpart (word_mode, temp);
@@ -8849,67 +8902,6 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
       expand_operands (treeop0, treeop1, subtarget, &op0, &op1, EXPAND_NORMAL);
       return REDUCE_BIT_FIELD (expand_mult (mode, op0, op1, target, unsignedp));
 
-    case FMA_EXPR:
-      {
-       optab opt = fma_optab;
-       gimple *def0, *def2;
-
-       /* If there is no insn for FMA, emit it as __builtin_fma{,f,l}
-          call.  */
-       if (optab_handler (fma_optab, mode) == CODE_FOR_nothing)
-         {
-           tree fn = mathfn_built_in (TREE_TYPE (treeop0), BUILT_IN_FMA);
-           tree call_expr;
-
-           gcc_assert (fn != NULL_TREE);
-           call_expr = build_call_expr (fn, 3, treeop0, treeop1, treeop2);
-           return expand_builtin (call_expr, target, subtarget, mode, false);
-         }
-
-       def0 = get_def_for_expr (treeop0, NEGATE_EXPR);
-       /* The multiplication is commutative - look at its 2nd operand
-          if the first isn't fed by a negate.  */
-       if (!def0)
-         {
-           def0 = get_def_for_expr (treeop1, NEGATE_EXPR);
-           /* Swap operands if the 2nd operand is fed by a negate.  */
-           if (def0)
-             std::swap (treeop0, treeop1);
-         }
-       def2 = get_def_for_expr (treeop2, NEGATE_EXPR);
-
-       op0 = op2 = NULL;
-
-       if (def0 && def2
-           && optab_handler (fnms_optab, mode) != CODE_FOR_nothing)
-         {
-           opt = fnms_optab;
-           op0 = expand_normal (gimple_assign_rhs1 (def0));
-           op2 = expand_normal (gimple_assign_rhs1 (def2));
-         }
-       else if (def0
-                && optab_handler (fnma_optab, mode) != CODE_FOR_nothing)
-         {
-           opt = fnma_optab;
-           op0 = expand_normal (gimple_assign_rhs1 (def0));
-         }
-       else if (def2
-                && optab_handler (fms_optab, mode) != CODE_FOR_nothing)
-         {
-           opt = fms_optab;
-           op2 = expand_normal (gimple_assign_rhs1 (def2));
-         }
-
-       if (op0 == NULL)
-         op0 = expand_expr (treeop0, subtarget, VOIDmode, EXPAND_NORMAL);
-       if (op2 == NULL)
-         op2 = expand_normal (treeop2);
-       op1 = expand_normal (treeop1);
-
-       return expand_ternary_op (TYPE_MODE (type), opt,
-                                 op0, op1, op2, target, 0);
-      }
-
     case MULT_EXPR:
       /* If this is a fixed-point operation, then we cannot use the code
         below because "expand_mult" doesn't support sat/no-sat fixed-point
@@ -9070,6 +9062,7 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
       return REDUCE_BIT_FIELD (temp);
 
     case ABS_EXPR:
+    case ABSU_EXPR:
       op0 = expand_expr (treeop0, subtarget,
                         VOIDmode, EXPAND_NORMAL);
       if (modifier == EXPAND_STACK_PARM)
@@ -9081,7 +9074,7 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
 
       /* Unsigned abs is simply the operand.  Testing here means we don't
         risk generating incorrect code below.  */
-      if (TYPE_UNSIGNED (type))
+      if (TYPE_UNSIGNED (TREE_TYPE (treeop0)))
        return op0;
 
       return expand_abs (mode, op0, target, unsignedp,
@@ -9515,6 +9508,8 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
 
     case VEC_UNPACK_HI_EXPR:
     case VEC_UNPACK_LO_EXPR:
+    case VEC_UNPACK_FIX_TRUNC_HI_EXPR:
+    case VEC_UNPACK_FIX_TRUNC_LO_EXPR:
       {
        op0 = expand_normal (treeop0);
        temp = expand_widen_pattern_expr (ops, op0, NULL_RTX, NULL_RTX,
@@ -9548,12 +9543,46 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
       gcc_assert (target);
       return target;
 
-    case VEC_PACK_TRUNC_EXPR:
     case VEC_PACK_SAT_EXPR:
     case VEC_PACK_FIX_TRUNC_EXPR:
       mode = TYPE_MODE (TREE_TYPE (treeop0));
       goto binop;
 
+    case VEC_PACK_TRUNC_EXPR:
+      if (VECTOR_BOOLEAN_TYPE_P (type)
+         && VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (treeop0))
+         && mode == TYPE_MODE (TREE_TYPE (treeop0))
+         && SCALAR_INT_MODE_P (mode))
+       {
+         struct expand_operand eops[4];
+         machine_mode imode = TYPE_MODE (TREE_TYPE (treeop0));
+         expand_operands (treeop0, treeop1,
+                          subtarget, &op0, &op1, EXPAND_NORMAL);
+         this_optab = vec_pack_sbool_trunc_optab;
+         enum insn_code icode = optab_handler (this_optab, imode);
+         create_output_operand (&eops[0], target, mode);
+         create_convert_operand_from (&eops[1], op0, imode, false);
+         create_convert_operand_from (&eops[2], op1, imode, false);
+         temp = GEN_INT (TYPE_VECTOR_SUBPARTS (type).to_constant ());
+         create_input_operand (&eops[3], temp, imode);
+         expand_insn (icode, 4, eops);
+         return eops[0].value;
+       }
+      mode = TYPE_MODE (TREE_TYPE (treeop0));
+      goto binop;
+
+    case VEC_PACK_FLOAT_EXPR:
+      mode = TYPE_MODE (TREE_TYPE (treeop0));
+      expand_operands (treeop0, treeop1,
+                      subtarget, &op0, &op1, EXPAND_NORMAL);
+      this_optab = optab_for_tree_code (code, TREE_TYPE (treeop0),
+                                       optab_default);
+      target = expand_binop (mode, this_optab, op0, op1, target,
+                            TYPE_UNSIGNED (TREE_TYPE (treeop0)),
+                            OPTAB_LIB_WIDEN);
+      gcc_assert (target);
+      return target;
+
     case VEC_PERM_EXPR:
       {
        expand_operands (treeop0, treeop1, target, &op0, &op1, EXPAND_NORMAL);
@@ -10292,11 +10321,11 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
          {
            poly_int64 offset = mem_ref_offset (exp).force_shwi ();
            base = TREE_OPERAND (base, 0);
+           poly_uint64 type_size;
            if (known_eq (offset, 0)
                && !reverse
-               && tree_fits_uhwi_p (TYPE_SIZE (type))
-               && known_eq (GET_MODE_BITSIZE (DECL_MODE (base)),
-                            tree_to_uhwi (TYPE_SIZE (type))))
+               && poly_int_tree_p (TYPE_SIZE (type), &type_size)
+               && known_eq (GET_MODE_BITSIZE (DECL_MODE (base)), type_size))
              return expand_expr (build1 (VIEW_CONVERT_EXPR, type, base),
                                  target, tmode, modifier);
            if (TYPE_MODE (type) == BLKmode)
@@ -10615,6 +10644,14 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
        mode2
          = CONSTANT_P (op0) ? TYPE_MODE (TREE_TYPE (tem)) : GET_MODE (op0);
 
+       /* Make sure bitpos is not negative, it can wreak havoc later.  */
+       if (maybe_lt (bitpos, 0))
+         {
+           gcc_checking_assert (offset == NULL_TREE);
+           offset = size_int (bits_to_bytes_round_down (bitpos));
+           bitpos = num_trailing_bits (bitpos);
+         }
+
        /* If we have either an offset, a BLKmode result, or a reference
           outside the underlying object, we must force it to memory.
           Such a case can occur in Ada if we have unchecked conversion
@@ -10624,6 +10661,8 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
           to a larger size.  */
        must_force_mem = (offset
                          || mode1 == BLKmode
+                         || (mode == BLKmode
+                             && !int_mode_for_size (bitsize, 1).exists ())
                          || maybe_gt (bitpos + bitsize,
                                       GET_MODE_BITSIZE (mode2)));
 
@@ -10863,6 +10902,7 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
                && GET_MODE_CLASS (ext_mode) == MODE_INT)
              reversep = TYPE_REVERSE_STORAGE_ORDER (type);
 
+           gcc_checking_assert (known_ge (bitpos, 0));
            op0 = extract_bit_field (op0, bitsize, bitpos, unsignedp,
                                     (modifier == EXPAND_STACK_PARM
                                      ? NULL_RTX : target),
@@ -10987,21 +11027,18 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
                                         DECL_ATTRIBUTES (fndecl))) != NULL)
          {
            const char *ident = lang_hooks.decl_printable_name (fndecl, 1);
-           warning_at (tree_nonartificial_location (exp), 0,
+           warning_at (tree_nonartificial_location (exp),
+                       OPT_Wattribute_warning,
                        "%Kcall to %qs declared with attribute warning: %s",
                        exp, identifier_to_locale (ident),
                        TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
          }
 
        /* Check for a built-in function.  */
-       if (fndecl && DECL_BUILT_IN (fndecl))
+       if (fndecl && fndecl_built_in_p (fndecl))
          {
            gcc_assert (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_FRONTEND);
-           if (CALL_WITH_BOUNDS_P (exp))
-             return expand_builtin_with_bounds (exp, target, subtarget,
-                                                tmode, ignore);
-           else
-             return expand_builtin (exp, target, subtarget, tmode, ignore);
+           return expand_builtin (exp, target, subtarget, tmode, ignore);
          }
       }
       return expand_call (exp, target, ignore);
@@ -11216,10 +11253,11 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
          {
            rtx_code_label *label = gen_label_rtx ();
            int value = TREE_CODE (rhs) == BIT_IOR_EXPR;
-           do_jump (TREE_OPERAND (rhs, 1),
-                    value ? label : 0,
-                    value ? 0 : label,
-                    profile_probability::uninitialized ());
+           profile_probability prob = profile_probability::uninitialized ();
+           if (value)
+             jumpifnot (TREE_OPERAND (rhs, 1), label, prob);
+           else
+             jumpif (TREE_OPERAND (rhs, 1), label, prob);
            expand_assignment (lhs, build_int_cst (TREE_TYPE (rhs), value),
                               false);
            do_pending_stack_adjust ();
@@ -11302,10 +11340,10 @@ reduce_to_bit_field_precision (rtx exp, rtx target, tree type)
   if (target && GET_MODE (target) != GET_MODE (exp))
     target = 0;
   /* For constant values, reduce using build_int_cst_type. */
-  if (CONST_INT_P (exp))
+  poly_int64 const_exp;
+  if (poly_int_rtx_p (exp, &const_exp))
     {
-      HOST_WIDE_INT value = INTVAL (exp);
-      tree t = build_int_cst_type (type, value);
+      tree t = build_int_cst_type (type, const_exp);
       return expand_expr (t, target, VOIDmode, EXPAND_NORMAL);
     }
   else if (TYPE_UNSIGNED (type))
@@ -11362,126 +11400,560 @@ is_aligning_offset (const_tree offset, const_tree exp)
 }
 \f
 /* Return the tree node if an ARG corresponds to a string constant or zero
-   if it doesn't.  If we return nonzero, set *PTR_OFFSET to the offset
-   in bytes within the string that ARG is accessing.  The type of the
-   offset will be `sizetype'.  */
+   if it doesn't.  If we return nonzero, set *PTR_OFFSET to the (possibly
+   non-constant) offset in bytes within the string that ARG is accessing.
+   If MEM_SIZE is non-zero the storage size of the memory is returned.
+   If DECL is non-zero the constant declaration is returned if available.  */
 
 tree
-string_constant (tree arg, tree *ptr_offset)
+string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
 {
-  tree array, offset, lower_bound;
+  tree array;
   STRIP_NOPS (arg);
 
+  /* Non-constant index into the character array in an ARRAY_REF
+     expression or null.  */
+  tree varidx = NULL_TREE;
+
+  poly_int64 base_off = 0;
+
   if (TREE_CODE (arg) == ADDR_EXPR)
     {
-      if (TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST)
-       {
-         *ptr_offset = size_zero_node;
-         return TREE_OPERAND (arg, 0);
-       }
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL)
+      arg = TREE_OPERAND (arg, 0);
+      tree ref = arg;
+      if (TREE_CODE (arg) == ARRAY_REF)
        {
-         array = TREE_OPERAND (arg, 0);
-         offset = size_zero_node;
-       }
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
-       {
-         array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-         offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1);
-         if (TREE_CODE (array) != STRING_CST && !VAR_P (array))
-           return 0;
-
-         /* Check if the array has a nonzero lower bound.  */
-         lower_bound = array_ref_low_bound (TREE_OPERAND (arg, 0));
-         if (!integer_zerop (lower_bound))
+         tree idx = TREE_OPERAND (arg, 1);
+         if (TREE_CODE (idx) != INTEGER_CST)
            {
-             /* If the offset and base aren't both constants, return 0.  */
-             if (TREE_CODE (lower_bound) != INTEGER_CST)
-               return 0;
-             if (TREE_CODE (offset) != INTEGER_CST)
-               return 0;
-             /* Adjust offset by the lower bound.  */
-             offset = size_diffop (fold_convert (sizetype, offset),
-                                   fold_convert (sizetype, lower_bound));
+             /* From a pointer (but not array) argument extract the variable
+                index to prevent get_addr_base_and_unit_offset() from failing
+                due to it.  Use it later to compute the non-constant offset
+                into the string and return it to the caller.  */
+             varidx = idx;
+             ref = TREE_OPERAND (arg, 0);
+
+             if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+               return NULL_TREE;
+
+             if (!integer_zerop (array_ref_low_bound (arg)))
+               return NULL_TREE;
+
+             if (!integer_onep (array_ref_element_size (arg)))
+               return NULL_TREE;
            }
        }
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == MEM_REF)
-       {
-         array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-         offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1);
-         if (TREE_CODE (array) != ADDR_EXPR)
-           return 0;
-         array = TREE_OPERAND (array, 0);
-         if (TREE_CODE (array) != STRING_CST && !VAR_P (array))
-           return 0;
-       }
-      else
-       return 0;
+      array = get_addr_base_and_unit_offset (ref, &base_off);
+      if (!array
+         || (TREE_CODE (array) != VAR_DECL
+             && TREE_CODE (array) != CONST_DECL
+             && TREE_CODE (array) != STRING_CST))
+       return NULL_TREE;
     }
   else if (TREE_CODE (arg) == PLUS_EXPR || TREE_CODE (arg) == POINTER_PLUS_EXPR)
     {
       tree arg0 = TREE_OPERAND (arg, 0);
       tree arg1 = TREE_OPERAND (arg, 1);
 
-      STRIP_NOPS (arg0);
-      STRIP_NOPS (arg1);
-
-      if (TREE_CODE (arg0) == ADDR_EXPR
-         && (TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST
-             || TREE_CODE (TREE_OPERAND (arg0, 0)) == VAR_DECL))
+      tree offset;
+      tree str = string_constant (arg0, &offset, mem_size, decl);
+      if (!str)
        {
-         array = TREE_OPERAND (arg0, 0);
-         offset = arg1;
+          str = string_constant (arg1, &offset, mem_size, decl);
+          arg1 = arg0;
        }
-      else if (TREE_CODE (arg1) == ADDR_EXPR
-              && (TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST
-                  || TREE_CODE (TREE_OPERAND (arg1, 0)) == VAR_DECL))
+
+      if (str)
        {
-         array = TREE_OPERAND (arg1, 0);
-         offset = arg0;
+         /* Avoid pointers to arrays (see bug 86622).  */
+         if (POINTER_TYPE_P (TREE_TYPE (arg))
+             && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == ARRAY_TYPE
+             && !(decl && !*decl)
+             && !(decl && tree_fits_uhwi_p (DECL_SIZE_UNIT (*decl))
+                  && mem_size && tree_fits_uhwi_p (*mem_size)
+                  && tree_int_cst_equal (*mem_size, DECL_SIZE_UNIT (*decl))))
+           return NULL_TREE;
+
+         tree type = TREE_TYPE (offset);
+         arg1 = fold_convert (type, arg1);
+         *ptr_offset = fold_build2 (PLUS_EXPR, type, offset, arg1);
+         return str;
        }
-      else
-       return 0;
+      return NULL_TREE;
     }
+  else if (TREE_CODE (arg) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (arg);
+      if (!is_gimple_assign (stmt))
+       return NULL_TREE;
+
+      tree rhs1 = gimple_assign_rhs1 (stmt);
+      tree_code code = gimple_assign_rhs_code (stmt);
+      if (code == ADDR_EXPR)
+       return string_constant (rhs1, ptr_offset, mem_size, decl);
+      else if (code != POINTER_PLUS_EXPR)
+       return NULL_TREE;
+
+      tree offset;
+      if (tree str = string_constant (rhs1, &offset, mem_size, decl))
+       {
+         /* Avoid pointers to arrays (see bug 86622).  */
+         if (POINTER_TYPE_P (TREE_TYPE (rhs1))
+             && TREE_CODE (TREE_TYPE (TREE_TYPE (rhs1))) == ARRAY_TYPE
+             && !(decl && !*decl)
+             && !(decl && tree_fits_uhwi_p (DECL_SIZE_UNIT (*decl))
+                  && mem_size && tree_fits_uhwi_p (*mem_size)
+                  && tree_int_cst_equal (*mem_size, DECL_SIZE_UNIT (*decl))))
+           return NULL_TREE;
+
+         tree rhs2 = gimple_assign_rhs2 (stmt);
+         tree type = TREE_TYPE (offset);
+         rhs2 = fold_convert (type, rhs2);
+         *ptr_offset = fold_build2 (PLUS_EXPR, type, offset, rhs2);
+         return str;
+       }
+      return NULL_TREE;
+    }
+  else if (DECL_P (arg))
+    array = arg;
   else
-    return 0;
+    return NULL_TREE;
+
+  tree offset = wide_int_to_tree (sizetype, base_off);
+  if (varidx)
+    {
+      if (TREE_CODE (TREE_TYPE (array)) != ARRAY_TYPE)
+       return NULL_TREE;
+
+      gcc_assert (TREE_CODE (arg) == ARRAY_REF);
+      tree chartype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (arg, 0)));
+      if (TREE_CODE (chartype) != INTEGER_TYPE)
+       return NULL;
+
+      offset = fold_convert (sizetype, varidx);
+    }
 
   if (TREE_CODE (array) == STRING_CST)
     {
       *ptr_offset = fold_convert (sizetype, offset);
+      if (mem_size)
+       *mem_size = TYPE_SIZE_UNIT (TREE_TYPE (array));
+      if (decl)
+       *decl = NULL_TREE;
+      gcc_checking_assert (tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE (array)))
+                          >= TREE_STRING_LENGTH (array));
       return array;
     }
-  else if (VAR_P (array) || TREE_CODE (array) == CONST_DECL)
+
+  if (!VAR_P (array) && TREE_CODE (array) != CONST_DECL)
+    return NULL_TREE;
+
+  tree init = ctor_for_folding (array);
+
+  /* Handle variables initialized with string literals.  */
+  if (!init || init == error_mark_node)
+    return NULL_TREE;
+  if (TREE_CODE (init) == CONSTRUCTOR)
     {
-      int length;
-      tree init = ctor_for_folding (array);
+      /* Convert the 64-bit constant offset to a wider type to avoid
+        overflow.  */
+      offset_int wioff;
+      if (!base_off.is_constant (&wioff))
+       return NULL_TREE;
 
-      /* Variables initialized to string literals can be handled too.  */
-      if (init == error_mark_node
-         || !init
-         || TREE_CODE (init) != STRING_CST)
-       return 0;
+      wioff *= BITS_PER_UNIT;
+      if (!wi::fits_uhwi_p (wioff))
+       return NULL_TREE;
 
-      /* Avoid const char foo[4] = "abcde";  */
-      if (DECL_SIZE_UNIT (array) == NULL_TREE
-         || TREE_CODE (DECL_SIZE_UNIT (array)) != INTEGER_CST
-         || (length = TREE_STRING_LENGTH (init)) <= 0
-         || compare_tree_int (DECL_SIZE_UNIT (array), length) < 0)
-       return 0;
+      base_off = wioff.to_uhwi ();
+      unsigned HOST_WIDE_INT fieldoff = 0;
+      init = fold_ctor_reference (NULL_TREE, init, base_off, 0, array,
+                                 &fieldoff);
+      HOST_WIDE_INT cstoff;
+      if (!base_off.is_constant (&cstoff))
+       return NULL_TREE;
 
-      /* If variable is bigger than the string literal, OFFSET must be constant
-        and inside of the bounds of the string literal.  */
-      offset = fold_convert (sizetype, offset);
-      if (compare_tree_int (DECL_SIZE_UNIT (array), length) > 0
-         && (! tree_fits_uhwi_p (offset)
-             || compare_tree_int (offset, length) >= 0))
-       return 0;
+      cstoff = (cstoff - fieldoff) / BITS_PER_UNIT;
+      tree off = build_int_cst (sizetype, cstoff);
+      if (varidx)
+       offset = fold_build2 (PLUS_EXPR, TREE_TYPE (offset), offset, off);
+      else
+       offset = off;
+    }
+
+  if (!init)
+    return NULL_TREE;
+
+  *ptr_offset = offset;
+
+  tree eltype = TREE_TYPE (init);
+  tree initsize = TYPE_SIZE_UNIT (eltype);
+  if (mem_size)
+    *mem_size = initsize;
 
-      *ptr_offset = offset;
-      return init;
+  if (decl)
+    *decl = array;
+
+  if (TREE_CODE (init) == INTEGER_CST
+      && (TREE_CODE (TREE_TYPE (array)) == INTEGER_TYPE
+         || TYPE_MAIN_VARIANT (eltype) == char_type_node))
+    {
+      /* For a reference to (address of) a single constant character,
+        store the native representation of the character in CHARBUF.
+        If the reference is to an element of an array or a member
+        of a struct, only consider narrow characters until ctors
+        for wide character arrays are transformed to STRING_CSTs
+        like those for narrow arrays.  */
+      unsigned char charbuf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
+      int len = native_encode_expr (init, charbuf, sizeof charbuf, 0);
+      if (len > 0)
+       {
+         /* Construct a string literal with elements of ELTYPE and
+            the representation above.  Then strip
+            the ADDR_EXPR (ARRAY_REF (...)) around the STRING_CST.  */
+         init = build_string_literal (len, (char *)charbuf, eltype);
+         init = TREE_OPERAND (TREE_OPERAND (init, 0), 0);
+       }
     }
 
-  return 0;
+  if (TREE_CODE (init) != STRING_CST)
+    return NULL_TREE;
+
+  gcc_checking_assert (tree_to_shwi (initsize) >= TREE_STRING_LENGTH (init));
+
+  return init;
+}
+\f
+/* Compute the modular multiplicative inverse of A modulo M
+   using extended Euclid's algorithm.  Assumes A and M are coprime.  */
+static wide_int
+mod_inv (const wide_int &a, const wide_int &b)
+{
+  /* Verify the assumption.  */
+  gcc_checking_assert (wi::eq_p (wi::gcd (a, b), 1));
+
+  unsigned int p = a.get_precision () + 1;
+  gcc_checking_assert (b.get_precision () + 1 == p);
+  wide_int c = wide_int::from (a, p, UNSIGNED);
+  wide_int d = wide_int::from (b, p, UNSIGNED);
+  wide_int x0 = wide_int::from (0, p, UNSIGNED);
+  wide_int x1 = wide_int::from (1, p, UNSIGNED);
+
+  if (wi::eq_p (b, 1))
+    return wide_int::from (1, p, UNSIGNED);
+
+  while (wi::gt_p (c, 1, UNSIGNED))
+    {
+      wide_int t = d;
+      wide_int q = wi::divmod_trunc (c, d, UNSIGNED, &d);
+      c = t;
+      wide_int s = x0;
+      x0 = wi::sub (x1, wi::mul (q, x0));
+      x1 = s;
+    }
+  if (wi::lt_p (x1, 0, SIGNED))
+    x1 += d;
+  return x1;
+}
+
+/* Optimize x % C1 == C2 for signed modulo if C1 is a power of two and C2
+   is non-zero and C3 ((1<<(prec-1)) | (C1 - 1)):
+   for C2 > 0 to x & C3 == C2
+   for C2 < 0 to x & C3 == (C2 & C3).  */
+enum tree_code
+maybe_optimize_pow2p_mod_cmp (enum tree_code code, tree *arg0, tree *arg1)
+{
+  gimple *stmt = get_def_for_expr (*arg0, TRUNC_MOD_EXPR);
+  tree treeop0 = gimple_assign_rhs1 (stmt);
+  tree treeop1 = gimple_assign_rhs2 (stmt);
+  tree type = TREE_TYPE (*arg0);
+  scalar_int_mode mode;
+  if (!is_a <scalar_int_mode> (TYPE_MODE (type), &mode))
+    return code;
+  if (GET_MODE_BITSIZE (mode) != TYPE_PRECISION (type)
+      || TYPE_PRECISION (type) <= 1
+      || TYPE_UNSIGNED (type)
+      /* Signed x % c == 0 should have been optimized into unsigned modulo
+        earlier.  */
+      || integer_zerop (*arg1)
+      /* If c is known to be non-negative, modulo will be expanded as unsigned
+        modulo.  */
+      || get_range_pos_neg (treeop0) == 1)
+    return code;
+
+  /* x % c == d where d < 0 && d <= -c should be always false.  */
+  if (tree_int_cst_sgn (*arg1) == -1
+      && -wi::to_widest (treeop1) >= wi::to_widest (*arg1))
+    return code;
+
+  int prec = TYPE_PRECISION (type);
+  wide_int w = wi::to_wide (treeop1) - 1;
+  w |= wi::shifted_mask (0, prec - 1, true, prec);
+  tree c3 = wide_int_to_tree (type, w);
+  tree c4 = *arg1;
+  if (tree_int_cst_sgn (*arg1) == -1)
+    c4 = wide_int_to_tree (type, w & wi::to_wide (*arg1));
+
+  rtx op0 = expand_normal (treeop0);
+  treeop0 = make_tree (TREE_TYPE (treeop0), op0);
+
+  bool speed_p = optimize_insn_for_speed_p ();
+
+  do_pending_stack_adjust ();
+
+  location_t loc = gimple_location (stmt);
+  struct separate_ops ops;
+  ops.code = TRUNC_MOD_EXPR;
+  ops.location = loc;
+  ops.type = TREE_TYPE (treeop0);
+  ops.op0 = treeop0;
+  ops.op1 = treeop1;
+  ops.op2 = NULL_TREE;
+  start_sequence ();
+  rtx mor = expand_expr_real_2 (&ops, NULL_RTX, TYPE_MODE (ops.type),
+                               EXPAND_NORMAL);
+  rtx_insn *moinsns = get_insns ();
+  end_sequence ();
+
+  unsigned mocost = seq_cost (moinsns, speed_p);
+  mocost += rtx_cost (mor, mode, EQ, 0, speed_p);
+  mocost += rtx_cost (expand_normal (*arg1), mode, EQ, 1, speed_p);
+
+  ops.code = BIT_AND_EXPR;
+  ops.location = loc;
+  ops.type = TREE_TYPE (treeop0);
+  ops.op0 = treeop0;
+  ops.op1 = c3;
+  ops.op2 = NULL_TREE;
+  start_sequence ();
+  rtx mur = expand_expr_real_2 (&ops, NULL_RTX, TYPE_MODE (ops.type),
+                               EXPAND_NORMAL);
+  rtx_insn *muinsns = get_insns ();
+  end_sequence ();
+
+  unsigned mucost = seq_cost (muinsns, speed_p);
+  mucost += rtx_cost (mur, mode, EQ, 0, speed_p);
+  mucost += rtx_cost (expand_normal (c4), mode, EQ, 1, speed_p);
+
+  if (mocost <= mucost)
+    {
+      emit_insn (moinsns);
+      *arg0 = make_tree (TREE_TYPE (*arg0), mor);
+      return code;
+    }
+
+  emit_insn (muinsns);
+  *arg0 = make_tree (TREE_TYPE (*arg0), mur);
+  *arg1 = c4;
+  return code;
+}
+
+/* Attempt to optimize unsigned (X % C1) == C2 (or (X % C1) != C2).
+   If C1 is odd to:
+   (X - C2) * C3 <= C4 (or >), where
+   C3 is modular multiplicative inverse of C1 and 1<<prec and
+   C4 is ((1<<prec) - 1) / C1 or ((1<<prec) - 1) / C1 - 1 (the latter
+   if C2 > ((1<<prec) - 1) % C1).
+   If C1 is even, S = ctz (C1) and C2 is 0, use
+   ((X * C3) r>> S) <= C4, where C3 is modular multiplicative
+   inverse of C1>>S and 1<<prec and C4 is (((1<<prec) - 1) / (C1>>S)) >> S.
+
+   For signed (X % C1) == 0 if C1 is odd to (all operations in it
+   unsigned):
+   (X * C3) + C4 <= 2 * C4, where
+   C3 is modular multiplicative inverse of (unsigned) C1 and 1<<prec and
+   C4 is ((1<<(prec - 1) - 1) / C1).
+   If C1 is even, S = ctz(C1), use
+   ((X * C3) + C4) r>> S <= (C4 >> (S - 1))
+   where C3 is modular multiplicative inverse of (unsigned)(C1>>S) and 1<<prec
+   and C4 is ((1<<(prec - 1) - 1) / (C1>>S)) & (-1<<S).
+
+   See the Hacker's Delight book, section 10-17.  */
+enum tree_code
+maybe_optimize_mod_cmp (enum tree_code code, tree *arg0, tree *arg1)
+{
+  gcc_checking_assert (code == EQ_EXPR || code == NE_EXPR);
+  gcc_checking_assert (TREE_CODE (*arg1) == INTEGER_CST);
+
+  if (optimize < 2)
+    return code;
+
+  gimple *stmt = get_def_for_expr (*arg0, TRUNC_MOD_EXPR);
+  if (stmt == NULL)
+    return code;
+
+  tree treeop0 = gimple_assign_rhs1 (stmt);
+  tree treeop1 = gimple_assign_rhs2 (stmt);
+  if (TREE_CODE (treeop0) != SSA_NAME
+      || TREE_CODE (treeop1) != INTEGER_CST
+      /* Don't optimize the undefined behavior case x % 0;
+        x % 1 should have been optimized into zero, punt if
+        it makes it here for whatever reason;
+        x % -c should have been optimized into x % c.  */
+      || compare_tree_int (treeop1, 2) <= 0
+      /* Likewise x % c == d where d >= c should be always false.  */
+      || tree_int_cst_le (treeop1, *arg1))
+    return code;
+
+  /* Unsigned x % pow2 is handled right already, for signed
+     modulo handle it in maybe_optimize_pow2p_mod_cmp.  */
+  if (integer_pow2p (treeop1))
+    return maybe_optimize_pow2p_mod_cmp (code, arg0, arg1);
+
+  tree type = TREE_TYPE (*arg0);
+  scalar_int_mode mode;
+  if (!is_a <scalar_int_mode> (TYPE_MODE (type), &mode))
+    return code;
+  if (GET_MODE_BITSIZE (mode) != TYPE_PRECISION (type)
+      || TYPE_PRECISION (type) <= 1)
+    return code;
+
+  signop sgn = UNSIGNED;
+  /* If both operands are known to have the sign bit clear, handle
+     even the signed modulo case as unsigned.  treeop1 is always
+     positive >= 2, checked above.  */
+  if (!TYPE_UNSIGNED (type) && get_range_pos_neg (treeop0) != 1)
+    sgn = SIGNED;
+
+  if (!TYPE_UNSIGNED (type))
+    {
+      if (tree_int_cst_sgn (*arg1) == -1)
+       return code;
+      type = unsigned_type_for (type);
+      if (!type || TYPE_MODE (type) != TYPE_MODE (TREE_TYPE (*arg0)))
+       return code;
+    }
+
+  int prec = TYPE_PRECISION (type);
+  wide_int w = wi::to_wide (treeop1);
+  int shift = wi::ctz (w);
+  /* Unsigned (X % C1) == C2 is equivalent to (X - C2) % C1 == 0 if
+     C2 <= -1U % C1, because for any Z >= 0U - C2 in that case (Z % C1) != 0.
+     If C1 is odd, we can handle all cases by subtracting
+     C4 below.  We could handle even the even C1 and C2 > -1U % C1 cases
+     e.g. by testing for overflow on the subtraction, punt on that for now
+     though.  */
+  if ((sgn == SIGNED || shift) && !integer_zerop (*arg1))
+    {
+      if (sgn == SIGNED)
+       return code;
+      wide_int x = wi::umod_trunc (wi::mask (prec, false, prec), w);
+      if (wi::gtu_p (wi::to_wide (*arg1), x))
+       return code;
+    }
+
+  imm_use_iterator imm_iter;
+  use_operand_p use_p;
+  FOR_EACH_IMM_USE_FAST (use_p, imm_iter, treeop0)
+    {
+      gimple *use_stmt = USE_STMT (use_p);
+      /* Punt if treeop0 is used in the same bb in a division
+        or another modulo with the same divisor.  We should expect
+        the division and modulo combined together.  */
+      if (use_stmt == stmt
+         || gimple_bb (use_stmt) != gimple_bb (stmt))
+       continue;
+      if (!is_gimple_assign (use_stmt)
+         || (gimple_assign_rhs_code (use_stmt) != TRUNC_DIV_EXPR
+             && gimple_assign_rhs_code (use_stmt) != TRUNC_MOD_EXPR))
+       continue;
+      if (gimple_assign_rhs1 (use_stmt) != treeop0
+         || !operand_equal_p (gimple_assign_rhs2 (use_stmt), treeop1, 0))
+       continue;
+      return code;
+    }
+
+  w = wi::lrshift (w, shift);
+  wide_int a = wide_int::from (w, prec + 1, UNSIGNED);
+  wide_int b = wi::shifted_mask (prec, 1, false, prec + 1);
+  wide_int m = wide_int::from (mod_inv (a, b), prec, UNSIGNED);
+  tree c3 = wide_int_to_tree (type, m);
+  tree c5 = NULL_TREE;
+  wide_int d, e;
+  if (sgn == UNSIGNED)
+    {
+      d = wi::divmod_trunc (wi::mask (prec, false, prec), w, UNSIGNED, &e);
+      /* Use <= floor ((1<<prec) - 1) / C1 only if C2 <= ((1<<prec) - 1) % C1,
+        otherwise use < or subtract one from C4.  E.g. for
+        x % 3U == 0 we transform this into x * 0xaaaaaaab <= 0x55555555, but
+        x % 3U == 1 already needs to be
+        (x - 1) * 0xaaaaaaabU <= 0x55555554.  */
+      if (!shift && wi::gtu_p (wi::to_wide (*arg1), e))
+       d -= 1;
+      if (shift)
+       d = wi::lrshift (d, shift);
+    }
+  else
+    {
+      e = wi::udiv_trunc (wi::mask (prec - 1, false, prec), w);
+      if (!shift)
+       d = wi::lshift (e, 1);
+      else
+       {
+         e = wi::bit_and (e, wi::mask (shift, true, prec));
+         d = wi::lrshift (e, shift - 1);
+       }
+      c5 = wide_int_to_tree (type, e);
+    }
+  tree c4 = wide_int_to_tree (type, d);
+
+  rtx op0 = expand_normal (treeop0);
+  treeop0 = make_tree (TREE_TYPE (treeop0), op0);
+
+  bool speed_p = optimize_insn_for_speed_p ();
+
+  do_pending_stack_adjust ();
+
+  location_t loc = gimple_location (stmt);
+  struct separate_ops ops;
+  ops.code = TRUNC_MOD_EXPR;
+  ops.location = loc;
+  ops.type = TREE_TYPE (treeop0);
+  ops.op0 = treeop0;
+  ops.op1 = treeop1;
+  ops.op2 = NULL_TREE;
+  start_sequence ();
+  rtx mor = expand_expr_real_2 (&ops, NULL_RTX, TYPE_MODE (ops.type),
+                               EXPAND_NORMAL);
+  rtx_insn *moinsns = get_insns ();
+  end_sequence ();
+
+  unsigned mocost = seq_cost (moinsns, speed_p);
+  mocost += rtx_cost (mor, mode, EQ, 0, speed_p);
+  mocost += rtx_cost (expand_normal (*arg1), mode, EQ, 1, speed_p);
+
+  tree t = fold_convert_loc (loc, type, treeop0);
+  if (!integer_zerop (*arg1))
+    t = fold_build2_loc (loc, MINUS_EXPR, type, t, fold_convert (type, *arg1));
+  t = fold_build2_loc (loc, MULT_EXPR, type, t, c3);
+  if (sgn == SIGNED)
+    t = fold_build2_loc (loc, PLUS_EXPR, type, t, c5);
+  if (shift)
+    {
+      tree s = build_int_cst (NULL_TREE, shift);
+      t = fold_build2_loc (loc, RROTATE_EXPR, type, t, s);
+    }
+
+  start_sequence ();
+  rtx mur = expand_normal (t);
+  rtx_insn *muinsns = get_insns ();
+  end_sequence ();
+
+  unsigned mucost = seq_cost (muinsns, speed_p);
+  mucost += rtx_cost (mur, mode, LE, 0, speed_p);
+  mucost += rtx_cost (expand_normal (c4), mode, LE, 1, speed_p);
+
+  if (mocost <= mucost)
+    {
+      emit_insn (moinsns);
+      *arg0 = make_tree (TREE_TYPE (*arg0), mor);
+      return code;
+    }
+
+  emit_insn (muinsns);
+  *arg0 = make_tree (type, mur);
+  *arg1 = c4;
+  return code == EQ_EXPR ? LE_EXPR : GT_EXPR;
 }
 \f
 /* Generate code to calculate OPS, and exploded expression
@@ -11531,17 +12003,15 @@ do_store_flag (sepops ops, rtx target, machine_mode mode)
   /* We won't bother with store-flag operations involving function pointers
      when function pointers must be canonicalized before comparisons.  */
   if (targetm.have_canonicalize_funcptr_for_compare ()
-      && ((TREE_CODE (TREE_TYPE (arg0)) == POINTER_TYPE
-          && (TREE_CODE (TREE_TYPE (TREE_TYPE (arg0)))
-              == FUNCTION_TYPE))
-         || (TREE_CODE (TREE_TYPE (arg1)) == POINTER_TYPE
-             && (TREE_CODE (TREE_TYPE (TREE_TYPE (arg1)))
-                 == FUNCTION_TYPE))))
+      && ((POINTER_TYPE_P (TREE_TYPE (arg0))
+          && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (arg0))))
+         || (POINTER_TYPE_P (TREE_TYPE (arg1))
+             && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (arg1))))))
     return 0;
 
   STRIP_NOPS (arg0);
   STRIP_NOPS (arg1);
-  
+
   /* For vector typed comparisons emit code to generate the desired
      all-ones or all-zeros mask.  Conveniently use the VEC_COND_EXPR
      expander for this.  */
@@ -11560,6 +12030,24 @@ do_store_flag (sepops ops, rtx target, machine_mode mode)
        }
     }
 
+  /* Optimize (x % C1) == C2 or (x % C1) != C2 if it is beneficial
+     into (x - C2) * C3 < C4.  */
+  if ((ops->code == EQ_EXPR || ops->code == NE_EXPR)
+      && TREE_CODE (arg0) == SSA_NAME
+      && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      enum tree_code code = maybe_optimize_mod_cmp (ops->code, &arg0, &arg1);
+      if (code != ops->code)
+       {
+         struct separate_ops nops = *ops;
+         nops.code = ops->code = code;
+         nops.op0 = arg0;
+         nops.op1 = arg1;
+         nops.type = TREE_TYPE (arg0);
+         return do_store_flag (&nops, target, mode);
+       }
+    }
+
   /* Get the rtx comparison code to use.  We know that EXP is a comparison
      operation of some type.  Some comparisons against 1 and -1 can be
      converted to comparisons with zero.  Do so here so that the tests
@@ -11778,11 +12266,26 @@ do_tablejump (rtx index, machine_mode mode, rtx range, rtx table_label,
     emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, mode, 1,
                             default_label, default_probability);
 
-
   /* If index is in range, it must fit in Pmode.
      Convert to Pmode so we can index with it.  */
   if (mode != Pmode)
-    index = convert_to_mode (Pmode, index, 1);
+    {
+      unsigned int width;
+
+      /* We know the value of INDEX is between 0 and RANGE.  If we have a
+        sign-extended subreg, and RANGE does not have the sign bit set, then
+        we have a value that is valid for both sign and zero extension.  In
+        this case, we get better code if we sign extend.  */
+      if (GET_CODE (index) == SUBREG
+         && SUBREG_PROMOTED_VAR_P (index)
+         && SUBREG_PROMOTED_SIGNED_P (index)
+         && ((width = GET_MODE_PRECISION (as_a <scalar_int_mode> (mode)))
+             <= HOST_BITS_PER_WIDE_INT)
+         && ! (UINTVAL (range) & (HOST_WIDE_INT_1U << (width - 1))))
+       index = convert_to_mode (Pmode, index, 0);
+      else
+       index = convert_to_mode (Pmode, index, 1);
+    }
 
   /* Don't let a MEM slip through, because then INDEX that comes
      out of PIC_CASE_VECTOR_ADDRESS won't be a valid address,