GCC 4.5.2 (on Ubuntu 11.10 x64 but compiling for 32 bit) generates invalid assembly code and I'm curious if it's possible to fix without changing code, just by applying options or something like that. Note that optimization is already -O0.
I have two functions:
inline long Class::Get()
{
long v = *(long*)(m_p);
m_p += 4;
return v;
}
inline void Class::Command()
{
m_p += Get();
}
GCC 4.5.2 generates this assembly code:
9840 m_p += Get();
f689eff5: mov 0x8(%ebp),%eax
f689eff8: mov 0xd4(%eax),%eax
f689effe: mov %eax,%esi
f689f000: mov 0x8(%ebp),%eax
f689f003: mov %eax,(%esp)
f689f006: call 0xf66116a0
f689f00b: lea (%esi,%eax,1),%edx
f689f00e: mov 0x8(%ebp),%eax
f689f011: mov %edx,0xd4(%eax)
As you can see it stores m_p value in %esi and later adds returned value to it using lea. BUT ::Get() does also change m_p! which GCC doesn't seem to be aware of. Thus the bug, m_p is incorrect (4 bytes less than expected) because value in %esi is obsolete.
I can fix it using
inline void Class::Command()
{
long v = Get();
m_p += v;
}
But I'm just wondering if I can apply some pragma or smth like that, without changing the code, to make the bug go away. As for gcc version I'm stuck with the given one.
That's not a bug. As you know, m_p += Get();
is really m_p = m_p + Get();
. The compiler is free to chose the order of evaluation for the addition. So fetching m_p
, then executing Get()
, then doing the addition is valid, and creates the code you just posted.
Your second example is different because you created a new sequence point. In this case Get()
is always evaluated first.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With