Страницы

Поиск по вопросам

пятница, 11 января 2019 г.

Удаление бесполезного кода компилятором

Рассмотрим простой код.
void f() { }
// ...
f();
Ясно, что функция f не делает ничего (точнее, не делает никакой полезной работы), поэтому компилятор может её спокойно "выбросить" без изменения поведения программы. (Вариант обращения к памяти за вершиной стека чтобы "сцапать" оттуда адрес возврата не берём, ибо "грязный хак", да ещё может и не сработать.)
Усложним задачу:
void g(int x) { }
int calc_x() { // Долго и упорно что-то вычисляем. }
// ...
g(42); // Ни на что не влияет. g(calc_x()); // А вот тут непонятно...
Ясно, что g(42) можно выкинуть. Но выкинуть g(calc_x()) в общем случае нельзя, поскольку у calc_x могут быть побочные эффекты.
Соответственно, вопрос: при каких условиях компилятор будет иметь право выбрасывать вызовы функции g?
P.S. Я понимаю, что компилятор может оставить даже вызов f(), если отключены все оптимизации. Вопрос следует понимать именно так, как написано - не выбросит, а имеет право выбросить


Ответ

Компилятор может превратить вызов g(calc_x()); в следующий [псевдо]код:
int x = ...; calc_x код x теперь чем-то проинициализирован. Здесь мог бы быть код из g, но его там нет. Увы.
Т.е. компилятор заинлайнил обе функции, после чего он смотрит, что x нигде не используется и на этом этапе он может выкинуть код вообще.
Для примера, вот такой C++ код:
void g(int x) { }
int calc_x() { int x = 0; while(x < 500) { ++x; } return x; }
int main() { g(42); g(calc_x()); }
Превращается в вот такой ассемблер(и в студии, и в gcc):
main: xor eax, eax ret

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

Комментариев нет:

Отправить комментарий