diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e0a2f3c0cc7ff154b7aae82e4f63235300bbb966..d3d6fff62e92c385e27ba37223347be00f5a277d 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2018-01-03  Richard Sandiford  <richard.sandiford@linaro.org>
+	    Alan Hayward  <alan.hayward@arm.com>
+	    David Sherwood  <david.sherwood@arm.com>
+
+	* match.pd: Handle bit operations involving three constants
+	and try to fold one pair.
+
 2018-01-03  Richard Sandiford  <richard.sandiford@linaro.org>
 
 	* tree-vect-loop-manip.c: Include gimple-fold.h.
diff --git a/gcc/match.pd b/gcc/match.pd
index 87012a243191e23e719a0e2766447c83d7adba91..06af2a72213316b8fe31d7a1ea8c0d658ef96e84 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1111,7 +1111,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 (for bitop (bit_and bit_ior bit_xor)
  (simplify
   (bitop (bitop @0 CONSTANT_CLASS_P@1) CONSTANT_CLASS_P@2)
-  (bitop @0 (bitop @1 @2))))
+  (if (!CONSTANT_CLASS_P (@0))
+   /* This is the canonical form regardless of whether (bitop @1 @2) can be
+      folded to a constant.  */
+   (bitop @0 (bitop @1 @2))
+   /* In this case we have three constants and (bitop @0 @1) doesn't fold
+      to a constant.  This can happen if @0 or @1 is a POLY_INT_CST and if
+      the values involved are such that the operation can't be decided at
+      compile time.  Try folding one of @0 or @1 with @2 to see whether
+      that combination can be decided at compile time.
+
+      Keep the existing form if both folds fail, to avoid endless
+      oscillation.  */
+   (with { tree cst1 = const_binop (bitop, type, @0, @2); }
+    (if (cst1)
+     (bitop @1 { cst1; })
+     (with { tree cst2 = const_binop (bitop, type, @1, @2); }
+      (if (cst2)
+       (bitop @0 { cst2; }))))))))
 
 /* Try simple folding for X op !X, and X op X with the help
    of the truth_valued_p and logical_inverted_value predicates.  */