re PR middle-end/64182 (wide-int rounding division is broken)
authorRichard Sandiford <richard.sandiford@arm.com>
Fri, 12 Dec 2014 15:46:57 +0000 (15:46 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Fri, 12 Dec 2014 15:46:57 +0000 (15:46 +0000)
gcc/
PR middle-end/64182
* wide-int.h (wi::div_round, wi::mod_round): Fix rounding of tied
cases.
* double-int.c (div_and_round_double): Fix handling of unsigned
cases.  Use same rounding approach as wide-int.h.

gcc/testsuite/
2014-xx-xx  Richard Sandiford  <richard.sandiford@arm.com>
    Joseph Myers  <joseph@codesourcery.com>

PR middle-end/64182
* gcc.dg/plugin/wide-int-test-1.c,
gcc.dg/plugin/wide-int_plugin.c: New test.
* gcc.dg/plugin/plugin.exp: Register it.
* gnat.dg/round_div.adb: New test.

Co-Authored-By: Joseph Myers <joseph@codesourcery.com>
From-SVN: r218678

gcc/ChangeLog
gcc/double-int.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/plugin/plugin.exp
gcc/testsuite/gcc.dg/plugin/wide-int-test-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/wide-int_plugin.c [new file with mode: 0644]
gcc/testsuite/gnat.dg/round_div.adb [new file with mode: 0644]
gcc/wide-int.h

index 69797f6b501adc3aed1506be613903d49c0fabc2..bf9571bed9820f84fd0eda2f199e6d5bad2cf1cc 100644 (file)
@@ -1,3 +1,11 @@
+2014-12-12  Richard Sandiford  <richard.sandiford@arm.com>
+
+       PR middle-end/64182
+       * wide-int.h (wi::div_round, wi::mod_round): Fix rounding of tied
+       cases.
+       * double-int.c (div_and_round_double): Fix handling of unsigned
+       cases.  Use same rounding approach as wide-int.h.
+
 2014-12-12  Marek Polacek  <polacek@redhat.com>
 
        PR middle-end/64274
index f6e340bebfe6f238206fd5005037a467573b245d..9dc501c8edf6e9f61a4a7737f7f659097acd92e7 100644 (file)
@@ -569,24 +569,23 @@ div_and_round_double (unsigned code, int uns,
       {
        unsigned HOST_WIDE_INT labs_rem = *lrem;
        HOST_WIDE_INT habs_rem = *hrem;
-       unsigned HOST_WIDE_INT labs_den = lden, ltwice;
-       HOST_WIDE_INT habs_den = hden, htwice;
+       unsigned HOST_WIDE_INT labs_den = lden, lnegabs_rem, ldiff;
+       HOST_WIDE_INT habs_den = hden, hnegabs_rem, hdiff;
 
        /* Get absolute values.  */
-       if (*hrem < 0)
+       if (!uns && *hrem < 0)
          neg_double (*lrem, *hrem, &labs_rem, &habs_rem);
-       if (hden < 0)
+       if (!uns && hden < 0)
          neg_double (lden, hden, &labs_den, &habs_den);
 
-       /* If (2 * abs (lrem) >= abs (lden)), adjust the quotient.  */
-       mul_double ((HOST_WIDE_INT) 2, (HOST_WIDE_INT) 0,
-                   labs_rem, habs_rem, &ltwice, &htwice);
+       /* If abs(rem) >= abs(den) - abs(rem), adjust the quotient.  */
+       neg_double (labs_rem, habs_rem, &lnegabs_rem, &hnegabs_rem);
+       add_double (labs_den, habs_den, lnegabs_rem, hnegabs_rem,
+                   &ldiff, &hdiff);
 
-       if (((unsigned HOST_WIDE_INT) habs_den
-            < (unsigned HOST_WIDE_INT) htwice)
-           || (((unsigned HOST_WIDE_INT) habs_den
-                == (unsigned HOST_WIDE_INT) htwice)
-               && (labs_den <= ltwice)))
+       if (((unsigned HOST_WIDE_INT) habs_rem
+            > (unsigned HOST_WIDE_INT) hdiff)
+           || (habs_rem == hdiff && labs_rem >= ldiff))
          {
            if (quo_neg)
              /* quo = quo - 1;  */
index 9624e3908eb7a0f771e70bf562c4452743c8812e..2c7b6159adb0d14a94c14941bc63ad1bcc2e87e3 100644 (file)
@@ -1,3 +1,12 @@
+2014-12-12  Richard Sandiford  <richard.sandiford@arm.com>
+           Joseph Myers  <joseph@codesourcery.com>
+
+       PR middle-end/64182
+       * gcc.dg/plugin/wide-int-test-1.c,
+       gcc.dg/plugin/wide-int_plugin.c: New test.
+       * gcc.dg/plugin/plugin.exp: Register it.
+       * gnat.dg/round_div.adb: New test.
+
 2014-12-12  Jakub Jelinek  <jakub@redhat.com>
 
        PR tree-optimization/64269
index c12b3dac4e3d166f6bd5898c608c0fb3a300917d..6c1d27f5a10bcea02977f193a5a24bc3f047a119 100644 (file)
@@ -62,6 +62,7 @@ set plugin_test_list [list \
     { sreal_plugin.c sreal-test-1.c } \
     { start_unit_plugin.c start_unit-test-1.c } \
     { finish_unit_plugin.c finish_unit-test-1.c } \
+    { wide-int_plugin.c wide-int-test-1.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/gcc.dg/plugin/wide-int-test-1.c b/gcc/testsuite/gcc.dg/plugin/wide-int-test-1.c
new file mode 100644 (file)
index 0000000..2de35d8
--- /dev/null
@@ -0,0 +1,9 @@
+/* Test that pass is inserted and invoked once. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+int
+main (int argc, char **argv)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/wide-int_plugin.c b/gcc/testsuite/gcc.dg/plugin/wide-int_plugin.c
new file mode 100644 (file)
index 0000000..b20ee28
--- /dev/null
@@ -0,0 +1,46 @@
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+
+int plugin_is_GPL_compatible;
+
+static void
+test_double_int_round_udiv (void)
+{
+  double_int dmin = { 0, HOST_WIDE_INT_MIN };
+  double_int dmax = { -1, HOST_WIDE_INT_MAX };
+  double_int dnegone = { -1, -1 };
+  double_int mod, div;
+  div = dmin.udivmod (dnegone, ROUND_DIV_EXPR, &mod);
+  if (div.low != 1 || div.high != 0
+      || mod.low != 1 || mod.high != HOST_WIDE_INT_MIN)
+    abort ();
+  div = dmax.udivmod (dnegone, ROUND_DIV_EXPR, &mod);
+  if (div.low != 0 || div.high != 0
+      || mod.low != dmax.low || mod.high != dmax.high)
+    abort ();
+}
+
+static void
+test_wide_int_round_sdiv (void)
+{
+  if (wi::ne_p (wi::div_round (2, 3, SIGNED), 1))
+    abort ();
+  if (wi::ne_p (wi::div_round (1, 3, SIGNED), 0))
+    abort ();
+  if (wi::ne_p (wi::mod_round (2, 3, SIGNED), -1))
+    abort ();
+  if (wi::ne_p (wi::mod_round (1, 3, SIGNED), 1))
+    abort ();
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+            struct plugin_gcc_version *version)
+{
+  test_double_int_round_udiv ();
+  test_wide_int_round_sdiv ();
+  return 0;
+}
diff --git a/gcc/testsuite/gnat.dg/round_div.adb b/gcc/testsuite/gnat.dg/round_div.adb
new file mode 100644 (file)
index 0000000..0a79291
--- /dev/null
@@ -0,0 +1,17 @@
+-- { dg-do run }
+-- { dg-options "-O3" }
+procedure Round_Div is
+   type Fixed is delta 1.0 range -2147483648.0 .. 2147483647.0;
+   A : Fixed := 1.0;
+   B : Fixed := 3.0;
+   C : Integer;
+   function Divide (X, Y : Fixed) return Integer is
+   begin
+      return Integer (X / Y);
+   end;
+begin
+   C := Divide (A, B);
+   if C /= 0 then
+      raise Program_Error;
+   end if;
+end Round_Div;
index f5735d8090491ee9f21c86b4d7ac4bba2d790421..acfdb40bc82eec70a2ca965b96196f837108467c 100644 (file)
@@ -2616,8 +2616,8 @@ wi::div_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
     {
       if (sgn == SIGNED)
        {
-         if (wi::ges_p (wi::abs (remainder),
-                        wi::lrshift (wi::abs (y), 1)))
+         WI_BINARY_RESULT (T1, T2) abs_remainder = wi::abs (remainder);
+         if (wi::geu_p (abs_remainder, wi::sub (wi::abs (y), abs_remainder)))
            {
              if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn))
                return quotient - 1;
@@ -2627,7 +2627,7 @@ wi::div_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
        }
       else
        {
-         if (wi::geu_p (remainder, wi::lrshift (y, 1)))
+         if (wi::geu_p (remainder, wi::sub (y, remainder)))
            return quotient + 1;
        }
     }
@@ -2784,8 +2784,8 @@ wi::mod_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
     {
       if (sgn == SIGNED)
        {
-         if (wi::ges_p (wi::abs (remainder),
-                        wi::lrshift (wi::abs (y), 1)))
+         WI_BINARY_RESULT (T1, T2) abs_remainder = wi::abs (remainder);
+         if (wi::geu_p (abs_remainder, wi::sub (wi::abs (y), abs_remainder)))
            {
              if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn))
                return remainder + y;
@@ -2795,7 +2795,7 @@ wi::mod_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
        }
       else
        {
-         if (wi::geu_p (remainder, wi::lrshift (y, 1)))
+         if (wi::geu_p (remainder, wi::sub (y, remainder)))
            return remainder - y;
        }
     }