Open nunoplopes opened 7 years ago
Johns-MacBook-Pro-2:code regehr$ clang -v clang version 6.0.0 (trunk 312932) Target: x86_64-apple-darwin16.7.0 Thread model: posix InstalledDir: /Users/regehr/llvm-install/bin
Johns-MacBook-Pro-2:code regehr$ clang -O2 alias4.c alias4-b.c Johns-MacBook-Pro-2:code regehr$ ./a.out a=0 x=0
Johns-MacBook-Pro-2:code regehr$ cat alias4.c
void f(int , int );
int main() { int a = 0, y[1], x = 0; //int a=0, x = 0, y[1]; uintptr_t pi = (uintptr_t)&x; uintptr_t yi = (uintptr_t)(y + 1); uintptr_t n = pi != yi;
if (n) { a = 100; pi = yi; }
if (n) { a = 100; pi = (uintptr_t)y; }
(int )pi = 15;
printf("a=%d x=%d\n", a, x);
f(&x, y);
return 0; }
Johns-MacBook-Pro-2:code regehr$ cat alias4-b.c void f(int x, int y) {}
I could not reproduce the problem either (same IR)
Weird, I see "a=0 x=0" at -O2 using both 4.0 and 5.0 on OS X and Ubuntu 16.04 on x86-64.
Can you verify this still happen on ToT ? In the meanwhile, I'm going to try with 5.0 on my machine (see if that applies, and bisect in case).
Weird, I see "a=0 x=0" at -O2 using both 4.0 and 5.0 on OS X and Ubuntu 16.04 on x86-64.
Also
[davide@cupiditate bin]$ ./clang c.c -O0 -S -emit-llvm -o - | ./opt -S -sroa > pre.ll [davide@cupiditate bin]$ ./clang c.c -O0 -S -emit-llvm -o - | ./opt -S -sroa -instcombine > post.ll [davide@cupiditate bin]$ diff -u pre.ll post.ll [davide@cupiditate bin]$
Presumably this might have been fixed/hidden? Which revision are you at? I'm at clang version 6.0.0 (trunk 312918) (llvm/trunk 312925)
Side note, I had to add
Reid, try this change:
int a = 0, y[1], x = 0; //int a=0, x = 0, y[1];
Doesn't seem to repro here as well (x86-64_linux) [same happens with John's suggested change].
[davide@cupiditate bin]$ ./clang c.c b.c -O1 -o foo && ./foo a=100 x=0 [davide@cupiditate bin]$ ./clang c.c b.c -O2 -o foo && ./foo a=100 x=0 [davide@cupiditate bin]$ ./clang c.c b.c -O3 -o foo && ./foo a=100 x=0
Reid, try this change:
int a = 0, y[1], x = 0; //int a=0, x = 0, y[1];
Personally I get "a=100 x=0" for this test case.
Does the soundness issue go away if we stop optimizing 'n' to 1? That seems like a much better fix, IMO. Integer/pointer casts are far more important to optimize through than
FWIW: opt
now has a --disable-i2p-p2i-opt
flag that disables the roundtrip cast.
Would it make sense to have a ptr2int2ptr(x)
instruction that allows InstCombine to merge ptr2int(int2ptr(x))
into a single operation, that can be then lowered by a backend more efficiently, without dropping the ptr2int
/int2ptr
casts (which I agree is unsound) ?
What is the semantics of that operation? Why would a frontend not just emit x
instead of ptr2int2ptr(x)
, if that operation has a semantics that permits such a transformation?
Depending on your provenance model, ptr2int2ptr(x)
would have the effect of escaping the pointer (e.g. in a PNVI-ae-udi-like model), while x
would not (preventing some compiler reorderings that x
would not).
Depending on your provenance model,
You mean, depending on which choice LLVM makes for its provenance mode? LLVM IR is a language with its own semantics, decisions like the provenance model have to be made by the LLVM project (as they require adjusting the optimizations to be conformant). Frontends don't get to choose the LLVM provenance model. Frontend languages will have their own provenance model and they have to ensure that it can be mapped into LLVM's (similar to how e.g. frontends have to map their notion of 'uninitialized memory' to what LLVM does).
ptr2int2ptr(x) would have the effect of escaping the pointer (e.g. in a PNVI-ae-udi-like model), while x would not (preventing some compiler reorderings that x would not).
That sounds like y = ptr2int2ptr(x);
would be equivalent to ptr2int(x); y = x;
. No need for a new operation.
Extended Description
Example of an end-to-end miscompilation by clang of the following code involving ptrtoint:
$ cat c.c
include
void f(int, int);
int main() { int a=0, y[1], x = 0; uintptr_t pi = (uintptr_t) &x; uintptr_t yi = (uintptr_t) (y+1); uintptr_t n = pi != yi;
if (n) { a = 100; pi = yi; }
if (n) { a = 100; pi = (uintptr_t) y; }
(int )pi = 15;
printf("a=%d x=%d\n", a, x);
f(&x,y);
return 0; }
$ cat b.c void f(intx, inty) {}
$ clang -O2 c.c b.c -o foo
$ ./foo a=0 x=0
This result is wrong. The two possible outcomes are: a=0 x=15, and a=100 x=0.
The bug is in Instcombine that treats inttoptr(ptrtoint(x)) == x, which is incorrect. This transformation can only be done if x is dereferenceable for the accesses through inttoptr. Compare the following: clang -O0 -S -emit-llvm -o - c.c | opt -S -sroa clang -O0 -S -emit-llvm -o - c.c | opt -S -sroa -instcombine
Integer compares are replaces with pointer compares (wrong) and load/stores are changed from inttoptr to pointers directly (also wrong).
Test case by Gil Hur.