TL: DR: Es ist eine Fehloptimierung durch gcc .
noreturnist ein Versprechen an den Compiler, dass die Funktion nicht zurückkehren wird. Dies ermöglicht Optimierungen und ist insbesondere in Fällen nützlich, in denen es für den Compiler schwierig ist, zu beweisen, dass eine Schleife niemals beendet wird, oder auf andere Weise zu beweisen, dass es keinen Pfad durch eine zurückkehrende Funktion gibt.
GCC optimiert bereits, um maindas Ende der Funktion zu func()verlieren, wenn zurückgegeben wird, selbst mit der Standardeinstellung -O0(Mindestoptimierungsstufe), die Sie verwendet haben.
Die Ausgabe für func()sich könnte als verpasste Optimierung angesehen werden. es könnte einfach alles nach dem Funktionsaufruf weglassen (da der Aufruf nicht zurückgegeben werden kann, ist die einzige Möglichkeit, wie die Funktion selbst sein kann noreturn). Es ist kein gutes Beispiel, da printfeine Standard-C-Funktion bekanntermaßen normal zurückkehrt (es sei denn, Sie setvbufgeben stdouteinen Puffer an, der fehlerhaft ist?).
Verwenden wir eine andere Funktion, die der Compiler nicht kennt.
void ext(void);
int foo;
_Noreturn void func(int *p, int a) {
ext();
*p = a;
foo = 1;
}
void bar() {
func(&foo, 3);
}
( Code + x86-64 asm im Godbolt-Compiler-Explorer . )
Die Ausgabe von gcc7.2 für bar()ist interessant. Es inline func()und beseitigt den foo=3toten Speicher, so dass nur:
bar:
sub rsp, 8 ## align the stack
call ext
mov DWORD PTR foo[rip], 1
## fall off the end
Gcc geht weiterhin davon aus, dass ext()nach Rückkehr wird, sonst könnte es nur Schwanz genannt hat ext()mit jmp ext. Aber gcc ruft keine Tailcall- noreturnFunktionen auf, da dadurch Backtrace-Informationen für Dinge wie verloren gehen abort(). Anscheinend ist es in Ordnung, sie zu inlinieren.
Gcc hätte optimieren können, indem der movLaden auch nach dem weggelassen wurde call. Wenn extzurückgegeben wird, wird das Programm abgespritzt, sodass es keinen Sinn macht, diesen Code zu generieren. Clang macht diese Optimierung in bar()/ main().
funcselbst ist interessanter und eine größere verpasste Optimierung .
gcc und clang strahlen fast dasselbe aus:
func:
push rbp # save some call-preserved regs
push rbx
mov ebp, esi # save function args for after ext()
mov rbx, rdi
sub rsp, 8 # align the stack before a call
call ext
mov DWORD PTR [rbx], ebp # *p = a;
mov DWORD PTR foo[rip], 1 # foo = 1
add rsp, 8
pop rbx # restore call-preserved regs
pop rbp
ret
Diese Funktion kann davon ausgehen, dass sie nicht zurückkehrt und verwendet rbxund rbpohne sie zu speichern / wiederherzustellen.
Gcc für ARM32 macht das tatsächlich, gibt aber immer noch Anweisungen aus, um ansonsten sauber zurückzukehren. Eine noreturnFunktion, die tatsächlich auf ARM32 zurückkehrt, unterbricht den ABI und verursacht schwer zu debuggende Probleme im Aufrufer oder später. (Undefiniertes Verhalten erlaubt dies, aber es handelt sich zumindest um ein Problem mit der Qualität der Implementierung: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82158 .)
Dies ist eine nützliche Optimierung in Fällen, in denen gcc nicht beweisen kann, ob eine Funktion zurückkehrt oder nicht. (Es ist jedoch offensichtlich schädlich, wenn die Funktion einfach zurückkehrt. Gcc warnt, wenn sicher ist, dass eine Noreturn-Funktion zurückkehrt.) Andere gcc-Zielarchitekturen tun dies nicht. Das ist auch eine verpasste Optimierung.
Aber gcc geht nicht weit genug: Wenn Sie auch die Rückgabeanweisung wegoptimieren (oder durch eine unzulässige Anweisung ersetzen), sparen Sie Code und garantieren einen lauten Fehler anstelle einer stillen Beschädigung.
Und wenn Sie das retoptimieren möchten, ist es sinnvoll, alles zu optimieren , was nur benötigt wird, wenn die Funktion zurückkehrt.
So func()könnte zusammengestellt werden zu :
sub rsp, 8
call ext
# *p = a; and so on assumed to never happen
ud2 # optional: illegal insn instead of fall-through
Jede andere vorhandene Anweisung ist eine verpasste Optimierung. Wenn extdeklariert noreturnwird, bekommen wir genau das.
Es kann davon ausgegangen werden, dass jeder Basisblock , der mit einer Rückgabe endet, niemals erreicht wird.