2015-01-06 10 views
7

W większości przekonałem się, że napotkałem błąd g ++ 4.8.3, ale pomyślałem, że najpierw zapytam tę listę, ponieważ mam bardzo mało doświadczenia z setjmp/longjmp. Uprościliśmy mój kod do następującego foo.cxx:Niespójne ostrzeżenie: zmienna może zostać zbuforowana przez "longjmp" lub "vfork"

#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; 
} 

Używam g ++ 4.8.3 do kompilacji tego kodu. Interesujące jest to, że kiedy definiuję USE_STRUCT, kompilacja nie powiedzie się, ale uda się z USE_INT. W kodzie znajdują się komentarze, które dodatkowo wskazują, jak dokonać kompilacji z USE_STRUCT. Kompilacja nie powiedzie się również z opcją -fPIC do g ++, ale jest to wymagany argument w moim środowisku.

Aby zobaczyć błąd kompilacji:

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] 

Ale za pomocą prostego int jest OK:

g++ -DUSE_INT -Wextra -Wno-unused-parameter -O3 -Werror -fPIC foo.cxx 

Czy ktoś może mi wyjaśnić dlaczego val może być niszczona, jeśli jest to struct, ale nie jeśli jest int? Wszelkie spostrzeżenia na temat innych sposobów kompilacji powiodły się z struct, jak wskazano w komentarzach w kodzie? Czy to wskazuje na błąd kompilatora?

Wszelkie spostrzeżenia i komentarze są bardzo doceniane.

+0

gdyż temu przez 'setjmp' etc jest prawdopodobnie związane z bycia w rejestrze. –

+1

Otwórz błąd, który może być tym https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48968 –

+1

Odnośnie komentarza Basile, co się stanie, jeśli obniżysz poziom optymalizacji? Czy sprawdziłeś, który kod (a nawet pośredni) generowany jest przez kompilator? To może dać ci wskazówkę, co się dzieje. –

Odpowiedz

3

setjmp() zapisuje bieżący stos. Ponieważ jest wywoływana przed deklaracją val, zmienna ta nie będzie w zapisanym stosie.

Po setjmp() zmienna jest inicjowana, a jeśli kod później przeskoczy z powrotem do punktu setjmp, zmienna zostanie zainicjowana ponownie, przywracając poprzednią zmienną. Jeśli nie byłoby nietrywialne destructor, które powinny być nazywane na starym przykład, to jest niezdefiniowane zachowanie (§18.10/4):

A setjmp/longjmp call pair has undefined behavior if replacing the setjmp and longjmp by catch and throw would invoke any non-trivial destructors for any automatic objects.

prawdopodobnie destructor starego instancji nie zostanie wywołana. Domyślam się, że gcc nie ostrzega o typach pierwotnych, ponieważ nie mają one destruktorów, ale ostrzega o bardziej skomplikowanych typach, w których może to być problematyczne.

+0

Ale "MyStruct" OP jest typem POD, więc nie ma inicjalizacji. Natychmiast po wykonaniu wiersza 'MyStruct val;', 'val.a' i' val.b' nie są bardziej zdefiniowane niż po powrocie do niego po 'longjmp'. – 5gon12eder

+0

@ 5gon12eder: To prawda, ale musisz się zastanowić, jak trudno jest kompilatorowi ustalić, czy ostrzeżenie jest uzasadnione. – MSalters

0

Istnieje kilka czynników w pracy tutaj:

  1. struct zamiast int
  2. nie używając memset (muszę przyznać, że nie rozumiem, jak to może pogorszyć sytuację)
  3. iteracji pętli więcej niż dwa razy - jeśli wykonasz tylko dwukrotne iteracje, kompilator rozwinie pętlę
  4. z opcją wiersza polecenia w wierszu polecenia (w ten sposób powstaje kod niezależny od pozycji)

Tylko jeśli wszystkie cztery czynniki są obecne, kompilator generuje ostrzeżenie. Wydaje się, że stanowią one doskonałą burzę dla optymalizatora, który ma rodzaj załamania nerwowego (patrz poniżej). Jeśli którykolwiek z tych czynników jest nieobecny, kompilator po prostu optymalizuje wszystko do zera, więc może po prostu zignorować wartość setjmp.

Niezależnie od tego, czy jest to błąd, pytanie jest otwarte - kod prawdopodobnie nadal działa (chociaż nie testowałem go).Ale w każdym przypadku problem wydaje się być rozwiązany w wersji 4.9, więc oczywistym rozwiązaniem jest aktualizacja.

Oto kod maszynowy (NSFW):

SomeFunc(MyStruct): 
    rep; ret 
main: 
    pushq %r12 
    pushq %rbp 
    pushq %rbx 
    subq $224, %rsp 
    leaq 16(%rsp), %rdi 
    call [email protected] 
    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 
Powiązane problemy