这是一个非常简化的程序,它重现了我在实际应用程序中遇到的问题:
#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
}
}
可以理解为什么会发生这种情况吗?有办法解决这个问题吗?
这看起来像是一个过于热心的优化器。我可以确认您使用 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,或者对其进行单元测试。
也许你应该尝试将“p”设置为易失性:
volatile xptr p = node;
while (p.addr != 0)
{
p = ((n_dsc*)p.addr)->rdsc;
}
这应该会阻止优化器避免比较。