【问题标题】:Why are multiplications by constant signed integer fractions not optimised?为什么不优化常数有符号整数分数的乘法?
【发布时间】:2020-10-10 17:34:04
【问题描述】:

由于有符号整数溢出是未定义的行为,我希望下面的三个函数编译为相同或相似的程序集。然而事实并非如此。 test2test1 略有不同,test3 使用两个 imul 指令,其他示例不需要。

int test1(int x)
{
    return x * 5 / 2;
}

int test2(int x)
{
    return x * 10 / 4;
}

int test3(int x)
{
    return x * 50 / 20;
}

comparison on compiler explorer

编译器不执行这种优化是否有原因?

【问题讨论】:

  • 我认为正式的 C++ 规范本身没有任何东西可以阻止这种优化。我认为这就像“以前没有人想到过”一样简单,可以尝试对整数乘法和除法进行微优化,以找到一些数学上相同的序列,其乘法和除法步骤可以通过优化的机器语言完成说明。
  • 如果这两个因素抵消为整数值,则执行您期望的优化。我不确定舍入除法是否会表现不同,或者编译器是否无法证明它没有。
  • 我怀疑这与 GCC 支持包装有符号整数溢出有关。这不是 C 标准所要求的,但在 GCC 文档中有所提及。 x * 5 / 2x * 10 / 4x * 50 / 20 在存在溢出的情况下会产生不同的结果,因此它们无法针对相同的代码进行优化。
  • @WeatherVane 这不一定是为了避免溢出。如果 GCC 有明确定义的有符号溢出,那么它必须确保它总是表现得好像确实发生了溢出一样。
  • 欢迎补丁。优化只在编译器中进行,因为有人有动机实现了它们。

标签: c++ c compiler-optimization


【解决方案1】:

这样的优化是否正确取决于实现是否保证整数溢出的影响:

  1. 如果一个实现保证整数加法和乘法总是表现得好像使用足够大的值来保存结果,然后在每次操作后将二进制补码截断为类型的大小,则将 x*(m*a)/(m*b) 替换为 x*a/b 会违反此类保证。
  2. 如果实现保证整数加法和乘法总是表现得好像它们产生了一些数字,但不保证临时值将被截断为任何特定大小,那么这样的优化将是有效的。
  3. 如果实现不保证溢出的影响,并要求程序员不惜一切代价避免它们,因为即使在不使用结果的程序部分中它们也可能导致错误行为,那么这样的优化将是有效的.

gcc 编译器提供了支持强保证选项 #1 或不提供任何保证的选项,并且整数溢出破坏不使用结果的部分代码的可能性不仅仅是理论上的。因为选项#3 对于从潜在不可靠来源接收输入的程序来说是不计后果的危险,并且因为 gcc 不提供 #1 和 #3 之间的任何设置,所以许多程序,包括选项 #2 会满足要求的程序,都是使用强制选项 #1 的 fwrapv 标志构建。

【讨论】:

    猜你喜欢
    • 2013-06-02
    • 1970-01-01
    • 2016-02-17
    • 2022-01-20
    • 2020-03-27
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    相关资源
    最近更新 更多