Могут ли компиляторы разрешать удаление бесконечных циклов, таких как компилятор Intel С++ с -O2?

Следующий код тестирования корректно работает в VS либо с отладкой, либо с выпуском, а также в GCC. Он также корректно выполняет ICC с отладкой, но не при включенной оптимизации (-O2).

#include <cstdio>

class tClassA{
public:
  int m_first, m_last;

  tClassA() : m_first(0), m_last(0) {}
  ~tClassA() {}

  bool isEmpty() const {return (m_first == m_last);}
  void updateFirst() {m_first = m_first + 1;}
  void updateLast() {m_last = m_last + 1;}
  void doSomething() {printf("should not reach here\r\n");}
};

int main() {
  tClassA q;
  while(true) {
    while(q.isEmpty()) ;
    q.doSomething();
  }
  return 1;
}

Предполагается остановиться на while(q.isEmpty()). Однако, когда -O2 включен в ICC (release), он начинает бесконечно "doSomething".

Так как это однопоточная программа, а isEmpty() должна оцениваться как true, я не могу найти причины, по которой ICC должен вести себя таким образом? Мне что-то не хватает?

Ответ 1

Поскольку цикл while (q.isEmpty()) ; не содержит операторов, которые могут когда-либо вызывать внешний видимый побочный эффект, весь цикл оптимизируется из существования. Это та же причина, что:

for (int i = 0; i < 10; i++)
    ;

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

На языке C на самом деле это выдающаяся кость раздора относительно того, можно ли оптимизировать бесконечный цикл таким образом (я не знаю, что такое ситуация с С++). Насколько я знаю, консенсуса по этому вопросу никогда не было достигнуто - умные и знающие люди взяли обе стороны.

Ответ 2

Это наверняка звучит как ошибка. Здесь (довольно дикая) догадка о том, что могло бы привести к этому рассуждение...

После вставки он видит:

while (q.m_first == q.m_last) /* do nothing */ ;
do_something();

и любая последовательность do nothing repeatedly ; do something может быть переведена на просто "сделать что-то". Это падает, если повторяющаяся часть бесконечна (как в этом случае). Но, возможно, они не проверяют их компиляцию на примерах, которые преднамеренно имеют бесконечный цикл: -).

Ответ 3

Любая вероятность того, что в коде, который вы создали и запустили, не было точки с запятой после while(q.isEmpty())? Это, несомненно, приведет к тому, что следующая строка будет бесконечно.

Ответ 4

Как небольшая сторона, эта версия icc делает то, что вы хотите. То есть, он никогда не вызывает doSomething().

[9:41am][[email protected] /tmp] icc --version
icc (ICC) 11.0 20081105

Ответ 5

Стандарт С++ позволяет удалять циклы без побочных эффектов, даже если они не завершаются:

Обычно считается, что это важно разрешить преобразование потенциально неконцевых петель (например, путем объединения двух циклов, которые итерации по тем же потенциальным бесконечное множество, или путем исключения без побочных эффектов), даже если это в противном случае не могут быть оправданы в случай, когда первый цикл никогда не был завершается. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm

Смотрите обсуждение здесь: http://blog.regehr.org/archives/161

Ответ 6

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

Ответ 7

Я думаю, что это была ваша версия gcc. Я скомпилировал вашу прог под 4.4.2 и работал точно так, как должен.