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

            Bug ID: 82158
           Summary: _Noreturn functions that do return clobber caller's
                    registers on ARM32 (but not other arches)
           Product: gcc
           Version: 7.1.0
               URL: https://godbolt.org/g/GhW4b8
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: peter at cordes dot ca
  Target Milestone: ---
            Target: arm*-*-*

ARM32 clobbers a call-preserved register in a function which does actually
return.  (It was declared _Noreturn, but gcc chooses to make a function which
returns instead of crashing).

Not sure if this is a feature or bug (came up in
https://stackoverflow.com/a/45982153/224132).  Probably a bug, since saving
registers in a noreturn function is potentially useful for backtraces (and gcc
avoids tailcall with noreturn for that reason), even if exceptions are disabled
so stack-unwinding doesn't need them.  And this is only happening on ARM32, not
the others I looked at: ARM64, MIPS, MIPS64, PowerPC64, MSP430, x86 -m32, or
x86 -m64.  (But ARM's multi-operand-at-once POP is probably handled specially,
so that could explain the difference...)

Anyway, in a function declared noreturn which actually *does* return, ARM32
still does the optimization of clobbering "call-preserved" even though it also
generates code to return instead of just falling off the end of the function. 
This is C undefined behaviour so it's legal, but is this desirable?  gcc warns
about it (even without -Wall), but it's so likely to cause hard-to-debug
problems that I'd suggest erroring by default, or not doing the optimization.


// https://godbolt.org/g/GhW4b8 for gcc6.3, also tested locally with gcc7.1

void ext(void);

ATTRIBUTE
void foo(int *p, int y) {
    ext();
    *p = y;   // then use args that had to survive a call
}

On ARM32 with gcc7.1 -O3 -DATTRIBUTE=_Noreturn produces this:

7 : <source>:7:1: warning: 'noreturn' function does return

foo:
  @ Function supports interworking.
  @ Volatile: function does not return.               # This line not present
without _Noreturn
  @ args = 0, pretend = 0, frame = 0
  @ frame_needed = 0, uses_anonymous_args = 0
  push {r4, lr}
  mov r5, r1
  mov r4, r0
  bl ext
  str r5, [r4]
  pop {r4, lr}
  bx lr


With ATTRIBUTE= (empty), we get push/pop {r4, r5, r6, lr} and things are
otherwise the same.  See https://godbolt.org/g/hYnrrD for a diff.

Reply via email to