Насколько последовательно компиляторы C оптимизируют недоступный код? - PullRequest
1 голос
/ 09 июля 2019

Скажем, у вас есть (по причинам, которые здесь не важны) следующий код:

 int k = 0;
 ...  /* no change to k can happen here */
 if (k) { 
   do_something();
 }

Используя флаг -O2, GCC не будет генерировать для него код, признавая, что ifтест всегда ложный

Мне интересно, является ли это довольно распространенным поведением в компиляторах или это то, на что я не должен полагаться.

Кто-нибудь знает?

Ответы [ 2 ]

2 голосов
/ 09 июля 2019

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

Однако вы должны знать, что иногда ваш код имеет больше потенциальных побочных эффектов, чем вы думаете.


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

int function_with_unpredictable_side_effects(const int &x);
void doit() {
  int k = 0;
  function_with_unpredictable_side_effects(k);
  if (k)
    printf("Never reached\n");
}

Таким образом, компилятор должен выполнить некоторую работу и может потерпеть неудачу даже для локальных переменных. Кстати, я считаю, что проблема, которая решается в этом случае, называется escape анализ .


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

struct MyArray {
  int num;
  int arr[100];
};
void doit(int idx) {
  MyArray x;
  x.num = 0;
  x.arr[idx] = 7;
  if (x.num)
    printf("Never reached\n");
}

Компилятор Visual C ++ не устраняет мертвый код , поскольку считает, что вы можете получить доступ к x.num как x.arr[-1]. Это может звучать как ужасная вещь для вас, но этот компилятор использовался в области Gamedev в течение многих лет, и такие хаки там не редкость, поэтому компилятор остается на безопасной стороне. С другой стороны, GCC удаляет мертвый код. Может быть, это связано с использованием строгого псевдонима указателя rule.


P.S. Клавиатура const никогда не используется оптимизатором, она присутствует только на языке C / C ++ для удобства программистов.

2 голосов
/ 09 июля 2019

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...