asan.c (handle_builtin_alloca): Deal with all alloca variants.
[gcc.git] / gcc / cse.c
index 96adf18e6b7abfdd24690279cfa2155055bfaf3b..25653ac77bb0d9b702f4235099b4411220371a23 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -1,5 +1,5 @@
 /* Common subexpression elimination for GNU compiler.
-   Copyright (C) 1987-2015 Free Software Foundation, Inc.
+   Copyright (C) 1987-2017 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -21,41 +21,28 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
-#include "cfghooks.h"
-#include "tree.h"
+#include "target.h"
 #include "rtl.h"
+#include "tree.h"
+#include "cfghooks.h"
 #include "df.h"
+#include "memmodel.h"
 #include "tm_p.h"
+#include "insn-config.h"
 #include "regs.h"
+#include "emit-rtl.h"
+#include "recog.h"
 #include "cfgrtl.h"
 #include "cfganal.h"
 #include "cfgcleanup.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "recog.h"
 #include "alias.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
-#include "calls.h"
-#include "emit-rtl.h"
-#include "varasm.h"
-#include "stmt.h"
-#include "expr.h"
-#include "diagnostic-core.h"
 #include "toplev.h"
-#include "except.h"
-#include "target.h"
 #include "params.h"
 #include "rtlhooks-def.h"
 #include "tree-pass.h"
 #include "dbgcnt.h"
 #include "rtl-iter.h"
 
-#ifndef LOAD_EXTEND_OP
-#define LOAD_EXTEND_OP(M) UNKNOWN
-#endif
-
 /* The basic idea of common subexpression elimination is to go
    through the code, keeping a record of expressions that would
    have the same value at the current scan point, and replacing
@@ -733,13 +720,14 @@ preferable (int cost_a, int regcost_a, int cost_b, int regcost_b)
 static int
 notreg_cost (rtx x, machine_mode mode, enum rtx_code outer, int opno)
 {
+  scalar_int_mode int_mode, inner_mode;
   return ((GET_CODE (x) == SUBREG
           && REG_P (SUBREG_REG (x))
-          && GET_MODE_CLASS (mode) == MODE_INT
-          && GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
-          && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+          && is_int_mode (mode, &int_mode)
+          && is_int_mode (GET_MODE (SUBREG_REG (x)), &inner_mode)
+          && GET_MODE_SIZE (int_mode) < GET_MODE_SIZE (inner_mode)
           && subreg_lowpart_p (x)
-          && TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (SUBREG_REG (x))))
+          && TRULY_NOOP_TRUNCATION_MODES_P (int_mode, inner_mode))
          ? 0
          : rtx_cost (x, mode, outer, opno, optimize_this_for_speed_p) * 2);
 }
@@ -2374,7 +2362,7 @@ hash_rtx_cb (const_rtx x, machine_mode mode,
       /* We don't hash on the address of the CODE_LABEL to avoid bootstrap
         differences and differences between each stage's debugging dumps.  */
         hash += (((unsigned int) LABEL_REF << 7)
-                 + CODE_LABEL_NUMBER (LABEL_REF_LABEL (x)));
+                 + CODE_LABEL_NUMBER (label_ref_label (x)));
       return hash;
 
     case SYMBOL_REF:
@@ -2627,7 +2615,7 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
       return x == y;
 
     case LABEL_REF:
-      return LABEL_REF_LABEL (x) == LABEL_REF_LABEL (y);
+      return label_ref_label (x) == label_ref_label (y);
 
     case SYMBOL_REF:
       return XSTR (x, 0) == XSTR (y, 0);
@@ -3064,7 +3052,7 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
         with floating-point operands.  */
       if (reverse_code)
        {
-         enum rtx_code reversed = reversed_comparison_code (x, NULL_RTX);
+         enum rtx_code reversed = reversed_comparison_code (x, NULL);
          if (reversed == UNKNOWN)
            break;
          else
@@ -3297,9 +3285,8 @@ fold_rtx (rtx x, rtx_insn *insn)
         consistent with the order in X.  */
       if (canonicalize_change_group (insn, x))
        {
-         rtx tem;
-         tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
-         tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
+         std::swap (const_arg0, const_arg1);
+         std::swap (folded_arg0, folded_arg1);
        }
 
       apply_change_group ();
@@ -3343,7 +3330,7 @@ fold_rtx (rtx x, rtx_insn *insn)
          if (SCALAR_FLOAT_MODE_P (mode))
            {
 #ifdef FLOAT_STORE_FLAG_VALUE
-             true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
+             true_rtx = (const_double_from_real_value
                          (FLOAT_STORE_FLAG_VALUE (mode), mode));
 #else
              true_rtx = NULL_RTX;
@@ -3517,7 +3504,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                : lookup_as_function (folded_arg0, MINUS);
 
              if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (y, 1)) == LABEL_REF_LABEL (const_arg1))
