Gcc-оптимизация? ошибка? и его практическое значение для проекта
Мои вопросы делятся на три части
Вопрос 1
Рассмотрим приведенный ниже код,
#include <iostream>
using namespace std;
int main( int argc, char *argv[])
{
const int v = 50;
int i = 0X7FFFFFFF;
cout<<(i + v)<<endl;
if ( i + v < i )
{
cout<<"Number is negative"<<endl;
}
else
{
cout<<"Number is positive"<<endl;
}
return 0;
}
Не используются конкретные параметры оптимизации компилятора или используется флаг O. Это основная команда компиляции g++ -o test main.cpp используется для формирования исполняемого файла.
По-видимому, очень простой код имеет странное поведение в 64-разрядной ОС SUSE, gcc версии 4.1.2. Ожидаемый вывод: "Число отрицательно", вместо этого только в 64-разрядной ОС SUSE, выход будет "Число положительное".
После некоторого анализа и выполнения "disass" кода я обнаружил, что компилятор оптимизирован в следующем формате -
- Так как я одинаково по обе стороны сравнения, его нельзя изменить в том же выражении, удалите 'i' из уравнения.
- Теперь сравнение приводит к
if ( v < 0 )
, где v является константой положительной. Поэтому во время самой компиляции в регистр добавляется адрес функции cout part else. Инструкции cmp/jmp не найдены.
Я вижу, что поведение только в gcc 4.1.2 SUSE 10. При попытке в AIX 5.1/5.3 и HP IA64 результат будет таким, как ожидалось.
Является ли приведенная выше оптимизация действительной?
Или, используется механизм переполнения для int не допустимый прецедент?
Вопрос 2
Теперь, когда я изменяю условный оператор от if (i + v < i)
до if ( (i + v) < i )
даже тогда, поведение такое же, это, по крайней мере, я лично не соглашусь, поскольку дополнительные скобки предоставлены, я ожидаю, что компилятор создаст временную встроенную переменную типа и они сравнивают, таким образом, сводят на нет оптимизацию.
Вопрос 3
Предположим, у меня есть огромная база кода, я переношу мою версию компилятора, такая ошибка/оптимизация может привести к хаосу в моем поведении системы. Конечно, с точки зрения бизнеса, очень сложно проверить все строки кода снова только из-за обновления компилятора.
Я думаю, что для всех практических целей подобные ошибки очень трудно поймать (во время обновления) и неизменно будут просачиваться на производственную площадку.
Может ли кто-нибудь предложить какой-либо возможный способ обеспечить, чтобы эта ошибка/оптимизация не влияла на мою существующую системную/кодовую базу?
PS:
- Когда const для v удаляется из кода, оптимизация не выполняется компилятором.
- Я считаю, что отлично использовать механизм переполнения, чтобы узнать, имеет ли переменная значение MAX-50 (в моем случае).
Update (1)
Чего я хочу достичь? переменная я будет счетчиком (вроде syncID). Если я выполняю операцию в автономном режиме (50 операций), то во время запуска я хотел бы reset мой счетчик. Для этого я проверяю значение границы (до reset it), а не добавляю его вслепую.
Я не уверен, полагаюсь ли я на аппаратную реализацию. Я знаю, что 0X7FFFFFFF является максимальным положительным значением. Все, что я делаю, добавляет к этому значение, я ожидаю, что возвращаемое значение будет отрицательным. Я не думаю, что эта логика имеет какое-то отношение к аппаратной реализации.
В любом случае, спасибо за ваш вклад.
Update (2)
Большая часть входной станции указывает, что я полагаюсь на поведение более низкого уровня при проверке переполнения. У меня есть один вопрос относительно того же,
- Если это так, для unsigned int как проверить и reset значение во время переполнения или переполнения? например, если v = 10, я = 0X7FFFFFFE, я хочу reset я = 9. Аналогично для underflow?
Я не смог бы этого сделать, если не проверю отрицательность числа. Поэтому я утверждаю, что int должен возвращать отрицательное число, когда значение добавляется в + MAX_INT.
Пожалуйста, дайте мне знать ваши данные.
Ответы
Ответ 1
Это известная проблема, и я не думаю, что она считала ошибку в компиляторе. Когда я компилирую gcc 4.5 с помощью -Wall -O2
, он предупреждает
предупреждение: при условии, что подписанное переполнение не происходит, если предположить, что (X + c) < X всегда false
Хотя ваш код переполняется.
Вы можете передать флаг -fno-strict-overflow
, чтобы отключить эту оптимизацию.
Ответ 2
В вашем коде создается поведение undefined. Языки C и С++ не имеют "механизма переполнения" для целочисленной арифметики со знаком. Ваши вычисления переполняют целые числа со знаком - поведение сразу undefined. Учитывая, что форма "ошибка в компиляторе или нет" не отличается от того, что пытается проанализировать примеры i = i++ + ++i
.
Компилятор GCC имеет оптимизацию, основанную на той части спецификации языков C/С++. Это называется "строгое переполнение семантики" или что-то такое озеро. Он основан на том, что добавление положительного значения в целое число со знаком в С++ всегда приводит к большему значению или приводит к поведению undefined. Это немедленно означает, что компилятор совершенно свободен предположить, что сумма всегда больше. Общий характер этой оптимизации очень похож на оптимизацию "строгих псевдонимов", также присутствующую в GCC. Они оба вызвали некоторые жалобы от более "хакерских" частей сообщества пользователей GCC, многие из которых даже не подозревали, что трюки, на которые они полагались в своих программах на C/С++, были просто незаконными хаками.
Ответ 3
Q1: Возможно, число действительно положительно в 64-битной реализации? Кто знает? Перед отладкой кода я бы просто напечатал f ( "% d", я + v);
Q2: Скобки заключаются только в том, чтобы сообщить компилятору, как разбирать выражение. Обычно это делается в виде дерева, поэтому оптимизатор вообще не видит никаких скобок. И это свободно преобразует выражение.
Q3: Поэтому, как программист c/С++, вы не должны писать код, который предполагает определенные свойства базового оборудования, например, например, что int представляет собой 32-разрядное количество в двух дополнительных формах.
Ответ 4
Что делает строка:
cout<<(i + v)<<endl;
Вывод в примере SUSE? Вы уверены, что у вас нет 64-битных ints?
Ответ 5
Хорошо, так что это было почти шесть лет назад, и на вопрос ответили. Тем не менее, я чувствую, что есть некоторые биты, которые не были удовлетворены моим удовлетворением, поэтому я добавляю несколько комментариев, надеюсь, на благо будущих читателей этой дискуссии. (Например, когда я получил поисковый хит для него.)
-
ОП задается с использованием gcc 4.1.2 без каких-либо специальных флагов. Я предполагаю, что отсутствие флага -O
эквивалентно -O0
. Без запроса оптимизации, почему gcc оптимизировал код в сообщении? Это кажется мне ошибкой компилятора. Я также предполагаю, что это было исправлено в более поздних версиях (например, один ответ упоминает gcc 4.5 и флаг оптимизации -fno-strict-overflow
). На текущей странице gcc man указано, что -fstrict-overflow
включен в -O2
или более.
-
В текущих версиях gcc существует опция -fwrapv
, которая позволяет использовать код, вызывающий проблемы для OP. Разумеется, вы убедитесь, что знаете размеры битов ваших целых типов. На странице gcc man:
-fstrict-overflow
.....
See also the -fwrapv option. Using -fwrapv means that integer signed overflow
is fully defined: it wraps. ... With -fwrapv certain types of overflow are
permitted. For example, if the compiler gets an overflow when doing arithmetic
on constants, the overflowed value can still be used with -fwrapv, but not otherwise.