В основном я убедил себя, что столкнулся с некоторой ошибкой g ++ 4.8.3, но я подумал, что сначала спрошу этот список, потому что у меня очень мало опыта с setjmp / longjmp. Я упростил свой код до следующего foo.cxx:
#include <setjmp.h>
#include <string.h>
// Changing MyStruct to be just a single int makes the compiler happy.
struct MyStruct
{
int a;
int b;
};
// Setting MyType to int makes the compiler happy.
#ifdef USE_STRUCT
typedef MyStruct MyType;
#elif USE_INT
typedef int MyType;
#endif
void SomeFunc(MyType val)
{
}
static void static_func(MyType val)
{
SomeFunc(val);
}
int main(int argc, char **argv)
{
jmp_buf env;
if (setjmp(env))
{
return 1;
}
MyType val;
#ifdef USE_STRUCT
val.a = val.b = 0;
#elif USE_INT
val = 0;
#endif
// Enabling the below memset call makes the compiler happy.
//memset(&val, 0, sizeof(val));
// Iterating 1 or 2 times makes the compiler happy.
for (unsigned i = 0; i < 3; i++)
{
// calling SomeFunc() directly makes the compiler happy.
static_func(val);
}
return 0;
}
Я использую g ++ 4.8.3 для компиляции этого кода. Что меня интересует, так это то, что когда я определяю USE_STRUCT, компиляция завершается неудачно, но успешно с USE_INT. В коде есть комментарии, которые дополнительно указывают, как сделать компиляцию успешной с помощью USE_STRUCT. Сбой компиляции также возможен только с параметром -fPIC для g ++, но это обязательный аргумент в моей среде.
Чтобы увидеть ошибку компиляции:
g++ -DUSE_STRUCT -Wextra -Wno-unused-parameter -O3 -Werror -fPIC foo.cxx
foo.cxx: In function ‘int main(int, char**)’:
foo.cxx:26:5: error: variable ‘val’ might be clobbered by ‘longjmp’ or ‘vfork’ [-Werror=clobbered]
Но с помощью простого int все в порядке:
g++ -DUSE_INT -Wextra -Wno-unused-parameter -O3 -Werror -fPIC foo.cxx
Может кто-нибудь объяснить мне, почему val может быть закрыт, если это структура, но не если это int? Любые идеи о других способах успешной компиляции со структурой, как указано в комментариях в коде? Или это указывает на ошибку компилятора?
Любые идеи и комментарии с благодарностью.
setjmp()
сохраняет текущий стек Поскольку он вызывается до объявления val
эта переменная не будет в сохраненном стеке.
После setjmp()
переменная инициализируется, и если код позже возвращается к setjmp
точка, переменная будет инициализирована снова, забивая старую переменную. Если бы существовал нетривиальный деструктор, который должен вызываться в старом экземпляре, это неопределенное поведение (§18.10 / 4):
setjmp
/longjmp
Пара вызовов имеет неопределенное поведение при заменеsetjmp
а такжеlongjmp
отcatch
а такжеthrow
будет вызывать любые нетривиальные деструкторы для любых автоматических объектов.
Вероятно, деструктор старого экземпляра не будет вызван. Я предполагаю, что gcc не предупреждает о примитивных типах, так как они не имеют деструкторов, но предупреждает о более сложных типах, где это может быть проблематично.
Здесь работают несколько факторов:
struct
вместо int
memset
(Признаюсь, я не понимаю, как это может усугубить ситуацию)-fPIC
опция командной строки (это производит независимый от позиции код)Только если присутствуют все четыре из этих факторов, компилятор выдаст предупреждение. Кажется, что они представляют собой идеальный шторм для оптимизатора, который имеет своего рода нервный срыв (см. Ниже). Если какой-либо из этих факторов отсутствует, компилятор просто оптимизирует все до нуля, поэтому он может просто игнорировать setjmp
,
Вопрос о том, является ли это ошибкой, остается открытым — по-видимому, код все еще работает (хотя я его не проверял). Но в любом случае проблема, похоже, была исправлена в версии 4.9, поэтому очевидным решением является обновление.
Вот машинный код (NSFW):
SomeFunc(MyStruct):
rep; ret
main:
pushq %r12
pushq %rbp
pushq %rbx
subq $224, %rsp
leaq 16(%rsp), %rdi
call _setjmp@PLT
testl %eax, %eax
movl %eax, %ebp
jne .L5
movl $3, %ebx
movabsq $-4294967296, %r12
.L4:
movq 8(%rsp), %rdx
andq %r12, %rdx
movl %edx, %eax
movq %rax, %rdi
movq %rax, 8(%rsp)
call SomeFunc(MyStruct)@PLT
subl $1, %ebx
jne .L4
.L3:
addq $224, %rsp
movl %ebp, %eax
popq %rbx
popq %rbp
popq %r12
ret
.L5:
movl $1, %ebp
jmp .L3