https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65752

--- Comment #25 from Richard Biener <rguenth at gcc dot gnu.org> ---
(In reply to Chung-Kil Hur from comment #24)
> (In reply to schwab from comment #23)
> > "gil.hur at sf dot snu.ac.kr" <gcc-bugzi...@gcc.gnu.org> writes:
> > 
> > > Since "hello" is not printed, I think the if-statement is the same as 
> > > no-op.
> > > Thus, removing the if-statement should not change the behavior of the 
> > > program
> > > according to ISO C11.
> > 
> > Unless you are invoking undefined behaviour.
> > 
> > Andreas.
> 
> ==============================
> #include <stdio.h>
> 
> int main() {
>   int x = 0;
>   uintptr_t xp = (uintptr_t) &x;
>   uintptr_t i, j;
> 
>   for (i = 0; i < xp; i++) { }
>   j = i;
> 
>   *(int*)((xp+i)-j) = 15;
> 
>   printf("%d\n", x);
> }
> =============================
> 
> This prints "15".
> And I do not think there is any UB.
> Please correct me if I am wrong.
> 
> Then, I add the if-statement.
> 
> ==============================
> #include <stdio.h>
> 
> int main() {
>   int x = 0;
>   uintptr_t xp = (uintptr_t) &x;
>   uintptr_t i, j;
> 
>   for (i = 0; i < xp; i++) { }
>   j = i;
> 
>   /****** begin *******/
>   if (j != xp) { 
>     printf("hello\n");
>     j = xp; 
>   }
>   /****** end *********/
> 
>   *(int*)((xp+i)-j) = 15;
> 
>   printf("%d\n", x);
> }
> =============================
> 
> This prints "0" without printing "hello".
> 
> Thus, this raises no UB unless "j != xp" raises UB.
> 
> If you think "j != xp" raises UB, please explain why and give some reference.
> 
> Otherwise, I think it is a bug of GCC.

The C standard only guarantees that you can convert a pointer to uintptr_t and
back, it doesn't guarantee that you can convert a modified uintptr_t back to
a pointer that is valid.

Thus, doing (int *)((xp + i) - j) is invoking undefined behavior.

That you see an effect of this undefined behavior just with the added if
is because that if confuses early GCC optimizations which would have
cancelled i - j to zero, retaining (int *)xp.  Instead it enables later
optimization to see that xp - j cancels and thus it is left with (int *)i.

This is because you are essentially computing (xp + xp) - xp == xp but
dependent on what two pieces get cancelled the pointer is based on
either xp (ok) or on i (not ok - that is related to xp only via an
implicit equivalency).

The net result is that I can't see how to "detect" this kind of situation
in points-to analysis in a way that does not pessimize all pointer-to-integer /
integer-to-pointer conversions.  In theory it would be possible to add a
flag similar to -fno-strict-aliasing to do this pessimization (but there
is already -fno-tree-pta which avoids the issue as well).

So in the end my conclusion is that either the testcase invokes undefined
behavior or the C standard has a defect.  Thus the bug is WONTFIX unless
somebody can come up with a way to handle these kind of equivalences
in the points-to algorithm in GCC in a way not pessimizing everything.
One might consider an incomplete approach like that in comment #6 but
I am not convinced about this hack (and one would need to evaluate its
effects on code generation).

Reply via email to