Spójrzmy prawdzie w interpretacji kodu źródłowego GCC 5.1 , aby zobaczyć, co dzieje się -O100, ponieważ nie jest jasne, na stronie man.
Wnioskujemy, że:
- wszystko powyżej
-O3do INT_MAXjest takie samo jak -O3, ale może się to łatwo zmienić w przyszłości, więc nie polegaj na tym.
- GCC 5.1 uruchamia niezdefiniowane zachowanie, jeśli wprowadzisz liczby całkowite większe niż
INT_MAX.
- argument może mieć tylko cyfry lub zawodzi. W szczególności wyklucza to ujemne liczby całkowite, takie jak
-O-1
Skoncentruj się na podprogramach
Po pierwsze należy pamiętać, że GCC jest tylko front-end dla cpp, as, cc1, collect2. Krótko ./XXX --helpmówiąc, tylko collect2i cc1bierz -O, więc skupmy się na nich.
I:
gcc -v -O100 main.c |& grep 100
daje:
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.
więc -Ozostał przekazany do obu cc1i collect2.
O in common.opt
common.opt to specyficzny dla GCC format opisu opcji CLI opisany w wewnętrznej dokumentacji i przetłumaczony na C przez opth-gen.awk i optc-gen.awk .
Zawiera następujące interesujące wiersze:
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
które określają wszystkie Oopcje. Zwróć uwagę, jak -O<n>jest w osobnej rodziny z drugiej strony Os, Ofasti Og.
Kiedy budujemy, generuje options.hplik, który zawiera:
OPT_O = 139, /* -O */
OPT_Ofast = 140, /* -Ofast */
OPT_Og = 141, /* -Og */
OPT_Os = 142, /* -Os */
Jako bonus, gdy grepujemy do \bO\nśrodka common.opt, zauważamy linie:
-optimize
Common Alias(O)
co nas uczy, że --optimize(podwójny myślnik, ponieważ zaczyna się od myślnika -optimizew .optpliku) jest nieudokumentowanym aliasem, -Októrego można użyć jako --optimize=3!
Gdzie OPT_O jest używany
Teraz grep:
git grep -E '\bOPT_O\b'
co wskazuje nam na dwa pliki:
Najpierw wytropmy opts.c
opts.c: default_options_optimization
Wszystkie opts.czwyczaje zdarzyć wewnątrz: default_options_optimization.
Wykonujemy grep backtrack, aby zobaczyć, kto wywołuje tę funkcję i widzimy, że jedyna ścieżka do kodu to:
main.c:main
toplev.c:toplev::main
opts-global.c:decode_opts
opts.c:default_options_optimization
i main.cjest punktem wejścia cc1. Dobry!
Pierwsza część tej funkcji:
- robi,
integral_argumentktóra wywołuje atoiciąg odpowiadający do, OPT_Oaby przeanalizować argument wejściowy
- przechowuje wartość wewnątrz,
opts->x_optimizegdzie optsjest struct gcc_opts.
struct gcc_opts
Po grepowaniu na próżno zauważamy, że structjest to również generowane pod adresem options.h:
struct gcc_options {
int x_optimize;
[...]
}
skąd x_optimizepochodzi z linii:
Variable
int optimize
obecny w common.opti że options.c:
struct gcc_options global_options;
więc domyślamy się, że to właśnie zawiera cały stan globalny konfiguracji i int x_optimizejest wartością optymalizacji.
255 to wewnętrzne maksimum
in opts.c:integral_argument, atoijest stosowany do argumentu wejściowego, więc INT_MAXjest to górna granica. A jeśli umieścisz coś większego, wydaje się, że GCC uruchamia niezdefiniowane zachowanie C. Auć?
integral_argumentrównież cienko zawija atoii odrzuca argument, jeśli którykolwiek znak nie jest cyfrą. Zatem wartości ujemne zawodzą wdzięcznie.
Wracając do opts.c:default_options_optimization, widzimy wiersz:
if ((unsigned int) opts->x_optimize > 255)
opts->x_optimize = 255;
tak aby poziom optymalizacji został obcięty do 255. Podczas czytania opth-gen.awknatknąłem się na:
# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.
i na wygenerowanych options.h:
struct GTY(()) cl_optimization
{
unsigned char x_optimize;
co wyjaśnia, dlaczego obcięcie: opcje muszą być również przekazane do cl_optimization, który używa a, charaby zaoszczędzić miejsce. Tak więc 255 jest właściwie wewnętrznym maksimum.
opts.c: może_default_options
Wracając do opts.c:default_options_optimization, natrafiliśmy na to, maybe_default_optionsco brzmi interesująco. Wchodzimy do niego, a potem maybe_default_optiondocieramy do dużego przełącznika:
switch (default_opt->levels)
{
[...]
case OPT_LEVELS_1_PLUS:
enabled = (level >= 1);
break;
[...]
case OPT_LEVELS_3_PLUS:
enabled = (level >= 3);
break;
Nie ma >= 4kontroli, co oznacza, że 3jest to możliwie największe.
Następnie szukamy definicji OPT_LEVELS_3_PLUSw 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! To mocny wskaźnik, że istnieją tylko 3 poziomy.
opts.c: default_options_table
opt_levelsjest tak interesujący, że grepujemy OPT_LEVELS_3_PLUSi natrafiamy na 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 },
[...]
}
więc w tym miejscu -Onjest kodowane mapowanie optymalizacyjne do określonego, o którym mowa w dokumentacji. Miły!
Upewnij się, że x_optimize nie ma już zastosowań
Głównym zastosowaniem x_optimizebyło ustawienie innych specyficznych opcji optymalizacji, takich -fdefer_popjak udokumentowane na stronie podręcznika. Czy jest ich więcej?
My grepi znajdujemy kilka innych. Liczba jest niewielka i po ręcznym sprawdzeniu widzimy, że każde użycie powoduje co najwyżej a x_optimize >= 3, więc nasz wniosek jest ważny.
lto-wrapper.c
Teraz przejdźmy do drugiego wystąpienia OPT_O, które było w lto-wrapper.c.
LTO oznacza optymalizację czasu łącza, która, jak nazwa sugeruje, będzie potrzebować -Oopcji i będzie połączona z collec2(co jest w zasadzie konsolidatorem).
W rzeczywistości pierwsza linijka lto-wrapper.cmówi:
/* Wrapper to call lto. Used by collect2 and the linker plugin.
W tym pliku OPT_Owystąpienia wydają się tylko normalizować wartość, Oaby przekazać ją dalej, więc powinno być dobrze.
man gccna Cygwin (12000 nieparzystych wierszy) możesz wyszukać-Oi znaleźć wszystko, co podają poniższe odpowiedzi, a następnie niektóre.