Open haberman opened 1 year ago
The harm in this example is relatively minor (one extra load), but in my actual code I was relying on constant propagation to eliminate many other branches that test the value of *pi
. So this impact of this missed optimization on my code is rather large.
As a workaround in your code, you could make the constant "static const".
If you look at the LLVM IR generated by clang, in the "good" case, the constant is actually evaluated by the frontend. In the "bad" case, it isn't, and clang doesn't currently try to represent the fact that the variable is const in LLVM IR. (llvm.invariant.start might be usable for this.)
As a workaround in your code, you could make the constant "static const".
I want to avoid having the constant take up any space in the binary (there are a lot of these).
Another workaround I found was:
void ExternFunc(const int*);
int Bad() {
const int i = 0;
const int* pi = &i;
ExternFunc(pi);
if (*pi != i) __builtin_unreachable();
return *pi;
}
It looks a bit silly in this case, but in the real code this is split across several inline functions, and it is more reasonable there.
llvm.invariant.start might be usable for this
That sounds great.
Repro:
Output from trunk (Godbolt link: https://godbolt.org/z/KT8dPM9ac):
In
Good()
, Clang correctly performs constant propagation on the locali
, even though the pointer escapes, becausei
is aconst
object that cannot be modified byExternFunc()
without invoking UB.In
Bad()
, we have basically the same scenario, except in this case Clang does not perform the constant propagation.pi
is not merely a const pointer, it is a pointer to a const object, and therefore the value it points to is guaranteed to be stable even across a function call where the pointer escapes. But Clang doesn't seem to realize this, and re-loadsi
after the function call, even thoughi
is a const object.