代码之家  ›  专栏  ›  技术社区  ›  Shcheklein

这是MS CL全局优化中的错误吗(/og)?

  •  1
  • Shcheklein  · 技术社区  · 15 年前

    下面是一个非常简单的程序,它再现了我在实际应用中面临的问题:

    #include "stdlib.h"
    typedef unsigned short int shft;
    struct xptr  {
        int layer;
        void *  addr;
    
        xptr() : layer(0), addr(NULL) {}
        xptr(int _layer, void *_addr) : layer(_layer), addr(_addr) {}
    
        xptr(const xptr& x) { layer = x.layer; addr = x.addr; }
    
    /* Uncomment this to remove the bug */
    /*   const xptr& operator= (const xptr& rhs) 
        {
            if (this != &rhs) {
                this->addr = rhs.addr;
                this->layer = rhs.layer;
            }
            return *this;
        }*/
    };
    
    
    struct n_dsc {
        xptr    pdsc;      
        xptr    ldsc;      
        xptr    rdsc;      
    };
    
    
    void dm_string_value_traverse(xptr node)
    {    
        xptr p = node;    
        while (p.addr != 0)
        {        
            p = ((n_dsc*)p.addr)->rdsc;    
        }
    }
    
    int main()
    {
        n_dsc n1, n2;
    
        n1.rdsc = xptr(0, &n2);
        xptr n = xptr(0, &n1);
    
        dm_string_value_traverse(n);
    }
    

    我完全不明白为什么cl 2008(with/og/zi/w4)为“dm_string_value_travel”函数生成以下程序集:

        while (p.addr != 0)
    004ABAC5  mov         eax,dword ptr [esp+10h] 
    004ABAC9  add         esp,0Ch 
    004ABACC  test        eax,eax 
    004ABACE  je          dm_string_value_traverse+5Fh (4ABADFh) 
        {
            p = ((n_dsc*)p.addr)->rdsc;
    004ABAD0  mov         ecx,dword ptr [eax+1Ch] 
    004ABAD3  mov         dword ptr [esp],ecx 
    004ABAD6  mov         eax,dword ptr [eax+20h] 
    004ABAD9  mov         dword ptr [esp+4],eax 
    004ABADD  jmp         dm_string_value_traverse+50h (4ABAD0h) ;NOTE THIS JMP TO THE ASSIGNMENT
        }
    

    }

    注:

    1. 条件(P.ADDR!=0)仅检查一次!请参见004ABADD无条件跳转到004ABAD0(分配)。
    2. 如果没有/og编译器,则生成正确的代码。
    3. 如果取消对复制构造函数的注释,编译器也将生成良好的代码。

    能理解为什么会这样吗?有办法解决这个问题吗?

    2 回复  |  直到 15 年前
        1
  •  2
  •   Oren Trutner    15 年前

    这看起来像是一个过分热心的优化器。我可以用Visual Studio 2008 SP1、/og和/fa确认您描述的程序集输出行为。这对风投来说不是第一次:试试看 google visual c++ "/Og" site:support.microsoft.com .

    解决方法之一是使用xptr指针而不是xptr值进行迭代。这还有一个有益的副作用,就是将每次迭代复制的字节数从8(xptr值)减少到4(xptr指针)。

    void dm_string_value_traverse(xptr node)
    {    
        xptr *p = &node;    
        while (p->addr != 0)
        {        
            p = &(((n_dsc*)(p->addr))->rdsc);    
        }
    }
    

    生成的带有/og的程序集代码现在看起来像这样。作为比较,我无法将程序集精确映射到源代码 (p->addr != 0) 现在发生在两个地方。然而,很明显,循环现在包含了对其结束条件的测试。

    ; prolog
          push  ebp
          mov   ebp, esp
    ; while (p->addr != 0)
          mov   eax, DWORD PTR _node$[ebp+4]
          test  eax, eax
          je    SHORT $LN1@dm_string_
          npad  6
    ; p = &(((n_dsc*)p->addr)->rdsc);
    $LL2@dm_string_:
          mov   eax, DWORD PTR [eax+20]
          test  eax, eax
          jne   SHORT $LL2@dm_string_
    $LN1@dm_string_:
    ; epilog
          pop   ebp
          ret   0
    

    考虑到这类bug有多难处理,代码中可能隐藏着类似的问题。对于处理xptr和n_dsc的代码部分,您可能需要考虑不使用/og,或者在四周进行单元测试。

        2
  •  0
  •   Matthew Iselin    15 年前

    也许您应该尝试将“p”设置为易失性:

    volatile xptr p = node;    
    while (p.addr != 0)
    {        
        p = ((n_dsc*)p.addr)->rdsc;    
    }
    

    这应该会阻止乐观主义者避免进行比较。