+                 && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg1))
                return XEXP (y, 0);
 
              /* Now try for a CONST of a MINUS like the above.  */
@@ -3525,7 +3512,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                        : lookup_as_function (folded_arg0, CONST))) != 0
                  && GET_CODE (XEXP (y, 0)) == MINUS
                  && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (XEXP (y, 0), 1)) == LABEL_REF_LABEL (const_arg1))
+                 && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg1))
                return XEXP (XEXP (y, 0), 0);
            }
 
@@ -3537,7 +3524,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                : lookup_as_function (folded_arg1, MINUS);
 
              if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (y, 1)) == LABEL_REF_LABEL (const_arg0))
+                 && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg0))
                return XEXP (y, 0);
 
              /* Now try for a CONST of a MINUS like the above.  */
@@ -3545,7 +3532,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                        : lookup_as_function (folded_arg1, CONST))) != 0
                  && GET_CODE (XEXP (y, 0)) == MINUS
                  && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (XEXP (y, 0), 1)) == LABEL_REF_LABEL (const_arg0))
+                 && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg0))
                return XEXP (XEXP (y, 0), 0);
            }
 
@@ -3569,7 +3556,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                 instead we test for the problematic value in a more direct
                 manner and hope the Sun compilers get it correct.  */
              && INTVAL (const_arg1) !=
-               ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1))
+               (HOST_WIDE_INT_1 << (HOST_BITS_PER_WIDE_INT - 1))
              && REG_P (folded_arg1))
            {
              rtx new_const = GEN_INT (-INTVAL (const_arg1));
@@ -3625,7 +3612,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                {
                  if (SHIFT_COUNT_TRUNCATED)
                    canon_const_arg1 = GEN_INT (INTVAL (const_arg1)
-                                               & (GET_MODE_BITSIZE (mode)
+                                               & (GET_MODE_UNIT_BITSIZE (mode)
                                                   - 1));
                  else
                    break;
@@ -3654,13 +3641,13 @@ fold_rtx (rtx x, rtx_insn *insn)
 
              if (code == PLUS && const_arg1 == inner_const
                  && ((HAVE_PRE_INCREMENT
-                         && exact_log2 (INTVAL (const_arg1)) >= 0)
+                         && pow2p_hwi (INTVAL (const_arg1)))
                      || (HAVE_POST_INCREMENT
-                         && exact_log2 (INTVAL (const_arg1)) >= 0)
+                         && pow2p_hwi (INTVAL (const_arg1)))
                      || (HAVE_PRE_DECREMENT
-                         && exact_log2 (- INTVAL (const_arg1)) >= 0)
+                         && pow2p_hwi (- INTVAL (const_arg1)))
                      || (HAVE_POST_DECREMENT
-                         && exact_log2 (- INTVAL (const_arg1)) >= 0)))
+                         && pow2p_hwi (- INTVAL (const_arg1)))))
                break;
 
              /* ??? Vector mode shifts by scalar
@@ -3674,7 +3661,8 @@ fold_rtx (rtx x, rtx_insn *insn)
                {
                  if (SHIFT_COUNT_TRUNCATED)
                    inner_const = GEN_INT (INTVAL (inner_const)
-                                          & (GET_MODE_BITSIZE (mode) - 1));
+                                          & (GET_MODE_UNIT_BITSIZE (mode)
+                                             - 1));
                  else
                    break;
                }
@@ -3704,7 +3692,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                  /* As an exception, we can turn an ASHIFTRT of this
                     form into a shift of the number of bits - 1.  */
                  if (code == ASHIFTRT)
