Lassen Sie uns den Quellcode von GCC 5.1 interpretieren
Wir werden versuchen zu verstehen, was passiert -O100
da es auf der Manpage nicht klar ist.
Wir kommen zu dem Schluss, dass:
- alles oben
-O3
bis zu INT_MAX
ist das gleiche wie -O3
aber das könnte sich in Zukunft leicht ändern, also verlassen Sie sich nicht darauf.
- GCC 5.1 führt undefiniertes Verhalten aus, wenn Sie Ganzzahlen größer als eingeben
INT_MAX
.
- das Argument darf nur Ziffern enthalten, oder es schlägt fehl. Dies schließt insbesondere negative ganze Zahlen wie aus
-O-1
Konzentrieren Sie sich auf Unterprogramme
Denken Sie zunächst daran, dass GCC nur ein Frontend für ist cpp
, as
, cc1
, collect2
. Eine schnelle ./XXX --help
sagt das nur collect2
und cc1
nehmen -O
konzentrieren wir uns also auf sie.
Und:
gcc -v -O100 main.c |& grep 100
gibt:
COLLECT_GCC_OPTIONS='-O100' '-v' '-mtune=generic' '-march=x86-64'
/usr/local/libexec/gcc/x86_64-unknown-linux-gnu/5.1.0/cc1 [[noise]] hello_world.c -O100 -o /tmp/ccetECB5.
Also -O
wurde an beide weitergeleitet cc1
und collect2
.
O gemeinsam.opt
common.opt ist ein GCC-spezifisches CLI-Optionsbeschreibungsformat, das in beschrieben wird Interne Dokumentation und nach C übersetzt von opth-gen.awk und optc-gen.awk.
Es enthält die folgenden interessanten Zeilen:
O
Common JoinedOrMissing Optimization
-O<number> Set optimization level to <number>
Os
Common Optimization
Optimize for space rather than speed
Ofast
Common Optimization
Optimize for speed disregarding exact standards compliance
Og
Common Optimization
Optimize for debugging experience rather than speed or size
die alle angeben O
Optionen. Beachten Sie, wie -O<n>
ist in einer anderen Familie als die andere Os
, Ofast
und Og
.
Wenn wir bauen, erzeugt dies a options.h
Datei, die enthält:
OPT_O = 139, /* -O */
OPT_Ofast = 140, /* -Ofast */
OPT_Og = 141, /* -Og */
OPT_Os = 142, /* -Os */
Als Bonus, während wir danach greifen \bO\n
Innerhalb common.opt
wir bemerken die Zeilen:
-optimize
Common Alias(O)
was uns das lehrt --optimize
(doppelter Bindestrich, weil es mit einem Bindestrich beginnt -optimize
auf der .opt
Datei) ist ein undokumentierter Alias für -O
was verwendet werden kann als --optimize=3
!
Wo OPT_O verwendet wird
Jetzt grep wir:
git grep -E '\bOPT_O\b'
was uns auf zwei Dateien verweist:
Lassen Sie uns zuerst aufspüren opts.c
opts.c:default_options_optimization
Alle opts.c
Verwendungen finden im Inneren statt: default_options_optimization
.
Wir grep backtrack, um zu sehen, wer diese Funktion aufruft, und wir sehen, dass der einzige Codepfad ist:
main.c:main
toplev.c:toplev::main
opts-global.c:decode_opts
opts.c:default_options_optimization
und main.c
ist der Einstiegspunkt von cc1
. Gut!
Der erste Teil dieser Funktion:
- tut
integral_argument
was ruft atoi
auf der entsprechenden Zeichenfolge OPT_O
um das Eingabeargument zu analysieren
- speichert den Wert im Inneren
opts->x_optimize
wo opts
ist ein struct gcc_opts
.
struct gcc_opts
Nachdem wir vergeblich herumgetastet haben, bemerken wir das struct
wird auch bei generiert options.h
:
struct gcc_options {
int x_optimize;
[...]
}
wo x_optimize
kommt aus den Zeilen:
Variable
int optimize
anwesend in common.opt
und das options.c
:
struct gcc_options global_options;
Wir vermuten also, dass dies den gesamten globalen Status der Konfiguration enthält, und int x_optimize
ist der Optimierungswert.
255 ist ein internes Maximum
in opts.c:integral_argument
, atoi
wird auf das Eingabeargument angewendet, also INT_MAX
ist eine Obergrenze. Und wenn Sie etwas Größeres angeben, scheint es, dass GCC C undefiniertes Verhalten ausführt. Autsch?
integral_argument
auch dünn wickeln atoi
und weist das Argument zurück, wenn irgendein Zeichen keine Ziffer ist. Negative Werte scheitern also elegant.
Zurück zu opts.c:default_options_optimization
sehen wir die Zeile:
if ((unsigned int) opts->x_optimize > 255)
opts->x_optimize = 255;
damit die Optimierungsebene abgeschnitten wird 255
. Während des Lesens opth-gen.awk
Ich war auf Folgendes gestoßen:
# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.
und auf dem generierten options.h
:
struct GTY(()) cl_optimization
{
unsigned char x_optimize;
was die Kürzung erklärt: die Optionen müssen auch weitergeleitet werden cl_optimization
die a verwendet char
um Platz zu sparen. 255 ist also eigentlich ein internes Maximum.
opts.c:maybe_default_options
Zurück zu opts.c:default_options_optimization
stoßen wir auf maybe_default_options
was interessant klingt. Wir betreten es, und dann maybe_default_option
wo wir einen großen Schalter erreichen:
switch (default_opt->levels)
{
[...]
case OPT_LEVELS_1_PLUS:
enabled = (level >= 1);
break;
[...]
case OPT_LEVELS_3_PLUS:
enabled = (level >= 3);
break;
Es gibt keine >= 4
Kontrollen, was darauf hindeutet 3
ist das größtmögliche.
Dann suchen wir nach der Definition von OPT_LEVELS_3_PLUS
in common-target.h
:
enum opt_levels
{
OPT_LEVELS_NONE, /* No levels (mark end of array). */
OPT_LEVELS_ALL, /* All levels (used by targets to disable options
enabled in target-independent code). */
OPT_LEVELS_0_ONLY, /* -O0 only. */
OPT_LEVELS_1_PLUS, /* -O1 and above, including -Os and -Og. */
OPT_LEVELS_1_PLUS_SPEED_ONLY, /* -O1 and above, but not -Os or -Og. */
OPT_LEVELS_1_PLUS_NOT_DEBUG, /* -O1 and above, but not -Og. */
OPT_LEVELS_2_PLUS, /* -O2 and above, including -Os. */
OPT_LEVELS_2_PLUS_SPEED_ONLY, /* -O2 and above, but not -Os or -Og. */
OPT_LEVELS_3_PLUS, /* -O3 and above. */
OPT_LEVELS_3_PLUS_AND_SIZE, /* -O3 and above and -Os. */
OPT_LEVELS_SIZE, /* -Os only. */
OPT_LEVELS_FAST /* -Ofast only. */
};
Ha! Dies ist ein starker Indikator dafür, dass es nur 3 Ebenen gibt.
opts.c:default_options_table
opt_levels
ist so interessant, dass wir grep OPT_LEVELS_3_PLUS
und komm vorbei opts.c:default_options_table
:
static const struct default_options default_options_table[] = {
/* -O1 optimizations. */
{ OPT_LEVELS_1_PLUS, OPT_fdefer_pop, NULL, 1 },
[...]
/* -O3 optimizations. */
{ OPT_LEVELS_3_PLUS, OPT_ftree_loop_distribute_patterns, NULL, 1 },
[...]
}
also hier ist die -On
zu einer bestimmten Optimierungszuordnung, die in den Dokumenten erwähnt wird, codiert ist. Nett!
Stellen Sie sicher, dass x_optimize nicht mehr verwendet werden kann
Die Hauptverwendung von x_optimize
war, andere spezifische Optimierungsoptionen wie einzustellen -fdefer_pop
wie auf der Manpage dokumentiert. Gibt es noch mehr?
Wir grep
, und finde noch ein paar mehr. Die Anzahl ist gering, und bei manueller Überprüfung sehen wir, dass jede Verwendung höchstens a bewirkt x_optimize >= 3
also gilt unsere Schlussfolgerung.
lto-wrapper.c
Jetzt gehen wir zum zweiten Vorkommen von OPT_O
was drin war lto-wrapper.c
.
LTO bedeutet Link Time Optimization, die, wie der Name schon sagt, eine benötigen wird -O
Option und wird verlinkt collec2
(was im Grunde ein Linker ist).
Tatsächlich ist die erste Zeile von lto-wrapper.c
sagt:
/* Wrapper to call lto. Used by collect2 and the linker plugin.
In dieser Datei ist die OPT_O
Vorkommnisse scheinen nur den Wert zu normalisieren O
um es weiterzugeben, also sollte es uns gut gehen.
@minitech Welchen FM schaust du dir an? Sogar mit
man gcc
auf Cygwin (12000 ungerade Zeilen) können Sie suchen-O
und finden Sie alles, was die Antworten unten angeben, und noch einige mehr.– Jens
25. Juli 2012 um 13:32 Uhr
@minmaxavg Nachdem ich die Quelle gelesen habe, stimme ich Ihnen nicht zu: alles größer als
3
ist das gleiche wie3
(Solange nichtint
Überlauf). Siehe meine Antwort.– Ciro Santilli OurBigBook.com
18. Mai 2015 um 16:17 Uhr
Tatsächlich hat GCC viele andere Flags zur Feinabstimmung von Optimierungen.
-fomit-stack-pointer
ändert den generierten Code.– Basile Starynkevitch
25. Juni 2015 um 16:27 Uhr