Генерация высококачественного кода для программ, написанных на СИ | страница 6



"Удаление недостижимого кода" - еще один метод оптимизации. Недостижимый код – это некоторая последовательность инструкций программы, которая недостижима ни по одному пути в программе. Он может образоваться как следствие предыдущих операций оптимизации, кода условной отладки, или частых изменений программы многими программистами. Следующие операторы - это вариант кода для проверки компилятора на выполнение этого метода оптимизации.

>#define DEBUG 0

>if(DEBUG)

> printf("Debug Function\n>");

Манифестные константы часто могут скрывать существование недостижимого кода, особенно если такой код определяется внутри включаемого файла-заголовка.

"Удаление лишних присваиваний" включает нахождение промежутка жизни переменной и удаление присваиваний этой переменной, если эти присваивания не могут изменить логику программы. Этот метод освобождает ограниченные ресурсы, такие как пространство стека или машинные регистры. В следующей последовательности команд:

>a = 5;

>b = 0;

>a = b;

первый оператор есть лишнее присваивание, и может быть удален безопасно. Лишние присваивания могут возникать непреднамеренно, когда промежуток жизни переменной велик и между вхождениями переменной имеется более-менее длинный код. Лишние присваивания могут быть также результатом предыдущих проходов оптимизации.

Цель "распределения переменных по регистрам" состоит в попытке обеспечить оптимальное назначение регистров путем сохранения часто используемых переменных в регистрах так долго, как это возможно, для того, чтобы исключить более медленный доступ к памяти. Количество регистров, доступных для использования, зависит от архитектуры процессора. Семейство микропроцессоров Intel 80x86 резервирует много регистров для специального использования и имеет несколько универсальных регистров. В помощь распределению переменных по регистрам язык Си предоставляет спецификатор класса регистровой памяти, который дает возможность программисту указывать, какие переменные должны располагаться в регистрах.

При назначении переменных регистрам компилятор принимает во внимание не только какие переменные нужно выделить, но также и регистры, которым они назначаются. Выбор переменных зависит от частоты их использования, промежутков жизни текущих регистровых переменных (которые определяются при анализе потоков данных) и количества доступных регистров. В зависимости от степени выполняемой компилятором оптимизации промежуток жизни переменной может определяться внутри единственного оператора, внутри базового блока или перекрывать несколько базовых блоков. Переменная сохраняется в регистре только если она будет снова использоваться. Если на переменную в дальнейшем не будет ссылок, то она сохраняется в оперативной памяти, освобождая регистр для другой переменной.