-                   new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1);
+                   new_const = GEN_INT (GET_MODE_UNIT_BITSIZE (mode) - 1);
                  else if (!side_effects_p (XEXP (y, 0)))
                    return CONST0_RTX (mode);
                  else
@@ -3816,7 +3804,7 @@ equiv_constant (rtx x)
         the subreg.  Note that the upper bits of paradoxical subregs
         are undefined, so they cannot be said to equal anything.  */
       if (REG_P (SUBREG_REG (x))
-         && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (imode)
+         && !paradoxical_subreg_p (x)
          && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0)
         return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x));
 
@@ -3885,6 +3873,13 @@ record_jump_equiv (rtx_insn *insn, bool taken)
   op0 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 0), insn);
   op1 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 1), insn);
 
+  /* On a cc0 target the cc0-setter and cc0-user may end up in different
+     blocks.  When that happens the tracking of the cc0-setter via
+     PREV_INSN_CC0 is spoiled.  That means that fold_rtx may return
+     NULL_RTX.  In those cases, there's nothing to record.  */
+  if (op0 == NULL_RTX || op1 == NULL_RTX)
+    return;
+
   code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
   if (! cond_known_true)
     {
@@ -3959,10 +3954,9 @@ record_jump_cond (enum rtx_code code, machine_mode mode, rtx op0,
      if we test MODE instead, we can get an infinite recursion
      alternating between two modes each wider than MODE.  */
 
-  if (code == NE && GET_CODE (op0) == SUBREG
-      && subreg_lowpart_p (op0)
-      && (GET_MODE_SIZE (GET_MODE (op0))
-         < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+  if (code == NE
+      && partial_subreg_p (op0)
+      && subreg_lowpart_p (op0))
     {
       machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
       rtx tem = record_jump_cond_subreg (inner_mode, op1);
@@ -3971,10 +3965,9 @@ record_jump_cond (enum rtx_code code, machine_mode mode, rtx op0,
                          reversed_nonequality);
     }
 
-  if (code == NE && GET_CODE (op1) == SUBREG
-      && subreg_lowpart_p (op1)
-      && (GET_MODE_SIZE (GET_MODE (op1))
-         < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
+  if (code == NE
+      && partial_subreg_p (op1)
+      && subreg_lowpart_p (op1))
     {
       machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
       rtx tem = record_jump_cond_subreg (inner_mode, op0);
@@ -4161,10 +4154,10 @@ struct set
      The size of this field should match the size of the mode
      field of struct rtx_def (see rtl.h).  */
   ENUM_BITFIELD(machine_mode) mode : 8;
-  /* A constant equivalent for SET_SRC, if any.  */
-  rtx src_const;
   /* Hash value of constant equivalent for SET_SRC.  */
   unsigned src_const_hash;
+  /* A constant equivalent for SET_SRC, if any.  */
+  rtx src_const;
   /* Table entry for constant equivalent for SET_SRC, if any.  */
   struct table_elt *src_const_elt;
   /* Table entry for the destination address.  */
@@ -4302,6 +4295,22 @@ find_sets_in_insn (rtx_insn *insn, struct set **psets)
   return n_sets;
 }
 \f
+/* Subroutine of canonicalize_insn.  X is an ASM_OPERANDS in INSN.  */
+
+static void
+canon_asm_operands (rtx x, rtx_insn *insn)
+{
+  for (int i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
+    {
+      rtx input = ASM_OPERANDS_INPUT (x, i);
+      if (!(REG_P (input) && HARD_REGISTER_P (input)))
+       {
+         input = canon_reg (input, insn);
+         validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1);
+       }
+    }
+}
+
 /* Where possible, substitute every register reference in the N_SETS
    number of SETS in INSN with the canonical register.
 
@@ -4365,17 +4374,7 @@ canonicalize_insn (rtx_insn *insn, struct set **psets, int n_sets)
     /* Canonicalize a USE of a pseudo register or memory location.  */
     canon_reg (x, insn);
   else if (GET_CODE (x) == ASM_OPERANDS)
-    {
-      for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
-       {
-         rtx input = ASM_OPERANDS_INPUT (x, i);
-         if (!(REG_P (input) && REGNO (input) < FIRST_PSEUDO_REGISTER))
-           {
-             input = canon_reg (input, insn);
-             validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1);
-           }
-       }
-    }
+    canon_asm_operands (x, insn);
   else if (GET_CODE (x) == CALL)
     {
       canon_reg (x, insn);
@@ -4404,6 +4403,8 @@ canonicalize_insn (rtx_insn *insn, struct set **psets, int n_sets)
                   && ! (REG_P (XEXP (y, 0))
                         && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
            canon_reg (y, insn);
+         else if (GET_CODE (y) == ASM_OPERANDS)
+           canon_asm_operands (y, insn);
          else if (GET_CODE (y) == CALL)
            {
              canon_reg (y, insn);
@@ -4529,12 +4530,13 @@ cse_insn (rtx_insn *insn)
      this case, and if it isn't set, then there will be no equivalence
      for the destination.  */
   if (n_sets == 1 && REG_NOTES (insn) != 0
-      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
-      && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))))
+      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0)
     {
-      if (GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART)
-       src_eqv = copy_rtx (XEXP (tem, 0));
 
+      if (GET_CODE (SET_DEST (sets[0].rtl)) != ZERO_EXTRACT
+         && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
+             || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
+       src_eqv = copy_rtx (XEXP (tem, 0));
       /* If DEST is of the form ZERO_EXTACT, as in:
         (set (zero_extract:SI (reg:SI 119)
                  (const_int 16 [0x10])
@@ -4549,20 +4551,23 @@ cse_insn (rtx_insn *insn)
               && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 2)))
        {
          rtx dest_reg = XEXP (SET_DEST (sets[0].rtl), 0);
+         /* This is the mode of XEXP (tem, 0) as well.  */
+         scalar_int_mode dest_mode
+           = as_a <scalar_int_mode> (GET_MODE (dest_reg));
          rtx width = XEXP (SET_DEST (sets[0].rtl), 1);
          rtx pos = XEXP (SET_DEST (sets[0].rtl), 2);
          HOST_WIDE_INT val = INTVAL (XEXP (tem, 0));
          HOST_WIDE_INT mask;
          unsigned int shift;
          if (BITS_BIG_ENDIAN)
-           shift = GET_MODE_PRECISION (GET_MODE (dest_reg))
-             - INTVAL (pos) - INTVAL (width);
+           shift = (GET_MODE_PRECISION (dest_mode)
+                    - INTVAL (pos) - INTVAL (width));
          else
            shift = INTVAL (pos);
          if (INTVAL (width) == HOST_BITS_PER_WIDE_INT)
-           mask = ~(HOST_WIDE_INT) 0;
+           mask = HOST_WIDE_INT_M1;
          else
-           mask = ((HOST_WIDE_INT) 1 << INTVAL (width)) - 1;
+           mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1;
          val = (val >> shift) & mask;
          src_eqv = GEN_INT (val);
        }
@@ -4578,6 +4583,7 @@ cse_insn (rtx_insn *insn)
   for (i = 0; i < n_sets; i++)
     {
       bool repeat = false;
+      bool mem_noop_insn = false;
       rtx src, dest;
       rtx src_folded;
       struct table_elt *elt = 0, *p;
@@ -4600,6 +4606,7 @@ cse_insn (rtx_insn *insn)
       /* Set nonzero if we need to call force_const_mem on with the
         contents of src_folded before using it.  */
       int src_folded_force_flag = 0;
+      scalar_int_mode int_mode;
 
       dest = SET_DEST (sets[i].rtl);
       src = SET_SRC (sets[i].rtl);
@@ -4639,7 +4646,7 @@ cse_insn (rtx_insn *insn)
 
       /* Simplify and foldable subexpressions in SRC.  Then get the fully-
         simplified result, which may not necessarily be valid.  */
-      src_folded = fold_rtx (src, insn);
+      src_folded = fold_rtx (src, NULL);
 
 #if 0
       /* ??? This caused bad code to be generated for the m68k port with -O2.
@@ -4659,7 +4666,7 @@ cse_insn (rtx_insn *insn)
              && INTVAL (width) < HOST_BITS_PER_WIDE_INT
              && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
            src_folded
-             = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
+             = GEN_INT (INTVAL (src) & ((HOST_WIDE_INT_1
                                          << INTVAL (width)) - 1));
        }
 #endif
@@ -4837,17 +4844,16 @@ cse_insn (rtx_insn *insn)
         wider mode.  */
 
       if (src_const && src_related == 0 && CONST_INT_P (src_const)
-         && GET_MODE_CLASS (mode) == MODE_INT
-         && GET_MODE_PRECISION (mode) < BITS_PER_WORD)
+         && is_int_mode (mode, &int_mode)
+         && GET_MODE_PRECISION (int_mode) < BITS_PER_WORD)
        {
-         machine_mode wider_mode;
-
-         for (wider_mode = GET_MODE_WIDER_MODE (mode);
-              wider_mode != VOIDmode
-              && GET_MODE_PRECISION (wider_mode) <= BITS_PER_WORD
-              && src_related == 0;
-              wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+         opt_scalar_int_mode wider_mode_iter;
+         FOR_EACH_WIDER_MODE (wider_mode_iter, int_mode)
            {
+             scalar_int_mode wider_mode = wider_mode_iter.require ();
+             if (GET_MODE_PRECISION (wider_mode) > BITS_PER_WORD)
+               break;
+
              struct table_elt *const_elt
                = lookup (src_const, HASH (src_const, wider_mode), wider_mode);
 
@@ -4858,9 +4864,12 @@ cse_insn (rtx_insn *insn)
                   const_elt; const_elt = const_elt->next_same_value)
                if (REG_P (const_elt->exp))
                  {
-                   src_related = gen_lowpart (mode, const_elt->exp);
+                   src_related = gen_lowpart (int_mode, const_elt->exp);
                    break;
                  }
+
+             if (src_related != 0)
+               break;
            }
        }
 
@@ -4871,16 +4880,19 @@ cse_insn (rtx_insn *insn)
         value.  */
 
       if (flag_expensive_optimizations && ! src_related
+         && is_a <scalar_int_mode> (mode, &int_mode)
          && GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1))
-         && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+         && GET_MODE_SIZE (int_mode) < UNITS_PER_WORD)
        {
-         machine_mode tmode;
+         opt_scalar_int_mode tmode_iter;
          rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1));
 
-         for (tmode = GET_MODE_WIDER_MODE (mode);
-              GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
-              tmode = GET_MODE_WIDER_MODE (tmode))
+         FOR_EACH_WIDER_MODE (tmode_iter, int_mode)
            {
+             scalar_int_mode tmode = tmode_iter.require ();
+             if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD)
+               break;
+
              rtx inner = gen_lowpart (tmode, XEXP (src, 0));
              struct table_elt *larger_elt;
 
@@ -4897,7 +4909,7 @@ cse_insn (rtx_insn *insn)
                    if (REG_P (larger_elt->exp))
                      {
                        src_related
-                         = gen_lowpart (mode, larger_elt->exp);
+                         = gen_lowpart (int_mode, larger_elt->exp);
                        break;
                      }
 
@@ -4912,28 +4924,30 @@ cse_insn (rtx_insn *insn)
         also have such operations, but this is only likely to be
         beneficial on these machines.  */
 
+      rtx_code extend_op;
       if (flag_expensive_optimizations && src_related == 0
-         && (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
-         && GET_MODE_CLASS (mode) == MODE_INT
          && MEM_P (src) && ! do_not_record
-         && LOAD_EXTEND_OP (mode) != UNKNOWN)
+         && is_a <scalar_int_mode> (mode, &int_mode)
+         && (extend_op = load_extend_op (int_mode)) != UNKNOWN)
        {
          struct rtx_def memory_extend_buf;
          rtx memory_extend_rtx = &memory_extend_buf;
-         machine_mode tmode;
 
          /* Set what we are trying to extend and the operation it might
             have been extended with.  */
          memset (memory_extend_rtx, 0, sizeof (*memory_extend_rtx));
-         PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
+         PUT_CODE (memory_extend_rtx, extend_op);
          XEXP (memory_extend_rtx, 0) = src;
 
-         for (tmode = GET_MODE_WIDER_MODE (mode);
-              GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
-              tmode = GET_MODE_WIDER_MODE (tmode))
+         opt_scalar_int_mode tmode_iter;
+         FOR_EACH_WIDER_MODE (tmode_iter, int_mode)
            {
              struct table_elt *larger_elt;
 
+             scalar_int_mode tmode = tmode_iter.require ();
+             if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD)
+               break;
+
              PUT_MODE (memory_extend_rtx, tmode);
              larger_elt = lookup (memory_extend_rtx,
                                   HASH (memory_extend_rtx, tmode), tmode);
@@ -4944,7 +4958,7 @@ cse_insn (rtx_insn *insn)
                   larger_elt; larger_elt = larger_elt->next_same_value)
                if (REG_P (larger_elt->exp))
                  {
-                   src_related = gen_lowpart (mode, larger_elt->exp);
+                   src_related = gen_lowpart (int_mode, larger_elt->exp);
                    break;
                  }
 
@@ -4998,8 +5012,8 @@ cse_insn (rtx_insn *insn)
              && ! (src != 0
                    && GET_CODE (src) == SUBREG
                    && GET_MODE (src) == GET_MODE (p->exp)
-                   && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
-                       < GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))))
+                   && partial_subreg_p (GET_MODE (SUBREG_REG (src)),
+                                        GET_MODE (SUBREG_REG (p->exp)))))
            continue;
 
          if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp))
