Description
Any modification to malloc'd local variable between __builtin_setjmp and __builtin_longjmp calls does not persist. It is optimized by either by earlyCSE or GVN (global value numbering) pass. __builtin_setjmp has attribute (llvm::Attribute::ReturnsTwice).
//bug.c
#include <stdio.h>
#include <stdlib.h>
void* buf[20];
attribute((noinline))
void foo(void)
{
printf("Calling longjmp from inside function foo\n");
__builtin_longjmp(buf, 1);
printf("This point should never be reached\n");
}
int main(void)
{
int *local_var = malloc(sizeof(int));
*local_var = 10;
printf("local_val=%d \n", *local_var);
if (__builtin_setjmp(buf) == 0)
{
*local_var = 20;
printf("Calling function foo local_var=%d \n", *local_var);
foo();
}
printf("longjmp has been called local_val=%d \n", *local_var);
}
/*******************Output should be as follows: **********************
local_val=10
Calling function foo local_var=20
Calling longjmp from inside function foo
longjmp has been called local_val=20
**********************************************************************/
$clang -O2 bug.c
$./a.out
local_val=10
Calling function foo local_var=20
Calling longjmp from inside function foo
longjmp has been called local_val=10
Value of local_var is modified between setjmp and longjmp calls to 20, but after return from longjmp local_var remains 10 because of GVN optimization.
After some analyses I have attached setjmp_longjmp_bug.zip files have two tests case, bug1.c and and bug2.c. bug1.c is compiled at -O1 and has information after each pass using -dump-after-all in file bug1_dump_earlycse_o1. earlyCSE optimizes malloc'd local_var.
Similarly, in bug2.c, GVN optimizes malloc'd local variable local_var at -O2. It's dump after each pass is in bug2_dump_gvn_o2 file. Both dump files are marked with "???" where wrong optimization (constant folding) happens.
setjmp_longjmp_bug.zip