基于整数溢出的GCC优化

GCC optimizations based on integer overflow

本文关键字:溢出 GCC 优化 整数 于整数      更新时间:2023-10-16

最近我讨论了一个关于有人想要检查像if (A + B < 2 * max(A, B))这样的signed int溢出的问题。让我们暂时忽略逻辑本身是错误的,并讨论C/c++上下文中的有符号整数溢出。(我认为它完全继承了C语言的这部分标准)。

哪些需要有符号整数溢出的检查将被当前的GCC优化掉,哪些不会?

由于原文并没有很好地表述,而且明显存在争议,我决定稍微改变一下这个问题,但将原文保留在下面。

下面使用的所有示例都测试了gcc版本4.7.2 (Debian 4.7.2-5),并使用-O3

编译

也就是说,它是未定义的,并且GCC臭名昭著地使用它来执行一些分支简化。我想到的第一个例子是

int i = 1;
while (i > 0){
    i *= 2;
}

会产生一个无限循环。这种优化的另一种情况是

if (A + 2 < A){
    /* Handle potential overflow */
}

,其中,假设A是有符号整型,溢出分支被完全移除。

更有趣的是,一些容易证明的整数溢出的情况没有被改变,例如
if (INT_MAX + 1 < 0){
    /* You wouldn't write this explicitly, but after static analysis the program
       could be shown to contain something like this. */
}

,它触发您期望的2的补码表示的分支。类似地,这段代码保留了条件分支

int C = abs(A);
if (A + C < 0){
    /* For this to be hit, overflow or underflow had to happen. */
}

现在的问题,是否有一个模式,看起来大致像if (A + B < C)if (A + B < c),将被优化掉?在写这篇文章之前,我在谷歌上搜索了一下,似乎最后一个代码片段应该被优化掉,但我无法在没有显式地使用constant操作的溢出检查中重现这种错误。

许多编译器会将包含有符号整数或指针的表达式替换为"false",如

a + 1 < a // signed integer a
p + 1 < p // Pointer p

,表达式只能在未定义行为的情况下为真。另一方面,这允许

for (char* q = p; q < p + 2; ++q) ...

被内联,代入q = p和q = p + 1,不需要任何检查,这是件好事。

if (A + abs (A) < 0)
对于许多编译器来说,

可能太复杂了。请注意,对于无符号整数,没有未定义行为。因此,使用unsigned 32位整数和64位指针的循环往往比必要的要慢,因为必须考虑环绕行为。对于unsigned 32位整数和64位指针,

&p [i] > &p [i+1]

没有未定义的行为(不包括64位整数或32位指针)。

如果我可以解释一下你的问题,我相信你是在问这样的问题。

是否存在这样一种编译器,它对有符号整数表达式进行了如此积极的优化,以至于它准备对此类表达式的某些类别进行详细分析,以确定表达式结果的整个range of representable values for the type中的依赖条件为真(或假),并通过这些方法删除条件测试?

您提供的编译器是GCC的一个特定版本,您提供的表达式属于一个狭窄的范围,但我认为您也有兴趣了解另一个编译器或密切相关的表达式。

答案是现在我不知道,但这可能只是时间问题。

现有的编译器对包含常量或某些可识别模式的表达式执行过早的求值,如果在求值过程中遇到未定义的行为,通常会避免对表达式进行优化。他们没有义务这样做。

数据流分析是CPU和内存密集型的,往往用于有很大好处的地方。最终,c++标准将停止改变(这么多),编译器编写者将有更多的时间。我们离编译器读取质数筛选程序并将其优化为单个print语句的那一天还有一点远,但它将会到来。

我回答的要点是指出这实际上是一个关于编译器技术的问题,与c++标准没有多大关系。