@@ -5109,8 +5123,8 @@ cse_insn (rtx_insn *insn)
              && ! (src != 0
                    && GET_CODE (src) == SUBREG
                    && GET_MODE (src) == GET_MODE (elt->exp)
-                   && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
-                       < GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))))
+                   && partial_subreg_p (GET_MODE (SUBREG_REG (src)),
+                                        GET_MODE (SUBREG_REG (elt->exp)))))
            {
              elt = elt->next_same_value;
              continue;
@@ -5169,7 +5183,7 @@ cse_insn (rtx_insn *insn)
            }
 
          /* Avoid creation of overlapping memory moves.  */
-         if (MEM_P (trial) && MEM_P (SET_DEST (sets[i].rtl)))
+         if (MEM_P (trial) && MEM_P (dest) && !rtx_equal_p (trial, dest))
            {
              rtx src, dest;
 
@@ -5221,18 +5235,21 @@ cse_insn (rtx_insn *insn)
                  HOST_WIDE_INT val = INTVAL (dest_cst);
                  HOST_WIDE_INT mask;
                  unsigned int shift;
+                 /* This is the mode of DEST_CST as well.  */
+                 scalar_int_mode dest_mode
+                   = as_a <scalar_int_mode> (GET_MODE (dest_reg));
                  if (BITS_BIG_ENDIAN)
-                   shift = GET_MODE_PRECISION (GET_MODE (dest_reg))
+                   shift = GET_MODE_PRECISION (dest_mode)
                            - INTVAL (pos) - INTVAL (width);
                  else
                    shift = INTVAL (pos);
                  if (INTVAL (width) == HOST_BITS_PER_WIDE_INT)
-                   mask = ~(HOST_WIDE_INT) 0;
+                   mask = HOST_WIDE_INT_M1;
                  else
-                   mask = ((HOST_WIDE_INT) 1 << INTVAL (width)) - 1;
+                   mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1;
                  val &= ~(mask << shift);
                  val |= (INTVAL (trial) & mask) << shift;
-                 val = trunc_int_for_mode (val, GET_MODE (dest_reg));
+                 val = trunc_int_for_mode (val, dest_mode);
                  validate_unshare_change (insn, &SET_DEST (sets[i].rtl),
                                           dest_reg, 1);
                  validate_unshare_change (insn, &SET_SRC (sets[i].rtl),
@@ -5280,6 +5297,21 @@ cse_insn (rtx_insn *insn)
              break;
            }
 
+         /* Similarly, lots of targets don't allow no-op
+            (set (mem x) (mem x)) moves.  */
+         else if (n_sets == 1
+                  && MEM_P (trial)
+                  && MEM_P (dest)
+                  && rtx_equal_p (trial, dest)
+                  && !side_effects_p (dest)
+                  && (cfun->can_delete_dead_exceptions
+                      || insn_nothrow_p (insn)))
+           {
+             SET_SRC (sets[i].rtl) = trial;
+             mem_noop_insn = true;
+             break;
+           }
+
          /* Reject certain invalid forms of CONST that we create.  */
          else if (CONSTANT_P (trial)
                   && GET_CODE (trial) == CONST
@@ -5492,12 +5524,22 @@ cse_insn (rtx_insn *insn)
       else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx)
        {
          /* One less use of the label this insn used to jump to.  */
-         delete_insn_and_edges (insn);
+         cse_cfg_altered |= delete_insn_and_edges (insn);
          cse_jumps_altered = true;
          /* No more processing for this set.  */
          sets[i].rtl = 0;
        }
 
+      /* Similarly for no-op MEM moves.  */
+      else if (mem_noop_insn)
+       {
+         if (cfun->can_throw_non_call_exceptions && can_throw_internal (insn))
+           cse_cfg_altered = true;
+         cse_cfg_altered |= delete_insn_and_edges (insn);
+         /* No more processing for this set.  */
+         sets[i].rtl = 0;
+       }
+
       /* If this SET is now setting PC to a label, we know it used to
         be a conditional or computed branch.  */
       else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF
@@ -5528,7 +5570,7 @@ cse_insn (rtx_insn *insn)
                  REG_NOTES (new_rtx) = note;
                }
 
