https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66795

            Bug ID: 66795
           Summary: Incorrect and missed optimizations of
                    __builtin_frame_address
           Product: gcc
           Version: 5.1.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: luto at mit dot edu
  Target Milestone: ---

Here's an evil little program (x86_64-specific, although I suspect that a
trivial change would have the same effect on x86_32):

#include <stdio.h>
#include <unistd.h>

void target(void)
{
        printf("Here\n");
        _exit(0);
}

 __attribute__((noinline,noclone)) void escape(void *x)
{
        /* Weird: this is needed to cause the call to escape to be emitted. */
        asm volatile ("");
}

int main()
{
        unsigned long *frame = __builtin_frame_address(0);
        frame[1] = (unsigned long)target;

        /* Uncommenting this fixes it:
        asm volatile ("nopq %0" : : "m" (frame[1]));
        */

        /* Oddly, this does *not* fix it. */
        asm volatile ("nopq %0" : : "r" (frame));

        /* Uncommenting this also fixes it:
        escape(&frame);
        */

        return 0;
}

Compiled without -O2, this program prints "Here" and exits.  Compiled with -O2,
it does nothing.  Examining the asm indicates that the assignment to frame[1]
never happens.  I think this means that, somehow, that assignment is considered
a dead store, despite the fact that it's very much not dead.

For one thing, it's an assignment to the return frame.  It has an effect. 
(Yes, this is evil, but I actually want to do something like this in Linux to
access the caller's frame where the caller is written in assembly.)

My best guess is that the return value from __builtin_frame_address is treated
as a pointer to a local, non-escaped array.  But this makes no sense to me
(even if it were a reasonable assumption), as the "r" (frame) constraint should
cause the compiler to realize that it has escaped.

I think there's also a missed optimization here.  Using
__builtin_frame_address(0) appears to force the creation of a frame pointer. 
Why should it need to do that?  Gcc should be able to figure out where the
frame starts without having to refer to the base pointer.

Reply via email to