public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Propagate get_nonzero_bits information in division [PR77980]
@ 2021-07-27  0:45 Victor Tong
  2021-08-23  3:11 ` Jeff Law
  0 siblings, 1 reply; 3+ messages in thread
From: Victor Tong @ 2021-07-27  0:45 UTC (permalink / raw)
  To: gcc-patches, pinskia

[-- Attachment #1: Type: text/plain, Size: 1185 bytes --]

This change enables the "t1 != 0" check to be optimized away in this code:

int x1 = 0;
unsigned int x2 = 1;

int main ()
{
    int t1 = x1*(1/(x2+x2));
    if (t1 != 0) __builtin_abort();
    return 0;
}

The change utilizes the VRP framework to propagate the get_nonzero_bits information from the "x2+x2" expression to the "1/(x2+x2)" division expression. Specifically, the framework knows that the least significant bit of the "x2+x2" expression must be zero.

The get_nonzero_bits information of the left hand side and right hand side of expressions needed to be passed down to operator_div::wi_fold() in the VRP framework. The majority of this change involves adding two additional parameters to propagate this information. There are future opportunities to use the non zero bit information to perform better optimizations in other types of expressions.

The changes were tested against x86_64-pc-linux-gnu and all tests in "make -k check" passed.

The original approach was to implement a match.pd pattern to support this but the pattern wasn't being triggered. More context is available in: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77980

Thanks,
Victor

[-- Attachment #2: pr77980.patch --]
[-- Type: application/octet-stream, Size: 57519 bytes --]

diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index e94bb355de3..6eca0885c09 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -216,9 +216,11 @@ gimple_range_fold (irange &res, const gimple *stmt,
 		   const irange &r1, const irange &r2)
 {
   gcc_checking_assert (gimple_range_handler (stmt));
+  tree type = gimple_expr_type (stmt);
+  unsigned prec = TYPE_PRECISION (type);
 
-  gimple_range_handler (stmt)->fold_range (res, gimple_expr_type (stmt),
-					   r1, r2);
+  gimple_range_handler (stmt)->fold_range (res, type,
+					   r1, r2, wi::shwi (-1, prec), wi::shwi (-1, prec));
 
   // If there are any gimple lookups, do those now.
   gimple_range_adjustment (res, stmt);
@@ -630,6 +632,7 @@ range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call,
   gcc_checking_assert (code == PLUS_EXPR || code == MINUS_EXPR
 		       || code == MULT_EXPR);
   tree type = gimple_call_return_type (call);
+  unsigned prec = TYPE_PRECISION (type);
   range_operator *op = range_op_handler (code, type);
   gcc_checking_assert (op);
   int_range_max ir0, ir1;
@@ -642,7 +645,7 @@ range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call,
   // Pretend the arithmetic is wrapping.  If there is any overflow,
   // we'll complain, but will actually do wrapping operation.
   flag_wrapv = 1;
-  op->fold_range (r, type, ir0, ir1);
+  op->fold_range (r, type, ir0, ir1, wi::shwi (-1, prec), wi::shwi (-1, prec));
   flag_wrapv = saved_flag_wrapv;
 
   // If for both arguments vrp_valueize returned non-NULL, this should
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 6041f75d824..dc25b28e37a 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1607,7 +1607,9 @@ ipa_value_range_from_jfunc (ipa_node_params *info, cgraph_edge *cs,
 	  tree op = ipa_get_jf_pass_through_operand (jfunc);
 	  value_range op_vr (op, op);
 
-	  range_fold_binary_expr (&op_res, operation, vr_type, &srcvr, &op_vr);
+	  range_fold_binary_expr (&op_res, operation, vr_type, &srcvr, &op_vr,
+							wi::shwi (-1, TYPE_PRECISION(vr_type)),
+							wi::shwi (-1, TYPE_PRECISION(vr_type)));
 	  if (ipa_vr_operation_and_type_effects (&res,
 						 &op_res,
 						 NOP_EXPR, parm_type,
@@ -2455,7 +2457,9 @@ propagate_vr_across_jump_function (cgraph_edge *cs, ipa_jump_func *jfunc,
 	  value_range op_res,res;
 
 	  range_fold_binary_expr (&op_res, operation, operand_type,
-				  &src_lats->m_value_range.m_vr, &op_vr);
+				  &src_lats->m_value_range.m_vr, &op_vr,
+				  wi::shwi (-1, TYPE_PRECISION(operand_type)),
+				  wi::shwi (-1, TYPE_PRECISION(operand_type)));
 	  ipa_vr_operation_and_type_effects (&vr,
 					     &op_res,
 					     NOP_EXPR, param_type,
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 18bbae145b9..03ab63a21bd 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -506,7 +506,9 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
 		      value_range op0 (op->val[0], op->val[0]);
 		      range_fold_binary_expr (&res, op->code, op->type,
 					      op->index ? &op0 : &vr,
-					      op->index ? &vr : &op0);
+					      op->index ? &vr : &op0,
+					      wi::shwi (-1, TYPE_PRECISION(op->type)),
+					      wi::shwi (-1, TYPE_PRECISION(op->type)));
 		    }
 		  else
 		    gcc_unreachable ();
@@ -519,7 +521,9 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
 		  value_range val_vr (c->val, c->val);
 		  range_fold_binary_expr (&res, c->code, boolean_type_node,
 					  &vr,
-					  &val_vr);
+					  &val_vr,
+					  wi::shwi (-1, TYPE_PRECISION(op->type)),
+					  wi::shwi (-1, TYPE_PRECISION(op->type)));
 		  if (res.zero_p ())
 		    continue;
 		}
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index ab8f4e211ac..3fded17fcfd 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -126,7 +126,9 @@ range_operator::wi_fold (irange &r, tree type,
 			 const wide_int &lh_lb ATTRIBUTE_UNUSED,
 			 const wide_int &lh_ub ATTRIBUTE_UNUSED,
 			 const wide_int &rh_lb ATTRIBUTE_UNUSED,
-			 const wide_int &rh_ub ATTRIBUTE_UNUSED) const
+			 const wide_int &rh_ub ATTRIBUTE_UNUSED,
+       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   gcc_checking_assert (irange::supports_type_p (type));
   r.set_varying (type);
@@ -138,7 +140,9 @@ range_operator::wi_fold (irange &r, tree type,
 bool
 range_operator::fold_range (irange &r, tree type,
 			    const irange &lh,
-			    const irange &rh) const
+			    const irange &rh,
+			    const wide_int& lh_nz,
+			    const wide_int& rh_nz) const
 {
   gcc_checking_assert (irange::supports_type_p (type));
   if (empty_range_varying (r, type, lh, rh))
@@ -151,7 +155,7 @@ range_operator::fold_range (irange &r, tree type,
   if (num_lh == 1 && num_rh == 1)
     {
       wi_fold (r, type, lh.lower_bound (0), lh.upper_bound (0),
-	       rh.lower_bound (0), rh.upper_bound (0));
+	       rh.lower_bound (0), rh.upper_bound (0), lh_nz, rh_nz);
       return true;
     }
 
@@ -164,7 +168,7 @@ range_operator::fold_range (irange &r, tree type,
 	wide_int lh_ub = lh.upper_bound (x);
 	wide_int rh_lb = rh.lower_bound (y);
 	wide_int rh_ub = rh.upper_bound (y);
-	wi_fold (tmp, type, lh_lb, lh_ub, rh_lb, rh_ub);
+	wi_fold (tmp, type, lh_lb, lh_ub, rh_lb, rh_ub, lh_nz, rh_nz);
 	r.union_ (tmp);
 	if (r.varying_p ())
 	  return true;
@@ -392,7 +396,9 @@ class operator_equal : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &val) const;
@@ -404,7 +410,9 @@ public:
 bool
 operator_equal::fold_range (irange &r, tree type,
 			    const irange &op1,
-			    const irange &op2) const
+			    const irange &op2,
+			    const wide_int &nz1 ATTRIBUTE_UNUSED,
+			    const wide_int &nz2 ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, op1, op2))
     return true;
@@ -477,7 +485,9 @@ class operator_not_equal : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -489,7 +499,9 @@ public:
 bool
 operator_not_equal::fold_range (irange &r, tree type,
 				const irange &op1,
-				const irange &op2) const
+				const irange &op2,
+				const wide_int &nz1 ATTRIBUTE_UNUSED,
+				const wide_int &nz2 ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, op1, op2))
     return true;
@@ -608,7 +620,9 @@ class operator_lt :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -620,7 +634,9 @@ public:
 bool
 operator_lt::fold_range (irange &r, tree type,
 			 const irange &op1,
-			 const irange &op2) const
+			 const irange &op2,
+       const wide_int &nz1 ATTRIBUTE_UNUSED,
+       const wide_int &nz2 ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, op1, op2))
     return true;
@@ -685,7 +701,9 @@ class operator_le :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -697,7 +715,9 @@ public:
 bool
 operator_le::fold_range (irange &r, tree type,
 			 const irange &op1,
-			 const irange &op2) const
+			 const irange &op2,
+       const wide_int &nz1 ATTRIBUTE_UNUSED,
+       const wide_int &nz2 ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, op1, op2))
     return true;
@@ -762,7 +782,9 @@ class operator_gt :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -773,7 +795,9 @@ public:
 
 bool
 operator_gt::fold_range (irange &r, tree type,
-			 const irange &op1, const irange &op2) const
+			 const irange &op1, const irange &op2,
+       const wide_int &nz1 ATTRIBUTE_UNUSED,
+       const wide_int &nz2 ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, op1, op2))
     return true;
@@ -837,7 +861,9 @@ class operator_ge :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -849,7 +875,9 @@ public:
 bool
 operator_ge::fold_range (irange &r, tree type,
 			 const irange &op1,
-			 const irange &op2) const
+			 const irange &op2,
+			 const wide_int &nz1 ATTRIBUTE_UNUSED,
+			 const wide_int &nz2 ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, op1, op2))
     return true;
@@ -922,13 +950,17 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
 } op_plus;
 
 void
 operator_plus::wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
-			const wide_int &rh_lb, const wide_int &rh_ub) const
+			const wide_int &rh_lb, const wide_int &rh_ub,
+			const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   wi::overflow_type ov_lb, ov_ub;
   signop s = TYPE_SIGN (type);
@@ -942,7 +974,9 @@ operator_plus::op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const
 {
-  return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op2);
+  return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op2,
+			  wi::shwi (-1, TYPE_PRECISION(type)),
+			  wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 bool
@@ -950,7 +984,9 @@ operator_plus::op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1) const
 {
-  return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op1);
+  return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op1,
+			  wi::shwi (-1, TYPE_PRECISION(type)),
+			  wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 
@@ -967,13 +1003,17 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
 } op_minus;
 
 void 
 operator_minus::wi_fold (irange &r, tree type,
 			 const wide_int &lh_lb, const wide_int &lh_ub,
-			 const wide_int &rh_lb, const wide_int &rh_ub) const
+			 const wide_int &rh_lb, const wide_int &rh_ub,
+			 const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			 const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   wi::overflow_type ov_lb, ov_ub;
   signop s = TYPE_SIGN (type);
@@ -987,7 +1027,9 @@ operator_minus::op1_range (irange &r, tree type,
 			   const irange &lhs,
 			   const irange &op2) const
 {
-  return range_op_handler (PLUS_EXPR, type)->fold_range (r, type, lhs, op2);
+  return range_op_handler (PLUS_EXPR, type)->fold_range (r, type, lhs, op2,
+			 wi::shwi (-1, TYPE_PRECISION(type)),
+			 wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 bool
@@ -995,7 +1037,8 @@ operator_minus::op2_range (irange &r, tree type,
 			   const irange &lhs,
 			   const irange &op1) const
 {
-  return fold_range (r, type, op1, lhs);
+  return fold_range (r, type, op1, lhs, wi::shwi (-1, TYPE_PRECISION(type)),
+					 wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 
@@ -1006,13 +1049,17 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
 } op_min;
 
 void
 operator_min::wi_fold (irange &r, tree type,
 		       const wide_int &lh_lb, const wide_int &lh_ub,
-		       const wide_int &rh_lb, const wide_int &rh_ub) const
+		       const wide_int &rh_lb, const wide_int &rh_ub,
+		       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+		       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   signop s = TYPE_SIGN (type);
   wide_int new_lb = wi::min (lh_lb, rh_lb, s);
@@ -1028,13 +1075,17 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
 } op_max;
 
 void
 operator_max::wi_fold (irange &r, tree type,
 		       const wide_int &lh_lb, const wide_int &lh_ub,
-		       const wide_int &rh_lb, const wide_int &rh_ub) const
+		       const wide_int &rh_lb, const wide_int &rh_ub,
+		       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+		       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   signop s = TYPE_SIGN (type);
   wide_int new_lb = wi::max (lh_lb, rh_lb, s);
@@ -1123,7 +1174,9 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
   virtual bool wi_op_overflows (wide_int &res, tree type,
 				const wide_int &w0, const wide_int &w1) const;
   virtual bool op1_range (irange &r, tree type,
@@ -1148,7 +1201,8 @@ operator_mult::op1_range (irange &r, tree type,
 
   if (op2.singleton_p (&offset) && !integer_zerop (offset))
     return range_op_handler (TRUNC_DIV_EXPR, type)->fold_range (r, type,
-								lhs, op2);
+								lhs, op2, wi::shwi (-1, TYPE_PRECISION(type)),
+								wi::shwi (-1, TYPE_PRECISION(type)));
   return false;
 }
 
@@ -1182,7 +1236,9 @@ operator_mult::wi_op_overflows (wide_int &res, tree type,
 void 
 operator_mult::wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
-			const wide_int &rh_lb, const wide_int &rh_ub) const
+			const wide_int &rh_lb, const wide_int &rh_ub,
+      const wide_int &lh_nz ATTRIBUTE_UNUSED,
+      const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   if (TYPE_OVERFLOW_UNDEFINED (type))
     {
@@ -1267,7 +1323,9 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
   virtual bool wi_op_overflows (wide_int &res, tree type,
 				const wide_int &, const wide_int &) const;
 private:
@@ -1319,7 +1377,9 @@ operator_div::wi_op_overflows (wide_int &res, tree type,
 void
 operator_div::wi_fold (irange &r, tree type,
 		       const wide_int &lh_lb, const wide_int &lh_ub,
-		       const wide_int &rh_lb, const wide_int &rh_ub) const
+		       const wide_int &rh_lb, const wide_int &rh_ub,
+		       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+		       const wide_int &rh_nz) const
 {
   // If we know we will divide by zero...
   if (rh_lb == 0 && rh_ub == 0)
@@ -1376,6 +1436,36 @@ operator_div::wi_fold (irange &r, tree type,
 			wi::one (prec), divisor_max);
       r.union_ (tmp);
     }
+
+  // Handle:
+  // x2.0_1 = x2D.2447;
+  // # RANGE [0, 4294967295] NONZERO 4294967294
+  // _2 = x2.0_1 * 2;
+  // # RANGE [0, 1] NONZERO 1
+  // _3 = 1 / _2;
+  // where the last bit of _2 is known to be zero because of the * 2
+  // From that information, we know that _2 can't hold the value of 1 so _3 can't be 1 as well
+  if (dividend_min == dividend_max
+      && wi::gt_p(dividend_min, wi::zero(prec), sign)
+      && wi::ge_p(divisor_min, wi::zero(prec), sign)
+      && r.num_pairs() == 1
+      && rh_nz != -1
+      && r.lower_bound(0) != r.upper_bound(0))
+    {
+      if (wi::ge_p(r.upper_bound(0), wi::one(prec), sign))
+        {
+          // Use the nonzero bits of the divisor to see if dividend_min is
+          // a possible result. If it isn't, then the result of the division
+          // can never be 1.
+          auto divisorZeroBits = ~rh_nz;
+          if (wi::ge_p(divisorZeroBits, dividend_min, sign))
+            {
+              value_range_with_overflow(r, type, r.lower_bound(0), wi::zero(prec));
+            }
+        }
+    
+    }
+
   // We shouldn't still have undefined here.
   gcc_checking_assert (!r.undefined_p ());
 }
@@ -1410,7 +1500,9 @@ operator_exact_divide::op1_range (irange &r, tree type,
   // If op2 is a multiple of 2, we would be able to set some non-zero bits.
   if (op2.singleton_p (&offset)
       && !integer_zerop (offset))
-    return range_op_handler (MULT_EXPR, type)->fold_range (r, type, lhs, op2);
+    return range_op_handler (MULT_EXPR, type)->fold_range (r, type, lhs, op2,
+											wi::shwi (-1, TYPE_PRECISION(type)),
+											wi::shwi (-1, TYPE_PRECISION(type)));
   return false;
 }
 
@@ -1423,11 +1515,14 @@ public:
 			  const irange &op2) const;
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
 
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
-			const wide_int &rh_lb, const wide_int &rh_ub) const;
+			const wide_int &rh_lb, const wide_int &rh_ub,
+      const wide_int &lh_nz, const wide_int &rh_nz) const;
   virtual bool wi_op_overflows (wide_int &res,
 				tree type,
 				const wide_int &,
@@ -1439,12 +1534,16 @@ class operator_rshift : public cross_product_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb,
 			const wide_int &lh_ub,
 			const wide_int &rh_lb,
-			const wide_int &rh_ub) const;
+			const wide_int &rh_ub,
+			const wide_int &lh_nz,
+			const wide_int &rh_nz) const;
   virtual bool wi_op_overflows (wide_int &res,
 				tree type,
 				const wide_int &w0,
@@ -1458,7 +1557,9 @@ public:
 bool
 operator_lshift::fold_range (irange &r, tree type,
 			     const irange &op1,
-			     const irange &op2) const
+			     const irange &op2,
+			     const wide_int &nz1 ATTRIBUTE_UNUSED,
+			     const wide_int &nz2 ATTRIBUTE_UNUSED) const
 {
   int_range_max shift_range;
   if (!get_shift_range (shift_range, type, op2))
@@ -1482,20 +1583,26 @@ operator_lshift::fold_range (irange &r, tree type,
       bool saved_flag_wrapv_pointer = flag_wrapv_pointer;
       flag_wrapv = 1;
       flag_wrapv_pointer = 1;
-      bool b = op_mult.fold_range (r, type, op1, mult);
+      bool b = op_mult.fold_range (r, type, op1, mult, 
+											wi::shwi (-1, TYPE_PRECISION(type)),
+											wi::shwi (-1, TYPE_PRECISION(type)));
       flag_wrapv = saved_flag_wrapv;
       flag_wrapv_pointer = saved_flag_wrapv_pointer;
       return b;
     }
   else
     // Otherwise, invoke the generic fold routine.
-    return range_operator::fold_range (r, type, op1, shift_range);
+    return range_operator::fold_range (r, type, op1, shift_range,
+											wi::shwi (-1, TYPE_PRECISION(type)),
+											wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 void
 operator_lshift::wi_fold (irange &r, tree type,
 			  const wide_int &lh_lb, const wide_int &lh_ub,
-			  const wide_int &rh_lb, const wide_int &rh_ub) const
+			  const wide_int &rh_lb, const wide_int &rh_ub,
+			  const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			  const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   signop sign = TYPE_SIGN (type);
   unsigned prec = TYPE_PRECISION (type);
@@ -1598,10 +1705,14 @@ operator_lshift::op1_range (irange &r,
 	  int_range_max tmp = lhs;
 	  utype = unsigned_type_for (type);
 	  range_cast (tmp, utype);
-	  op_rshift.fold_range (r, utype, tmp, op2);
+	  op_rshift.fold_range (r, utype, tmp, op2, 
+													wi::shwi (-1, TYPE_PRECISION(type)),
+													wi::shwi (-1, TYPE_PRECISION(type)));
 	}
       else
-	op_rshift.fold_range (r, utype, lhs, op2);
+	op_rshift.fold_range (r, utype, lhs, op2, 
+												wi::shwi (-1, TYPE_PRECISION(type)),
+												wi::shwi (-1, TYPE_PRECISION(type)));
 
       // Start with ranges which can produce the LHS by right shifting the
       // result by the shift amount.
@@ -1653,7 +1764,9 @@ operator_rshift::op1_range (irange &r,
       // Folding the original operation may discard some impossible
       // ranges from the LHS.
       int_range_max lhs_refined;
-      op_rshift.fold_range (lhs_refined, type, int_range<1> (type), op2);
+      op_rshift.fold_range (lhs_refined, type, int_range<1> (type), op2,
+						wi::shwi (-1, TYPE_PRECISION(type)),
+						wi::shwi (-1, TYPE_PRECISION(type)));
       lhs_refined.intersect (lhs);
       if (lhs_refined.undefined_p ())
 	{
@@ -1662,7 +1775,9 @@ operator_rshift::op1_range (irange &r,
 	}
       int_range_max shift_range (shift, shift);
       int_range_max lb, ub;
-      op_lshift.fold_range (lb, type, lhs_refined, shift_range);
+      op_lshift.fold_range (lb, type, lhs_refined, shift_range,
+						wi::shwi (-1, TYPE_PRECISION(type)),
+						wi::shwi (-1, TYPE_PRECISION(type)));
       //    LHS
       // 0000 0111 = OP1 >> 3
       //
@@ -1674,7 +1789,9 @@ operator_rshift::op1_range (irange &r,
 					    build_minus_one_cst (type),
 					    shift));
       int_range_max mask_range (build_zero_cst (type), mask);
-      op_plus.fold_range (ub, type, lb, mask_range);
+      op_plus.fold_range (ub, type, lb, mask_range, 
+						wi::shwi (-1, TYPE_PRECISION(type)),
+						wi::shwi (-1, TYPE_PRECISION(type)));
       r = lb;
       r.union_ (ub);
       if (!lhs_refined.contains_p (build_zero_cst (type)))
@@ -1709,7 +1826,9 @@ operator_rshift::wi_op_overflows (wide_int &res,
 bool
 operator_rshift::fold_range (irange &r, tree type,
 			     const irange &op1,
-			     const irange &op2) const
+			     const irange &op2,
+			     const wide_int &nz1,
+			     const wide_int &nz2) const
 {
   int_range_max shift;
   if (!get_shift_range (shift, type, op2))
@@ -1721,13 +1840,16 @@ operator_rshift::fold_range (irange &r, tree type,
       return true;
     }
 
-  return range_operator::fold_range (r, type, op1, shift);
+  return range_operator::fold_range (r, type, op1, shift, 
+																		 nz1, nz2);
 }
 
 void
 operator_rshift::wi_fold (irange &r, tree type,
 			  const wide_int &lh_lb, const wide_int &lh_ub,
-			  const wide_int &rh_lb, const wide_int &rh_ub) const
+			  const wide_int &rh_lb, const wide_int &rh_ub,
+			  const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			  const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   wi_cross_product (r, type, lh_lb, lh_ub, rh_lb, rh_ub);
 }
@@ -1738,7 +1860,9 @@ class operator_cast: public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -1819,7 +1943,9 @@ operator_cast::fold_pair (irange &r, unsigned index,
 bool
 operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			   const irange &inner,
-			   const irange &outer) const
+			   const irange &outer,
+			   const wide_int &inner_nz ATTRIBUTE_UNUSED,
+			   const wide_int &outer_nz ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, inner, outer))
     return true;
@@ -1903,7 +2029,9 @@ operator_cast::op1_range (irange &r, tree type,
 	  range_op_handler (PLUS_EXPR, type)->fold_range (lhs_neg,
 							  type,
 							  converted_lhs,
-							  lim_range);
+							  lim_range,
+								wi::shwi (-1, TYPE_PRECISION(type)),
+								wi::shwi (-1, TYPE_PRECISION(type)));
 	  // lhs_neg now has all the negative versions of the LHS.
 	  // Now union in all the values from SIGNED MIN (0x80000) to
 	  // lim-1 in order to fill in all the ranges with the upper
@@ -1938,14 +2066,18 @@ operator_cast::op1_range (irange &r, tree type,
       // the range of the RHS by this assignment.
       //
       // Cast the range of the RHS to the type of the LHS.
-      fold_range (tmp, lhs_type, int_range<1> (type), int_range<1> (lhs_type));
+      fold_range (tmp, lhs_type, int_range<1> (type), int_range<1> (lhs_type),
+									wi::shwi (-1, TYPE_PRECISION(lhs_type)),
+									wi::shwi (-1, TYPE_PRECISION(lhs_type)));
       // Intersect this with the LHS range will produce the range,
       // which will be cast to the RHS type before returning.
       tmp.intersect (lhs);
     }
 
   // Cast the calculated range to the type of the RHS.
-  fold_range (r, type, tmp, int_range<1> (type));
+  fold_range (r, type, tmp, int_range<1> (type),
+							wi::shwi (-1, TYPE_PRECISION(type)),
+							wi::shwi (-1, TYPE_PRECISION(type)));
   return true;
 }
 
@@ -1955,7 +2087,9 @@ class operator_logical_and : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   const wide_int &lh_nz,
+			   const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -1968,7 +2102,9 @@ public:
 bool
 operator_logical_and::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh) const
+				  const irange &rh,
+				  const wide_int &lh_nz ATTRIBUTE_UNUSED,
+				  const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -2022,7 +2158,9 @@ class operator_bitwise_and : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   const wide_int &lh_nz,
+			   const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2033,7 +2171,9 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
 private:
   void simple_op1_range_solver (irange &r, tree type,
 				const irange &lhs,
@@ -2107,9 +2247,12 @@ operator_bitwise_and::remove_impossible_ranges (irange &r,
 bool
 operator_bitwise_and::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh) const
+				  const irange &rh,
+				  const wide_int &lh_nz,
+				  const wide_int &rh_nz) const
 {
-  if (range_operator::fold_range (r, type, lh, rh))
+  if (range_operator::fold_range (r, type, lh, rh, 
+																	lh_nz, rh_nz))
     {
       // FIXME: This is temporarily disabled because, though it
       // generates better ranges, it's noticeably slower for evrp.
@@ -2252,7 +2395,9 @@ operator_bitwise_and::wi_fold (irange &r, tree type,
 			       const wide_int &lh_lb,
 			       const wide_int &lh_ub,
 			       const wide_int &rh_lb,
-			       const wide_int &rh_ub) const
+			       const wide_int &rh_ub,
+			       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   if (wi_optimize_and_or (r, BIT_AND_EXPR, type, lh_lb, lh_ub, rh_lb, rh_ub))
     return;
@@ -2432,7 +2577,9 @@ class operator_logical_or : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   const wide_int &lh_nz,
+			   const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2444,7 +2591,9 @@ public:
 bool
 operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 				 const irange &lh,
-				 const irange &rh) const
+				 const irange &rh,
+				 const wide_int &lh_nz ATTRIBUTE_UNUSED,
+				 const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -2497,7 +2646,9 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
 } op_bitwise_or;
 
 void
@@ -2505,7 +2656,9 @@ operator_bitwise_or::wi_fold (irange &r, tree type,
 			      const wide_int &lh_lb,
 			      const wide_int &lh_ub,
 			      const wide_int &rh_lb,
-			      const wide_int &rh_ub) const
+			      const wide_int &rh_ub,
+			      const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			      const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   if (wi_optimize_and_or (r, BIT_IOR_EXPR, type, lh_lb, lh_ub, rh_lb, rh_ub))
     return;
@@ -2577,7 +2730,9 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2591,7 +2746,9 @@ operator_bitwise_xor::wi_fold (irange &r, tree type,
 			       const wide_int &lh_lb,
 			       const wide_int &lh_ub,
 			       const wide_int &rh_lb,
-			       const wide_int &rh_ub) const
+			       const wide_int &rh_ub,
+			       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   signop sign = TYPE_SIGN (type);
   wide_int maybe_nonzero_lh, mustbe_nonzero_lh;
@@ -2666,7 +2823,9 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2680,7 +2839,9 @@ operator_trunc_mod::wi_fold (irange &r, tree type,
 			     const wide_int &lh_lb,
 			     const wide_int &lh_ub,
 			     const wide_int &rh_lb,
-			     const wide_int &rh_ub) const
+			     const wide_int &rh_ub,
+			     const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			     const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   wide_int new_lb, new_ub, tmp;
   signop sign = TYPE_SIGN (type);
@@ -2784,7 +2945,9 @@ class operator_logical_not : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   const wide_int &lh_nz,
+			   const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2807,7 +2970,9 @@ public:
 bool
 operator_logical_not::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh ATTRIBUTE_UNUSED) const
+				  const irange &rh ATTRIBUTE_UNUSED,
+				  const wide_int &lh_nz ATTRIBUTE_UNUSED,
+				  const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -2826,7 +2991,9 @@ operator_logical_not::op1_range (irange &r,
 				 const irange &op2) const
 {
   // Logical NOT is involutary...do it again.
-  return fold_range (r, type, lhs, op2);
+  return fold_range (r, type, lhs, op2, 
+										 wi::shwi (-1, TYPE_PRECISION(type)),
+										 wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 
@@ -2835,7 +3002,9 @@ class operator_bitwise_not : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   const wide_int &lh_nz,
+			   const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2844,19 +3013,21 @@ public:
 bool
 operator_bitwise_not::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh) const
+				  const irange &rh,
+				  const wide_int &lh_nz,
+				  const wide_int &rh_nz) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
 
   if (types_compatible_p (type, boolean_type_node))
-    return op_logical_not.fold_range (r, type, lh, rh);
+    return op_logical_not.fold_range (r, type, lh, rh, lh_nz, rh_nz);
 
   // ~X is simply -1 - X.
   int_range<1> minusone (type, wi::minus_one (TYPE_PRECISION (type)),
 			 wi::minus_one (TYPE_PRECISION (type)));
   return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, minusone,
-							  lh);
+							  lh, lh_nz, rh_nz);
 }
 
 bool
@@ -2868,7 +3039,9 @@ operator_bitwise_not::op1_range (irange &r, tree type,
     return op_logical_not.op1_range (r, type, lhs, op2);
 
   // ~X is -1 - X and since bitwise NOT is involutary...do it again.
-  return fold_range (r, type, lhs, op2);
+  return fold_range (r, type, lhs, op2, 
+										 wi::shwi (-1, TYPE_PRECISION(type)),
+										 wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 
@@ -2877,13 +3050,17 @@ class operator_cst : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
 } op_integer_cst;
 
 bool
 operator_cst::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			  const irange &lh,
-			  const irange &rh ATTRIBUTE_UNUSED) const
+			  const irange &rh ATTRIBUTE_UNUSED,
+			  const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			  const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   r = lh;
   return true;
@@ -2895,7 +3072,9 @@ class operator_identity : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2904,7 +3083,9 @@ public:
 bool
 operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			       const irange &lh,
-			       const irange &rh ATTRIBUTE_UNUSED) const
+			       const irange &rh ATTRIBUTE_UNUSED,
+			       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   r = lh;
   return true;
@@ -2925,13 +3106,17 @@ class operator_unknown : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
 } op_unknown;
 
 bool
 operator_unknown::fold_range (irange &r, tree type,
 			      const irange &lh ATTRIBUTE_UNUSED,
-			      const irange &rh ATTRIBUTE_UNUSED) const
+			      const irange &rh ATTRIBUTE_UNUSED,
+			      const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			      const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   r.set_varying (type);
   return true;
@@ -2945,7 +3130,9 @@ class operator_abs : public range_operator
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -2955,7 +3142,9 @@ void
 operator_abs::wi_fold (irange &r, tree type,
 		       const wide_int &lh_lb, const wide_int &lh_ub,
 		       const wide_int &rh_lb ATTRIBUTE_UNUSED,
-		       const wide_int &rh_ub ATTRIBUTE_UNUSED) const
+		       const wide_int &rh_ub ATTRIBUTE_UNUSED,
+		       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+		       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   wide_int min, max;
   signop sign = TYPE_SIGN (type);
@@ -3056,14 +3245,17 @@ class operator_absu : public range_operator
  public:
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
-			const wide_int &rh_lb, const wide_int &rh_ub) const;
+			const wide_int &rh_lb, const wide_int &rh_ub,
+			const wide_int &lh_nz, const wide_int &rh_nz) const;
 } op_absu;
 
 void
 operator_absu::wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
 			const wide_int &rh_lb ATTRIBUTE_UNUSED,
-			const wide_int &rh_ub ATTRIBUTE_UNUSED) const
+			const wide_int &rh_ub ATTRIBUTE_UNUSED,
+			const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   wide_int new_lb, new_ub;
 
@@ -3100,7 +3292,9 @@ class operator_negate : public range_operator
  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -3109,14 +3303,16 @@ class operator_negate : public range_operator
 bool
 operator_negate::fold_range (irange &r, tree type,
 			     const irange &lh,
-			     const irange &rh) const
+			     const irange &rh,
+			     const wide_int &lh_nz,
+			     const wide_int &rh_nz) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
   // -X is simply 0 - X.
   return range_op_handler (MINUS_EXPR, type)->fold_range (r, type,
 							  range_zero (type),
-							  lh);
+							  lh, lh_nz, rh_nz);
 }
 
 bool
@@ -3125,7 +3321,9 @@ operator_negate::op1_range (irange &r, tree type,
 			    const irange &op2) const
 {
   // NEGATE is involutory.
-  return fold_range (r, type, lhs, op2);
+  return fold_range (r, type, lhs, op2, 
+										 wi::shwi (-1, TYPE_PRECISION(type)),
+										 wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 
@@ -3134,7 +3332,9 @@ class operator_addr_expr : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   const wide_int &nz1,
+			   const wide_int &nz2) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2) const;
@@ -3143,7 +3343,9 @@ public:
 bool
 operator_addr_expr::fold_range (irange &r, tree type,
 				const irange &lh,
-				const irange &rh) const
+				const irange &rh,
+				const wide_int &lh_nz ATTRIBUTE_UNUSED,
+				const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -3163,7 +3365,9 @@ operator_addr_expr::op1_range (irange &r, tree type,
 			       const irange &lhs,
 			       const irange &op2) const
 {
-  return operator_addr_expr::fold_range (r, type, lhs, op2);
+  return operator_addr_expr::fold_range (r, type, lhs, op2,
+			       wi::shwi (-1, TYPE_PRECISION(type)),
+			       wi::shwi (-1, TYPE_PRECISION(type)));
 }
 
 
@@ -3174,7 +3378,9 @@ public:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz,
+		        const wide_int &rh_nz) const;
 } op_pointer_plus;
 
 void
@@ -3182,7 +3388,9 @@ pointer_plus_operator::wi_fold (irange &r, tree type,
 				const wide_int &lh_lb,
 				const wide_int &lh_ub,
 				const wide_int &rh_lb,
-				const wide_int &rh_ub) const
+				const wide_int &rh_ub,
+				const wide_int &lh_nz ATTRIBUTE_UNUSED,
+				const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   // Check for [0,0] + const, and simply return the const.
   if (lh_lb == 0 && lh_ub == 0 && rh_lb == rh_ub)
@@ -3227,7 +3435,8 @@ class pointer_min_max_operator : public range_operator
 public:
   virtual void wi_fold (irange & r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
-			const wide_int &rh_lb, const wide_int &rh_ub) const;
+			const wide_int &rh_lb, const wide_int &rh_ub,
+			const wide_int &lh_nz, const wide_int &rh_nz) const;
 } op_ptr_min_max;
 
 void
@@ -3235,7 +3444,9 @@ pointer_min_max_operator::wi_fold (irange &r, tree type,
 				   const wide_int &lh_lb,
 				   const wide_int &lh_ub,
 				   const wide_int &rh_lb,
-				   const wide_int &rh_ub) const
+				   const wide_int &rh_ub,
+				   const wide_int &lh_nz ATTRIBUTE_UNUSED,
+				   const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   // For MIN/MAX expressions with pointers, we only care about
   // nullness.  If both are non null, then the result is nonnull.
@@ -3256,7 +3467,8 @@ class pointer_and_operator : public range_operator
 public:
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
-			const wide_int &rh_lb, const wide_int &rh_ub) const;
+			const wide_int &rh_lb, const wide_int &rh_ub,
+			const wide_int &lh_nz, const wide_int &rh_nz) const;
 } op_pointer_and;
 
 void
@@ -3264,7 +3476,9 @@ pointer_and_operator::wi_fold (irange &r, tree type,
 			       const wide_int &lh_lb,
 			       const wide_int &lh_ub,
 			       const wide_int &rh_lb ATTRIBUTE_UNUSED,
-			       const wide_int &rh_ub ATTRIBUTE_UNUSED) const
+			       const wide_int &rh_ub ATTRIBUTE_UNUSED,
+			       const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			       const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   // For pointer types, we are really only interested in asserting
   // whether the expression evaluates to non-NULL.
@@ -3286,7 +3500,8 @@ public:
 			  const irange &op1) const;
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
-			const wide_int &rh_lb, const wide_int &rh_ub) const;
+			const wide_int &rh_lb, const wide_int &rh_ub,
+			const wide_int &lh_nz, const wide_int &rh_nz) const;
 } op_pointer_or;
 
 bool
@@ -3317,7 +3532,9 @@ pointer_or_operator::wi_fold (irange &r, tree type,
 			      const wide_int &lh_lb,
 			      const wide_int &lh_ub,
 			      const wide_int &rh_lb,
-			      const wide_int &rh_ub) const
+			      const wide_int &rh_ub,
+			      const wide_int &lh_nz ATTRIBUTE_UNUSED,
+			      const wide_int &rh_nz ATTRIBUTE_UNUSED) const
 {
   // For pointer types, we are really only interested in asserting
   // whether the expression evaluates to non-NULL.
@@ -3464,7 +3681,9 @@ range_cast (irange &r, tree type)
   int_range_max tmp = r;
   range_operator *op = range_op_handler (CONVERT_EXPR, type);
   // Call op_convert, if it fails, the result is varying.
-  if (!op->fold_range (r, type, tmp, int_range<1> (type)))
+  if (!op->fold_range (r, type, tmp, int_range<1> (type),
+											 wi::shwi (-1, TYPE_PRECISION(type)),
+											 wi::shwi (-1, TYPE_PRECISION(type))))
     r.set_varying (type);
 }
 
@@ -3623,7 +3842,9 @@ range_op_lshift_tests ()
 				build_int_cst (big_type, 48));
     op_bitwise_and.fold_range (res, big_type,
 			       int_range <1> (big_type),
-			       int_range <1> (big_num, big_num));
+			       int_range <1> (big_num, big_num),
+			       wi::shwi (-1, TYPE_PRECISION(big_type)),
+			       wi::shwi (-1, TYPE_PRECISION(big_type)));
     // val = 0x8,0000,0000,0000
     tree val = fold_build2 (LSHIFT_EXPR, big_type,
 			    build_int_cst (big_type, 0x8),
@@ -3650,7 +3871,9 @@ range_op_lshift_tests ()
       //  op1 << 1   should be [0x8000,0x8000] << 1,
       //  which should result in [0,0].
       int_range_max result;
-      op_lshift.fold_range (result, unsigned_type_node, op1, shift);
+      op_lshift.fold_range (result, unsigned_type_node, op1, shift,
+			       wi::shwi (-1, TYPE_PRECISION(unsigned_type_node)),
+			       wi::shwi (-1, TYPE_PRECISION(unsigned_type_node)));
       ASSERT_TRUE (result == zero);
     }
   // signed VARYING = op1 << 1 should be VARYING.
@@ -3673,7 +3896,9 @@ range_op_lshift_tests ()
       //  op1 << 1   shuould be [0x8000,0x8000] << 1,
       //  which should result in [0,0].
       int_range_max result;
-      op_lshift.fold_range (result, unsigned_type_node, op1, shift);
+      op_lshift.fold_range (result, unsigned_type_node, op1, shift,
+			       wi::shwi (-1, TYPE_PRECISION(unsigned_type_node)),
+			       wi::shwi (-1, TYPE_PRECISION(unsigned_type_node)));
       ASSERT_TRUE (result == zero);
     }
 }
@@ -3758,4 +3983,4 @@ range_op_tests ()
 
 } // namespace selftest
 
-#endif // CHECKING_P
+#endif // CHECKING_P
\ No newline at end of file
diff --git a/gcc/range-op.h b/gcc/range-op.h
index d3d44083093..2683695f9c1 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -52,7 +52,9 @@ public:
   // Perform an operation between 2 ranges and return it.
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   const wide_int& lh_nz,
+			   const wide_int& rh_nz) const;
 
   // Return the range for op[12] in the general case.  LHS is the range for
   // the LHS of the expression, OP[12]is the range for the other
@@ -78,7 +80,9 @@ protected:
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
-		        const wide_int &rh_ub) const;
+		        const wide_int &rh_ub,
+		        const wide_int &lh_nz, 
+		        const wide_int &rh_nz) const;
 };
 
 extern range_operator *range_op_handler (enum tree_code code, tree type);
diff --git a/gcc/testsuite/gcc.dg/pr77980-neg.c b/gcc/testsuite/gcc.dg/pr77980-neg.c
new file mode 100644
index 00000000000..34d791dd564
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr77980-neg.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-vrp1" } */
+
+int x1 = 0;
+int x2 = 1;
+
+int test1 ()
+{
+	int t1 = x1*(1/(x2+x2));
+	// y2 can be negative so t1!=0 cannot be optimized away
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+int test2 (int y1, int y2)
+{
+	int t1 = y1*(1/(y2+y2));
+	// y2 can be negative so t1!=0 cannot be optimized away
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+int test3 (int y1, unsigned int y2)
+{
+	int t1 = y1*(2/(y2+y2));
+	// y2 can equal 1 so 2/(y2+y2) can equal 1 and the value of t1
+	// would be dependent on y1 so t1!=0 cannot be optimized away
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+int test4 (int y1, unsigned int y2)
+{
+	int t1 = y1*(4/(y2+y2+y2+y2));
+	// y2 can equal 1 so 4/(y2+y2+y2+y2) can equal 1 and the value of t1
+	// would be dependent on y1 so t1!=0 cannot be optimized away
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+// All if statements shouldn't get optimized away
+/* { dg-final { scan-tree-dump-times "if \\\(" 4 "vrp1" } } */
diff --git a/gcc/testsuite/gcc.dg/pr77980.c b/gcc/testsuite/gcc.dg/pr77980.c
new file mode 100644
index 00000000000..63b6f1ba5f1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr77980.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-vrp1" } */
+
+int x1 = 0;
+unsigned int x2 = 1;
+
+int test1 ()
+{
+	int t1 = x1*(1/(x2+x2));
+	// This check should be optimized away because we know that 
+	// t1 can only be 0 because 1/(x2+x2) can never be 1, because
+	// x2+x2 can never be 1)
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+int test2 (int y1, unsigned int y2)
+{
+	int t1 = y1*(1/(y2+y2));
+	// This check should be optimized away because we know that 
+	// t1 can only be 0 because 1/(y2+y2) can never be 1 because
+	// y2+y2 can never be 1
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+int test3 (int y1, unsigned int y2)
+{
+	int t1 = y1*(2/(y2+y2+y2+y2));
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+int test4 (int y1, unsigned int y2)
+{
+	int t1 = y1*(3/(y2+y2+y2+y2));
+	if (t1 != 0) __builtin_abort();
+	return 0;
+}
+
+// All if statements should get optimized away
+/* { dg-final { scan-tree-dump-times "if \\\(" 0 "vrp1" } } */
diff --git a/gcc/tree-data-ref.c b/gcc/tree-data-ref.c
index e6dd5f15bed..b005de191e6 100644
--- a/gcc/tree-data-ref.c
+++ b/gcc/tree-data-ref.c
@@ -614,7 +614,9 @@ compute_distributive_range (tree type, value_range &op0_range,
   if (result_range)
     {
       range_operator *op = range_op_handler (code, type);
-      op->fold_range (*result_range, type, op0_range, op1_range);
+      op->fold_range (*result_range, type, op0_range, op1_range, 
+											wi::shwi (-1, TYPE_PRECISION(type)),
+											wi::shwi (-1, TYPE_PRECISION(type)));
     }
 
   /* The distributive property guarantees that if TYPE is no narrower
@@ -662,7 +664,9 @@ compute_distributive_range (tree type, value_range &op0_range,
   range_operator *op = range_op_handler (code, ssizetype);
   bool saved_flag_wrapv = flag_wrapv;
   flag_wrapv = 1;
-  op->fold_range (wide_range, ssizetype, op0_range, op1_range);
+  op->fold_range (wide_range, ssizetype, op0_range, op1_range,
+									wi::shwi (-1, TYPE_PRECISION(type)),
+									wi::shwi (-1, TYPE_PRECISION(type)));
   flag_wrapv = saved_flag_wrapv;
   if (wide_range.num_pairs () != 1 || !range_int_cst_p (&wide_range))
     return false;
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index 12e6e6f3e22..8f262e8a75b 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -1026,7 +1026,9 @@ range_fold_binary_symbolics_p (value_range *vr,
       const range_operator *op = get_range_op_handler (vr, code, expr_type);
       vr0.normalize_symbolics ();
       vr1.normalize_symbolics ();
-      return op->fold_range (*vr, expr_type, vr0, vr1);
+      return op->fold_range (*vr, expr_type, vr0, vr1,
+						wi::shwi (-1, TYPE_PRECISION(expr_type)),
+						wi::shwi (-1, TYPE_PRECISION(expr_type)));
     }
   return false;
 }
@@ -1047,7 +1049,9 @@ range_fold_unary_symbolics_p (value_range *vr,
 	  /* -X is simply 0 - X.  */
 	  value_range zero;
 	  zero.set_zero (vr0->type ());
-	  range_fold_binary_expr (vr, MINUS_EXPR, expr_type, &zero, vr0);
+	  range_fold_binary_expr (vr, MINUS_EXPR, expr_type, &zero, vr0,
+						wi::shwi (-1, TYPE_PRECISION(expr_type)),
+						wi::shwi (-1, TYPE_PRECISION(expr_type)));
 	  return true;
 	}
       if (code == BIT_NOT_EXPR)
@@ -1055,13 +1059,17 @@ range_fold_unary_symbolics_p (value_range *vr,
 	  /* ~X is simply -1 - X.  */
 	  value_range minusone;
 	  minusone.set (build_int_cst (vr0->type (), -1));
-	  range_fold_binary_expr (vr, MINUS_EXPR, expr_type, &minusone, vr0);
+	  range_fold_binary_expr (vr, MINUS_EXPR, expr_type, &minusone, vr0,
+						wi::shwi (-1, TYPE_PRECISION(expr_type)),
+						wi::shwi (-1, TYPE_PRECISION(expr_type)));
 	  return true;
 	}
       const range_operator *op = get_range_op_handler (vr, code, expr_type);
       value_range vr0_cst (*vr0);
       vr0_cst.normalize_symbolics ();
-      return op->fold_range (*vr, expr_type, vr0_cst, value_range (expr_type));
+      return op->fold_range (*vr, expr_type, vr0_cst, value_range (expr_type),
+						wi::shwi (-1, TYPE_PRECISION(expr_type)),
+						wi::shwi (-1, TYPE_PRECISION(expr_type)));
     }
   return false;
 }
@@ -1073,7 +1081,9 @@ range_fold_binary_expr (value_range *vr,
 			enum tree_code code,
 			tree expr_type,
 			const value_range *vr0_,
-			const value_range *vr1_)
+			const value_range *vr1_,
+      const wide_int& nz0,
+      const wide_int& nz1)
 {
   if (!supported_types_p (vr, expr_type)
       || !defined_ranges_p (vr, vr0_, vr1_))
@@ -1093,7 +1103,7 @@ range_fold_binary_expr (value_range *vr,
     vr1.set_varying (expr_type);
   vr0.normalize_addresses ();
   vr1.normalize_addresses ();
-  op->fold_range (*vr, expr_type, vr0, vr1);
+  op->fold_range (*vr, expr_type, vr0, vr1, nz0, nz1);
 }
 
 /* Perform a unary operation on a range.  */
@@ -1116,7 +1126,9 @@ range_fold_unary_expr (value_range *vr,
 
   value_range vr0_cst (*vr0);
   vr0_cst.normalize_addresses ();
-  op->fold_range (*vr, expr_type, vr0_cst, value_range (expr_type));
+  op->fold_range (*vr, expr_type, vr0_cst, value_range (expr_type),
+						wi::shwi (-1, TYPE_PRECISION(expr_type)),
+						wi::shwi (-1, TYPE_PRECISION(expr_type)));
 }
 
 /* If the range of values taken by OP can be inferred after STMT executes,
@@ -4618,7 +4630,8 @@ determine_value_range_1 (value_range *vr, tree expr)
       determine_value_range_1 (&vr0, TREE_OPERAND (expr, 0));
       determine_value_range_1 (&vr1, TREE_OPERAND (expr, 1));
       range_fold_binary_expr (vr, TREE_CODE (expr), TREE_TYPE (expr),
-			      &vr0, &vr1);
+			      &vr0, &vr1, wi::shwi (-1, TYPE_PRECISION(TREE_TYPE (expr))),
+			      wi::shwi (-1, TYPE_PRECISION(TREE_TYPE (expr))));
     }
   else if (UNARY_CLASS_P (expr))
     {
@@ -4662,4 +4675,4 @@ determine_value_range (tree expr, wide_int *min, wide_int *max)
     }
 
   return VR_VARYING;
-}
+}
\ No newline at end of file
diff --git a/gcc/tree-vrp.h b/gcc/tree-vrp.h
index 989d8430c98..06506ee7af3 100644
--- a/gcc/tree-vrp.h
+++ b/gcc/tree-vrp.h
@@ -51,7 +51,8 @@ extern int operand_less_p (tree, tree);
 void range_fold_unary_expr (value_range *, enum tree_code, tree type,
 			    const value_range *, tree op0_type);
 void range_fold_binary_expr (value_range *, enum tree_code, tree type,
-			     const value_range *, const value_range *);
+			     const value_range *, const value_range *, 
+			     const wide_int&, const wide_int&);
 
 extern enum value_range_kind intersect_range_with_nonzero_bits
   (enum value_range_kind, wide_int *, wide_int *, const wide_int &, signop);
diff --git a/gcc/vr-values.c b/gcc/vr-values.c
index 08b237b2632..f918264e460 100644
--- a/gcc/vr-values.c
+++ b/gcc/vr-values.c
@@ -825,15 +825,22 @@ vr_values::extract_range_from_binary_expr (value_range_equiv *vr,
   /* Get value ranges for each operand.  For constant operands, create
      a new value range with the operand to simplify processing.  */
   value_range vr0, vr1;
+  wide_int nonzero0 = wi::shwi (-1, TYPE_PRECISION(expr_type)), nonzero1 = wi::shwi (-1, TYPE_PRECISION(expr_type));
   if (TREE_CODE (op0) == SSA_NAME)
-    vr0 = *(get_value_range (op0));
+    {
+      vr0 = *(get_value_range (op0));
+      nonzero0 = get_nonzero_bits(op0);
+    }
   else if (is_gimple_min_invariant (op0))
     vr0.set (op0);
   else
     vr0.set_varying (TREE_TYPE (op0));
 
   if (TREE_CODE (op1) == SSA_NAME)
-    vr1 = *(get_value_range (op1));
+    {
+      vr1 = *(get_value_range (op1));
+      nonzero1 = get_nonzero_bits(op1);
+    }
   else if (is_gimple_min_invariant (op1))
     vr1.set (op1);
   else
@@ -850,7 +857,7 @@ vr_values::extract_range_from_binary_expr (value_range_equiv *vr,
 	vr1 = value_range (vrp_val_min (expr_type), vrp_val_max (expr_type));
     }
 
-  range_fold_binary_expr (vr, code, expr_type, &vr0, &vr1);
+  range_fold_binary_expr (vr, code, expr_type, &vr0, &vr1, nonzero0, nonzero1);
 
   /* Set value_range for n in following sequence:
      def = __builtin_memchr (arg, 0, sz)
@@ -911,7 +918,7 @@ vr_values::extract_range_from_binary_expr (value_range_equiv *vr,
       else
 	n_vr1.set (op1, op1);
 
-      range_fold_binary_expr (vr, code, expr_type, &vr0, &n_vr1);
+      range_fold_binary_expr (vr, code, expr_type, &vr0, &n_vr1, nonzero0, nonzero1);
     }
 
   if (vr->varying_p ()
@@ -935,7 +942,7 @@ vr_values::extract_range_from_binary_expr (value_range_equiv *vr,
       else
 	n_vr0.set (op0);
 
-      range_fold_binary_expr (vr, code, expr_type, &n_vr0, &vr1);
+      range_fold_binary_expr (vr, code, expr_type, &n_vr0, &vr1, nonzero0, nonzero1);
     }
 
   /* If we didn't derive a range for MINUS_EXPR, and
@@ -1318,7 +1325,8 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
 						     type, op0);
 		      extract_range_from_unary_expr (&vr1, NOP_EXPR,
 						     type, op1);
-		      range_fold_binary_expr (vr, subcode, type, &vr0, &vr1);
+		      range_fold_binary_expr (vr, subcode, type, &vr0, &vr1, 
+								 wi::shwi (-1, TYPE_PRECISION(type)), wi::shwi (-1, TYPE_PRECISION(type)));
 		      flag_wrapv = saved_flag_wrapv;
 		    }
 		  return;
@@ -1723,7 +1731,9 @@ bounds_of_var_in_loop (tree *min, tree *max, range_query *query,
 	      tree tem = wide_int_to_tree (TREE_TYPE (init), wtmp);
 	      vr1.set (tem, tem);
 	      range_fold_binary_expr (&maxvr, PLUS_EXPR,
-				      TREE_TYPE (init), &vr0, &vr1);
+				      TREE_TYPE (init), &vr0, &vr1, 
+				      wi::shwi (-1, TYPE_PRECISION(TREE_TYPE(init))),
+				      wi::shwi (-1, TYPE_PRECISION(TREE_TYPE(init))));
 
 	      /* Likewise if the addition did.  */
 	      if (maxvr.kind () == VR_RANGE)
@@ -4250,4 +4260,4 @@ vr_values::swap_vr_value (tree var, value_range_equiv *vr)
     return NULL;
   std::swap (vr_value[SSA_NAME_VERSION (var)], vr);
   return vr;
-}
+}
\ No newline at end of file

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] Propagate get_nonzero_bits information in division [PR77980]
  2021-07-27  0:45 [PATCH] Propagate get_nonzero_bits information in division [PR77980] Victor Tong
@ 2021-08-23  3:11 ` Jeff Law
  2021-08-30 20:33   ` [EXTERNAL] " Victor Tong
  0 siblings, 1 reply; 3+ messages in thread
From: Jeff Law @ 2021-08-23  3:11 UTC (permalink / raw)
  To: Victor Tong, gcc-patches, pinskia



On 7/26/2021 6:45 PM, Victor Tong via Gcc-patches wrote:
> This change enables the "t1 != 0" check to be optimized away in this code:
>
> int x1 = 0;
> unsigned int x2 = 1;
>
> int main ()
> {
>      int t1 = x1*(1/(x2+x2));
>      if (t1 != 0) __builtin_abort();
>      return 0;
> }
>
> The change utilizes the VRP framework to propagate the get_nonzero_bits information from the "x2+x2" expression to the "1/(x2+x2)" division expression. Specifically, the framework knows that the least significant bit of the "x2+x2" expression must be zero.
>
> The get_nonzero_bits information of the left hand side and right hand side of expressions needed to be passed down to operator_div::wi_fold() in the VRP framework. The majority of this change involves adding two additional parameters to propagate this information. There are future opportunities to use the non zero bit information to perform better optimizations in other types of expressions.
>
> The changes were tested against x86_64-pc-linux-gnu and all tests in "make -k check" passed.
>
> The original approach was to implement a match.pd pattern to support this but the pattern wasn't being triggered. More context is available in: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77980
So you're going to want to sync with Aldy & Andrew as they're the 
experts on the Ranger design & implementation.  This hits the Ranger API 
as well as design questions about how best to tie in the nonzero_bits 
capabilities.

You might also want to reach out to Roger Sayle.  He's been poking 
around in a closely related area, though more focused on the bitwise 
conditional constant propagation rather than Ranger/VRP.  In fact, I 
just acked a patch of his that looks closely related.

https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577888.html

Jeff

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [EXTERNAL] Re: [PATCH] Propagate get_nonzero_bits information in division [PR77980]
  2021-08-23  3:11 ` Jeff Law
@ 2021-08-30 20:33   ` Victor Tong
  0 siblings, 0 replies; 3+ messages in thread
From: Victor Tong @ 2021-08-30 20:33 UTC (permalink / raw)
  To: Jeff Law, gcc-patches, pinskia

Thanks Jeff. I've reached out to Roger to see if the fix would be better suited in CCP. If that isn't the right spot, I'll reach out to Aldy and Andrew about getting the fix in VRP/Ranger.


From: Jeff Law <jeffreyalaw@gmail.com>
Sent: Sunday, August 22, 2021 8:11 PM
To: Victor Tong <vitong@microsoft.com>; gcc-patches@gcc.gnu.org <gcc-patches@gcc.gnu.org>; pinskia@gcc.gnu.org <pinskia@gcc.gnu.org>
Subject: [EXTERNAL] Re: [PATCH] Propagate get_nonzero_bits information in division [PR77980] 
 


On 7/26/2021 6:45 PM, Victor Tong via Gcc-patches wrote:
> This change enables the "t1 != 0" check to be optimized away in this code:
>
> int x1 = 0;
> unsigned int x2 = 1;
>
> int main ()
> {
>      int t1 = x1*(1/(x2+x2));
>      if (t1 != 0) __builtin_abort();
>      return 0;
> }
>
> The change utilizes the VRP framework to propagate the get_nonzero_bits information from the "x2+x2" expression to the "1/(x2+x2)" division expression. Specifically, the framework knows that the least significant bit of the "x2+x2" expression must be zero.
>
> The get_nonzero_bits information of the left hand side and right hand side of expressions needed to be passed down to operator_div::wi_fold() in the VRP framework. The majority of this change involves adding two additional parameters to propagate this information. There are future opportunities to use the non zero bit information to perform better optimizations in other types of expressions.
>
> The changes were tested against x86_64-pc-linux-gnu and all tests in "make -k check" passed.
>
> The original approach was to implement a match.pd pattern to support this but the pattern wasn't being triggered. More context is available in: https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgcc.gnu.org%2Fbugzilla%2Fshow_bug.cgi%3Fid%3D77980&amp;data=04%7C01%7Cvitong%40microsoft.com%7C08dcd27e5482418d559708d965e3a826%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637652850726534114%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=S1WWWvuXwEIA20pmv%2Fn8kk6xlwv9VwsS%2BTl0jqEHf4w%3D&amp;reserved=0
So you're going to want to sync with Aldy & Andrew as they're the 
experts on the Ranger design & implementation.  This hits the Ranger API 
as well as design questions about how best to tie in the nonzero_bits 
capabilities.

You might also want to reach out to Roger Sayle.  He's been poking 
around in a closely related area, though more focused on the bitwise 
conditional constant propagation rather than Ranger/VRP.  In fact, I 
just acked a patch of his that looks closely related.

https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgcc.gnu.org%2Fpipermail%2Fgcc-patches%2F2021-August%2F577888.html&amp;data=04%7C01%7Cvitong%40microsoft.com%7C08dcd27e5482418d559708d965e3a826%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637652850726534114%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=gn1L1exTW5TjdV8CDBDqB0Z5a4V7EP2tBM2uTF2ihck%3D&amp;reserved=0

Jeff

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-08-30 20:33 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-27  0:45 [PATCH] Propagate get_nonzero_bits information in division [PR77980] Victor Tong
2021-08-23  3:11 ` Jeff Law
2021-08-30 20:33   ` [EXTERNAL] " Victor Tong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).