Hi,

After sulking for a bit :) here's v8 of backprop.  Actually the patch
had already been OK'd before we established that it can't work with
signed types because of the possibility of new overflow.

Thus, one change to the last version is TYPE_OVERFLOW_WRAPS guards
so we leave signed types in peace.

Separately, I noticed an nasty issue with multiplications.  In a * b we
relied on the nonzero bits of b in order to determine the lower bound of 
nonzero bits.  But take a situation like:

 unsigned int p = x & 4;
 unsigned int b = p * p;
 ...

Depending on b's demand, we can deduce that p's demand is outside of
& 4, eventually elide the mask, and, in doing so, invalidate our 
deduction.  I believe we could do slightly better but this patch now 
just restricts MULT_EXPR to constant RHS and adds two new tests.

Bootstrapped and regtested on x86, power10, and aarch64.
Regtested on riscv64.

Regards
 Robin


This patch adds backpropagation of demand-bit information to the
backprop pass.  It is intended to catch cases like this

  int
  foo (int in)
    {
      int a = in & 0xff;
      int b = a << 1;
      int c = b & 0xf;
      return c;
    }

where we can propagate B's demand of 0xf back to IN and elide the
& 0xff masking.  This works in situations where forward-looking
optimizations like in match.pd fail, for example due to non-single
use.

The implementation here covers BIT_AND_EXPR, BIT_IOR_EXPR, MULT_EXPR,
LSHIFT_EXPR, RSHIFT_EXPR, and PLUS_EXPR, MINUS_EXPR, as well as
TRUNC_DIV_EXPR.

Once the demand has reached a BIT_AND_EXPR and we see that it does not
exceed what we mask off anyway, we elide the mask.  Similarly, we zero
assignments whose nonzero bits do not overlap the demanded bits.

        PR tree-optimization/113487
        PR tree-optimization/118650

gcc/ChangeLog:

        * gimple-ssa-backprop.cc (strip_vectype): New function.
        (get_precision): New function.
        (usage_info::intersection_identity): Add dropped-bits handling.
        (usage_info::operator&): Add dropped bits.
        (usage_info::is_useful): Ditto.
        (dump_usage_info): Log demand bits.
        (backprop::~backprop): Destroy usage_infos.
        (backprop::process_assign_use): Handle dropped bits.
        (backprop::intersect_uses): Ditto.
        (backprop::process_var): Ditto.
        (backprop::optimize_assign): Elide mask if possible.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/backprop-bits-1.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-10.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-11.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-12.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-13.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-14.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-15.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-16.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-17.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-18.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-2.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-3.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-4.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-5.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-6.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-7.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-8.c: New test.
        * gcc.dg/tree-ssa/backprop-bits-9.c: New test.
        * gcc.dg/torture/crc-29.c: Add -fno-ssa-backprop.
---
 gcc/gimple-ssa-backprop.cc                    | 418 +++++++++++++++++-
 gcc/testsuite/gcc.dg/torture/crc-29.c         |   2 +-
 .../gcc.dg/tree-ssa/backprop-bits-1.c         |  15 +
 .../gcc.dg/tree-ssa/backprop-bits-10.c        |  36 ++
 .../gcc.dg/tree-ssa/backprop-bits-11.c        |  15 +
 .../gcc.dg/tree-ssa/backprop-bits-12.c        |  32 ++
 .../gcc.dg/tree-ssa/backprop-bits-13.c        |  22 +
 .../gcc.dg/tree-ssa/backprop-bits-14.c        |  25 ++
 .../gcc.dg/tree-ssa/backprop-bits-15.c        |  47 ++
 .../gcc.dg/tree-ssa/backprop-bits-16.c        |  23 +
 .../gcc.dg/tree-ssa/backprop-bits-17.c        |  24 +
 .../gcc.dg/tree-ssa/backprop-bits-18.c        |  21 +
 .../gcc.dg/tree-ssa/backprop-bits-2.c         |  24 +
 .../gcc.dg/tree-ssa/backprop-bits-3.c         |  44 ++
 .../gcc.dg/tree-ssa/backprop-bits-4.c         |  39 ++
 .../gcc.dg/tree-ssa/backprop-bits-5.c         |  18 +
 .../gcc.dg/tree-ssa/backprop-bits-6.c         |  41 ++
 .../gcc.dg/tree-ssa/backprop-bits-7.c         |  32 ++
 .../gcc.dg/tree-ssa/backprop-bits-8.c         |  24 +
 .../gcc.dg/tree-ssa/backprop-bits-9.c         |  24 +
 20 files changed, 903 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-10.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-11.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-12.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-13.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-14.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-15.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-16.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-17.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-18.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-3.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-4.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-5.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-6.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-7.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-8.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-9.c

diff --git a/gcc/gimple-ssa-backprop.cc b/gcc/gimple-ssa-backprop.cc
index a95e2f95c5f..de63e2caa80 100644
--- a/gcc/gimple-ssa-backprop.cc
+++ b/gcc/gimple-ssa-backprop.cc
@@ -23,12 +23,13 @@ along with GCC; see the file COPYING3.  If not see
    fully or partially dead code, but the main focus is simplifying
    computations.
 
