--- gcc/tree-vrp.c.jj	2012-09-25 14:45:48.000000000 +0200
+++ gcc/tree-vrp.c	2012-10-04 11:43:32.334988401 +0200
@@ -4712,6 +4712,11 @@ register_edge_assert_for_2 (tree name, e
       tree val2 = NULL_TREE;
       double_int mask = double_int_zero;
       unsigned int prec = TYPE_PRECISION (TREE_TYPE (val));
+      unsigned int nprec = prec;
+      enum tree_code rhs_code = ERROR_MARK;
+
+      if (is_gimple_assign (def_stmt))
+	rhs_code = gimple_assign_rhs_code (def_stmt);
 
       /* Add asserts for NAME cmp CST and NAME being defined
 	 as NAME = (int) NAME2.  */
@@ -4721,7 +4726,7 @@ register_edge_assert_for_2 (tree name, e
 	  && gimple_assign_cast_p (def_stmt))
 	{
 	  name2 = gimple_assign_rhs1 (def_stmt);
-	  if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt))
+	  if (CONVERT_EXPR_CODE_P (rhs_code)
 	      && INTEGRAL_TYPE_P (TREE_TYPE (name2))
 	      && TYPE_UNSIGNED (TREE_TYPE (name2))
 	      && prec == TYPE_PRECISION (TREE_TYPE (name2))
@@ -4767,8 +4772,7 @@ register_edge_assert_for_2 (tree name, e
 	 NAME = NAME2 >> CST2.
 
 	 Extract CST2 from the right shift.  */
-      if (is_gimple_assign (def_stmt)
-	  && gimple_assign_rhs_code (def_stmt) == RSHIFT_EXPR)
+      if (rhs_code == RSHIFT_EXPR)
 	{
 	  name2 = gimple_assign_rhs1 (def_stmt);
 	  cst2 = gimple_assign_rhs2 (def_stmt);
@@ -4840,21 +4844,37 @@ register_edge_assert_for_2 (tree name, e
       /* Add asserts for NAME cmp CST and NAME being defined as
 	 NAME = NAME2 & CST2.
 
-	 Extract CST2 from the and.  */
+	 Extract CST2 from the and.
+
+	 Also handle
+	 NAME = (unsigned) NAME2;
+	 casts where NAME's type is unsigned and has smaller precision
+	 than NAME2's type as if it was NAME = NAME2 & MASK.  */
       names[0] = NULL_TREE;
       names[1] = NULL_TREE;
       cst2 = NULL_TREE;
-      if (is_gimple_assign (def_stmt)
-	  && gimple_assign_rhs_code (def_stmt) == BIT_AND_EXPR)
+      if (rhs_code == BIT_AND_EXPR
+	  || (CONVERT_EXPR_CODE_P (rhs_code)
+	      && TREE_CODE (TREE_TYPE (val)) == INTEGER_TYPE
+	      && TYPE_UNSIGNED (TREE_TYPE (val))
+	      && TYPE_PRECISION (TREE_TYPE (gimple_assign_rhs1 (def_stmt)))
+		 > prec
+	      && !retval))
 	{
 	  name2 = gimple_assign_rhs1 (def_stmt);
-	  cst2 = gimple_assign_rhs2 (def_stmt);
+	  if (rhs_code == BIT_AND_EXPR)
+	    cst2 = gimple_assign_rhs2 (def_stmt);
+	  else
+	    {
+	      cst2 = TYPE_MAX_VALUE (TREE_TYPE (val));
+	      nprec = TYPE_PRECISION (TREE_TYPE (name2));
+	    }
 	  if (TREE_CODE (name2) == SSA_NAME
 	      && INTEGRAL_TYPE_P (TREE_TYPE (name2))
 	      && TREE_CODE (cst2) == INTEGER_CST
 	      && !integer_zerop (cst2)
-	      && prec <= HOST_BITS_PER_DOUBLE_INT
-	      && (prec > 1
+	      && nprec <= HOST_BITS_PER_DOUBLE_INT
+	      && (nprec > 1
 		  || TYPE_UNSIGNED (TREE_TYPE (val))))
 	    {
 	      gimple def_stmt2 = SSA_NAME_DEF_STMT (name2);
@@ -4881,12 +4901,12 @@ register_edge_assert_for_2 (tree name, e
 	  bool valid_p = false, valn = false, cst2n = false;
 	  enum tree_code ccode = comp_code;
 
-	  valv = tree_to_double_int (val).zext (prec);
-	  cst2v = tree_to_double_int (cst2).zext (prec);
+	  valv = tree_to_double_int (val).zext (nprec);
+	  cst2v = tree_to_double_int (cst2).zext (nprec);
 	  if (!TYPE_UNSIGNED (TREE_TYPE (val)))
 	    {
-	      valn = valv.sext (prec).is_negative ();
-	      cst2n = cst2v.sext (prec).is_negative ();
+	      valn = valv.sext (nprec).is_negative ();
+	      cst2n = cst2v.sext (nprec).is_negative ();
 	    }
 	  /* If CST2 doesn't have most significant bit set,
 	     but VAL is negative, we have comparison like
@@ -4894,7 +4914,7 @@ register_edge_assert_for_2 (tree name, e
 	  if (!cst2n && valn)
 	    ccode = ERROR_MARK;
 	  if (cst2n)
-	    sgnbit = double_int_one.llshift (prec - 1, prec).zext (prec);
+	    sgnbit = double_int_one.llshift (nprec - 1, nprec).zext (nprec);
 	  else
 	    sgnbit = double_int_zero;
 	  minv = valv & cst2v;
@@ -4906,12 +4926,12 @@ register_edge_assert_for_2 (tree name, e
 		 have folded the comparison into false) and
 		 maximum unsigned value is VAL | ~CST2.  */
 	      maxv = valv | ~cst2v;
-	      maxv = maxv.zext (prec);
+	      maxv = maxv.zext (nprec);
 	      valid_p = true;
 	      break;
 	    case NE_EXPR:
 	      tem = valv | ~cst2v;
-	      tem = tem.zext (prec);
+	      tem = tem.zext (nprec);
 	      /* If VAL is 0, handle (X & CST2) != 0 as (X & CST2) > 0U.  */
 	      if (valv.is_zero ())
 		{
@@ -4921,7 +4941,7 @@ register_edge_assert_for_2 (tree name, e
 		}
 	      /* If (VAL | ~CST2) is all ones, handle it as
 		 (X & CST2) < VAL.  */
-	      if (tem == double_int::mask (prec))
+	      if (tem == double_int::mask (nprec))
 		{
 		  cst2n = false;
 		  valn = false;
@@ -4929,8 +4949,9 @@ register_edge_assert_for_2 (tree name, e
 		  goto lt_expr;
 		}
 	      if (!cst2n
-		  && cst2v.sext (prec).is_negative ())
-		sgnbit = double_int_one.llshift (prec - 1, prec).zext (prec);
+		  && cst2v.sext (nprec).is_negative ())
+		sgnbit
+		  = double_int_one.llshift (nprec - 1, nprec).zext (nprec);
 	      if (!sgnbit.is_zero ())
 		{
 		  if (valv == sgnbit)
@@ -4939,7 +4960,7 @@ register_edge_assert_for_2 (tree name, e
 		      valn = true;
 		      goto gt_expr;
 		    }
-		  if (tem == double_int::mask (prec - 1))
+		  if (tem == double_int::mask (nprec - 1))
 		    {
 		      cst2n = true;
 		      goto lt_expr;
@@ -4958,22 +4979,22 @@ register_edge_assert_for_2 (tree name, e
 		{
 		  /* If (VAL & CST2) != VAL, X & CST2 can't be equal to
 		     VAL.  */
-		  minv = masked_increment (valv, cst2v, sgnbit, prec);
+		  minv = masked_increment (valv, cst2v, sgnbit, nprec);
 		  if (minv == valv)
 		    break;
 		}
-	      maxv = double_int::mask (prec - (cst2n ? 1 : 0));
+	      maxv = double_int::mask (nprec - (cst2n ? 1 : 0));
 	      valid_p = true;
 	      break;
 	    case GT_EXPR:
 	    gt_expr:
 	      /* Find out smallest MINV where MINV > VAL
 		 && (MINV & CST2) == MINV, if any.  If VAL is signed and
-		 CST2 has MSB set, compute it biased by 1 << (prec - 1).  */
-	      minv = masked_increment (valv, cst2v, sgnbit, prec);
+		 CST2 has MSB set, compute it biased by 1 << (nprec - 1).  */
+	      minv = masked_increment (valv, cst2v, sgnbit, nprec);
 	      if (minv == valv)
 		break;
-	      maxv = double_int::mask (prec - (cst2n ? 1 : 0));
+	      maxv = double_int::mask (nprec - (cst2n ? 1 : 0));
 	      valid_p = true;
 	      break;
 	    case LE_EXPR:
@@ -4989,13 +5010,13 @@ register_edge_assert_for_2 (tree name, e
 		maxv = valv;
 	      else
 		{
-		  maxv = masked_increment (valv, cst2v, sgnbit, prec);
+		  maxv = masked_increment (valv, cst2v, sgnbit, nprec);
 		  if (maxv == valv)
 		    break;
 		  maxv -= double_int_one;
 		}
 	      maxv |= ~cst2v;
-	      maxv = maxv.zext (prec);
+	      maxv = maxv.zext (nprec);
 	      minv = sgnbit;
 	      valid_p = true;
 	      break;
@@ -5017,13 +5038,13 @@ register_edge_assert_for_2 (tree name, e
 		}
 	      else
 		{
-		  maxv = masked_increment (valv, cst2v, sgnbit, prec);
+		  maxv = masked_increment (valv, cst2v, sgnbit, nprec);
 		  if (maxv == valv)
 		    break;
 		}
 	      maxv -= double_int_one;
 	      maxv |= ~cst2v;
-	      maxv = maxv.zext (prec);
+	      maxv = maxv.zext (nprec);
 	      minv = sgnbit;
 	      valid_p = true;
 	      break;
@@ -5031,7 +5052,7 @@ register_edge_assert_for_2 (tree name, e
 	      break;
 	    }
 	  if (valid_p
-	      && (maxv - minv).zext (prec) != double_int::mask (prec))
+	      && (maxv - minv).zext (nprec) != double_int::mask (nprec))
 	    {
 	      tree tmp, new_val, type;
 	      int i;
@@ -5044,7 +5065,7 @@ register_edge_assert_for_2 (tree name, e
 		    type = TREE_TYPE (names[i]);
 		    if (!TYPE_UNSIGNED (type))
 		      {
-			type = build_nonstandard_integer_type (prec, 1);
+			type = build_nonstandard_integer_type (nprec, 1);
 			tmp = build1 (NOP_EXPR, type, names[i]);
 		      }
 		    if (!minv.is_zero ())
--- gcc/testsuite/gcc.dg/tree-ssa/vrp85.c.jj	2012-10-04 11:32:18.787781121 +0200
+++ gcc/testsuite/gcc.dg/tree-ssa/vrp85.c	2012-10-04 11:49:18.128031124 +0200
@@ -0,0 +1,40 @@
+/* PR tree-optimization/54810 */
+/* { dg-do link } */
+/* { dg-options "-O2 -fdump-tree-vrp1" } */
+
+extern void link_error (void);
+
+#define T(n, ntype, wtype) \
+void				\
+f##n (wtype s)			\
+{				\
+  if ((ntype) s == 0)		\
+    return;			\
+  if (s == 0)			\
+    link_error ();		\
+}
+
+T(1, unsigned char, unsigned char)
+T(2, unsigned char, unsigned short)
+T(3, unsigned char, unsigned int)
+T(4, unsigned char, unsigned long int)
+T(5, unsigned char, unsigned long long int)
+T(6, unsigned short int, unsigned short int)
+T(7, unsigned short int, unsigned int)
+T(8, unsigned short int, unsigned long int)
+T(9, unsigned short int, unsigned long long int)
+T(10, unsigned int, unsigned int)
+T(11, unsigned int, unsigned long int)
+T(12, unsigned int, unsigned long long int)
+T(13, unsigned long int, unsigned long int)
+T(14, unsigned long int, unsigned long long int)
+T(15, unsigned long long int, unsigned long long int)
+
+int
+main ()
+{
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "link_error" "vrp1"} } */
+/* { dg-final { cleanup-tree-dump "vrp1" } } */
