llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.02k stars 11.96k forks source link

res=__builtin_setjmp(); -> invalid assembler #19348

Open llvmbot opened 10 years ago

llvmbot commented 10 years ago
Bugzilla Link 18974
Version trunk
OS Linux
Attachments minimal example
Reporter LLVM Bugzilla Contributor
CC @hfinkel,@bogner

Extended Description

__builtin_setjmp() only works when we immediately switch on its result and branch into two different code paths. If instead we write this kind of code:

int res = __builtin_setjmp();
do_this_in_all_cases();

then, at least on x86-64, it produces this kind of assembler for the first line:

movq    %rbp, -80(%rbp)
movq    %rsp, -64(%rbp)
movq    $".LBB4_-1", -72(%rbp)

where ".LBB4_-1" is a bogus label not defined anywhere in the .s file. It fails to assemble, of course.

bogner commented 10 years ago

Hmm, looks like that fails too though. We're doing something wrong here.

bogner commented 10 years ago

This is undefined behaviour. From C99 (n1256) 7.13:

  1. An invocation of the setjmp macro shall appear only in one of the following contexts:

    — the entire controlling expression of a selection or iteration statement;

    — one operand of a relational or equality operator with the other operand an integer constant expression, with the resulting expression being the entire controlling expression of a selection or iteration statement;

    — the operand of a unary ! operator with the resulting expression being the entire controlling expression of a selection or iteration statement; or

    • the entire expression of an expression statement (possibly cast to void).
  2. If the invocation appears in any other context, the behavior is undefined.

If you want to ignore __builtin_setjmp()'s result, you should write:

(void)__builtin_setjmp(...);
llvmbot commented 10 years ago

Note that the C standard says specifically this:

s = setjmp(env);  /* WRONG! */

It's of little relevance here. If you consider it is, please view this bug report as complaining about the following kind of code, which is explicitly allowed by the C standard:

__builtin_setjmp(..);   /* ignore the return value */
do_this_in_all_cases();

Or:

if (__builtin_setjmp(..) == 1)
     ;   /* nothing */
do_this_in_all_cases();
llvmbot commented 10 years ago

A workaround that should always work: replace a bare __builtin_setjmp(buf) line with:

while (__builtin_setjmp(buf)) { }

Here, LLVM cannot optimize the two paths back into one, unlike e.g. the same statement with "if" instead of "while".

It means that after a __builtin_longjmp() we will repeat the __builtin_setjmp() once, which is at worst a minor performance problem, and which can be considered a good thing to do if you're (rarely but) possibly doing several longjmp() to go back to this point, like in some search algorithm.