-             delete_insn_and_edges (insn);
+             cse_cfg_altered |= delete_insn_and_edges (insn);
              insn = new_rtx;
            }
          else
@@ -5728,6 +5770,13 @@ cse_insn (rtx_insn *insn)
     {
       if (!(RTL_CONST_OR_PURE_CALL_P (insn)))
        invalidate_memory ();
+      else
+       /* For const/pure calls, invalidate any argument slots, because
+          those are owned by the callee.  */
+       for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
+         if (GET_CODE (XEXP (tem, 0)) == USE
+             && MEM_P (XEXP (XEXP (tem, 0), 0)))
+           invalidate (XEXP (XEXP (tem, 0), 0), VOIDmode);
       invalidate_for_call ();
     }
 
@@ -5860,15 +5909,7 @@ cse_insn (rtx_insn *insn)
            || GET_MODE (dest) == BLKmode
            /* If we didn't put a REG_EQUAL value or a source into the hash
               table, there is no point is recording DEST.  */
-           || sets[i].src_elt == 0
-           /* If DEST is a paradoxical SUBREG and SRC is a ZERO_EXTEND
-              or SIGN_EXTEND, don't record DEST since it can cause
-              some tracking to be wrong.
-
-              ??? Think about this more later.  */
-           || (paradoxical_subreg_p (dest)
-               && (GET_CODE (sets[i].src) == SIGN_EXTEND
-                   || GET_CODE (sets[i].src) == ZERO_EXTEND)))
+           || sets[i].src_elt == 0)
          continue;
 
        /* STRICT_LOW_PART isn't part of the value BEING set,
@@ -5887,6 +5928,11 @@ cse_insn (rtx_insn *insn)
              sets[i].dest_hash = HASH (dest, GET_MODE (dest));
            }
 
+       /* If DEST is a paradoxical SUBREG, don't record DEST since the bits
+          outside the mode of GET_MODE (SUBREG_REG (dest)) are undefined.  */
+       if (paradoxical_subreg_p (dest))
+         continue;
+
        elt = insert (dest, sets[i].src_elt,
                      sets[i].dest_hash, GET_MODE (dest));
 