-   At the moment the pass only handles one piece of information: whether the
-   sign of a value matters, and therefore whether sign-changing operations
-   can be skipped.  The pass could be extended to more interesting
-   information in future, such as which bits of an integer are significant.
+   The pass only handles two distinct properties:
+      (a) Whether the sign of a value matters, and therefore whether
+         sign-changing operations can be skipped.
+      (b) The bits a variable demands.  This opens up possibilities to
+         elide redundant bit masking.
 
-   For example, take the function:
+   For an example of (a), take the function:
 
      double
      f (double *a, int n, double start)
@@ -48,6 +49,28 @@ along with GCC; see the file COPYING3.  If not see
    with start.  Since there are no other uses of the fabs result,
    the call would get deleted as dead.
 
+   A typical case for (b) is:
+
+     void use (unsigned);
+
+     void
+     foo (unsigned x)
+     {
+       unsigned a = x & 0xff;
+       unsigned b = a * 4;
+       use (b & 0x10);
+       use (b & 0xe0);
+     }
+
+   Both use sites of B mask it and we cannot look through the multiplication to
+   elide the masking directly (via e.g. match.pd or ccp).  Seeing the demand
+   on B (= 0xf0), we can back-propagate it to B's definition and deduce which
+   bits A demands (= 0xf0 >> 2 = 0x3c).
+   Then, continuing at A's definition we see that there is no bit demand
+   outside of 0xff, so no necessary bit the mask would clear.  As a
+   consequence, the masking can be elided.
+
+
    The algorithm is:
 
    (1) Do a post-order traversal of the blocks in the function, walking
@@ -118,18 +141,42 @@ along with GCC; see the file COPYING3.  If not see
 
 namespace {
 
+/* Return either TYPE or the inner type if TYPE is a vector type.  */
+static inline tree
+strip_vectype (tree type)
+{
+  if (VECTOR_TYPE_P (type))
+    type = TREE_TYPE (type);
+  return type;
+}
+
+/* Return the precision of TYPE if it is an integral type or
+   HOST_BITS_PER_WIDE_INT otherwise.  */
+static inline HOST_WIDE_INT
+get_precision (tree type)
+{
+  type = strip_vectype (type);
+  if (INTEGRAL_TYPE_P (type))
+    return TYPE_PRECISION (type);
+  else
+    return HOST_BITS_PER_WIDE_INT;
+}
+
 /* Information about a group of uses of an SSA name.  */
 class usage_info
 {
 public:
-  usage_info () : flag_word (0) {}
+  usage_info (tree type)
+    : flag_word (0), dropped_bits (wi::zero (get_precision (type))) {}
+
   usage_info &operator &= (const usage_info &);
   usage_info operator & (const usage_info &) const;
   bool operator == (const usage_info &) const;
   bool operator != (const usage_info &) const;
   bool is_useful () const;
+  HOST_WIDE_INT precision () const { return dropped_bits.get_precision (); }
 
-  static usage_info intersection_identity ();
+  static usage_info intersection_identity (tree type);
 
   union
   {
@@ -141,16 +188,27 @@ public:
     /* All the flag bits as a single int.  */
     unsigned int flag_word;
   };
+
+  /* If the SSA name is a scalar integer, this field says which bits of
+     the integer are dropped.  If the SSA name is a vector of integers,
+     the field says which bits are dropped from every element.
+     In other cases, the field is zero.  */
+  wide_int dropped_bits;
 };
 
 /* Return an X such that X & Y == Y for all Y.  This is the most
    optimistic assumption possible.  */
 
 usage_info
-usage_info::intersection_identity ()
+usage_info::intersection_identity (tree type)
 {
-  usage_info ret;
+  usage_info ret (type);
   ret.flag_word = -1;
+
+  /* Only start optimistically for integral types.  */
+  type = strip_vectype (type);
+  if (INTEGRAL_TYPE_P (type))
+    ret.dropped_bits = wi::minus_one (ret.precision ());
   return ret;
 }
 
@@ -161,6 +219,7 @@ usage_info &
 usage_info::operator &= (const usage_info &other)
 {
   flag_word &= other.flag_word;
+  dropped_bits &= other.dropped_bits;
   return *this;
 }
 
@@ -168,7 +227,7 @@ usage_info::operator &= (const usage_info &other)
    describes all uses covered by *THIS and OTHER.  */
 
 usage_info
-usage_info::operator & (const usage_info &other) const
+usage_info::operator& (const usage_info &other) const
 {
   usage_info info (*this);
   info &= other;
@@ -178,7 +237,8 @@ usage_info::operator & (const usage_info &other) const
 bool
 usage_info::operator == (const usage_info &other) const
 {
-  return flag_word == other.flag_word;
+  return (flag_word == other.flag_word
+         && dropped_bits == other.dropped_bits);
 }
 
 bool
@@ -192,7 +252,7 @@ usage_info::operator != (const usage_info &other) const
 bool
 usage_info::is_useful () const
 {
-  return flag_word != 0;
+  return flag_word != 0 || dropped_bits != 0;
 }
 
 /* Start a dump line about SSA name VAR.  */
@@ -215,6 +275,14 @@ dump_usage_info (FILE *file, tree var, usage_info *info)
       dump_usage_prefix (file, var);
       fprintf (file, "sign bit not important\n");
     }
+
+  if (info->dropped_bits != 0)
+    {
+      dump_usage_prefix (file, var);
+      fprintf (file, "demand bit mask: ");
+      print_hex (~info->dropped_bits, file);
+      fprintf (file, "\n");
+    }
 }
 
 /* Information about an SSA name that we might want to optimize.  */
