Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC assembly code generation bug and workaround

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.

like image 808
queen3 Avatar asked Dec 20 '22 22:12

queen3


1 Answers

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.

like image 130
Karoly Horvath Avatar answered Dec 24 '22 02:12

Karoly Horvath