【问题标题】:Divide by zero with loop optimization VC++ 2015使用循环优化 VC++ 2015 除以零
【发布时间】:2018-04-26 09:36:50
【问题描述】:

史前史:

我们刚刚将我们的开发环境从 VC++ 2008 切换到 VC++ 2015。之后我们发现了问题:尽管在 C++ 代码中测试了除法器,但程序引发了除数为 0。

测试代码:

#include <cstdlib>

int test(int n, int test_for_zero)
{
    int result = 0;
    for (int i = 0; i < n; ++i) {

        if (test_for_zero)
            result += rand() >> ((8 % test_for_zero) * test_for_zero);
    }

    return result;
}

int main()
{
    return test(rand(), rand() & 0x80000000);
}

由具有默认发布选项的 VC++ 2015 Update 3 或 VC++ 2017 编译,运行时将除数为零。用VC 2008编译运行就好了。

分析:

; 6    :    for (int i = 0; i < n; ++i) {

    test    edi, edi
    jle SHORT $LN15@main

; 7    : 
; 8    :        if (test_for_zero)
; 9    :            result += rand() >> ((8 % test_for_zero) * test_for_zero);

    mov ecx, DWORD PTR _test_for_zero$1$[ebp]
    mov eax, 8
    cdq
    idiv    ecx  ; <== IT'S HERE, IDIV BEFORE CHECKING FOR ZERO
    mov eax, edx
    imul    eax, ecx
    mov DWORD PTR tv147[ebp], eax
    test    ecx, ecx
    je  SHORT $LN15@main
$LL11@main:
    call    ebx
    mov ecx, DWORD PTR tv147[ebp]
    sar eax, cl
    add esi, eax
    sub edi, 1
    jne SHORT $LL11@main

编译器从循环体中取出常量部分((8 % test_for_zero) * test_for_zero),并在除法之前忘记测试test_for_zero。显然,只需执行编译器工作,就可以很容易地修复它,但要正确。

我使用了几个编译器选项,例如 -d2SSAOptimizer--Oxx,但解决此问题的唯一选项是 -Od

问题:

  1. 是否是错误?自 VC 2008 以来,C++ 标准发生了重大变化,所以它会受到这样的影响吗?
  2. 主要问题是,除了 -Ob 之外,是否有任何解决方法可以通过编译器选项解决此问题?

【问题讨论】:

  • 这并不能回答您的问题,但是如果该变量为 0,那么完全跳过循环不是更有意义吗?你永远不会改变它,所以如果它在一次迭代中为 0,那么所有的都是 0,在这种情况下循环不会做任何事情。
  • 另外,如果您将条件更改为检查 0 而不是仅检查数字是否为真,会发生什么情况?应该没关系,但是这个错误没有意义。
  • 也可能是缺少大括号。我想知道 C++ 究竟是如何解析这样的语句的。尝试添加大括号。
  • 这是一个错误。很难提供超越#pragma optimize 的解决方法,过于综合。在 VS2017 中使用帮助 > 发送反馈 > 报告问题。

标签: visual-c++ visual-studio-2015 visual-studio-2017 compiler-optimization divide-by-zero


【解决方案1】:

感谢您的报告和小型重现(尤其是来自 Visual Studio 内部的反馈!),他们对隔离问题非常有帮助。

这确实是编译器本身的一个错误,特别是“不变代码运动”优化过程中的安全检查。我已经对此进行了修复,它应该出现在 VS 15.7 中。

目前,最简单的解决方法是通过 https://docs.microsoft.com/en-us/cpp/preprocessor/optimize 禁用对具有此代码模式的函数的优化:

    #pragma optimize( "", off )  
    <Function containing code with this loop>
    #pragma optimize( "", on )  

不幸的是,这个优化过程直接绑定到优化器,所以除了完全禁用优化器 (-Od) 之外没有编译器选项可以解决这个问题。

【讨论】:

    猜你喜欢
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    • 2021-06-22
    • 2016-07-14
    • 1970-01-01
    相关资源
    最近更新 更多