Hello,
I bumped into a strange bug today: the program hang at a point,
inspecting the disassembly revealed that it was a single jmp instruction
which jumped onto itself. Most of the code from the C++ function was
missing. The bug occurs at -O2 or -O3, -O1 generates correct code. The
function itself didn't change recently, but some referenced classes did;
earlier the code was correct even at -O3.
It turned out that during maintenance, an infinite recursion creeped in.
At -O1, the program crashed with SIGSEGV as expected due to exhausting
the stack.
Details:
The original structure looked like this:
struct J;
struct S
{
J& m_root;
S(J& j) : m_root(j) {}
void Dump(Printer&);
};
struct J
{
S m_root;
J() : m_root(*this) {}
S& GetRoot() { return m_root; }
void Dump(Printer& p) { m_root.Dump(p); }
};
An S is embedded in J, and J::Dump() is forwarded to it. Then, some
minor changes occured: instead of embedding S, inheritance was used:
struct J : S
{
// m_root removed
J() : S(*this);
// GetRoot() removed
void Dump(Printer& p) { m_root.Dump(p); }
};
Accidentally, J::Dump() was not removed, and by the incident that both S
and J had an m_root member in the first version, m_root.Dump(p) called
itself in an infinite recursion.
Here is the test case:
----8<---8<----8<---8<----8<---8<----8<---8<---
struct J;
struct S
{
J& j;
S(J& j_) : j(j_) { }
void F() { }
};
struct J : S
{
J() : S(*this) { }
void F() { j.F(); }
};
void foo()
{
J j;
j.F();
}
int main()
{
foo();
}
----8<---8<----8<---8<----8<---8<----8<---8<---
The generated code at -O3 is:
(gdb) disassemble foo
Dump of assembler code for function foo():
0x0000000000400470 <+0>: jmp 0x400470 <foo()>
End of assembler dump.
-O1 is interesting:
(gdb) disassemble foo
Dump of assembler code for function foo():
0x000000000040046c <+0>: sub rsp,0x18
0x0000000000400470 <+4>: lea rax,[rsp]
0x0000000000400474 <+8>: mov QWORD PTR [rsp],rax
0x0000000000400478 <+12>: mov rdi,rsp
0x000000000040047b <+15>: call 0x400498 <J::F()>
0x0000000000400480 <+20>: add rsp,0x18
0x0000000000400484 <+24>: ret
End of assembler dump.
So far so good, but J::F() is strange:
Dump of assembler code for function J::F():
0x0000000000400498 <+0>: sub rsp,0x8
0x000000000040049c <+4>: mov rax,QWORD PTR [rdi]
0x000000000040049f <+7>: mov rax,QWORD PTR [rax]
0x00000000004004a2 <+10>: mov rax,QWORD PTR [rax]
0x00000000004004a5 <+13>: mov rax,QWORD PTR [rax]
0x00000000004004a8 <+16>: mov rax,QWORD PTR [rax]
0x00000000004004ab <+19>: mov rax,QWORD PTR [rax]
0x00000000004004ae <+22>: mov rax,QWORD PTR [rax]
0x00000000004004b1 <+25>: mov rax,QWORD PTR [rax]
0x00000000004004b4 <+28>: mov rdi,QWORD PTR [rax]
0x00000000004004b7 <+31>: call 0x400498 <J::F()>
0x00000000004004bc <+36>: add rsp,0x8
0x00000000004004c0 <+40>: ret
End of assembler dump.
What are those mov rax,QWORD PTR [rax]'s ?
My question is: wouldn't it be possible to print a warning when a jmp to
itself or trivial infinite recursion is generated? The code compiled
fine w/ -Wall -Wextra -Werror w/ 4.6 and 4.7.
Cheers, Peter