Hello,
here is a patch handling multiplication of wrapping integer types in VRP.
It passed bootstrap+testsuite limited to c,c++ and without the testcase,
so I'll retest it better during the night. For some reason the test
libgomp.graphite/force-parallel-6.c which used to fail passed with the
patch.
gcc/
2012-08-03 Marc Glisse <[email protected]>
PR tree-optimization/30318
* double-int.c (mul_double_wide_with_sign): New function.
(mul_double_with_sign): Call the new function.
* double-int.h (mul_double_wide_with_sign): Declare the new function.
* tree-vrp.c (extract_range_from_binary_expr_1) [MULT_EXPR]:
Handle integer types that wrap on overflow.
(quad_int_cmp): New helper function.
(quad_int_pair_sort): Likewise.
gcc/testsuite/
2012-08-03 Marc Glisse <[email protected]>
PR tree-optimization/30318
* gcc.dg/tree-ssa/vrp77.c: New testcase.
--
Marc GlisseIndex: gcc/tree-vrp.c
===================================================================
--- gcc/tree-vrp.c (revision 190103)
+++ gcc/tree-vrp.c (working copy)
@@ -2181,20 +2181,42 @@ extract_range_from_multiplicative_op_1 (
{
/* If the new range has its limits swapped around (MIN > MAX),
then the operation caused one of them to wrap around, mark
the new range VARYING. */
set_value_range_to_varying (vr);
}
else
set_value_range (vr, type, min, max, NULL);
}
+/* Some quadruple precision helpers. */
+static int
+quad_int_cmp (double_int l0, double_int h0,
+ double_int l1, double_int h1, bool uns)
+{
+ int c = double_int_cmp (h0, h1, uns);
+ if (c != 0) return c;
+ return double_int_ucmp (l0, l1);
+}
+
+static void
+quad_int_pair_sort (double_int *l0, double_int *h0,
+ double_int *l1, double_int *h1, bool uns)
+{
+ if (quad_int_cmp (*l0, *h0, *l1, *h1, uns) > 0)
+ {
+ double_int tmp;
+ tmp = *l0; *l0 = *l1; *l1 = tmp;
+ tmp = *h0; *h0 = *h1; *h1 = tmp;
+ }
+}
+
/* Extract range information from a binary operation CODE based on
the ranges of each of its operands, *VR0 and *VR1 with resulting
type EXPR_TYPE. The resulting range is stored in *VR. */
static void
extract_range_from_binary_expr_1 (value_range_t *vr,
enum tree_code code, tree expr_type,
value_range_t *vr0_, value_range_t *vr1_)
{
value_range_t vr0 = *vr0_, vr1 = *vr1_;
@@ -2562,20 +2584,137 @@ extract_range_from_binary_expr_1 (value_
{
/* For operations that make the resulting range directly
proportional to the original ranges, apply the operation to
the same end of each range. */
min = vrp_int_const_binop (code, vr0.min, vr1.min);
max = vrp_int_const_binop (code, vr0.max, vr1.max);
}
}
else if (code == MULT_EXPR)
{
+ /* Fancy code so that with unsigned, [-3,-1]*[-3,-1] does not
+ drop to varying. */
+ if (range_int_cst_p (&vr0)
+ && range_int_cst_p (&vr1)
+ && TYPE_OVERFLOW_WRAPS (expr_type))
+ {
+ double_int min0, max0, min1, max1, sizem1, size;
+ double_int prod0l, prod0h, prod1l, prod1h,
+ prod2l, prod2h, prod3l, prod3h;
+ bool uns0, uns1, uns;
+
+ sizem1 = double_int_max_value (TYPE_PRECISION (expr_type), true);
+ size = double_int_add (sizem1, double_int_one);
+
+ min0 = tree_to_double_int (vr0.min);
+ max0 = tree_to_double_int (vr0.max);
+ min1 = tree_to_double_int (vr1.min);
+ max1 = tree_to_double_int (vr1.max);
+
+ uns0 = TYPE_UNSIGNED (expr_type);
+ uns1 = uns0;
+
+ /* Canonicalize the intervals. */
+ if (TYPE_UNSIGNED (expr_type))
+ {
+ double_int min2 = double_int_sub (size, min0);
+ if (double_int_cmp (min2, max0, true) < 0)
+ {
+ min0 = double_int_neg (min2);
+ max0 = double_int_sub (max0, size);
+ uns0 = false;
+ }
+
+ min2 = double_int_sub (size, min1);
+ if (double_int_cmp (min2, max1, true) < 0)
+ {
+ min1 = double_int_neg (min2);
+ max1 = double_int_sub (max1, size);
+ uns1 = false;
+ }
+ }
+ uns = uns0 & uns1;
+
+ mul_double_wide_with_sign (min0.low, min0.high,
+ min1.low, min1.high,
+ &prod0l.low, &prod0l.high,
+ &prod0h.low, &prod0h.high, true);
+ if (!uns0 && double_int_negative_p (min0))
+ prod0h = double_int_sub (prod0h, min1);
+ if (!uns1 && double_int_negative_p (min1))
+ prod0h = double_int_sub (prod0h, min0);
+
+ mul_double_wide_with_sign (min0.low, min0.high,
+ max1.low, max1.high,
+ &prod1l.low, &prod1l.high,
+ &prod1h.low, &prod1h.high, true);
+ if (!uns0 && double_int_negative_p (min0))
+ prod1h = double_int_sub (prod1h, max1);
+ if (!uns1 && double_int_negative_p (max1))
+ prod1h = double_int_sub (prod1h, min0);
+
+ mul_double_wide_with_sign (max0.low, max0.high,
+ min1.low, min1.high,
+ &prod2l.low, &prod2l.high,
+ &prod2h.low, &prod2h.high, true);
+ if (!uns0 && double_int_negative_p (max0))
+ prod2h = double_int_sub (prod2h, min1);
+ if (!uns1 && double_int_negative_p (min1))
+ prod2h = double_int_sub (prod2h, max0);
+
+ mul_double_wide_with_sign (max0.low, max0.high,
+ max1.low, max1.high,
+ &prod3l.low, &prod3l.high,
+ &prod3h.low, &prod3h.high, true);
+ if (!uns0 && double_int_negative_p (max0))
+ prod3h = double_int_sub (prod3h, max1);
+ if (!uns1 && double_int_negative_p (max1))
+ prod3h = double_int_sub (prod3h, max0);
+
+ /* Sort the 4 products. */
+ quad_int_pair_sort (&prod0l, &prod0h, &prod3l, &prod3h, uns);
+ quad_int_pair_sort (&prod1l, &prod1h, &prod2l, &prod2h, uns);
+ quad_int_pair_sort (&prod0l, &prod0h, &prod1l, &prod1h, uns);
+ quad_int_pair_sort (&prod2l, &prod2h, &prod3l, &prod3h, uns);
+
+ /* Max - min. */
+ if (double_int_zero_p (prod0l))
+ {
+ prod1l = double_int_zero;
+ prod1h = double_int_neg (prod0h);
+ }
+ else
+ {
+ prod1l = double_int_neg (prod0l);
+ prod1h = double_int_not (prod0h);
+ }
+ prod2l = double_int_add (prod3l, prod1l);
+ prod2h = double_int_add (prod3h, prod1h);
+ if (double_int_ucmp (prod2l, prod3l) < 0)
+ prod2h = double_int_add (prod2h, double_int_one); /* carry */
+
+ if (!double_int_zero_p (prod2h)
+ || double_int_cmp (prod2l, sizem1, true) >= 0)
+ {
+ /* the range covers all values. */
+ set_value_range_to_varying (vr);
+ return;
+ }
+
+ /* The following should handle the wrapping and selecting
+ VR_ANTI_RANGE for us. */
+ min = double_int_to_tree (expr_type, prod0l);
+ max = double_int_to_tree (expr_type, prod3l);
+ set_and_canonicalize_value_range (vr, VR_RANGE, min, max, NULL);
+ return;
+ }
+
/* If we have an unsigned MULT_EXPR with two VR_ANTI_RANGEs,
drop to VR_VARYING. It would take more effort to compute a
precise range for such a case. For example, if we have
op0 == 65536 and op1 == 65536 with their ranges both being
~[0,0] on a 32-bit machine, we would have op0 * op1 == 0, so
we cannot claim that the product is in ~[0,0]. Note that we
are guaranteed to have vr0.type == vr1.type at this
point. */
if (vr0.type == VR_ANTI_RANGE
&& !TYPE_OVERFLOW_UNDEFINED (expr_type))
Index: gcc/testsuite/gcc.dg/tree-ssa/vrp77.c
===================================================================
--- gcc/testsuite/gcc.dg/tree-ssa/vrp77.c (revision 0)
+++ gcc/testsuite/gcc.dg/tree-ssa/vrp77.c (revision 0)
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#ifdef __SIZEOF_INT128__
+#define T __int128
+#else
+#define T long long
+#endif
+
+extern void impossible (void);
+
+void f(T x)
+{
+ unsigned T y;
+ unsigned T z;
+ if (x < -7)
+ return;
+ if (x > 2)
+ return;
+ y = x;
+ z = y * y;
+ if (z == 666)
+ impossible ();
+}
+
+void g(unsigned T x)
+{
+ unsigned T y;
+ unsigned T z;
+ unsigned T m = -1;
+ m = m / 2;
+ if (x < m-2)
+ return;
+ if (x > m-1)
+ return;
+ y = x;
+ z = y * y;
+ if (z == 7)
+ impossible ();
+}
+
+/* { dg-final { scan-tree-dump-not "impossible" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Property changes on: gcc/testsuite/gcc.dg/tree-ssa/vrp77.c
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision URL
Added: svn:eol-style
+ native
Index: gcc/double-int.c
===================================================================
--- gcc/double-int.c (revision 190103)
+++ gcc/double-int.c (working copy)
@@ -128,27 +128,42 @@ neg_double (unsigned HOST_WIDE_INT l1, H
Each argument is given as two `HOST_WIDE_INT' pieces.
One argument is L1 and H1; the other, L2 and H2.
The value is stored as two `HOST_WIDE_INT' pieces in *LV and *HV. */
int
mul_double_with_sign (unsigned HOST_WIDE_INT l1, HOST_WIDE_INT h1,
unsigned HOST_WIDE_INT l2, HOST_WIDE_INT h2,
unsigned HOST_WIDE_INT *lv, HOST_WIDE_INT *hv,
bool unsigned_p)
{
+ unsigned HOST_WIDE_INT toplow;
+ HOST_WIDE_INT tophigh;
+
+ return mul_double_wide_with_sign (l1, h1, l2, h2,
+ lv, hv, &toplow, &tophigh,
+ unsigned_p);
+}
+
+int
+mul_double_wide_with_sign (unsigned HOST_WIDE_INT l1, HOST_WIDE_INT h1,
+ unsigned HOST_WIDE_INT l2, HOST_WIDE_INT h2,
+ unsigned HOST_WIDE_INT *lv, HOST_WIDE_INT *hv,
+ unsigned HOST_WIDE_INT *lw, HOST_WIDE_INT *hw,
+ bool unsigned_p)
+{
HOST_WIDE_INT arg1[4];
HOST_WIDE_INT arg2[4];
HOST_WIDE_INT prod[4 * 2];
unsigned HOST_WIDE_INT carry;
int i, j, k;
- unsigned HOST_WIDE_INT toplow, neglow;
- HOST_WIDE_INT tophigh, neghigh;
+ unsigned HOST_WIDE_INT neglow;
+ HOST_WIDE_INT neghigh;
encode (arg1, l1, h1);
encode (arg2, l2, h2);
memset (prod, 0, sizeof prod);
for (i = 0; i < 4; i++)
{
carry = 0;
for (j = 0; j < 4; j++)
@@ -158,39 +173,39 @@ mul_double_with_sign (unsigned HOST_WIDE
carry += arg1[i] * arg2[j];
/* Since prod[p] < 0xFFFF, this sum <= 0xFFFFFFFF. */
carry += prod[k];
prod[k] = LOWPART (carry);
carry = HIGHPART (carry);
}
prod[i + 4] = carry;
}
decode (prod, lv, hv);
- decode (prod + 4, &toplow, &tophigh);
+ decode (prod + 4, lw, hw);
/* Unsigned overflow is immediate. */
if (unsigned_p)
- return (toplow | tophigh) != 0;
+ return (*lw | *hw) != 0;
/* Check for signed overflow by calculating the signed representation of the
top half of the result; it should agree with the low half's sign bit. */
if (h1 < 0)
{
neg_double (l2, h2, &neglow, &neghigh);
- add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
+ add_double (neglow, neghigh, *lw, *hw, lw, hw);
}
if (h2 < 0)
{
neg_double (l1, h1, &neglow, &neghigh);
- add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
+ add_double (neglow, neghigh, *lw, *hw, lw, hw);
}
- return (*hv < 0 ? ~(toplow & tophigh) : toplow | tophigh) != 0;
+ return (*hv < 0 ? ~(*lw & *hw) : *lw | *hw) != 0;
}
/* Shift the doubleword integer in L1, H1 right by COUNT places
keeping only PREC bits of result. ARITH nonzero specifies
arithmetic shifting; otherwise use logical shift.
Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
static void
rshift_double (unsigned HOST_WIDE_INT l1, HOST_WIDE_INT h1,
unsigned HOST_WIDE_INT count, unsigned int prec,
Index: gcc/double-int.h
===================================================================
--- gcc/double-int.h (revision 190103)
+++ gcc/double-int.h (working copy)
@@ -300,20 +300,25 @@ extern int add_double_with_sign (unsigne
unsigned HOST_WIDE_INT *, HOST_WIDE_INT *,
bool);
#define add_double(l1,h1,l2,h2,lv,hv) \
add_double_with_sign (l1, h1, l2, h2, lv, hv, false)
extern int neg_double (unsigned HOST_WIDE_INT, HOST_WIDE_INT,
unsigned HOST_WIDE_INT *, HOST_WIDE_INT *);
extern int mul_double_with_sign (unsigned HOST_WIDE_INT, HOST_WIDE_INT,
unsigned HOST_WIDE_INT, HOST_WIDE_INT,
unsigned HOST_WIDE_INT *, HOST_WIDE_INT *,
bool);
+extern int mul_double_wide_with_sign (unsigned HOST_WIDE_INT, HOST_WIDE_INT,
+ unsigned HOST_WIDE_INT, HOST_WIDE_INT,
+ unsigned HOST_WIDE_INT *, HOST_WIDE_INT *,
+ unsigned HOST_WIDE_INT *, HOST_WIDE_INT *,
+ bool);
#define mul_double(l1,h1,l2,h2,lv,hv) \
mul_double_with_sign (l1, h1, l2, h2, lv, hv, false)
extern void lshift_double (unsigned HOST_WIDE_INT, HOST_WIDE_INT,
HOST_WIDE_INT, unsigned int,
unsigned HOST_WIDE_INT *, HOST_WIDE_INT *, bool);
extern int div_and_round_double (unsigned, int, unsigned HOST_WIDE_INT,
HOST_WIDE_INT, unsigned HOST_WIDE_INT,
HOST_WIDE_INT, unsigned HOST_WIDE_INT *,
HOST_WIDE_INT *, unsigned HOST_WIDE_INT *,
HOST_WIDE_INT *);