public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jakub Jelinek <jakub@redhat.com>
To: Aldy Hernandez <aldyh@redhat.com>
Cc: gcc-patches@gcc.gnu.org, Andrew MacLeod <amacleod@redhat.com>
Subject: [PATCH] range-op-float: Improve binary reverse operations
Date: Mon, 5 Dec 2022 16:33:53 +0100	[thread overview]
Message-ID: <Y44PYa8n3RkNIfCn@tucnak> (raw)
In-Reply-To: <32b96813-616d-ba73-811f-8a36e70f9ecd@redhat.com>

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

Hi!

On Mon, Dec 05, 2022 at 02:29:36PM +0100, Aldy Hernandez wrote:
> > So like this for multiplication op1/2_range if it passes bootstrap/regtest?
> > For division I'll need to go to a drawing board...
> 
> Sure, looks good to me.

Ulrich just filed PR107972, so in the light of that PR the following patch
attempts to do that differently.

Is this also ok if it passes bootstrap/regtest?

As for testcase, I've tried both attached testcases, but unfortunately it
seems that in neither of the cases we actually figure out that res range
is finite (or for last function non-zero ordered).  So there is further
work needed on that.

2022-12-05  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/107972
	* range-op-float.cc (frange_drop_infs): New function.
	(float_binary_op_range_finish): Add OP argument.  If OP is not
	RDIV_EXPR and lhs is finite, r must be finite too.
	(foperator_plus::op1_range, foperator_minus::op1_range,
	foperator_minus::op2_range, foperator_mult::op1_range): Pass
	operation code to float_binary_op_range_finish.
	(foperator_div::op1_range): Pass RDIV_EXPR to
	float_binary_op_range_finish.  If lhs is finite, r must be finite
	too.
	(foperator_div::op2_range): Pass RDIV_EXPR to
	float_binary_op_range_finish.  If lhs is not NAN nor zero, r must
	be finite.

--- gcc/range-op-float.cc.jj	2022-12-05 11:17:34.900573272 +0100
+++ gcc/range-op-float.cc	2022-12-05 16:13:54.414845672 +0100
@@ -330,6 +330,18 @@ frange_drop_ninf (frange &r, tree type)
   r.intersect (tmp);
 }
 
+// Crop R to [MIN, MAX] where MAX is the maximum representable number
+// for TYPE and MIN the minimum representable number for TYPE.
+
+static inline void
+frange_drop_infs (frange &r, tree type)
+{
+  REAL_VALUE_TYPE max = real_max_representable (type);
+  REAL_VALUE_TYPE min = real_min_representable (type);
+  frange tmp (type, min, max);
+  r.intersect (tmp);
+}
+
 // If zero is in R, make sure both -0.0 and +0.0 are in the range.
 
 static inline void
@@ -1883,7 +1895,7 @@ foperator_unordered_equal::op1_range (fr
 
 static bool
 float_binary_op_range_finish (bool ret, frange &r, tree type,
-			      const frange &lhs)
+			      const frange &lhs, enum tree_code op)
 {
   if (!ret)
     return false;
@@ -1904,7 +1916,16 @@ float_binary_op_range_finish (bool ret,
   // If lhs isn't NAN, then neither operand could be NAN,
   // even if the reverse operation does introduce a maybe_nan.
   if (!lhs.maybe_isnan ())
-    r.clear_nan ();
+    {
+      r.clear_nan ();
+      if (op != RDIV_EXPR
+	  && !real_isinf (&lhs.lower_bound ())
+	  && !real_isinf (&lhs.upper_bound ()))
+	// For reverse + or - or *, if result is finite, then r must
+	// be finite too, as X + INF or X - INF or X * INF is always
+	// +-INF or NAN.  For division handle it in the caller.
+	frange_drop_infs (r, type);
+    }
   // If lhs is a maybe or known NAN, the operand could be
   // NAN.
   else
@@ -2020,7 +2041,7 @@ public:
     if (!minus)
       return false;
     return float_binary_op_range_finish (minus.fold_range (r, type, lhs, op2),
-					 r, type, lhs);
+					 r, type, lhs, PLUS_EXPR);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2067,7 +2088,7 @@ public:
       return false;
     return float_binary_op_range_finish (fop_plus.fold_range (r, type, lhs,
 							      op2),
-					 r, type, lhs);
+					 r, type, lhs, MINUS_EXPR);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2077,7 +2098,7 @@ public:
     if (lhs.undefined_p ())
       return false;
     return float_binary_op_range_finish (fold_range (r, type, op1, lhs),
-					 r, type, lhs);
+					 r, type, lhs, MINUS_EXPR);
   }
 private:
   void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,
