Считается ли целочисленное переполнение со знаком в безопасном Rust в режиме выпуска неопределенным поведением? - PullRequest
3 голосов
/ 15 февраля 2020

Rust по-разному обрабатывает целочисленное переполнение со знаком в режиме отладки и выпуска. Когда это происходит, Rust паникует в режиме отладки, в то время как молча выполняет перенос двух дополнений в режиме выпуска.

Насколько я знаю, C / C ++ рассматривает переполнение целых чисел со знаком как неопределенное поведение отчасти потому, что:

  1. В то время, когда C стандартизировал, другая базовая архитектура представления целых чисел со знаком, такая как дополнение, могла еще где-то использоваться. Компиляторы не могут делать предположения о том, как обрабатывается переполнение в аппаратном обеспечении.
  2. Более поздние компиляторы, делающие предположения, такие как сумма двух положительных целых чисел, также должны быть положительными для генерации оптимизированного машинного кода.

Таким образом, если компиляторы Rust выполняют те же виды оптимизации, что и компиляторы C / C ++, в отношении целых чисел со знаком, то почему Rustonomicon заявляет:

Независимо от того, что может сделать Safe Rust ' t вызывает неопределенное поведение.

Или даже если компиляторы Rust не выполняют такую ​​оптимизацию, программисты Rust по-прежнему не ожидают увидеть обтекание целым числом со знаком. Разве это не может быть названо "неопределенным поведением"?

1 Ответ

5 голосов
/ 15 февраля 2020

Q: Так что, если компиляторы Rust выполняют ту же самую оптимизацию, что и компиляторы C / C ++ в отношении целых чисел со знаком

Rust - нет. Поскольку, как вы заметили, он не может выполнить эту оптимизацию, поскольку целочисленные переполнения хорошо определены.

Для добавления в режим выпуска Rust выдаст следующую инструкцию LLVM (вы можете проверить на игровой площадке ):

add i32 %b, %a

С другой стороны, clang выдаст следующую инструкцию LLVM (вы можете проверить через clang -S -emit-llvm add.c):

add nsw i32 %6, %8

Разница составляет nsw ( без подписи). Как указано в ссылке LLVM о add:

Если сумма имеет переполнение без знака, возвращаемый результат является математическим результатом по модулю 2n, где n - ширина бита результат.

Поскольку целые числа LLVM используют представление в виде дополнения до двух, эта инструкция подходит для целых чисел как со знаком, так и без знака. «Без подписи», соответственно. Если присутствуют ключевые слова nuw и / или nsw, результирующее значение дополнения является ошибочным значением, если происходит переполнение без знака и / или со знаком, соответственно.

Значение яда что приводит к неопределенному поведению. Если флаги отсутствуют, результат хорошо определяется как перенос дополнения 2.


Q: Или даже если компиляторы Rust не выполняют такую ​​оптимизацию, программисты Rust все еще не ожидайте увидеть подписанное целое число, оборачивающееся вокруг. Разве это не может быть названо «неопределенным поведением»?

«Неопределенное поведение», как используется в этом контексте, имеет очень специфическое c значение, которое отличается от интуитивного значения Engli sh два слова. В частности, UB здесь означает, что компилятор может предполагать, что переполнение никогда не произойдет, и что , если произойдет переполнение, любое поведение программы разрешено. Это не то, что указывает Rust.

Однако целочисленное переполнение с помощью арифметических c операторов считается ошибкой в ​​Rust. Это потому, что, как вы сказали, это обычно не ожидается. Если вы намеренно хотите использовать режим обтекания, есть такие методы, как i32::wrapping_add.


Некоторые дополнительные ресурсы:

...