@@ -5920,8 +5966,7 @@ cse_insn (rtx_insn *insn)
            && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
                 / UNITS_PER_WORD)
                == (GET_MODE_SIZE (GET_MODE (dest)) - 1) / UNITS_PER_WORD)
-           && (GET_MODE_SIZE (GET_MODE (dest))
-               >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
+           && !partial_subreg_p (dest)
            && sets[i].src_elt != 0)
          {
            machine_mode new_mode = GET_MODE (SUBREG_REG (dest));
@@ -5933,7 +5978,6 @@ cse_insn (rtx_insn *insn)
                rtx new_src = 0;
                unsigned src_hash;
                struct table_elt *src_elt;
-               int byte = 0;
 
                /* Ignore invalid entries.  */
                if (!REG_P (elt->exp)
@@ -5946,13 +5990,8 @@ cse_insn (rtx_insn *insn)
                  new_src = elt->exp;
                else
                  {
-                   /* Calculate big endian correction for the SUBREG_BYTE.
-                      We have already checked that M1 (GET_MODE (dest))
-                      is not narrower than M2 (new_mode).  */
-                   if (BYTES_BIG_ENDIAN)
-                     byte = (GET_MODE_SIZE (GET_MODE (dest))
-                             - GET_MODE_SIZE (new_mode));
-
+                   unsigned int byte
+                     = subreg_lowpart_offset (new_mode, GET_MODE (dest));
                    new_src = simplify_gen_subreg (new_mode, elt->exp,
                                                   GET_MODE (dest), byte);
                  }
@@ -6461,10 +6500,10 @@ check_for_label_ref (rtx_insn *insn)
       if (GET_CODE (x) == LABEL_REF
          && !LABEL_REF_NONLOCAL_P (x)
          && (!JUMP_P (insn)
-             || !label_is_jump_target_p (LABEL_REF_LABEL (x), insn))
-         && LABEL_P (LABEL_REF_LABEL (x))
-         && INSN_UID (LABEL_REF_LABEL (x)) != 0
-         && !find_reg_note (insn, REG_LABEL_OPERAND, LABEL_REF_LABEL (x)))
+             || !label_is_jump_target_p (label_ref_label (x), insn))
+         && LABEL_P (label_ref_label (x))
+         && INSN_UID (label_ref_label (x)) != 0
+         && !find_reg_note (insn, REG_LABEL_OPERAND, label_ref_label (x)))
        return true;
     }
   return false;
