为什么乘以常数有符号整数分数没有优化?

xib*_*ibu 14 c c++ compiler-optimization

由于有符号整数溢出是未定义的行为,我希望下面的三个函数编译为相同或相似的程序集。然而事实并非如此。与其他示例test2略有不同,test1test3使用了两条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;
}
Run Code Online (Sandbox Code Playgroud)

编译器资源管理器上的比较

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

sup*_*cat 1

这种优化是否正确将取决于实现对整数溢出影响的保证(如果有的话):

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

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