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

时间:2009-08-15 14:52:35

标签: optimization visual-c++ compiler-construction

这是一个非常简化的程序,它重现了我在实际应用中遇到的问题:

#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(使用/ Og / Zi / W4)为“dm_string_value_traverse”函数生成以下程序集:

    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. 如果取消注释,复制构造函数编译器也会生成良好的代码。
  4. 有可能理解为什么会这样吗?有没有办法解决这个问题?

2 个答案:

答案 0 :(得分:2)

这看起来像是一个过分热心的优化者。我可以确认您使用Visual Studio 2008 SP1,/ Og和/ Fa描述的用于程序集输出的行为。这不是VC的第一次:尝试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

考虑到这类错误是多么棘手,可能存在潜伏在代码中隐藏的类似问题。对于处理xptr和n_dsc的代码部分,您可能需要考虑不使用/ Og,或者对它进行单元测试。

答案 1 :(得分:0)

也许您应该尝试将“p”设置为volatile:

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

这应该阻止优化者避免比较。