https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88433
Bug ID: 88433 Summary: wrong code for printf after a pointer cast from a pointer to an adjacent object Product: gcc Version: 9.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: target Assignee: unassigned at gcc dot gnu.org Reporter: msebor at gcc dot gnu.org Target Milestone: --- This is from Exploring C Semantics and Pointer Provenance: https://www.cl.cam.ac.uk/~pes20/cerberus/cerberus-popl2019.pdf the following test case: https://cerberus.cl.cam.ac.uk/cerberus?defacto/provenance_basic_using_uintptr_t_global_yx.c GCC emits code with different effects for each of the two functions below even when the objects x and y are adjacent to each other: in f(), the call to printf outputs the modified value of y. In g(), however, it outputs the value of y before modification. I consider the code in the test case undefined, but a) my understanding from Richard is that the middle-end intentionally doesn't track pointer provenance through integer conversions (and so doesn't necessarily treat this sort of "object hopping" as undefined), and b) the PNVI model outlined in the paper above and expected to be proposed for C2X makes this code valid (the model allows p to take on the provenance of y as a result of the integer <-> pointer casts). Looking at the dumps, I think (a) is true for this test case in both f() and g(). The different output from g() appears to be due to the x86_64 back performing the assignment *p = 11 only after it has stored the value of y in the register passed to printf. Other back-ends I've looked at produce the same output from g() as from f(). int y = 2, x = 1; void f (void) { long ix = (long)&x; long iy = (long)&y; ix += 4; int *p = (int*)ix; int *q = (int*)iy; if (p == q) { *p = 11; __builtin_printf ("%i", y); // prints 11 } } void g (void) { long ix = (long)&x; long iy = (long)&y; ix += 4; int *p = (int*)ix; int *q = (int*)iy; if (!__builtin_memcmp (&p, &q, sizeof p)) { *p = 11; __builtin_printf ("%i", y); // prints 2 } }