diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp49.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp49.c
new file mode 100644
index 0000000..55503d7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp49.c
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftree-vrp" } */
+/* PR28632 was concerned about VRP understanding bitwise OR and AND expressions.
+ */
+
+void v4 (unsigned a, unsigned b)
+{
+  if (a < 0x1000) return;
+  if (a > 0x1000) return;
+  if (b < 0x0110) return;
+  /* constant true.  */
+  if (!__builtin_constant_p ((a|b) >= 0x01000))
+    __asm__("bug.always.true");
+  /* VRP must not think that this is constant.  */
+  if (__builtin_constant_p ((a|b) >= 0x10000))
+    __asm__("bug.not.always.true");
+}
+void u4 (unsigned n)
+{
+  if (n > 0x10111) return;
+  if (n < 0x10101) return;
+  /* always true.  */
+  if (!__builtin_constant_p (n & 0x00100))
+    __asm__("bug.always.true");
+  /* VRP must not think that this is constant true.  */
+  if (__builtin_constant_p (n & 0x00001))
+    __asm__("bug.not.always.true");
+  /* Out of range, always evaluates to constant false.  */
+  if (!__builtin_constant_p (n & 0x01000))
+    __asm__("bug.always.false");
+}
+void u5 (unsigned n)
+{
+  struct s {unsigned exp:8;} x;
+  x.exp = n;
+  if (__builtin_constant_p(((n + 1) & 255) > 1))
+    __asm__("bug.not.always.true");
+}
+void v5 (int a, int b)
+{
+  if (a < 0x1000) return;
+  if (a > 0x1000) return;
+  if (b < 0x0110) return;
+  /* constant true.  */
+  if (!__builtin_constant_p ((a|b) >= 0x01000))
+    __asm__("bug.always.true");
+  /* VRP must not think that this is always true.  */
+  if (__builtin_constant_p ((a|b) >= 0x10000))
+    __asm__("bug.not.always.true");
+}
+/* { dg-final { scan-assembler-not "bug\." } } */
diff --git a/gcc/testsuite/gcc.dg/vect/slp-perm-4.c b/gcc/testsuite/gcc.dg/vect/slp-perm-4.c
index 867dfd4..f452a92 100644
--- a/gcc/testsuite/gcc.dg/vect/slp-perm-4.c
+++ b/gcc/testsuite/gcc.dg/vect/slp-perm-4.c
@@ -65,6 +65,7 @@ int main (int argc, const char* argv[])
 
   for (i = 0; i < N; i++)
     {
+      __asm__("":"+r"(i)); /* don't vectorize this.. */
       input[i] = i%256;
       if (input[i] > 200)
         abort();
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index a0f4c3d..1375c95 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -391,10 +391,10 @@ set_value_range (value_range_t *vr, enum value_range_type t, tree min,
     }
 
   if (t == VR_UNDEFINED || t == VR_VARYING)
-    gcc_assert (min == NULL_TREE && max == NULL_TREE);
-
-  if (t == VR_UNDEFINED || t == VR_VARYING)
-    gcc_assert (equiv == NULL || bitmap_empty_p (equiv));
+    {
+      gcc_assert (min == NULL_TREE && max == NULL_TREE);
+      gcc_assert (equiv == NULL || bitmap_empty_p (equiv));
+    }
 #endif
 
   vr->type = t;
@@ -2339,7 +2339,6 @@ extract_range_from_binary_expr (value_range_t *vr,
 	      || range_includes_zero_p (&vr1)))
 	{
 	  tree zero = build_int_cst (TREE_TYPE (vr0.min), 0);
-	  int cmp;
 
 	  sop = false;
 	  min = NULL_TREE;
@@ -2493,30 +2492,235 @@ extract_range_from_binary_expr (value_range_t *vr,
     }
   else if (code == BIT_AND_EXPR)
     {
+#define DEBUG_STUFF(vr0, vr1, type, str) \
+	    { \
+	      fprintf (dump_file, "Incoming " str " (%s", \
+		  vr0.type == VR_UNDEFINED ? "UNDEFINED " : \
+		  vr0.type == VR_RANGE ? "" : \
+		  vr0.type == VR_ANTI_RANGE ? "~" : \
+		  vr0.type == VR_VARYING ? "VARYING " : "XXX"); \
+	      fprintf (dump_file, "["); \
+	      print_generic_expr (dump_file, vr0.min, 0); \
+	      fprintf (dump_file, ", "); \
+	      print_generic_expr (dump_file, vr0.max, 0); \
+	      fprintf (dump_file, "] , %s[", \
+		  vr1.type == VR_UNDEFINED ? "UNDEFINED " : \
+		  vr1.type == VR_RANGE ? "" : \
+		  vr1.type == VR_ANTI_RANGE ? "~" : \
+		  vr1.type == VR_VARYING ? "VARYING " : "XXX"); \
+	      print_generic_expr (dump_file, vr1.min, 0); \
+	      fprintf (dump_file, ", "); \
+	      print_generic_expr (dump_file, vr1.max, 0); \
+	      fprintf (dump_file, "]"); \
+	      fprintf (dump_file, ") using %s", \
+		  type == VR_UNDEFINED ? "UNDEFINED " : \
+		  type == VR_RANGE ? "" : \
+		  type == VR_ANTI_RANGE ? "~" : \
+		  type == VR_VARYING ? "VARYING " : "XXX"); \
+	      fprintf (dump_file, "["); \
+	      print_generic_expr (dump_file, min, 0); \
+	      fprintf (dump_file, ", "); \
+	      print_generic_expr (dump_file, max, 0); \
+	      fprintf (dump_file, "]\n"); \
+	    }
+
+      double_int vr0_min, vr0_max, vr1_min, vr1_max;
+      double_int bits0, bits1, tmp0, tmp1;
+      double_int and_min, and_max;
+
+      if ((vr0.type != VR_RANGE
+	   && vr0.type != VR_ANTI_RANGE
+	   && vr1.type != VR_RANGE
+	   && vr1.type != VR_ANTI_RANGE)
+	  /* -ENOIDEA */
+	  || (vr0.type == VR_VARYING && range_is_nonnull (&vr1))
+	  || (vr1.type == VR_VARYING && range_is_nonnull (&vr0)))
+	{
+	  set_value_range_to_varying (vr);
+	  return;
+	}
+
+      /* For usual RANGE, RANGE we can just take the normal ranges.
+       *
+       * If either one of the ranges is an ANTI_RANGE though, then we
+       * currently have to use 0 or -1 as the minimum or maximum, respectively.
+       *
+       * If the non-anti range is, however, VARYING or it's minimum or maximum
+       * otherwise NULL_TREE, then give up (by setting the current range
+       * to VARYING too).
+       */
       if (vr0.type == VR_RANGE
-	  && vr0.min == vr0.max
-	  && TREE_CODE (vr0.max) == INTEGER_CST
-	  && !TREE_OVERFLOW (vr0.max)
-	  && tree_int_cst_sgn (vr0.max) >= 0)
+	  && vr0.min != NULL_TREE
+	  && TREE_CODE (vr0.min) == INTEGER_CST)
+	  vr0_min = tree_to_double_int (vr0.min);
+      else if (vr1.min != NULL_TREE && TREE_CODE (vr1.min) == INTEGER_CST)
 	{
-	  min = build_int_cst (expr_type, 0);
-	  max = vr0.max;
+	  vr0_min.low = 0;
+	  vr0_min.high = 0;
 	}
-      else if (vr1.type == VR_RANGE
-	       && vr1.min == vr1.max
-	       && TREE_CODE (vr1.max) == INTEGER_CST
-	       && !TREE_OVERFLOW (vr1.max)
-	       && tree_int_cst_sgn (vr1.max) >= 0)
+      else
 	{
-	  type = VR_RANGE;
-	  min = build_int_cst (expr_type, 0);
-	  max = vr1.max;
+	  set_value_range_to_varying (vr);
+	  return;
+	}
+      if (vr1.type == VR_RANGE
+	  && vr1.min != NULL_TREE
+	  && TREE_CODE (vr1.min) == INTEGER_CST)
+	  vr1_min = tree_to_double_int (vr1.min);
+      else if (vr0.min != NULL_TREE && TREE_CODE (vr0.min) == INTEGER_CST)
+	{
+	  vr1_min.low = 0;
+	  vr1_min.high = 0;
+	}
+      else
+	{
+	  set_value_range_to_varying (vr);
+	  return;
+	}
+      if (vr0.type == VR_RANGE
+	  && vr0.max != NULL_TREE
+	  && TREE_CODE (vr0.max) == INTEGER_CST)
+	  vr0_max = tree_to_double_int (vr0.max);
+      else if (vr1.max != NULL_TREE && TREE_CODE (vr1.max) == INTEGER_CST)
+	{
+	  vr0_max.low = ALL_ONES;
+	  vr0_max.high = ALL_ONES;
 	}
       else
 	{
 	  set_value_range_to_varying (vr);
 	  return;
 	}
+      if (vr1.type == VR_RANGE
+	  && vr1.max != NULL_TREE
+	  && TREE_CODE (vr1.max) == INTEGER_CST)
+	  vr1_max = tree_to_double_int (vr1.max);
+      else if (vr0.max != NULL_TREE && TREE_CODE (vr0.max) == INTEGER_CST)
+	{
+	  vr1_max.low = ALL_ONES;
+	  vr1_max.high = ALL_ONES;
+	}
+      else
+	{
+	  set_value_range_to_varying (vr);
+	  return;
+	}
+
+      type = VR_RANGE;
+
+      /* Undo the damage of set_value_range_to_nonnull().  */
+      if (range_is_nonnull (&vr0)
+	  && vr1.type == VR_RANGE
+	  && !integer_zerop (vr1.min))
+	{
+	  type = VR_RANGE;
+	  max = vr1.max;
+	  if (compare_values (vr1.min, vr1.max) == 0)
+	    min = build_int_cst (expr_type, 0);
+	  else
+	    min = vr1.min;
+
+	  if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+	    DEBUG_STUFF(vr0, vr1, type, "nonnull0")
+	}
+      else if (range_is_nonnull (&vr1)
+	  && vr0.type == VR_RANGE
+	  && !integer_zerop (vr0.min))
+	{
+	  type = VR_RANGE;
+	  max = vr0.max;
+	  if (compare_values (vr0.min, vr0.max) == 0)
+	    min = build_int_cst (expr_type, 0);
+	  else
+	    min = vr0.min;
+
+	  if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+	    DEBUG_STUFF(vr0, vr1, type, "nonnull1")
+	}
+      else
+	/* Two ranges, finally!  */
+	{
+	  bits0.low = vr0_min.low ^ vr0_max.low;
+	  bits0.high = vr0_min.high ^ vr0_max.high;
+	  bits1.low = vr1_min.low ^ vr1_max.low;
+	  bits1.high = vr1_min.high ^ vr1_max.high;
+
+	  /* Handle bits that are always high i.e. set.  */
+	  tmp0.high = vr0_min.high & vr0_max.high;
+	  if (bits0.high != 0)
+	    {
+	      tmp0.low = (unsigned HOST_WIDE_INT) 0u;
+	      tmp0.high &= ~(((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits0.high)) - 1);
+	    }
+	  else if (bits0.low != 0)
+	    {
+	      tmp0.low = (vr0_min.low & vr0_max.low)
+			    & ~(((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits0.low)) - 1);
+	    }
+	  else
+	    tmp0.low = vr0_min.low & vr0_max.low;
+	  tmp1.high = vr1_min.high & vr1_max.high;
+	  if (bits1.high != 0)
+	    {
+	      tmp1.low = (unsigned HOST_WIDE_INT) 0u;
+	      tmp1.high &= ~(((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits1.high)) - 1);
+	    }
+	  else if (bits1.low != 0)
+	    {
+	      tmp1.low = (vr1_min.low & vr1_max.low)
+			    & ~(((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits1.low)) - 1);
+	    }
+	  else
+	    tmp1.low = vr1_min.low & vr1_max.low;
+	  /* Finally BIT_AND_EXPR them together.  */
+	  and_min.low = tmp0.low & tmp1.low;
+	  and_min.high = tmp0.high & tmp1.high;
+
+	  /* Handle bits that are always low i.e. unset.  */
+	  tmp0.high = vr0_min.high | vr0_max.high;
+	  if (bits0.high != 0)
+	    {
+	      tmp0.low = ~(unsigned HOST_WIDE_INT) 0u;
+	      tmp0.high |= ((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits0.high)) - 1;
+	    }
+	  else if (bits0.low & ((~(unsigned HOST_WIDE_INT) 0u) - 1))
+	    {
+	      tmp0.low = (vr0_min.low | vr0_max.low)
+			    | (((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits0.low)) - 1);
+	    }
+	  else
+	    tmp0.low = vr0_min.low | vr0_max.low;
+	  tmp1.high = vr1_min.high | vr1_max.high;
+	  if (bits1.high != 0)
+	    {
+	      tmp1.low = ~(unsigned HOST_WIDE_INT) 0u;
+	      tmp1.high |= ((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits1.high)) - 1;
+	    }
+	  else if (bits1.low & ((~(unsigned HOST_WIDE_INT) 0u) - 1))
+	    {
+	      tmp1.low = (vr1_min.low | vr1_max.low)
+			    | (((unsigned HOST_WIDE_INT) 1u
+				<< floor_log2 (bits1.low)) - 1);
+	    }
+	  else
+	    tmp1.low = vr1_min.low | vr1_max.low;
+	  /* Finally BIT_AND_EXPR them together.  */
+	  and_max.low = tmp0.low & tmp1.low;
+	  and_max.high = tmp0.high & tmp1.high;
+
+	  min = double_int_to_tree (expr_type, and_min);
+	  max = double_int_to_tree (expr_type, and_max);
+
+	  if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+	    DEBUG_STUFF(vr0, vr1, type, "")
+	}
     }
   else if (code == BIT_IOR_EXPR)
     {
