基于整数溢出的GCC优化

时间:2014-05-27 12:06:39

标签: c++ c gcc compiler-optimization integer-overflow

最近我讨论了一个想要检查签名int溢出的人,如if (A + B < 2 * max(A, B))。让我们暂时忽略逻辑本身是错误的,并在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. */
}

它会触发您期望的两个补码表示的分支。同样,此代码使条件分支保持不变

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)的模式,它会被优化掉?当我在写这篇文章之前谷歌搜索时,似乎最后的片段应该被优化掉了,但是我无法在溢出检查中重现这种错误,这种错误不会明确地与常量一起运行。

2 个答案:

答案 0 :(得分:4)

许多编译器将使用&#34; false&#34;替换涉及有符号整数或指针的表达式,如

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

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

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

要内联,用q = p和q = p + 1代替,不做任何检查,这样做是件好事。

if (A + abs (A) < 0)

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

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

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

答案 1 :(得分:1)

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

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

您提供的编译器是GCC的特定版本,并且您提供的表达式属于窄范围,但我认为您还有兴趣了解其他编译器或密切相关的表达式。

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

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

数据流分析是CPU和内存密集型的,并且往往在有大量收益的地方使用。最终,C ++标准将停止更改(如此多),编译器编写者将有时间在他们手中。当编译器读取素数筛子程序并将其优化为单个打印语句时,我们仍然有点不足,但它会来。

我的回答主要是指出这实际上是一个关于编译器技术的问题,与C ++标准没什么关系。也许你应该直接向GCC小组询问。