Under -fwrapv-pointer, pointer overflow/wrap is well-defined.
This patch ensures that IVOPTS does not make non-overflow assumptions
for pointer types when flag_wrapv_pointer is active.
Bootstrapped and tested on x86_64-unknown-linux-gnu.
Documentation is a bit vague about the effects of -fwrapv-pointer,
but IIRC the intention(?) was pointer arithmetic behaves like
it was done on an unsigned integer type, thus C pointer arithmetic
within-object rules do not apply, neither does wrapping around zero.
That would also mean -fno-delete-null-pointer-checks that guard
comparisons might need to go? The middle-end treats &<...>
as pointer arithmetic, so that would even apply to &a->p != 0 then,
irrespective of the offset of p? PTA most definitely also gets
this wrong, the question is whether we should really fix it up
in the way of the patch or require the IL to actually use an
unsigned integer type in the first place?
Of course test coverage is quite bad here.
Any comments?
Thanks,
Richard.
PR tree-optimization/127497
* tree-ssa-loop-niter.cc (number_of_iterations_lt_to_ne):
Do not assume fv_comp_no_overflow is true for pointer types
when flag_wrapv_pointer is active.
(assert_no_overflow_lt): Support pointer types using type1
(sizetype) for min/max value calculation.
(assert_loop_rolls_lt): Do not assume no_overflow_p is true
for pointer types, and generate proper boundary assumptions using
type1 when flag_wrapv_pointer is active.
(number_of_iterations_le): Do not skip boundary check for pointer
types when flag_wrapv_pointer is active.
(number_of_iterations_cond): Do not set no_overflow on pointer types,
nor assume pointer comparisons never overflow when flag_wrapv_pointer
is active.
* gcc.dg/torture/pr127497.c: New testcase
---
gcc/testsuite/gcc.dg/torture/pr127497.c | 26 ++++++++++++++++
gcc/tree-ssa-loop-niter.cc | 41 +++++++++++++------------
2 files changed, 48 insertions(+), 19 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/torture/pr127497.c
diff --git a/gcc/testsuite/gcc.dg/torture/pr127497.c
b/gcc/testsuite/gcc.dg/torture/pr127497.c
new file mode 100644
index 00000000000..684643efd1c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr127497.c
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-additional-options "-fwrapv-pointer" } */
+
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+__attribute__((noipa)) uintptr_t
+f (char *p, uintptr_t len)
+{
+ char *q = p + len;
+ uintptr_t n = 0;
+ do
+ {
+ n++;
+ p += 4;
+ }
+ while (p < q);
+ return n;
+}
+
+int
+main (void)
+{
+ if (f ((char *) (uintptr_t) -3, 8) != 2)
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc
index bbfa16c6d6b..0815a5a6e3d 100644
--- a/gcc/tree-ssa-loop-niter.cc
+++ b/gcc/tree-ssa-loop-niter.cc
@@ -1174,7 +1174,7 @@ number_of_iterations_lt_to_ne (tree type, affine_iv *iv0,
affine_iv *iv1,
as the code cannot rely on the object to that the pointer points being
placed at the end of the address space (and more pragmatically,
TYPE_{MIN,MAX}_VALUE is not defined for pointers). */
- if (integer_zerop (mod) || POINTER_TYPE_P (type))
+ if (integer_zerop (mod) || (POINTER_TYPE_P (type) && !flag_wrapv_pointer))
fv_comp_no_overflow = true;
else if (!exit_must_be_taken)
fv_comp_no_overflow = false;
@@ -1246,6 +1246,7 @@ assert_no_overflow_lt (tree type, affine_iv *iv0,
affine_iv *iv1,
{
tree bound, d, assumption, diff;
tree niter_type = TREE_TYPE (step);
+ tree type1 = POINTER_TYPE_P (type) ? sizetype : type;
if (integer_nonzerop (iv0->step))
{
@@ -1260,15 +1261,16 @@ assert_no_overflow_lt (tree type, affine_iv *iv0,
affine_iv *iv1,
if (TREE_CODE (iv0->base) == INTEGER_CST)
{
d = fold_build2 (MINUS_EXPR, niter_type,
- fold_convert (niter_type, TYPE_MAX_VALUE (type)),
+ fold_convert (niter_type, TYPE_MAX_VALUE (type1)),
fold_convert (niter_type, iv0->base));
diff = fold_build2 (FLOOR_MOD_EXPR, niter_type, d, step);
}
else
diff = fold_build2 (MINUS_EXPR, niter_type, step,
build_int_cst (niter_type, 1));
- bound = fold_build2 (MINUS_EXPR, type,
- TYPE_MAX_VALUE (type), fold_convert (type, diff));
+ bound = fold_build2 (MINUS_EXPR, type1,
+ TYPE_MAX_VALUE (type1), fold_convert (type1, diff));
+ bound = fold_convert (type, bound);
assumption = fold_build2 (LE_EXPR, boolean_type_node,
iv1->base, bound);
}
@@ -1282,14 +1284,15 @@ assert_no_overflow_lt (tree type, affine_iv *iv0,
affine_iv *iv1,
{
d = fold_build2 (MINUS_EXPR, niter_type,
fold_convert (niter_type, iv1->base),
- fold_convert (niter_type, TYPE_MIN_VALUE (type)));
+ fold_convert (niter_type, TYPE_MIN_VALUE (type1)));
diff = fold_build2 (FLOOR_MOD_EXPR, niter_type, d, step);
}
else
diff = fold_build2 (MINUS_EXPR, niter_type, step,
build_int_cst (niter_type, 1));
- bound = fold_build2 (PLUS_EXPR, type,
- TYPE_MIN_VALUE (type), fold_convert (type, diff));
+ bound = fold_build2 (PLUS_EXPR, type1,
+ TYPE_MIN_VALUE (type1), fold_convert (type1, diff));
+ bound = fold_convert (type, bound);
assumption = fold_build2 (GE_EXPR, boolean_type_node,
iv0->base, bound);
}
@@ -1365,7 +1368,7 @@ assert_loop_rolls_lt (tree type, affine_iv *iv0,
affine_iv *iv1,
Gcc in general does not allow or handle objects larger
than half of the address space, hence the upper bound
is satisfied for pointers. */
- || POINTER_TYPE_P (type));
+ || (POINTER_TYPE_P (type) && !flag_wrapv_pointer));
mpz_clear (mstep);
mpz_clear (max);
@@ -1387,12 +1390,12 @@ assert_loop_rolls_lt (tree type, affine_iv *iv0,
affine_iv *iv1,
/* We need to know that iv0->base >= MIN + iv0->step - 1. Since
0 address never belongs to any object, we can assume this for
pointers. */
- if (!POINTER_TYPE_P (type))
+ if (!(POINTER_TYPE_P (type) && !flag_wrapv_pointer))
{
bound = fold_build2 (PLUS_EXPR, type1,
- TYPE_MIN_VALUE (type), diff);
+ TYPE_MIN_VALUE (type1), diff);
assumption = fold_build2 (GE_EXPR, boolean_type_node,
- iv0->base, bound);
+ fold_convert (type1, iv0->base), bound);
}
/* And then we can compute iv0->base - diff, and compare it with
@@ -1406,12 +1409,12 @@ assert_loop_rolls_lt (tree type, affine_iv *iv0,
affine_iv *iv1,
diff = fold_build2 (PLUS_EXPR, type1,
iv1->step, build_int_cst (type1, 1));
- if (!POINTER_TYPE_P (type))
+ if (!(POINTER_TYPE_P (type) && !flag_wrapv_pointer))
{
bound = fold_build2 (PLUS_EXPR, type1,
- TYPE_MAX_VALUE (type), diff);
+ TYPE_MAX_VALUE (type1), diff);
assumption = fold_build2 (LE_EXPR, boolean_type_node,
- iv1->base, bound);
+ fold_convert (type1, iv1->base), bound);
}
mbzl = fold_convert (type1, iv0->base);
@@ -1705,14 +1708,14 @@ number_of_iterations_le (class loop *loop, tree type,
affine_iv *iv0,
the address space (and more pragmatically, TYPE_{MIN,MAX}_VALUE is
not defined for pointers). */
- if (!exit_must_be_taken && !POINTER_TYPE_P (type))
+ if (!exit_must_be_taken && !(POINTER_TYPE_P (type) && !flag_wrapv_pointer))
{
if (integer_nonzerop (iv0->step))
assumption = fold_build2 (NE_EXPR, boolean_type_node,
- iv1->base, TYPE_MAX_VALUE (type));
+ iv1->base, fold_convert (type, TYPE_MAX_VALUE
(type1)));
else
assumption = fold_build2 (NE_EXPR, boolean_type_node,
- iv0->base, TYPE_MIN_VALUE (type));
+ iv0->base, fold_convert (type, TYPE_MIN_VALUE
(type1)));
if (integer_zerop (assumption))
return false;
@@ -1827,7 +1830,7 @@ number_of_iterations_cond (class loop *loop,
code = swap_tree_comparison (code);
}
- if (POINTER_TYPE_P (type))
+ if (POINTER_TYPE_P (type) && !flag_wrapv_pointer)
{
/* Comparison of pointers is undefined unless both iv0 and iv1 point
to the same object. If they do, the control variable cannot wrap
@@ -1885,7 +1888,7 @@ number_of_iterations_cond (class loop *loop,
|| wi::gtu_p (wi::abs (wi::to_widest (step)),
wi::abs (wi::to_widest (iv0->step))))
{
- if (POINTER_TYPE_P (type) && code != NE_EXPR)
+ if (POINTER_TYPE_P (type) && !flag_wrapv_pointer && code != NE_EXPR)
/* For relational pointer compares we have further guarantees
that the pointers always point to the same object (or one
after it) and that objects do not cross the zero page. So
--
2.51.0