Почему не было встроенного доступа к регистру состояния процессора при проектировании как C, так и C ++?

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

if(a < a + 100) //detect overflow

Я понимаю, что есть безопасные альтернативы, такие как:

if(a > (INT_MAX - 100)) //detected overflow

Однако может показаться, что доступ к регистру состояния или отдельным флагам в нем отсутствует как в языках C, так и в C ++. Почему эта функция не была включена или какие языковые решения были приняты, чтобы запретить эту функцию?

6

Решение

  • Поскольку C ++ спроектирован как переносимый язык, то есть тот, который компилируется на многих процессорах (например, x86, ARM, LSI-11/2, с такими устройствами, как игровые парни, мобильные телефоны, морозильные камеры, самолеты, чипы для манипуляции человеком и лазерные мечи).
    • доступные флаги для разных процессоров могут сильно отличаться
    • даже в одном и том же процессоре флаги могут отличаться (принять скалярные и векторные инструкции x86)
    • некоторые процессоры могут даже не иметь желаемого флага
  • На вопрос нужно ответить: Должен ли компилятор всегда поставлять / включать этот флаг, когда он не может определить, используется ли он вообще?, который не соответствует платить только за то, что вы используете неписаный, но святой закон как C, так и C ++
  • Потому что компиляторы должны быть запрещены для оптимизации и, например, изменить порядок кода, чтобы эти флаги были действительными

Пример для последнего:

int x = 7;
x += z;
int y = 2;
y += z;

Оптимизатор может преобразовать это в этот псевдо-ассемблерный код:

alloc_stack_frame 2*sizeof(int)
load_int 7, $0
load_int 2, $1
add z, $0
add z, $1

который в свою очередь будет больше похож на

int x = 7;
int y = 2;
x += z;
y += z;

Теперь, если вы запрашиваете регистры между

int x = 7;
x += z;
if (check_overflow($0)) {...}
int y = 2;
y += z;

затем, после оптимизации и разборки вас может быть конец с этим:

int x = 7;
int y = 2;
x += z;
y += z;
if (check_overflow($0)) {...}

что тогда неверно.

Можно создать больше примеров, например, что происходит с переполнением при постоянном сворачивании-компиляции.


Sidenotes: Я помню старый компилятор Borland C ++ с небольшим API для чтения текущих регистров процессора. Однако приведенная выше аргументация об оптимизации все еще применима.

На другом знаке: для проверки на переполнение:

// desired expression: int z = x + y
would_overflow = x > MAX-y;

более конкретный

auto would_overflow = x > std::numeric_limits<int>::max()-y;

или лучше, менее конкретно:

auto would_overflow = x > std::numeric_limits<decltype(x+y)>::max()-y;
6

Другие решения

Потому что C и C ++ разработаны, чтобы быть независимыми от платформы. Статус в реестре нет.

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

Кстати, вы не может полагаться на неопределенное поведение для обнаружения переполнения, потому что разумные компиляторы увидев

if(a < a + 100)

напишет предупреждение и скомпилирует

if(true)

… (при условии, что оптимизации включены, а конкретная оптимизация не отключена).

И обратите внимание, что вы не можете положиться на предупреждение. Компилятор выдаст предупреждение только когда условие закончится true или же false после эквивалентных преобразований, но есть много случаев, когда условие будет изменено при наличии переполнения, не заканчивая как простой true/false,

8

Я могу думать о следующих причинах.

  1. Предоставляя доступ к флагам регистрации, переносимость языка между платформами жестко ограничена.

  2. Оптимизатор может радикально изменить выражения и сделать ваши флаги бесполезными.

  3. Это сделало бы язык более сложным

  4. Большинство компиляторов имеют большой набор встроенных функций для выполнения наиболее распространенных операций (например, сложение с переносом) без использования флагов.

  5. Большинство выражений можно переписать безопасным способом, чтобы избежать переполнения.

  6. Вы всегда можете вернуться к встроенной сборке, если у вас есть очень специфические потребности

Доступ к регистрам статуса кажется недостаточно необходимым, чтобы пройти процесс стандартизации.

4
По вопросам рекламы [email protected]