Недосяжний код
У програмуванні та теорії компіляторів, недося́жним ко́дом називають частину коду програми, яка за жодних умов не може бути виконаною, оскільки є недосяжною в графі потоку управління[1][2].
Недосяжний код часто вважають одним із типів мертвого коду, така термінологія зазвичай застосовується при розгляді сирцевого коду програми[3][4]. Однак у теорії компіляторів, ці поняття ніяк не пов'язані, мертвим кодом там називають тільки досяжний код, який не впливає на вивід програми[1][2][5].
Основні недоліки наявності в програмі недосяжного коду:
- займає зайву пам'ять;
- є причиною зайвого кешування інструкцій у кеші інструкцій процесора, яке також знижує локальність даних;
- ускладнює підтримку застосунків — час і сили можуть витрачатися на підтримку і документування частини коду, яка є недосяжною, а отже ніколи не виконується.
Причини виникнення
Існування недосяжного коду обумовлюють різні фактори, наприклад:
- програмні помилки в складних умовних переходах;
- наслідки внутрішніх перетворень; виконуваних оптимізувальним компілятором;
- неповне тестування нової або модифікованої програми, за якого не вдалося виявити недосяжний код;
- виправляючи одну помилку, програміст створив іншу помилку, яка обходить недосяжний код і не була виявлена під час тестування;
- застарілий код, який не був повністю видалений програмістом, оскільки був змішаний з робочим кодом;
- застарілий код, який програміст забув видалити;
- раніше корисний код, який ніколи не буде виконаний, оскільки введення даних ніколи не призведе до виконання цього коду;
- застарілий код, який навмисно збережено, але зроблено недосяжним, для того щоб його можна було, за необхідності, знову включити в програму;
- налагоджувальні конструкції і залишкові частини коду, які ще мають бути видалені з програми.
В останніх п'яти випадках, недосяжний код є успадкованим, тобто це код, який раніше був корисним, але зараз не використовується.
Приклад
Розглянемо наступний приклад на мові Сі:
int foo(int x, int y)
{
return x + y;
int z = x*y; /* Недостижимый код */
}
Операція int z = x*y
є недосяжним кодом, оскільки перед нею виконується вихід з процедури (операції, що стоять після повернення з процедури можуть і не бути недосяжним кодом, наприклад, якщо на мітку, розташовану після повернення посилається оператор goto).
Аналіз
Пошук недосяжного коду в сирцевому коді можна провести за допомогою статичного аналізу коду[3][4]. За використання оптимізувального компілятора, виявити і видалити недосяжний код здатна оптимізація видалення недосяжного коду, яка знаходить у графі потоку керування недосяжні вузли і видаляє їх[6]. Простий аналіз цього графа на наявність недосяжних вузлів часто буває реалізованим у компіляторі у вигляді окремої функції, так званого прибиральника, яка викликається зразу після перетворень, здатних змінювати граф потоку керування[7].
Код може ставати недосяжним унаслідок виконання компілятором інших перетворень над проміжним поданням, наприклад оптимізації усунення спільних підвиразів.
На практиці, складність реалізованого аналізу істотно впливає на кількість виявленого недосяжного коду. Наприклад, після згортання констант і простого аналізу потоку керування можна виявити, що код усередині оператора if
у такому прикладі є недосяжним:
int foo(void)
{
int n = 2 + 1;
if (n > 4)
{
printf("%d", n); /* Недостижимый код */
}
}
Однак, для того щоб виявити недосяжний код у наступному прикладі, слід застосувати значно складніший алгоритм аналізу:
int foo(void)
{
double x = sqrt(2);
if (x > 4)
{
printf("%lf", x); /* Недостижимый код */
}
}
Одним із практичних рішень є підхід, за якого спочатку виконується простий аналіз наявності недосяжного коду, а потім використовується профілювальник для опрацювання складніших випадків. Профілювальник не може довести, що певна ділянка коду є недосяжною, але він може бути гарною евристикою для пошуку підозрілих вузлів, які, ймовірно, є недосяжними. Після виявлення цих потенційно недосяжних вузлів, можна застосувати якийсь потужний алгоритм аналізу недосяжного коду.
Примітки
- Engineering a Compiler — С. 544.
- Debray, S. K., Evans, W., Muth, R., and De Sutter, B. 2000. Compiler techniques for code compaction. ACM Trans. Program. Lang. Syst. 22, 2 (Mar. 2000), 378—415. (summary Архивировано)
- Dead code detection and removal. Aivosto. Архів оригіналу за 5 серпня 2012. Процитовано 12 липня 2012.
- Compares some free alternatives to DCD (Dead Code Detector). Java.net. Архів оригіналу за 23 вересня 2012. Процитовано 12 липня 2012.
- Компиляторы — принципы, технологии, инструменты — С. 669 (недостижимый код), 713 (мёртвый код).
- Engineering a Compiler — С. 550.
- А. Ю. Дроздов, А. М. Степаненков. Управляемые пакеты оптимизаций. В Информационные технологии и вычислительные системы, 2004, № 3 (текст Архивировано)
Література
- Cooper and Torczon. Engineering a Compiler. — Morgan Kaufmann, 2011. — С. 544-550, 593. — ISBN 978-0-12-088478-0.
- Альфред Ахо, Моника Лам, Рави Сети, Джеффри Ульман. Компиляторы: принципы, технологии и инструментарий = Compilers: Principles, Techniques, and Tools. — 2-е издание. — М.: «Вильямс», 2008. — 1184 с. — 1500 экз. — ISBN 978-5-8459-1349-4.
- Muchnick, Steven S. Advanced Compiler Design and Implementation. — Morgan Kaufmann Publishers, 1997. — С. 580-582. — ISBN 1-55860-320-4.