Zhi*_* Ma 5 integer-overflow rust llvm-codegen
Rust 在调试和发布模式下以不同的方式处理有符号整数溢出。当它发生时,Rust 在调试模式下会发生恐慌,而在发布模式下默默地执行二进制补码包装。
据我所知,C/C++ 将有符号整数溢出视为未定义行为,部分原因是:
因此,如果 Rust 编译器确实在有符号整数方面执行与 C/C++ 编译器相同类型的优化,那么为什么The Rustonomicon指出:
无论如何,Safe Rust 不会导致未定义行为。
或者即使 Rust 编译器不执行这样的优化,Rust 程序员仍然不希望看到有符号整数环绕。不能称为“未定义行为”吗?
问:那么,如果 Rust 编译器确实对有符号整数执行了与 C/C++ 编译器相同类型的优化
锈没有。因为,正如您所注意到的,它无法执行这些优化,因为整数溢出是明确定义的。
对于发布模式的添加,Rust 将发出以下 LLVM 指令(您可以查看Playground):
add i32 %b, %a
Run Code Online (Sandbox Code Playgroud)
另一方面,clang 将发出以下 LLVM 指令(您可以通过查看clang -S -emit-llvm add.c):
add nsw i32 %6, %8
Run Code Online (Sandbox Code Playgroud)
不同之处在于nsw(无签名换行)标志。正如LLVM 参考中add所指定的:
如果和有无符号溢出,返回的结果是数学结果模 2n,其中 n 是结果的位宽。
由于 LLVM 整数使用二进制补码表示,因此该指令适用于有符号和无符号整数。
nuw并分别nsw代表“No Unsigned Wrap”和“No Signed Wrap”。如果nuw和/或nsw关键字存在,并且分别发生无符号和/或有符号溢出,则加法的结果值是毒值。
毒值是导致未定义行为的原因。如果标志不存在,则结果很好地定义为 2 的补码包装。
问:或者即使 Rust 编译器不执行这样的优化,Rust 程序员仍然不希望看到有符号整数环绕。不能称为“未定义行为”吗?
在此上下文中使用的“未定义行为”具有非常具体的含义,与这两个词的直观英文含义不同。UB 在这里具体意味着编译器可以假设永远不会发生溢出,并且如果发生溢出,则允许任何程序行为。这不是 Rust 规定的。
然而,通过算术运算符的整数溢出被认为是 Rust 中的一个错误。那是因为,正如你所说,它通常是无法预料的。如果您有意想要包装行为,可以使用诸如i32::wrapping_add.
一些额外的资源: