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.