Оптимизирующие компиляторы могут переопределить порядок следования инструкций в программе и сохранить конкретные переменные в регистрах центрального процессора, а не в оперативной памяти. Обычно такая оптимизация зависит от потока управления ходом выполнения программы, отражающего лексическую структуру программы. Поскольку операции перехода, выполняемые с помощью вызовов функций setjmp() и longjmp(), создаются и происходят в ходе выполнения программы, оптимизатор компилятора не может взять их в расчет в процессе своей работы. Более того, семантика некоторых реализаций двоичных интерфейсов приложений (ABI) требует, чтобы функция longjmp() восстанавливала копии регистров центрального процессора, сохраненные ранее при вызове функции setjmp().

Это говорит о том, что в результате вызова longjmp() в оптимизированных переменных могут оказаться неверные значения. Убедиться в этом можно, изучив поведение программы, представленной в листинге 6.6.

Листинг 6.6. Демонстрация взаимного влияния оптимизации при компиляции и функции longjmp()

proc/setjmp_vars.c

#include

#include

#include

static jmp_buf env;

static void

doJump(int nvar, int rvar, int vvar)

{

printf("Inside doJump(): nvar=%d rvar=%d vvar=%d\n", nvar, rvar, vvar);

longjmp(env, 1);

}

int

main(int argc, char *argv[])

{

int nvar;

register int rvar; /* По возможности выделяется в регистре */

volatile int vvar; /* Смотрите текст */

nvar = 111;

rvar = 222;

vvar = 333;

if (setjmp(env) == 0) { /* Код, выполняемый после setjmp() */

nvar = 777;

rvar = 888;

vvar = 999;

doJump(nvar, rvar, vvar);

} else { /* Код, выполняемый после longjmp() */

printf("After longjmp(): nvar=%d rvar=%d vvar=%d\n", nvar, rvar, vvar);

}

exit(EXIT_SUCCESS);

}

proc/setjmp_vars.c

При компиляции без оптимизации программы, представленной в листинге 6.6, мы увидим на выходе вполне ожидаемую информацию:

$ cc — o setjmp_vars setjmp_vars.c

$ ./setjmp_vars

Inside doJump(): nvar=777 rvar=888 vvar=999

After longjmp(): nvar=777 rvar=888 vvar=999

Но при компиляции с оптимизацией будут получены такие неожиданные результаты:

$ cc — O — o setjmp_vars setjmp_vars.c

$ ./setjmp_vars

Inside doJump(): nvar=777 rvar=888 vvar=999

After longjmp(): nvar=111 rvar=222 vvar=999

Здесь видно, что после вызова longjmp() переменные nvar и rvar были переопределены, получив значения, имевшиеся у них ко времени вызова функции setjmp(). Это произошло потому, что вследствие вызова longjmp() реорганизация оптимизатором кода привела к путанице. Эта проблема может коснуться любых локальных переменных, являющихся кандидатами на оптимизацию. Как правило, она касается переменных-указателей и переменных любого простого типа: char, int, float и long.

Подобной реорганизации кода можно избежать, объявив переменные изменяемыми — volatile, что даст указание оптимизатору не оптимизировать их. В предыдущем выводе информации из программы было показано, что переменная vvar, объявленная volatile, была правильно обработана даже при компиляции с оптимизацией.

Поскольку различные компиляторы используют различные приемы оптимизации, в портируемых программах в тех функциях, которые вызывают setjmp(), ключевое слово volatile должно указываться со всеми локальными переменными вышеупомянутых типов.

Если компилятору GNU C задать ключ — Wextra (extra warnings — «дополнительные предупреждения»), то в отношении программы setjmp_vars.c он выдаст следующие полезные предупреждения:

$ cc — Wall — Wextra — O — o setjmp_vars setjmp_vars.c

setjmp_vars.c: In function 'main':

setjmp_vars.c:17: warning: variable 'nvar' might be clobbered

by 'longjmp' or 'vfork'

(Переменная nvar может быть «затерта» функцией longjmp или vfork.)

setjmp_vars.c:18: warning: variable 'rvar' might be clobbered

by 'longjmp' or 'vfork'

(Переменная rvar может быть «затерта» функцией longjmp или vfork.)

Поучительно будет взглянуть на ассемблерный выход, создаваемый при компиляции программы setjmp_vars.c как с оптимизацией, так и без нее. Команда cc — S создает файл с расширением. s, где содержится сгенерированный для программы ассемблерный код.

Использовать ли функции setjmp() и longjmp()

Перейти на страницу:

Похожие книги