gcc -O0 optimiert weiterhin ungenutzten Code. Gibt es ein Compiler-Flag, um das zu ändern?

 C Programming >> C-Programmierung >  >> Tags >> GCC
gcc -O0 optimiert weiterhin ungenutzten Code. Gibt es ein Compiler-Flag, um das zu ändern?


Wie ich in dieser Frage angesprochen habe, entfernt gcc (ja, mit -O0 ) eine Codezeile _mm_div_ss(s1, s2); vermutlich weil das Ergebnis nicht gespeichert wird. Dies sollte jedoch löst eine Fließkomma-Ausnahme aus und löst SIGFPE aus, was nicht passieren kann, wenn der Aufruf entfernt wird.


Frage :Gibt es ein Flag oder mehrere Flags, die an gcc übergeben werden, damit der Code so kompiliert wird, wie er ist? Ich denke so etwas wie fno-remove-unused aber sowas sehe ich nicht. Idealerweise wäre dies ein Compiler-Flag, anstatt meinen Quellcode ändern zu müssen, aber wenn dies nicht unterstützt wird, gibt es stattdessen ein gcc-Attribut/Pragma?


Dinge, die ich versucht habe:


$ gcc --help=optimizers | grep -i remove

keine Ergebnisse.


$ gcc --help=optimizers | grep -i unused

keine Ergebnisse.


Und explizites Deaktivieren aller Flags für toten Code/Eliminierung – beachten Sie, dass es keine Warnung über unbenutzten Code gibt:


$ gcc -O0 -msse2 -Wall -Wextra -pedantic -Winline \
-fno-dce -fno-dse -fno-tree-dce \
-fno-tree-dse -fno-tree-fre -fno-compare-elim -fno-gcse \
-fno-gcse-after-reload -fno-gcse-las -fno-rerun-cse-after-loop \
-fno-tree-builtin-call-dce -fno-tree-cselim a.c
a.c: In function ‘main’:
a.c:25:5: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
__m128 s1, s2;
^
$


Quellprogramm


#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <xmmintrin.h>
static void sigaction_sfpe(int signal, siginfo_t *si, void *arg)
{
printf("%d,%d,%d\n", signal, si!=NULL?1:0, arg!=NULL?1:0);
printf("inside SIGFPE handler\nexit now.\n");
exit(1);
}
int main()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = sigaction_sfpe;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGFPE, &sa, NULL);
_mm_setcsr(0x00001D80);
__m128 s1, s2;
s1 = _mm_set_ps(1.0, 1.0, 1.0, 1.0);
s2 = _mm_set_ps(0.0, 0.0, 0.0, 0.0);
_mm_div_ss(s1, s2);
printf("done (no error).\n");
return 0;
}

Das Kompilieren des obigen Programms ergibt


$ ./a.out
done (no error).

Ändern der Zeile


_mm_div_ss(s1, s2);

zu


s2 = _mm_div_ss(s1, s2); // add "s2 = "

erzeugt das erwartete Ergebnis:


$ ./a.out
inside SIGFPE handler


Bearbeiten Sie mit mehr Details.


Dies scheint mit __always_inline__ zusammenzuhängen Attribut auf _mm_div_ss Definition.


$ cat t.c
int
div(int b)
{
return 1/b;
}
int main()
{
div(0);
return 0;
}
$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$

(keine Warnungen oder Fehler)


$ ./t.out
Floating point exception
$

vs unten (gleich außer für Funktionsattribute)


$ cat t.c
__inline int __attribute__((__always_inline__))
div(int b)
{
return 1/b;
}
int main()
{
div(0);
return 0;
}
$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$

(keine Warnungen oder Fehler)


$ ./t.out
$

Hinzufügen des Funktionsattributs __warn_unused_result__ gibt zumindest eine hilfreiche Nachricht:


$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
t.c: In function ‘main’:
t.c:9:5: warning: ignoring return value of ‘div’, declared with attribute warn_unused_result [-Wunused-result]
div(0);
^

Bearbeiten:


Einige Diskussionen auf der gcc-Mailingliste. Letztendlich denke ich, dass alles wie beabsichtigt funktioniert.


Antworten:


GCC "optimiert" hier nichts heraus. Es generiert einfach keinen nutzlosen Code. Es scheint eine weit verbreitete Illusion zu sein, dass es eine reine Form von Code gibt, die der Compiler generieren sollte, und alle Änderungen daran sind eine "Optimierung". So etwas gibt es nicht.


Der Compiler erstellt eine Datenstruktur, die darstellt, was der Code bedeutet, wendet dann einige Transformationen auf diese Datenstruktur an und generiert daraus Assembler, der dann zu Anweisungen kompiliert wird. Wenn Sie ohne "Optimierungen" kompilieren, bedeutet dies nur, dass der Compiler nur den geringstmöglichen Aufwand betreiben wird, um Code zu generieren.


In diesem Fall ist die ganze Anweisung nutzlos, weil sie nichts bewirkt und sofort weggeworfen wird (nach dem Erweitern der Inlines und was die Builtins bedeuten, ist es gleichbedeutend mit dem Schreiben von a/b; , der Unterschied besteht darin, dass a/b; geschrieben wird wird eine Warnung über statement with no effect ausgeben während die Builtins wahrscheinlich nicht von denselben Warnungen behandelt werden). Dies ist keine Optimierung, der Compiler müsste tatsächlich zusätzliche Anstrengungen unternehmen, um eine Bedeutung für eine bedeutungslose Anweisung zu erfinden, dann eine temporäre Variable vortäuschen, um das Ergebnis dieser Anweisung zu speichern, um es dann wegzuwerfen.


Was Sie suchen, sind nicht Flags zum Deaktivieren von Optimierungen, sondern Pessimisierungs-Flags. Ich glaube nicht, dass Compiler-Entwickler Zeit damit verschwenden, solche Flags zu implementieren. Außer vielleicht als Aprilscherz.