Cerchiamo di interpretare il codice sorgente di GCC 5.1 per vedere cosa succede in -O100
quanto non è chiaro nella pagina del manuale.
Concluderemo che:
- qualsiasi cosa sopra
-O3
fino a INT_MAX
è uguale a -O3
, ma potrebbe facilmente cambiare in futuro, quindi non fare affidamento su di esso.
- GCC 5.1 esegue un comportamento indefinito se inserisci numeri interi maggiori di
INT_MAX
.
- l'argomento può contenere solo cifre o non riesce correttamente. In particolare, questo esclude interi negativi come
-O-1
Concentrati sui sottoprogrammi
In primo luogo ricordare che GCC è solo un front-end per cpp
, as
, cc1
, collect2
. Un rapido ./XXX --help
dice che solo collect2
e cc1
prendi -O
, quindi concentriamoci su di loro.
E:
gcc -v -O100 main.c |& grep 100
dà:
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.
così è -O
stato inoltrato a entrambi cc1
e collect2
.
O in comune. Opt
common.opt è un formato di descrizione dell'opzione CLI specifico di GCC descritto nella documentazione interna e tradotto in C da opth-gen.awk e optc-gen.awk .
Contiene le seguenti righe interessanti:
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
che specificano tutte le O
opzioni. Si noti come -O<n>
è in una famiglia separata dalle altre Os
, Ofast
e Og
.
Quando creiamo, questo genera un options.h
file che contiene:
OPT_O = 139, /* -O */
OPT_Ofast = 140, /* -Ofast */
OPT_Og = 141, /* -Og */
OPT_Os = 142, /* -Os */
Come bonus, mentre cerchiamo l' \bO\n
interno common.opt
notiamo le linee:
-optimize
Common Alias(O)
che ci insegna che --optimize
(doppio trattino perché inizia con un trattino -optimize
sul .opt
file) è un alias non documentato per il -O
quale può essere usato come --optimize=3
!
Dove viene utilizzato OPT_O
Ora grep:
git grep -E '\bOPT_O\b'
che ci indica due file:
Prima rintracciamo opts.c
opts.c: default_options_optimization
Tutti gli opts.c
usi avvengono all'interno: default_options_optimization
.
Facciamo un backtrack per vedere chi chiama questa funzione e vediamo che l'unico percorso del codice è:
main.c:main
toplev.c:toplev::main
opts-global.c:decode_opts
opts.c:default_options_optimization
ed main.c
è il punto di ingresso di cc1
. Buona!
La prima parte di questa funzione:
- fa ciò
integral_argument
che chiama atoi
la stringa corrispondente a OPT_O
per analizzare l'argomento di input
- memorizza il valore all'interno di
opts->x_optimize
dove opts
è un struct gcc_opts
.
struct gcc_opts
Dopo aver invano invano, notiamo che questo struct
viene generato anche in options.h
:
struct gcc_options {
int x_optimize;
[...]
}
da dove x_optimize
viene le righe:
Variable
int optimize
presente in common.opt
, e che options.c
:
struct gcc_options global_options;
quindi supponiamo che questo sia ciò che contiene l'intero stato globale della configurazione, ed int x_optimize
è il valore di ottimizzazione.
255 è un massimo interno
in opts.c:integral_argument
, atoi
viene applicato all'argomento di input, quindi INT_MAX
è un limite superiore. E se metti qualcosa di più grande, sembra che GCC esegua un comportamento C indefinito. Ahia?
integral_argument
inoltre racchiude in modo sottile atoi
e rifiuta l'argomento se un carattere non è una cifra. Quindi i valori negativi falliscono con grazia.
Torna a opts.c:default_options_optimization
, vediamo la linea:
if ((unsigned int) opts->x_optimize > 255)
opts->x_optimize = 255;
in modo che il livello di ottimizzazione venga troncato a 255
. Durante la lettura opth-gen.awk
mi ero imbattuto:
# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.
e sul generato options.h
:
struct GTY(()) cl_optimization
{
unsigned char x_optimize;
il che spiega il motivo del troncamento: le opzioni devono essere inoltrate anche a cl_optimization
, che utilizza a char
per risparmiare spazio. Quindi 255 è un massimo interno in realtà.
opts.c: forse_default_options
Tornando a opts.c:default_options_optimization
, ci imbattiamo in ciò maybe_default_options
che sembra interessante. Lo entriamo, e poi maybe_default_option
dove raggiungiamo un grande interruttore:
switch (default_opt->levels)
{
[...]
case OPT_LEVELS_1_PLUS:
enabled = (level >= 1);
break;
[...]
case OPT_LEVELS_3_PLUS:
enabled = (level >= 3);
break;
Non ci sono >= 4
controlli, il che indica che 3
è il più grande possibile.
Quindi cerchiamo la definizione di 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. */
};
Ah! Questo è un forte indicatore che ci sono solo 3 livelli.
opts.c: default_options_table
opt_levels
è così interessante, che grep OPT_LEVELS_3_PLUS
, e ci imbattiamo 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 },
[...]
}
quindi è qui che -On
viene codificata la mappatura per l'ottimizzazione specifica menzionata nei documenti. Bello!
Assicurati che non ci siano più usi per x_optimize
L'utilizzo principale di x_optimize
era quello di impostare altre opzioni di ottimizzazione specifiche -fdefer_pop
come documentato nella pagina man. Ce ne sono altri?
Noi grep
, e ne troviamo altri. Il numero è piccolo e dopo un'ispezione manuale vediamo che ogni utilizzo fa solo al massimo a x_optimize >= 3
, quindi la nostra conclusione vale.
lto-wrapper.c
Ora passiamo alla seconda occorrenza di OPT_O
, che era in lto-wrapper.c
.
LTO significa Ottimizzazione del tempo di collegamento, che come suggerisce il nome avrà bisogno di -O
un'opzione e sarà collegata a collec2
(che è fondamentalmente un linker).
In effetti, la prima riga di lto-wrapper.c
dice:
/* Wrapper to call lto. Used by collect2 and the linker plugin.
In questo file, le OPT_O
occorrenze sembrano normalizzare solo il valore di O
per passarlo in avanti, quindi dovremmo stare bene.
man gcc
Cygwin (12000 righe dispari) puoi cercare-O
e trovare tutto ciò che le risposte sottostanti indicano, e poi alcune.