@@ -2166,7 +2187,7 @@ public:
     // or if lhs must be zero and op2 doesn't include zero, it would be
     // UNDEFINED, while rdiv.fold_range computes a zero or singleton INF
     // range.  Those are supersets of UNDEFINED, so let's keep that way.
-    return float_binary_op_range_finish (ret, r, type, lhs);
+    return float_binary_op_range_finish (ret, r, type, lhs, MULT_EXPR);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2313,7 +2334,12 @@ public:
 	zero_to_inf_range (lb, ub, signbit_known);
 	r.set (type, lb, ub);
       }
-    return float_binary_op_range_finish (ret, r, type, lhs);
+    // If lhs must be finite (can't be +-INF nor NAN), then op1 must be
+    // finite too - +-INF / anything is either +-INF or NAN (NAN if op2 is
+    // +-INF or NAN).
+    if (!real_isinf (&lhs_lb) && !real_isinf (&lhs_ub) && !lhs.maybe_isnan ())
+      frange_drop_infs (r, type);
+    return float_binary_op_range_finish (ret, r, type, lhs, RDIV_EXPR);
   }
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
@@ -2341,7 +2367,11 @@ public:
 	zero_to_inf_range (lb, ub, signbit_known);
 	r.set (type, lb, ub);
       }
-    return float_binary_op_range_finish (ret, r, type, lhs);
+    // If lhs can't be zero nor NAN, then op2 must be finite - anything / +-INF
+    // is either +-0 or NAN (NAN if op1 is +-INF or NAN).
+    if (!contains_zero_p (lhs_lb, lhs_ub) && !lhs.maybe_isnan ())
+      frange_drop_infs (r, type);
+    return float_binary_op_range_finish (ret, r, type, lhs, RDIV_EXPR);
   }
 private:
   void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,


	Jakub

[-- Attachment #2: pr107972.c --]
[-- Type: text/plain, Size: 1075 bytes --]

double
foo (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a + b;
  if (!__builtin_isfinite (res))
    __builtin_unreachable ();
  return res;
}

double
bar (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a - b;
  if (!__builtin_isfinite (res))
    __builtin_unreachable ();
  return res;
}

double
baz (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a * b;
  if (!__builtin_isfinite (res))
    __builtin_unreachable ();
  return res;
}

double
qux (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  double res = a / b;
  if (!__builtin_isfinite (res))
    __builtin_unreachable ();
  return res;
}

double
quux (double a, double b)
{
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a  / b;
  if (__builtin_isnan (res) || res == 0.0)
    __builtin_unreachable ();
  return res;
}

[-- Attachment #3: pr107972-2.c --]
[-- Type: text/plain, Size: 1032 bytes --]

double
foo (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a + b;
  __attribute__((assume (__builtin_isfinite (res))));
  return res;
}

double
bar (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a - b;
  __attribute__((assume (__builtin_isfinite (res))));
  return res;
}

double
baz (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a * b;
  __attribute__((assume (__builtin_isfinite (res))));
  return res;
}

double
qux (double a, double b)
{
  if (!__builtin_isfinite (a))
    return 42.0;
  double res = a / b;
  __attribute__((assume (__builtin_isfinite (res))));
  return res;
}

double
quux (double a, double b)
{
  if (!__builtin_isfinite (b))
    return 42.0;
  double res = a  / b;
  __attribute__((assume (!__builtin_isnan (res) && res != 0.0)));
  return res;
}

  reply	other threads:[~2022-12-05 15:34 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-29  9:43 [PATCH] range-op-float: Fix up multiplication and division reverse operation [PR107879] Jakub Jelinek
2022-12-05  9:20 ` Aldy Hernandez
2022-12-05  9:37   ` Jakub Jelinek
2022-12-05  9:54     ` Aldy Hernandez
2022-12-05 11:59       ` [PATCH] range-op-float: Improve multiplication reverse operation Jakub Jelinek
2022-12-05 13:29         ` Aldy Hernandez
2022-12-05 15:33           ` Jakub Jelinek [this message]
2022-12-05 20:43             ` [PATCH] range-op-float: Improve binary reverse operations Andrew MacLeod
2022-12-05 20:54               ` Jakub Jelinek
2022-12-05 22:38                 ` Jakub Jelinek
2022-12-05 23:49                   ` Andrew MacLeod

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Y44PYa8n3RkNIfCn@tucnak \
    --to=jakub@redhat.com \
    --cc=aldyh@redhat.com \
    --cc=amacleod@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).