@@ -359,6 +427,11 @@ backprop::backprop (function *fn)
 
 backprop::~backprop ()
 {
+  /* Destroy usage infos explicitly, as wide_int with BitInt refers to
+     separately allocated memory.  */
+  for (auto info : m_vars)
+    if (info)
+      info->info.~usage_info ();
   m_var_pool.release ();
 }
 
@@ -483,7 +556,10 @@ void
 backprop::process_assign_use (gassign *assign, tree rhs, usage_info *info)
 {
   tree lhs = gimple_assign_lhs (assign);
-  switch (gimple_assign_rhs_code (assign))
+  tree lhs_type = strip_vectype (TREE_TYPE (lhs));
+  tree_code code = gimple_assign_rhs_code (assign);
+
+  switch (code)
     {
     case ABS_EXPR:
     case ABSU_EXPR:
@@ -500,10 +576,45 @@ backprop::process_assign_use (gassign *assign, tree rhs, 
usage_info *info)
       break;
 
     case MULT_EXPR:
-      /* In X * X, the sign of X doesn't matter.  */
-      if (gimple_assign_rhs1 (assign) == rhs
-         && gimple_assign_rhs2 (assign) == rhs)
-       info->flags.ignore_sign = true;
+      {
+       /* In X * X, the sign of X doesn't matter.  */
+       if (gimple_assign_rhs1 (assign) == rhs
+           && gimple_assign_rhs2 (assign) == rhs)
+         info->flags.ignore_sign = true;
+
+       tree other = (rhs == gimple_assign_rhs1 (assign))
+                    ? gimple_assign_rhs2 (assign)
+                    : gimple_assign_rhs1 (assign);
+
+       /* Given the output demand, we assume that all input bits not
+          higher than the output's highest set bit can contribute to the
+          output.  If we know that a number of low bits are zero, we
+          have a lower bound on the bits.
+          For a multiplication by a power of two there is no carry and
+          we can simply shift the output.  */
+       const var_info *lhs_info = lookup_operand (lhs);
+       if (tree uniform_cst = uniform_integer_cst_p (other))
+         other = uniform_cst;
+       if (INTEGRAL_TYPE_P (lhs_type)
+           && TYPE_OVERFLOW_WRAPS (lhs_type)
+           && TREE_CODE (other) == INTEGER_CST
+           && lhs_info && lhs_info->info.dropped_bits != 0)
+         {
+           auto mul = wi::to_wide (other);
+           HOST_WIDE_INT lo = wi::ctz (mul);
+           HOST_WIDE_INT hi
+             = wi::floor_log2 (~lhs_info->info.dropped_bits) + 1;
+
+           if (mul == 0 || hi <= lo)
+             info->dropped_bits = wi::minus_one (info->precision ());
+           else if (wi::exact_log2 (mul) >= 0)
+             info->dropped_bits
+               = ~wi::lrshift (~lhs_info->info.dropped_bits, lo);
+           else
+             info->dropped_bits = wi::mask (hi - lo, true, info->precision ());
+         }
+      }
+
       /* Fall through.  */
 
     case NEGATE_EXPR:
@@ -515,6 +626,178 @@ backprop::process_assign_use (gassign *assign, tree rhs, 
usage_info *info)
          info->flags.ignore_sign = lhsv->info.flags.ignore_sign;
       break;
 
+    case TRUNC_DIV_EXPR:
+      {
+       if (!INTEGRAL_TYPE_P (lhs_type) || !TYPE_UNSIGNED (lhs_type))
+         break;
+
+       tree rhs1 = gimple_assign_rhs1 (assign);
+       tree rhs2 = gimple_assign_rhs2 (assign);
+       if (tree uniform_cst = uniform_integer_cst_p (rhs2))
+         rhs2 = uniform_cst;
+       if (rhs != rhs1 || TREE_CODE (rhs2) != INTEGER_CST)
+         break;
+
+       const var_info *lhs_info = lookup_operand (lhs);
+       if (!lhs_info || lhs_info->info.dropped_bits == 0)
+         break;
+
+       auto div = wi::to_widest (rhs2);
+       if (div == 0)
+         break;
+
+       /* Opposite to multiplication, with division all upper bits
+          may contribute.  The only thing we can deduce from a
+          non-pow2 divisor is how many lower bits of the dividend
+          are dropped.  */
+
+       HOST_WIDE_INT log_k = wi::exact_log2 (div);
+       if (log_k >= 0)
+         info->dropped_bits = ~wi::lshift (~lhs_info->info.dropped_bits,
+                                           log_k);
+       else
+         {
+           HOST_WIDE_INT lo_cst = wi::ctz (div);
+           info->dropped_bits = wi::mask (lo_cst, false,
+                                          lhs_info->info.precision ());
+         }
+      }
+      break;
+
+    case BIT_AND_EXPR:
+    case BIT_IOR_EXPR:
+      {
+       const var_info *lhs_info = lookup_operand (lhs);
+
+       if (!INTEGRAL_TYPE_P (lhs_type))
+         break;
+
+       tree other;
+       if (rhs == gimple_assign_rhs1 (assign))
+         other = gimple_assign_rhs2 (assign);
+       else
+         other = gimple_assign_rhs1 (assign);
+       if (tree uniform_cst = uniform_integer_cst_p (other))
+         other = uniform_cst;
+
+       if (lhs_info)
+         info->dropped_bits = lhs_info->info.dropped_bits;
+
+       if (code == BIT_AND_EXPR)
+         info->dropped_bits |= ~get_nonzero_bits (other);
+       else if (code == BIT_IOR_EXPR && TREE_CODE (other) == INTEGER_CST)
+         info->dropped_bits |= get_nonzero_bits (other);
+      }
+      break;
+
+    case LSHIFT_EXPR:
+    case RSHIFT_EXPR:
+      if (rhs == gimple_assign_rhs1 (assign)
+         && (code == RSHIFT_EXPR || TYPE_OVERFLOW_WRAPS (lhs_type)))
+       {
+         const var_info *lhs_info = lookup_operand (lhs);
+         tree rhs2 = gimple_assign_rhs2 (assign);
+         if (TREE_CODE (rhs2) == INTEGER_CST
+             && lhs_info)
+           {
+             auto shift_count = wi::to_wide (rhs2);
+
+             if (code == LSHIFT_EXPR)
+               info->dropped_bits
+                 = ~wi::lrshift (~lhs_info->info.dropped_bits, shift_count);
+             else
+               {
+                 /* ??? Treat sign extension conservatively for now.  */
+                 if (TYPE_UNSIGNED (TREE_TYPE (rhs)))
+                   info->dropped_bits
+                     = ~wi::lshift (~lhs_info->info.dropped_bits,
+                                    shift_count);
+               }
+           }
+       }
+      break;
+
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      if (INTEGRAL_TYPE_P (lhs_type)
+         && TYPE_OVERFLOW_WRAPS (lhs_type)
+         && (rhs == gimple_assign_rhs1 (assign)
+             || rhs == gimple_assign_rhs2 (assign)))
+       {
+         const var_info *lhs_info = lookup_operand (lhs);
+
+         if (lhs_info && lhs_info->info.dropped_bits != 0)
+           {
+             int hi = wi::floor_log2 (~lhs_info->info.dropped_bits);
+             info->dropped_bits = wi::mask (hi + 1, true,
+                                            lhs_info->info.precision ());
+           }
+       }
+      break;
+
+    /* LROTATE CST is canonicalized to RROTATE CST', thus only check
+       the latter.  */
+    case RROTATE_EXPR:
+      if (rhs == gimple_assign_rhs1 (assign))
+       {
+         tree rhs2 = gimple_assign_rhs2 (assign);
+
+         if (TREE_CODE (rhs2) == INTEGER_CST)
+           {
+             auto count = wi::to_wide (rhs2);
+             const var_info *lhs_info = lookup_operand (lhs);
+
+             if (lhs_info)
+               info->dropped_bits = wi::lrotate (lhs_info->info.dropped_bits,
+                                                 count,
+                                                 lhs_info->info.precision ());
+           }
+       }
+      break;
+
+    CASE_CONVERT:
+      {
+       tree rhs1 = gimple_assign_rhs1 (assign);
+       tree rhs_type = strip_vectype (TREE_TYPE (rhs1));
+
+       const var_info *lhs_info = lookup_operand (lhs);
+       if (!INTEGRAL_TYPE_P (lhs_type)
+           || !INTEGRAL_TYPE_P (rhs_type)
+           || !lhs_info)
+         break;
+
+       HOST_WIDE_INT rhs_prec = TYPE_PRECISION (rhs_type);
+       HOST_WIDE_INT lhs_prec = TYPE_PRECISION (lhs_type);
+
+       /* When narrowing, the lower bits contribute.  For zero extension,
+          all bits do.
+          As in the RSHIFT case, treat sign extension conservatively
+          for now.  */
+       if (lhs_prec <= rhs_prec)
+         {
+           info->dropped_bits
+             = wide_int::from (lhs_info->info.dropped_bits,
+                               info->precision (), UNSIGNED)
+             | wi::mask (lhs_prec, true, info->precision ());
+         }
+       else if (TYPE_UNSIGNED (rhs_type))
+         info->dropped_bits
+           = wide_int::from (lhs_info->info.dropped_bits,
+                             info->precision (), UNSIGNED);
+      }
+      break;
+
+      /* These don't affect the needed bits so we can just
+        pass through the output.  */
+    case BIT_XOR_EXPR:
+    case BIT_NOT_EXPR:
+      {
+       const var_info *lhs_info = lookup_operand (lhs);
+       if (lhs_info)
+         info->dropped_bits = lhs_info->info.dropped_bits;
+       break;
+      }
+
     default:
       break;
     }
@@ -564,7 +847,7 @@ backprop::intersect_uses (tree var, usage_info *info)
 {
   imm_use_iterator iter;
   use_operand_p use_p;
-  *info = usage_info::intersection_identity ();
+  *info = usage_info::intersection_identity (TREE_TYPE (var));
   FOR_EACH_IMM_USE_FAST (use_p, iter, var)
     {
       gimple *stmt = USE_STMT (use_p);
@@ -587,7 +870,7 @@ backprop::intersect_uses (tree var, usage_info *info)
        }
       else
        {
-         usage_info subinfo;
+         usage_info subinfo (TREE_TYPE (var));
          process_use (stmt, var, &subinfo);
          *info &= subinfo;
          if (!info->is_useful ())
@@ -633,7 +916,7 @@ backprop::process_var (tree var, var_info *v)
   if (has_zero_uses (var))
     return;
 
-  usage_info info;
+  usage_info info (TREE_TYPE (var));
 
   /* Propagating along abnormal edges is delicate, punt for now.  */
   if (!SSA_NAME_OCCURS_IN_ABNORMAL_PHI (var))
@@ -641,6 +924,7 @@ backprop::process_var (tree var, var_info *v)
 
   gimple *stmt = SSA_NAME_DEF_STMT (var);
   auto hash = SSA_NAME_VERSION (var);
+
   if (info.is_useful ())
     {
       if (!v)
@@ -947,7 +1231,29 @@ backprop::replace_assign_rhs (var_info *v, tree rhs1, 
tree rhs2, tree rhs3)
 void
 backprop::optimize_assign (gassign *assign, var_info *v)
 {
-  switch (gimple_assign_rhs_code (assign))
+  tree_code code = gimple_assign_rhs_code (assign);
+  tree lhs = gimple_get_lhs (assign);
+
+  if (v->info.dropped_bits != 0 && !gimple_has_side_effects (assign))
+    {
+      auto nz = get_nonzero_bits (lhs);
+
+      /* If there is no overlap between the LHS's nonzero bits and
+        its needed bits, we can zero out the assignment.  */
+      if ((v->info.dropped_bits & nz) == nz)
+       {
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Zeroing RHS in ");
+             print_gimple_stmt (dump_file, assign, 0, TDF_SLIM);
+           }
+
+         set_new_value (v, build_zero_cst (TREE_TYPE (lhs)));
+         return;
+       }
+    }
+
+  switch (code)
     {
     case MULT_EXPR:
     case RDIV_EXPR:
@@ -968,6 +1274,73 @@ backprop::optimize_assign (gassign *assign, var_info *v)
                            strip_sign_op (gimple_assign_rhs3 (assign)));
       break;
 
+    case BIT_AND_EXPR:
+    case BIT_IOR_EXPR:
+    case BIT_XOR_EXPR:
+      if (v->info.dropped_bits != 0)
+       {
+         tree rhs2 = gimple_assign_rhs2 (assign);
+         if (tree uniform_cst = uniform_integer_cst_p (rhs2))
+           rhs2 = uniform_cst;
+         if (TREE_CODE (rhs2) == INTEGER_CST)
+           {
+             auto mask = wi::to_wide (rhs2);
+             auto needed_bits = ~v->info.dropped_bits;
+
+             bool elide_p = false;
+
+             /* If we mask off bits that are being discarded later on anyway,
+                we can elide the mask.  */
+             if (code == BIT_AND_EXPR
+                 && (needed_bits & ~mask) == 0)
+               elide_p = true;
+
+             /* For IOR and XOR it's the other way around.  If all we modify
+                is a range outside of the needed bits, elide.  */
+             if ((code == BIT_IOR_EXPR || code == BIT_XOR_EXPR)
+                 && (needed_bits & mask) == 0)
+               elide_p = true;
+
+             if (elide_p)
+               {
+                 /* Just make the assignment a nop here.  */
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   {
+                     fprintf (dump_file, "Eliding redundant mask in ");
+                     print_gimple_stmt (dump_file, assign, 0, TDF_SLIM);
+                   }
+
+                 set_new_value (v, gimple_assign_rhs1 (assign));
+               }
+           }
+       }
+      break;
+
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      if (v->info.dropped_bits != 0)
+       {
+         auto needed_bits = ~v->info.dropped_bits;
+         tree rhs2 = gimple_assign_rhs2 (assign);
+         if (tree uniform_cst = uniform_integer_cst_p (rhs2))
+           rhs2 = uniform_cst;
+         auto nz = get_nonzero_bits (rhs2);
+
+         if ((needed_bits & nz) == 0
+             && wi::ctz (nz) > wi::floor_log2 (needed_bits))
+           {
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 fprintf (dump_file, "Eliding redundant"
+                                     " addition/subtraction ");
+                 print_gimple_stmt (dump_file, assign, 0, TDF_SLIM);
+               }
+
+             set_new_value (v, gimple_assign_rhs1 (assign));
+           }
+       }
+      break;
+
     default:
       break;
     }
@@ -1139,7 +1512,8 @@ backprop::execute ()
       if (TREE_CODE (v->new_value) == SSA_NAME
          && !SSA_NAME_IS_DEFAULT_DEF (v->new_value)
          && has_zero_uses (v->new_value)
-         && bitmap_set_bit (deleted_vars, SSA_NAME_VERSION (v->new_value)))
+         && bitmap_set_bit (deleted_vars, SSA_NAME_VERSION (v->new_value))
+         && !gimple_has_side_effects (SSA_NAME_DEF_STMT (v->new_value)))
        {
          gimple *stmt = SSA_NAME_DEF_STMT (v->new_value);
          gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
diff --git a/gcc/testsuite/gcc.dg/torture/crc-29.c 
b/gcc/testsuite/gcc.dg/torture/crc-29.c
index 285f7061580..45aad3c8822 100644
--- a/gcc/testsuite/gcc.dg/torture/crc-29.c
+++ b/gcc/testsuite/gcc.dg/torture/crc-29.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-fdump-tree-crc-details" } */
+/* { dg-options "-fdump-tree-crc-details -fno-ssa-backprop" } */
 /* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */
 /* { dg-require-effective-target int32plus } */
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-1.c
new file mode 100644
index 00000000000..6fea313ea7b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details" } */
+
+#include <stdbool.h>
+
+void use (unsigned);
+
+void
+src (bool c, unsigned a, unsigned b) {
+    unsigned s = c ? a & 24 : b & 25;
+    use(s & 8);
+    use(s & 16);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 2 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-10.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-10.c
new file mode 100644
index 00000000000..889397373a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-10.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details -std=gnu99" } */
+
+typedef unsigned int v4ui __attribute__ ((vector_size (16)));
+typedef unsigned short v8uhi __attribute__ ((vector_size (16)));
+
+extern void use_v4 (v4ui);
+extern void use_v8 (v8uhi);
+
+void
+foo (v4ui x)
+{
+  v4ui a = x & (v4ui) {0xff, 0xff, 0xff, 0xff};
+  v4ui b = a << 16;
+  use_v4 (b & (v4ui) {0xff, 0xff, 0xff, 0xff});
+}
+
+void
+bar (v4ui x)
+{
+  v4ui a = x & (v4ui) {0xff00, 0xff00, 0xff00, 0xff00};
+  v4ui b = a >> 8;
+  use_v4 (b & (v4ui) {0xff00, 0xff00, 0xff00, 0xff00});
+}
+
+void
+baz (v8uhi x)
+{
+  v8uhi m = {0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf};
+  v8uhi a = x & m;
+  v8uhi b = a * (v8uhi) {16, 16, 16, 16, 16, 16, 16, 16};
+  v8uhi n = {0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf};
+  use_v8 (b & n);
+}
+
+/* { dg-final { scan-tree-dump-times "Zeroing RHS" 3 "backprop" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-11.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-11.c
new file mode 100644
index 00000000000..a16fe47a946
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-11.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdisable-tree-phiopt1 
-fdump-tree-backprop-details" } */
+
+#include <stdbool.h>
+
+void use (unsigned);
+
+void
+foo (bool c, unsigned a, unsigned b)
+{
+  unsigned s = c ? (a & 0xfe) : (b & 0xfe);
+  use ((s | 0x1) & 0xff);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 2 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-12.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-12.c
new file mode 100644
index 00000000000..1508bdfca59
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-12.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fno-tree-forwprop 
-fdump-tree-backprop-details" } */
+
+extern void use (unsigned);
+extern void use2 (unsigned short);
+
+void
+foo (unsigned x)
+{
+  unsigned a = x & 0xff;
+  unsigned short b = (unsigned short) a;
+  use2 (b & 0xf);
+}
+
+void
+bar (unsigned char x)
+{
+  unsigned char a = x & 0x3f;
+  unsigned b = a;
+  use (b & 0xf);
+}
+
+/* Sign extension falls back to "no dropped bits".  */
+void
+baz (signed char x)
+{
+  signed char a = x & 0x3f;
+  int b = a;
+  use ((unsigned) b & 0xf);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 2 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-13.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-13.c
new file mode 100644
index 00000000000..3f7ccf9ff78
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-13.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details" } */
+
+extern void use (unsigned);
+
+void
+foo (unsigned x)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a * 3;
+  use (b & 0xf);
+}
+
+void
+bar (unsigned x)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a * 5;
+  use (b & 0x7);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 2 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-14.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-14.c
new file mode 100644
index 00000000000..f599ba4e087
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-14.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fno-tree-forwprop 
-fdump-tree-backprop-details" } */
+
+extern void use (unsigned);
+extern void use2 (int);
+
+void
+foo (unsigned x)
+{
+  unsigned a = x & 0xff0;
+  unsigned b = a >> 4;
+  use (b & 0xf);
+}
+
+/* Signed shift falls back to "no dropped bits", therefore we cannot elide
+   anything.  */
+void
+bar (int x)
+{
+  int a = x & 0xff0;
+  int b = a >> 4;
+  use2 (b & 0xf);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 1 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-15.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-15.c
new file mode 100644
index 00000000000..a9e653cafe6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-15.c
@@ -0,0 +1,47 @@
+/* { dg-do run } */
+/* { dg-additional-options "-O2 -fdump-tree-backprop-details" } */
+
+/* Ensure we do not delete a call with side effects during backprop.
+   This was found in a bootstrap where tree-ssa-sccvn.cc was miscompiled.
+   We have
+     _5 = set_ssa_val_to (_4, _4);
+     _7 = _5 | changed_6;
+   and do
+     [DEF] Updating information for _7 = _5 | changed_6;
+      _7: demand bit mask: 0x0.
+     [USE] _5 in _7 = _5 | changed_6;
+     _5: demand bit mask: 0x0
+   Subsequently,
+     Zeroing RHS in _7 = _5 | changed_6;
+     Deleting _5 = set_ssa_val_to (_4, _4);
+   but set_ssa_val_to has side effects and we must not delete it.  */
+
+int counter;
+
+int __attribute__((noinline))
+side_effect (int x)
+{
+  counter++;
+  return x & 1;
+}
+
+static int __attribute__((noinline))
+foo (int n)
+{
+  int changed = 0;
+  int i;
+  for (i = 0; i < n; i++)
+    changed |= side_effect (i);
+  return changed;
+}
+
+int
+main (void)
+{
+  foo (10);
+  if (counter != 10)
+    __builtin_abort ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "Deleting \[_0-9\]+ = side" "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-16.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-16.c
new file mode 100644
index 00000000000..f0cecbea404
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-16.c
@@ -0,0 +1,23 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+unsigned foo;
+
+unsigned __attribute__((noipa))
+f (unsigned p, unsigned q)
+{
+  unsigned a = p & 0x00ffffff;
+  unsigned x = a + q;
+  unsigned v = x & 0x0000ffff;
+  unsigned k = v * 3;
+  foo = k & 0xff;
+  return (x >> 20) & 1;
+}
+
+int
+main (void)
+{
+  if (f (0x100000, 0) != 1)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-17.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-17.c
new file mode 100644
index 00000000000..63b342b0e65
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-17.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details" } */
+
+extern void use (unsigned);
+
+void
+foo (unsigned x, unsigned y)
+{
+  unsigned p = x & 4;
+  unsigned q = y & 0x100;
+  unsigned b = p * q;
+  use (b & 0x400);
+}
+
+void
+bar (unsigned x)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a * 4;
+  use (b & 0x10);
+  use (b & 0xe0);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 1 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-18.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-18.c
new file mode 100644
index 00000000000..f4da2073c2b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-18.c
@@ -0,0 +1,21 @@
+/* { dg-do run } */
+/* { dg-additional-options "-O" } */
+
+__attribute__((noipa)) unsigned
+foo (unsigned x, unsigned y, unsigned z)
+{
+  unsigned p = x & 4;
+  unsigned q = y & 0x100;
+  unsigned b = p * q;
+  return (b | z) & 0x400;
+}
+
+int
+main (void)
+{
+  if (foo (2, 0x200, 0) != 0)
+    __builtin_abort ();
+  if (foo (4, 0x100, 0) != 0x400)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-2.c
new file mode 100644
index 00000000000..f993a22fdcf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-2.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target bitint } } */
+/* { dg-require-effective-target bitint575 } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details -std=c23 
-fno-tree-forwprop" } */
+
+extern void use (unsigned _BitInt (575));
+
+unsigned _BitInt (575)
+demand (unsigned _BitInt (575) t)
+{
+  t = (t << 548) + 0x123wb;
+  return t & (((unsigned _BitInt (575)) 0xffffffwb) << 548);
+}
+
+void
+elide (unsigned _BitInt (575) x)
+{
+  unsigned _BitInt (575) a
+    = x & (((unsigned _BitInt (575)) 0xffffwb) << 548);
+  use (a & (((unsigned _BitInt (575)) 0xff00wb) << 548));
+  use (a & (((unsigned _BitInt (575)) 0x00ffwb) << 548));
+}
+
+/* { dg-final { scan-tree-dump "demand bit mask: 0xffffff\[0\]{137}" 
"backprop" } } */
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 1 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-3.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-3.c
new file mode 100644
index 00000000000..955a6dea86f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-3.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details -std=gnu99" } */
+
+void use (unsigned);
+
+void
+foo (unsigned x)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a * 4;
+  use (b & 0x10);
+  use (b & 0xe0);
+}
+
+void
+bar (unsigned x)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a / 3;
+  use (b & 0x3);
+  use (b & 0xc);
+}
+
+void
+baz (unsigned x)
+{
+  unsigned a = x & 0xffff;
+  unsigned a2 = a * 2;
+  unsigned b = a2 & 0xff;
+  use (b & 0x3);
+  use (b & 0xc);
+}
+
+void
+baf (unsigned x)
+{
+  unsigned a = x & ~1u;
+  unsigned b = a / 6;
+  use (b & 0x3);
+  use (b & 0xc);
+}
+
+/* bar is not simplified, division only drops lower bits.  */
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 3 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-4.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-4.c
new file mode 100644
index 00000000000..7a0ab3ade7d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-4.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details -std=gnu99" } */
+
+void use (unsigned);
+
+void
+foo (unsigned x)
+{
+  unsigned a = x & 0xff;
+  use (a & 0xf);
+  use (a);
+}
+
+void
+bar (unsigned x, unsigned y)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a + y;
+  use (b & 0xf);
+}
+
+void
+baz (unsigned x, unsigned s)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a << s;
+  use (b & 0xf);
+}
+
+unsigned
+baf (unsigned x, int n)
+{
+  unsigned a = x & 0xff;
+  for (int i = 0; i < n; i++)
+    a = (a + 1) & 0xff;
+  return a;
+}
+
+/* { dg-final { scan-tree-dump-not "Eliding redundant mask" "backprop" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-5.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-5.c
new file mode 100644
index 00000000000..d155c58606b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-5.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdisable-tree-phiopt1 
-fdump-tree-backprop-details -std=gnu99" } */
+
+void use (unsigned);
+
+void
+f_phi (int c, unsigned x, unsigned y)
+{
+  unsigned t;
+  if (c)
+    t = x & 0xff;
+  else
+    t = y & 0xff;
+  use (t & 0x3);
+  use (t & 0xc);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant mask" 2 "backprop" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-6.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-6.c
new file mode 100644
index 00000000000..795e461160f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-6.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details -std=gnu99" } */
+
+void use (unsigned);
+
+void
+foo (unsigned x)
+{
+  unsigned a = x | 0xff00;
+  unsigned b = a * 2;
+  use (b & 0x3);
+  use (b & 0xc);
+}
+
+void
+bar (unsigned x)
+{
+  unsigned a = x ^ 0xff00;
+  unsigned b = a * 2;
+  use (b & 0x3);
+  use (b & 0xc);
+}
+
+void
+baz (unsigned x)
+{
+  unsigned a = x | 0x18;
+  use (a & 0x3);
+  use (a & 0xc);
+}
+
+void
+baf (unsigned x)
+{
+  unsigned a = x ^ 0x18;
+  use (a & 0x3);
+  use (a & 0xc);
+}
+
+/* foo and bar can be simplified by eliding the IOR.  baz and baf can't.  */
+/* { dg-final { scan-tree-dump-times "Eliding redundant" 2 "backprop" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-7.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-7.c
new file mode 100644
index 00000000000..3df489051f1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-7.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details -std=gnu99" } */
+
+void use (unsigned);
+
+void
+foo (unsigned x)
+{
+  unsigned b = x + 0xff00;
+  use (b & 0x1);
+  use (b & 0xe);
+}
+
+void
+bar (unsigned x, unsigned y)
+{
+  unsigned m = y & 0xFF00;
+  unsigned b = x - m;
+  use (b & 0xf);
+  use (b & 0xf0);
+}
+
+void
+baz (unsigned x)
+{
+  unsigned a = x << 8;
+  unsigned b = a / 3;
+  use (b & 0x3);
+}
+
+/* { dg-final { scan-tree-dump-times "Eliding redundant" 2 "backprop" } } */
+/* { dg-final { scan-tree-dump-times "Zeroing RHS" 1 "backprop" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-8.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-8.c
new file mode 100644
index 00000000000..014ffd2e097
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-8.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details -std=gnu99" } */
+
+void use (unsigned);
+
+void
+foo (unsigned x, unsigned y)
+{
+  unsigned a = x & 0xff;
+  unsigned r = (a << 16) | (a >> (32 - 16));
+  unsigned d = r - y;
+  use (d & 0xff);
+}
+
+void
+bar (unsigned x, unsigned y)
+{
+  unsigned a = x & 0xff;
+  unsigned r = (a >> 8) | (a << (32 - 8));
+  unsigned d = r - y;
+  use (d & 0xffff);
+}
+
+/* { dg-final { scan-tree-dump-times "Zeroing RHS" 4 "backprop" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-9.c 
b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-9.c
new file mode 100644
index 00000000000..b74ec90df3e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/backprop-bits-9.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O -fdump-tree-backprop-details" } */
+
+void use (unsigned);
+
+void
+foo (unsigned x, unsigned y)
+{
+  unsigned a = x & 0xff;
+  unsigned b = a * 16;
+  unsigned d = b - y;
+  use (d & 0xf);
+}
+
+void
+bar (unsigned x, unsigned y)
+{
+  unsigned a = x & 0xf;
+  unsigned b = a * 256;
+  unsigned d = b - y;
+  use (d & 0xff);
+}
+
+/* { dg-final { scan-tree-dump-times "Zeroing RHS" 4 "backprop" } } */
-- 
2.55.0

Reply via email to