The optimizer sometimes changes `if (p == q) use p` to `if (p == q) use q` if it can track the provenance of `q` but not of `p`. This is wrong when the actual provenance of `p` differs from that of `q`. Examples demonstrate the problem in different cases: - with integers and with live pointers (to show that the problem is not in casts to integers); - with past-the-end pointers and without them (to show that even changing the standard to make their comparisons UB will not help); - with two allocations and with only one (to show that it's not related to how memory is allocated by the compiler/libc). Plus, all examples behaves quite well: - respect provenance of pointers including via casts to integers (so this bug is not about (im)possibility to clear provenance by casts to integers or something); - use only one comparison (so the question of its stability is not touched). There is some previous analysis of propagation of conditional equivalences in other bugs, e.g., pr65752#c52, pr61502#c25. Somewhat more general clang bug -- https://bugs.llvm.org/show_bug.cgi?id=44313. Previous lengthy discussion is in https://bugs.llvm.org/show_bug.cgi?id=34548. Example with a past-the-end pointer (the wrong optimization seems to be applied in vrp1): ---------------------------------------------------------------------- #include <stdio.h> __attribute__((noipa,optnone)) // imagine it in a separate TU static void *opaque(void *p) { return p; } int main() { int x[5]; int y[1]; int *p = x; int *q = y + 1; int *r = opaque(p); // hide provenance of p if (r == q) { *p = 1; *r = 2; printf("result: %d\n", *p); } opaque(q); } ---------------------------------------------------------------------- $ gcc -std=c11 -pedantic -Wall -Wextra -Wno-attributes test.c && ./a.out result: 2 $ gcc -std=c11 -pedantic -Wall -Wextra -Wno-attributes -O3 test.c && ./a.out test.c: In function ‘main’: test.c:17:9: warning: array subscript 1 is outside array bounds of ‘int[1]’ [-Warray-bounds] 17 | *r = 2; | ^~ test.c:9:9: note: while referencing ‘y’ 9 | int y[1]; | ^ result: 1 ---------------------------------------------------------------------- gcc x86-64 version: gcc (GCC) 10.0.0 20191223 (experimental) ---------------------------------------------------------------------- The warning nicely illustrates the problem:-) Based on the example from Harald van Dijk in pr61502#c4.
Example with a restricted pointer (dom2): ---------------------------------------------------------------------- #include <stdio.h> __attribute__((noipa,optnone)) // imagine it in a separate TU static void *opaque(void *p) { return p; } __attribute__((noipa)) // imagine it in a separate TU static void f(int *restrict p, int *restrict q) { int *r = opaque(p); // hide provenance of p if (r == q) { *p = 1; *r = 2; printf("result: %d\n", *p); } opaque(q); } int main() { int x; f(&x, &x); } ---------------------------------------------------------------------- $ gcc -std=c11 -pedantic -Wall -Wextra -Wno-attributes test.c && ./a.out test.c: In function ‘main’: test.c:22:7: warning: passing argument 1 to ‘restrict’-qualified parameter aliases with argument 2 [-Wrestrict] 22 | f(&x, &x); | ^~ ~~ result: 2 $ gcc -std=c11 -pedantic -Wall -Wextra -Wno-attributes -O3 test.c && ./a.out test.c: In function ‘main’: test.c:22:7: warning: passing argument 1 to ‘restrict’-qualified parameter aliases with argument 2 [-Wrestrict] 22 | f(&x, &x); | ^~ ~~ result: 1 ---------------------------------------------------------------------- gcc x86-64 version: gcc (GCC) 10.0.0 20191223 (experimental) ---------------------------------------------------------------------- Strictly speaking this example is not about provenance (both pointers have the same provenance) but, for the optimizer, different restricted pointers probably play similar roles. Despite the warning, equal restricted pointers are fine per se -- see, e.g., Example 3 in C11, 6.7.3.1p10.
Example with a dead malloc (not in tree-opt): ---------------------------------------------------------------------- #include <stdint.h> #include <stdlib.h> #include <stdio.h> __attribute__((noipa,optnone)) // imagine it in a separate TU static void *opaque(void *p) { return p; } int main() { int *q = malloc(sizeof(int)); uintptr_t iq = (uintptr_t)(void *)q; free(q); int *p = malloc(sizeof(int)); uintptr_t ir = (uintptr_t)(void *)opaque(p); // hide provenance of p if (ir == iq) { *p = 1; *(int *)ir = 2; printf("result: %d\n", *p); } } ---------------------------------------------------------------------- $ gcc -std=c11 -pedantic -Wall -Wextra -Wno-attributes test.c && ./a.out result: 2 $ gcc -std=c11 -pedantic -Wall -Wextra -Wno-attributes -O3 test.c && ./a.out result: 1 ---------------------------------------------------------------------- gcc x86-64 version: gcc (GCC) 10.0.0 20191223 (experimental) ----------------------------------------------------------------------
For completeness, an example with a guessed pointer, based on the testcase from bug 65752, comment 0, gcc-only (dom2): ---------------------------------------------------------------------- #include <stdint.h> #include <stdio.h> __attribute__((noipa)) // imagine it in a separate TU static uintptr_t opaque_to_int(void *p) { return (uintptr_t)p; } int main() { int x; int *p = &x; uintptr_t ip = (uintptr_t)p; uintptr_t iq = 0; while (iq < ip) iq++; uintptr_t ir = opaque_to_int(p); // hide provenance of p if (ir == iq) { *p = 1; *(int *)ir = 2; printf("result: %d\n", *p); } } ---------------------------------------------------------------------- $ gcc -std=c11 -pedantic -Wall -Wextra -O3 test.c && ./a.out result: 1 ---------------------------------------------------------------------- gcc x86-64 version: gcc (GCC) 10.0.0 20191224 (experimental) ----------------------------------------------------------------------
There's duplicates about this conditional propagation issue. It also applies to integers since we track provenance through them as well. Not sure if it's really useful to have yet another bug about this.
*** Bug 103343 has been marked as a duplicate of this bug. ***