Commit 4db4954f by Richard Sandiford Committed by Richard Sandiford

re PR middle-end/64182 (wide-int rounding division is broken)

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
parent 8f596ff5
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> 2014-12-12 Marek Polacek <polacek@redhat.com>
PR middle-end/64274 PR middle-end/64274
...@@ -569,24 +569,23 @@ div_and_round_double (unsigned code, int uns, ...@@ -569,24 +569,23 @@ div_and_round_double (unsigned code, int uns,
{ {
unsigned HOST_WIDE_INT labs_rem = *lrem; unsigned HOST_WIDE_INT labs_rem = *lrem;
HOST_WIDE_INT habs_rem = *hrem; HOST_WIDE_INT habs_rem = *hrem;
unsigned HOST_WIDE_INT labs_den = lden, ltwice; unsigned HOST_WIDE_INT labs_den = lden, lnegabs_rem, ldiff;
HOST_WIDE_INT habs_den = hden, htwice; HOST_WIDE_INT habs_den = hden, hnegabs_rem, hdiff;
/* Get absolute values. */ /* Get absolute values. */
if (*hrem < 0) if (!uns && *hrem < 0)
neg_double (*lrem, *hrem, &labs_rem, &habs_rem); neg_double (*lrem, *hrem, &labs_rem, &habs_rem);
if (hden < 0) if (!uns && hden < 0)
neg_double (lden, hden, &labs_den, &habs_den); neg_double (lden, hden, &labs_den, &habs_den);
/* If (2 * abs (lrem) >= abs (lden)), adjust the quotient. */ /* If abs(rem) >= abs(den) - abs(rem), adjust the quotient. */
mul_double ((HOST_WIDE_INT) 2, (HOST_WIDE_INT) 0, neg_double (labs_rem, habs_rem, &lnegabs_rem, &hnegabs_rem);
labs_rem, habs_rem, &ltwice, &htwice); add_double (labs_den, habs_den, lnegabs_rem, hnegabs_rem,
&ldiff, &hdiff);
if (((unsigned HOST_WIDE_INT) habs_den if (((unsigned HOST_WIDE_INT) habs_rem
< (unsigned HOST_WIDE_INT) htwice) > (unsigned HOST_WIDE_INT) hdiff)
|| (((unsigned HOST_WIDE_INT) habs_den || (habs_rem == hdiff && labs_rem >= ldiff))
== (unsigned HOST_WIDE_INT) htwice)
&& (labs_den <= ltwice)))
{ {
if (quo_neg) if (quo_neg)
/* quo = quo - 1; */ /* quo = quo - 1; */
......
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> 2014-12-12 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/64269 PR tree-optimization/64269
......
...@@ -62,6 +62,7 @@ set plugin_test_list [list \ ...@@ -62,6 +62,7 @@ set plugin_test_list [list \
{ sreal_plugin.c sreal-test-1.c } \ { sreal_plugin.c sreal-test-1.c } \
{ start_unit_plugin.c start_unit-test-1.c } \ { start_unit_plugin.c start_unit-test-1.c } \
{ finish_unit_plugin.c finish_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 { foreach plugin_test $plugin_test_list {
......
/* Test that pass is inserted and invoked once. */
/* { dg-do compile } */
/* { dg-options "-O" } */
int
main (int argc, char **argv)
{
return 0;
}
#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;
}
-- { 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;
...@@ -2616,8 +2616,8 @@ wi::div_round (const T1 &x, const T2 &y, signop sgn, bool *overflow) ...@@ -2616,8 +2616,8 @@ wi::div_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
{ {
if (sgn == SIGNED) if (sgn == SIGNED)
{ {
if (wi::ges_p (wi::abs (remainder), WI_BINARY_RESULT (T1, T2) abs_remainder = wi::abs (remainder);
wi::lrshift (wi::abs (y), 1))) if (wi::geu_p (abs_remainder, wi::sub (wi::abs (y), abs_remainder)))
{ {
if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn)) if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn))
return quotient - 1; return quotient - 1;
...@@ -2627,7 +2627,7 @@ wi::div_round (const T1 &x, const T2 &y, signop sgn, bool *overflow) ...@@ -2627,7 +2627,7 @@ wi::div_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
} }
else else
{ {
if (wi::geu_p (remainder, wi::lrshift (y, 1))) if (wi::geu_p (remainder, wi::sub (y, remainder)))
return quotient + 1; return quotient + 1;
} }
} }
...@@ -2784,8 +2784,8 @@ wi::mod_round (const T1 &x, const T2 &y, signop sgn, bool *overflow) ...@@ -2784,8 +2784,8 @@ wi::mod_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
{ {
if (sgn == SIGNED) if (sgn == SIGNED)
{ {
if (wi::ges_p (wi::abs (remainder), WI_BINARY_RESULT (T1, T2) abs_remainder = wi::abs (remainder);
wi::lrshift (wi::abs (y), 1))) if (wi::geu_p (abs_remainder, wi::sub (wi::abs (y), abs_remainder)))
{ {
if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn)) if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn))
return remainder + y; return remainder + y;
...@@ -2795,7 +2795,7 @@ wi::mod_round (const T1 &x, const T2 &y, signop sgn, bool *overflow) ...@@ -2795,7 +2795,7 @@ wi::mod_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
} }
else else
{ {
if (wi::geu_p (remainder, wi::lrshift (y, 1))) if (wi::geu_p (remainder, wi::sub (y, remainder)))
return remainder - y; return remainder - y;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment