setjmp in llvm

Hi,
I’m trying to prevent llvm instruction motion around an intrinsic function call. Throughout my experimenting, I was told that setjmp could create fake entry points into a region of code and that might prevent code motion.
What I found is something surprising, and probably is a misuse of setjmp but I couldn’t find an explanation for it.
Consider this:
#include

std::jmp_buf jb;
int main() {
int s = 1;
setjmp(jb);
if (s) {
s = 0;
std::longjmp(jb, 1);
return 2;
}
return 1;
}

One would expect that the load of s in the if condition is not optimized away (by being replaced with if(1)), but clang at -O3 (on linux) generates this:
define signext i32 @main() local_unnamed_addr #0 {
entry:
%call = call signext i32 @_setjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @jb, i64 0, i64 0)) #3
call void @longjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @jb, i64 0, i64 0), i32 signext 1) #4
unreachable
}

which leads to an infinite loop execution at runtime.
Aren’t we breaking the as-if rule because the semantics of the program imply that the value of s is unknown after the setjmp (because you can enter main from the location of setjmp in the program).

Thanks.

Wael

Hi Wael,

Aren't we breaking the as-if rule because the semantics of the program imply
that the value of s is unknown after the setjmp (because you can enter main
from the location of setjmp in the program).

Congratulations! You've found one of 2.5 valid uses for "volatile". In
C11 (7.3.2.1p3):

All accessible objects have values, and all other components of the abstract machine have state, as of the time the longjmp function was called, except that the values of objects of automatic storage duration that are local to the function containing the invocation of the corresponding setjmp macro that do not have volatile-qualified type and have been changed between the setjmp invocation and longjmp call are indeterminate.

So the value of "s" is indeterminate because it's not declared
volatile. C++ inherits these rules and adds a couple more restrictions
about destructors.

I'm trying to prevent llvm instruction motion around an intrinsic function call.

This is pretty much impossible in LLVM if you mean instructions LLVM
considers side-effect free (e.g. a simple "add"). It's theoretically a
pain in the neck when dealing with cycle-counters for performance.
Though in practice it usually doesn't matter much: LLVM usually
doesn't do anything problematic, and if it does putting the region of
interest into a noinline function tends to be enough.

Cheers.

Tim.

(Adding the list back in, especially as other people have taken an
interest in really immobile intrinsics before).

Thank you Tim. The C spec never stops enlightening me with new quirks.
Out of curiousity, did you know about this or grep'ed the spec for setjmp?

I've got a standing interest in both threading (a canonical place
where volatile is definitely not what you want) and OS programming
(the extra 0.5 where it is, for memory mapped I/O) so I've read lots
and lots about it. The final standards-blessed use is for reading
variables in a signal handler.

BTW, how confident are you about the statement you made:
"This is pretty much impossible in LLVM if you mean instructions LLVM
   considers side-effect free (e.g. a simple "add")."
Because I was tempted for a while to just ask the community about it, after having
spent few weeks experimenting, with a hope that there's a way to actually prevent
all instructions (including the side-effect free ones) from moving across calls.

Pretty sure, I'm afraid. Even the strongest property, HasSideEffects
which is basically a "this does stuff you don't understand"
instruction to LLVM doesn't prevent that kind of code motion. Because
even though the intrinsic might do things LLVM doesn't understand,
"add" doesn't.

Cheers.

Tim.