Сворачивание констант и распространение констант — это связанные оптимизации компилятора, используемые многими современными компиляторами . [1] Расширенная форма распространения констант, известная как разреженное условное распространение констант, может более точно распространять константы и одновременно удалять мертвый код .
Сворачивание констант — это процесс распознавания и оценки константных выражений во время компиляции, а не их вычисления во время выполнения. Термины в константных выражениях обычно являются простыми литералами, такими как целочисленный литерал 2
, но они также могут быть переменными, значения которых известны во время компиляции. Рассмотрим выражение:
я = 320 * 200 * 32 ;
Большинство компиляторов на самом деле не будут генерировать две инструкции умножения и хранилище для этого оператора. Вместо этого они идентифицируют такие конструкции и подставляют вычисленные значения во время компиляции (в данном случае, 2,048,000
).
Сворачивание констант может использовать арифметические тождества. Если x
является числовым, значение 0 * x
равно нулю, даже если компилятор не знает значение x
(обратите внимание, что это недопустимо для чисел с плавающей точкой IEEE, поскольку x
может быть Infinity или NaN . Тем не менее, некоторые среды, которые способствуют производительности, такие как шейдеры GLSL, допускают это для констант, что иногда может вызывать ошибки).
Константное сворачивание может применяться не только к числам. Конкатенация строковых литералов и константных строк может быть константно свернута. Код, такой как "abc" + "def"
можно заменить на "abcdef"
.
При реализации кросс-компилятора необходимо позаботиться о том, чтобы поведение арифметических операций на архитектуре хоста соответствовало поведению на целевой архитектуре, так как в противном случае включение сворачивания констант изменит поведение программы. Это особенно важно в случае операций с плавающей точкой , точная реализация которых может сильно различаться.
Распространение констант — это процесс подстановки значений известных констант в выражения во время компиляции. К таким константам относятся те, что определены выше, а также встроенные функции, применяемые к константным значениям. Рассмотрим следующий псевдокод:
int x = 14 ; int y = 7 - x / 2 ; вернуть y * ( 28 / x + 2 );
Размножение x дает:
int x = 14 ; int y = 7 - 14 / 2 ; вернуть y * ( 28 / 14 + 2 );
Продолжение распространения дает следующее (что, вероятно, можно было бы дополнительно оптимизировать путем исключения мертвого кода как x, так и y.)
int x = 14 ; int y = 0 ; вернуть 0 ;
Распространение констант реализовано в компиляторах с использованием результатов анализа определения достижения . Если все определения достижения переменной являются одним и тем же присваиванием, которое присваивает переменной одну и ту же константу, то переменная имеет постоянное значение и может быть заменена константой.
Постоянное распространение также может привести к упрощению условных ветвей до одного или нескольких безусловных операторов, когда условное выражение может быть оценено как истинное или ложное во время компиляции, чтобы определить единственно возможный результат.
Постоянное сворачивание и распространение обычно используются вместе для достижения множества упрощений и сокращений, и их чередующееся, итеративное применение продолжается до тех пор, пока эти эффекты не прекратятся.
Рассмотрим этот неоптимизированный псевдокод, возвращающий неизвестное число, ожидающее анализа:
int a = 30 ; int b = 9 - ( a / 5 ); int c = b * 4 ; если ( c > 10 ) { c = c - 10 ; } вернуть c * ( 60 / a );
Применение постоянного распространения один раз, а затем постоянного свертывания дает:
int a = 30 ; int b = 3 ; int c = b * 4 ; если ( c > 10 ) { c = c - 10 ; } вернуть c * 2 ;
Повторение обоих шагов дважды дает:
int a = 30 ; int b = 3 ; int c = 12 ; если ( истина ) { c = 2 ; } вернуть c * 2 ;
Заменив все использования переменных a
константами b
, компилятор применяет к этим переменным функцию устранения мертвого кода , оставляя:
int c = 12 ; если ( истина ) { c = 2 ; } вернуть c * 2 ;
(Булевы конструкции различаются в зависимости от языка и компилятора, но их детали — такие как статус, происхождение и представление истины — не влияют на эти принципы оптимизации.)
Традиционное распространение констант не приводит к дальнейшей оптимизации; оно не реструктурирует программы.
Однако, аналогичная оптимизация, sparse conditional constant propagation , идет дальше, выбирая соответствующую условную ветвь, [2] и удаляя всегда истинный условный тест. Таким образом, переменная c
становится избыточной, и остается только операция над константой:
возврат 4 ;
Если этот псевдокод представляет собой тело функции, компилятор знает, что функция вычисляется как целочисленная константа 4
, что позволяет заменить вызовы функции на 4
и дополнительно повысить эффективность программы.
распространение ИЛИ постоянное сворачивание.