Устранение мертвого кода - Dead code elimination

В теории компиляторов , устранении мертвых коды (также известный как АКД , мертвое удаление кода , мертвый код вскрышного или мёртвый код полоса ) является оптимизация компилятор для удаления кода , который не влияет на результатах программы. Удаление такого кода имеет несколько преимуществ: оно уменьшает размер программы, что является важным соображением в некоторых контекстах, и позволяет запущенной программе избегать выполнения нерелевантных операций, что сокращает время ее выполнения. Он также может обеспечить дальнейшую оптимизацию за счет упрощения структуры программы. Мертвый код включает в себя код, который никогда не может быть выполнен ( недоступный код ), и код, который влияет только на мертвые переменные (записывается, но никогда не читается снова), то есть не имеет отношения к программе.

Примеры

Рассмотрим следующий пример , написанный на C .

 int foo(void)
 {
   int a = 24;
   int b = 25; /* Assignment to dead variable */
   int c;
   c = a * 4;
   return c;
   b = 24; /* Unreachable code */
   return 0;
 }

Простой анализ использования значений покажет, что значение bпосле первого присваивания не используется внутри foo. Кроме того, bон объявлен как локальная переменная внутри foo, поэтому его значение нельзя использовать снаружи foo. Таким образом, переменная bявляется мертвым и оптимизатор может вернуть себе место для хранения и устранить его инициализации.

Кроме того, поскольку первый оператор return выполняется безоговорочно, ни один из возможных путей выполнения не достигает второго назначения b. Таким образом, назначение недоступно и может быть удалено. Если у процедуры был более сложный поток управления , такой как метка после оператора return и gotoгде- то еще в процедуре, то возможный путь выполнения мог бы существовать для присвоения b.

Кроме того , даже если некоторые вычисления выполняются в функции, их значения не хранятся в местах , доступных за пределами сферы этой функции. Кроме того, учитывая, что функция возвращает статическое значение (96), ее можно упростить до значения, которое она возвращает (это упрощение называется сворачиванием констант ).

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

Обычно удаление мертвого кода используется как альтернатива необязательному включению кода через препроцессор . Рассмотрим следующий код.

 int main(void) {
   int a = 5;
   int b = 6;
   int c;
   c = a * (b / 2);
   if (0) {   /* DEBUG */
     printf("%d\n", c);
   }
   return c;
 }

Поскольку выражение 0 всегда будет иметь значение false , код внутри оператора if никогда не может быть выполнен, и удаление мертвого кода полностью удалит его из оптимизированной программы. Этот метод является обычным при отладке, чтобы дополнительно активировать блоки кода; использование оптимизатора с устранением мертвого кода устраняет необходимость использования препроцессора для выполнения той же задачи.

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

Исторически устранение мертвого кода выполнялось с использованием информации, полученной в результате анализа потока данных . Алгоритм, основанный на статической форме единого назначения (SSA), представлен в оригинальной журнальной статье о форме SSA, написанной Роном Ситроном и др. Роберт Шиллингсбург (он же Шилльнер) улучшил алгоритм и разработал сопутствующий алгоритм для удаления бесполезных операций потока управления.

Динамическое устранение мертвого кода

Мертвый код обычно считается мертвым безоговорочно . Следовательно, разумно попытаться удалить мертвый код путем удаления мертвого кода во время компиляции .

Однако на практике разделы кода также часто представляют мертвый или недоступный код только при определенных условиях , которые могут быть неизвестны во время компиляции или сборки. Такие условия могут быть наложены разными средами выполнения (например, разными версиями операционной системы или разными наборами и комбинациями драйверов или служб, загруженными в конкретной целевой среде), что может потребовать различных наборов особых случаев в коде, но при заодно стали условно мертвым кодом для остальных случаев. Кроме того, программное обеспечение (например, драйвер или резидентная служба) можно настраивать для включения или исключения определенных функций в зависимости от предпочтений пользователя, делая неиспользуемые части кода бесполезными в конкретном сценарии. Хотя модульное программное обеспечение может быть разработано для динамической загрузки библиотек только по запросу, в большинстве случаев невозможно загрузить только соответствующие подпрограммы из конкретной библиотеки, и даже если это будет поддерживаться, подпрограмма может по-прежнему включать разделы кода, которые могут считаться мертвым кодом в данном сценарии, но не может быть исключен уже во время компиляции.

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

Большинство языков программирования, компиляторов и операционных систем не предлагают или почти не поддерживают больше, чем динамическая загрузка библиотек и поздняя компоновка , поэтому программное обеспечение, использующее динамическое устранение мертвого кода, очень редко встречается в сочетании с языками, скомпилированными заранее или написанными на языке ассемблера . Однако языковые реализации, выполняющие своевременную компиляцию, могут динамически оптимизироваться для устранения мертвого кода.

Подобные подходы иногда используются для динамического обновления программного обеспечения и установки «горячих» исправлений, хотя и с совершенно иной направленностью .

Смотрите также

использованная литература

дальнейшее чтение

внешние ссылки