@@ -6609,6 +6648,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
         equivalences due to the condition being tested.  */
       insn = BB_END (bb);
       if (path_entry < path_size - 1
+         && EDGE_COUNT (bb->succs) == 2
          && JUMP_P (insn)
          && single_set (insn)
          && any_condjump_p (insn))
@@ -6646,6 +6686,10 @@ cse_main (rtx_insn *f ATTRIBUTE_UNUSED, int nregs)
   int *rc_order = XNEWVEC (int, last_basic_block_for_fn (cfun));
   int i, n_blocks;
 
+  /* CSE doesn't use dominane info but can invalidate it in different ways.
+     For simplicity free dominance info here.  */
+  free_dominance_info (CDI_DOMINATORS);
+
   df_set_flags (DF_LR_RUN_DCE);
   df_note_add_problem ();
   df_analyze ();
@@ -7108,7 +7152,7 @@ delete_trivially_dead_insns (rtx_insn *insns, int nreg)
              count_reg_usage (insn, counts, NULL_RTX, -1);
              ndead++;
            }
-         delete_insn_and_edges (insn);
+         cse_cfg_altered |= delete_insn_and_edges (insn);
        }
     }
 
@@ -7404,7 +7448,7 @@ cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src,
                                    newreg);
        }
 
-      delete_insn_and_edges (insns[i]);
+      cse_cfg_altered |= delete_insn_and_edges (insns[i]);
     }
 
   return mode;
