diff --git a/gcc/match.pd b/gcc/match.pd
index b9621a47cdf16c924054080867d82cb1188bd9a3..17613ec8ade4f8142b36472c6ad52e430da37639 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -4906,6 +4906,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 			- TYPE_PRECISION (TREE_TYPE (@2)))))
   (bit_and (convert @0) (lshift { build_minus_one_cst (type); } @1))))
 
+#if GIMPLE
+/* (X >> C1) << (C1 + C2) -> X << C2 if the low C1 bits of X are zero.  */
+(simplify
+ (lshift (convert? (rshift (with_possible_nonzero_bits2 @0) INTEGER_CST@1))
+         INTEGER_CST@2)
+ (if (INTEGRAL_TYPE_P (type)
+      && wi::ltu_p (wi::to_wide (@1), element_precision (type))
+      && wi::ltu_p (wi::to_wide (@2), element_precision (type))
+      && wi::to_widest (@2) >= wi::to_widest (@1)
+      && wi::to_widest (@1) <= wi::ctz (get_nonzero_bits (@0)))
+  (lshift (convert @0) (minus @2 @1))))
+#endif
+
 /* For (x << c) >> c, optimize into x & ((unsigned)-1 >> c) for
    unsigned x OR truncate into the precision(type) - c lowest bits
    of signed x (if they have mode precision or a precision of 1).  */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c b/gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..d88500ca8ddfecbf5f7337db6e9ffaede5281245
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c
@@ -0,0 +1,61 @@
+/* { dg-options "-O2 -fdump-tree-optimized-raw" } */
+
+unsigned int
+f1 (unsigned int x)
+{
+  if (x & 3)
+    __builtin_unreachable ();
+  x >>= 2;
+  return x << 3;
+}
+
+unsigned int
+f2 (unsigned int x)
+{
+  if (x & 3)
+    __builtin_unreachable ();
+  unsigned char y = x;
+  y >>= 2;
+  return y << 3;
+}
+
+unsigned long
+f3 (unsigned int x)
+{
+  if (x & 3)
+    __builtin_unreachable ();
+  x >>= 2;
+  return (unsigned long) x << 3;
+}
+
+int
+f4 (int x)
+{
+  if (x & 15)
+    __builtin_unreachable ();
+  x >>= 4;
+  return x << 5;
+}
+
+unsigned int
+f5 (int x)
+{
+  if (x & 31)
+    __builtin_unreachable ();
+  x >>= 5;
+  return x << 6;
+}
+
+unsigned int
+f6 (unsigned int x)
+{
+  if (x & 1)
+    __builtin_unreachable ();
+  x >>= 1;
+  return x << (sizeof (int) * __CHAR_BIT__ - 1);
+}
+
+/* { dg-final { scan-tree-dump-not {<[a-z]*_div_expr,} "optimized" } } */
+/* { dg-final { scan-tree-dump-not {<rshift_expr,} "optimized" } } */
+/* { dg-final { scan-tree-dump-times {<lshift_expr, [^,]*, [^,]*, 1,} 5 "optimized" } } */
+/* { dg-final { scan-tree-dump {<lshift_expr, [^,]*, [^,]*, 30,} "optimized" { target int32 } } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c b/gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..67ba4a75aec4d749b046f40ea55ff55f950b7bb0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c
@@ -0,0 +1,21 @@
+/* { dg-options "-O2 -fdump-tree-optimized-raw" } */
+
+unsigned int
+f1 (unsigned int x)
+{
+  if (x & 3)
+    __builtin_unreachable ();
+  x >>= 3;
+  return x << 4;
+}
+
+unsigned int
+f2 (unsigned int x)
+{
+  if (x & 3)
+    __builtin_unreachable ();
+  x >>= 2;
+  return x << 1;
+}
+
+/* { dg-final { scan-tree-dump-times {<rshift_expr,} 2 "optimized" } } */