Hi,
In our (non-C) compiler we use setjmp/longjmp to implement exception
handling. For the initial implementation LLVM backend, I'm keeping that
model. In order to ensure that changes performed in a try/setjmp==0
block survive the longjmp, the changes must be done via volatile operations.
Given that volatility is a property of individual load/store
instructions rather than of memory slots in LLVM, I thought I could
optimise this by only marking the stores in try-blocks as volatile.
After all, I don't mind LLVM removing extra loads of the same variable
in a try-block.
However, if I do that (instead of making all loads/stores to the
variable volatile), then some kind of (in my case invalid) value
propagation seems to happen from the try-block to the exception block.
From what I can see with opt -print-after-all, it's the GVN pass that
does this.
I have attached a C program that demonstrates the issue: compiled with
clang -O0 or -O1 it works fine, but with -O2 it prints an error because
the "loops" variable has a wrong value on exit.
Mind you: I do not claim that the attached program is valid according to
any C standard or setjmp/longjmp documentation (it might be, but I don't
know). It's just that if you compile this program at with clang -O0
-emit-llvm, you get more or less the same LLVM IR as what we generate
for our code. (*) Next you can process the resulting .ll file with e.g.
opt -O2 to reproduce the issue (i.e., without any regard to a C
standard, to the extent that LLVM IR behaviour can be considered to be
unrelated to any C standard).
My question is: is this a bug in LLVM, or is this behaviour deemed to be
by design? Also: our setjmp is not called setjmp, but we do mark its
replacement also as "returns_twice". So even if the above behaviour
would be considered "expected" for setjmp, would the same go for any
function marked as "returns_twice"?
I tested with clang/LLVM 3.7.0 and the clang from 7.0.1. I don't have
newer versions available here.
Thanks,
Jonas
(*) The only difference is that in our code there is no alias of the
"loops" variable through a pointer: we simply directly store to the
loops variable with a volatile store inside the try/setjmp==0 block. At
the LLVM IR level, that should not make a difference though since the
aliasing from the pointer to the loops variable is trivial. Removing the
aliasing manually from the generated IR does not change anything either,
as expected.
tint643c.c (677 Bytes)