[arm-gnu] Strange assembly generated in G++ ARM Thumb mode
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[arm-gnu] Strange assembly generated in G++ ARM Thumb mode



I have a question about some assembly code that's being generated by
G++.  It looks wrong to me (and causes a crash when run), but I'm not
yet entirely certain whether it's an error in the compiler or some
kind of mistake in the code that it's compiling.  I'd appreciate any
insights.

A test program that illustrates the problem is the following:

-----------------------------------------------------------

#include <boost/signal.hpp>

bool func()
{
  return false;
}

int main(int argc, char *argv[])
{
  boost::signal<bool ()> sig;
  sig.connect(func);
  bool result = sig();
}

------------------------------------------------------------

When compiled with most combinations of ARM flags, this works just
fine (this is G++ 4.5.2 on Windows, with Boost version 1.40).
However, when I try to build this as -mthumb -Os, I get the following
code sequence down inside some random Boost function:

00000000 <boost::detail::postfix_increment_result<boost::signals::detail::slot_call_iterator<boost::signals::detail::call_bound0<bool>::caller<boost::functi
   0:   b537            push    {r0, r1, r2, r4, r5, lr}
   2:   1c05            adds    r5, r0, #0
   4:   ac01            add     r4, sp, #4
   6:   1c29            adds    r1, r5, #0
   8:   1c20            adds    r0, r4, #0
   a:   f7ff fffe       bl      0
<boost::detail::postfix_increment_result<boost::signals::detail::slot_call_iterator<boost::signals::detail::call_bound0<bo
   e:   1c28            adds    r0, r5, #0
  10:   f7ff fffe       bl      0
<boost::detail::postfix_increment_result<boost::signals::detail::slot_call_iterator<boost::signals::detail::call_bound0<bo
  14:   7820            ldrb    r0, [r4, #0]
  16:   bc37            pop     {r0, r1, r2, r4, r5}
  18:   bc02            pop     {r1}
  1a:   4708            bx      r1

I have no idea what this particular function does (it's Boost, so who
really does), but stepping through this code in a debugger shows that
its return value is getting corrupted.  Examining the assembly reveals
why.  This function returns a bool, and you can see it loading up r0
with the byte at [r4, #0] at the end of the function, in preparation
for function return.  However, immediately after that, we pop off
stack contents into r0-r2, destroying this value, and causing the
function to return garbage to its caller.

If I change the type of the return from bool to int, the problem goes away:

00000000 <boost::detail::postfix_increment_result<boost::signals::detail::slot_call_iterator<boost::signals::detail::call_bound0<int>::caller<boost::functio
   0:   b513            push    {r0, r1, r4, lr}
   2:   1c04            adds    r4, r0, #0
   4:   1c21            adds    r1, r4, #0
   6:   a801            add     r0, sp, #4
   8:   f7ff fffe       bl      0
<boost::detail::postfix_increment_result<boost::signals::detail::slot_call_iterator<boost::signals::detail::call_bound0<in
   c:   1c20            adds    r0, r4, #0
   e:   f7ff fffe       bl      0
<boost::detail::postfix_increment_result<boost::signals::detail::slot_call_iterator<boost::signals::detail::call_bound0<in
  12:   9801            ldr     r0, [sp, #4]
  14:   bc16            pop     {r1, r2, r4}
  16:   bc02            pop     {r1}
  18:   4708            bx      r1

This leads to a couple questions:

1)  Why is GCC saving r0-r2 anyway?  They're caller-saved, so it
shouldn't have any problem overwriting them in the function.
2)  Given that it is saving them, why is it restoring them at the
expense of the function's return value?
3)  Even in the second case, where the return value is being preserved
properly, we're getting this return value from [sp, #4], which is the
value that we're also going to then put into r2 during the final pop.
Why are we saving registers to the stack and then turning around and
using those locations as temp space?

My best guess at this point is that we're pushing r0-r2 during the
prologue as some poor man's way of creating temp space on the stack
without needing a separate 'sub sp, #' instruction, but then we're
forgetting about the fact that we're returning a value at the end of
the function, so we accidentally pop over the top of r0 during the
epilogue, or something like that.

I always hate to be the idiot who cries "compiler bug" at the first
sign of weirdness, only to eventually discover some more mundane
solution, but this does seem pretty weird to me.  Can anybody shed any
light on what's going on here?

Thanks,
Matt