【问题标题】:GCC optimizations based on integer overflow基于整数溢出的 GCC 优化
【发布时间】:2014-05-27 12:06:39
【问题描述】:

最近我讨论过有人想像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 &lt; C)if (A + B &lt; c) 的模式会被优化掉?当我在写这篇文章之前四处搜索时,似乎最后一个 sn-p 应该被优化掉,但我无法在没有明确使用常量的溢出检查中重现这种错误。

【问题讨论】:

  • 您的最后一个示例比其他示例复杂得多。它需要通过函数调用跟踪 A 和 C 之间的依赖关系,并在其他函数已经需要的基础上对该函数进行“数学理解”。
  • 我真的不明白你到底要什么。 “看起来大致像这样”有点模糊
  • 问题是,因为它是未定义的行为,任何答案都是有效的。
  • @KerrekSB 我会说它臭名昭著,因为试图检查溢出以防止所述恶意用户搞砸事情,以使代码运行更快的名义得到优化。老实说,我认为标准的整个部分都是一团糟(可以通过将其定义为实现而不是未定义来防止)。
  • @Xarn 我只能说你错了。事实上,在无符号类型的情况下定义行为存在很多反对意见。定义的行为在某些架构上具有显着的运行时成本。并且某些架构确实(或至少确实)捕获了溢出,这也是造成这种未定义行为的重要原因。

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


【解决方案1】:

许多编译器会将涉及有符号整数或指针的表达式替换为“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)

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

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

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

【讨论】:

  • 指针不可能。指针算法在数组边界之外未定义。
  • @R..:可能有一个数组,其大小太大而无法放入uint32_t,在这种情况下,指示的表达式不会包含任何 UB。
【解决方案2】:

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

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

您提供的编译器是特定版本的 GCC,您提供的表达式范围很窄,但我假设您也有兴趣了解其他编译器或密切相关的表达式。

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

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

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

我的回答主要是要指出,这其实是一个关于编译器技术的问题,和C++标准关系不大。或许你应该直接询问 GCC 小组。

【讨论】:

  • 感谢您的回答,但实际问题有些不同(经过深思熟虑后,我在 OP 中正确制定了它)。
  • 似乎是同一个问题,只是您将其限制为仅 GCC。我的答案(最后是段落)更正确。
猜你喜欢
  • 2020-12-03
  • 1970-01-01
  • 2014-04-02
  • 2021-04-25
  • 2017-08-26
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多