@@ -7539,11 +7583,11 @@ rest_of_handle_cse (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (CLEANUP_CFG_CHANGED);
+      cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
   else if (tem == 1 || optimize > 1)
-    cleanup_cfg (0);
+    cse_cfg_altered |= cleanup_cfg (0);
 
   return 0;
 }
@@ -7608,11 +7652,11 @@ rest_of_handle_cse2 (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (CLEANUP_CFG_CHANGED);
+      cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
   else if (tem == 1)
-    cleanup_cfg (0);
+    cse_cfg_altered |= cleanup_cfg (0);
 
   cse_not_expected = 1;
   return 0;
@@ -7672,7 +7716,7 @@ rest_of_handle_cse_after_global_opts (void)
 
   rebuild_jump_labels (get_insns ());
   tem = cse_main (get_insns (), max_reg_num ());
-  purge_all_dead_edges ();
+  cse_cfg_altered |= purge_all_dead_edges ();
   delete_trivially_dead_insns (get_insns (), max_reg_num ());
 
   cse_not_expected = !flag_rerun_cse_after_loop;
@@ -7682,11 +7726,11 @@ rest_of_handle_cse_after_global_opts (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (CLEANUP_CFG_CHANGED);
+      cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
   else if (tem == 1)
-    cleanup_cfg (0);
+    cse_cfg_altered |= cleanup_cfg (0);
 
   flag_cse_follow_jumps = save_cfj;
   return 0;