Vamos a interpretar el código fuente de GCC 5.1 para ver lo que ocurre en -O100
ya que no está claro en la página del manual.
Concluiremos que:
- todo lo anterior
-O3
hasta INT_MAX
es lo mismo que -O3
, pero eso podría cambiar fácilmente en el futuro, así que no confíe en él.
- GCC 5.1 ejecuta un comportamiento indefinido si ingresa números enteros mayores que
INT_MAX
.
- el argumento solo puede tener dígitos, o falla correctamente. En particular, esto excluye enteros negativos como
-O-1
Centrarse en subprogramas
En primer lugar, recuerde que GCC es sólo un front-end para cpp
, as
, cc1
, collect2
. Un rápido ./XXX --help
dice eso solo collect2
y cc1
toma -O
, así que centrémonos en ellos.
Y:
gcc -v -O100 main.c |& grep 100
da:
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.
por lo que -O
se reenvió a ambos cc1
y collect2
.
O en common.opt
common.opt es un formato de descripción de opción CLI específico de GCC descrito en la documentación interna y traducido a C por opth-gen.awk y optc-gen.awk .
Contiene las siguientes líneas interesantes:
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
que especifican todas las O
opciones. Tenga -O<n>
en cuenta cómo está en una familia separada de la otra Os
, Ofast
y Og
.
Cuando construimos, esto genera un options.h
archivo que contiene:
OPT_O = 139, /* -O */
OPT_Ofast = 140, /* -Ofast */
OPT_Og = 141, /* -Og */
OPT_Os = 142, /* -Os */
Como beneficio adicional, mientras buscamos \bO\n
dentro common.opt
, notamos las líneas:
-optimize
Common Alias(O)
lo que nos enseña que --optimize
(doble guión porque comienza con un guión -optimize
en el .opt
archivo) es un alias indocumentado para el -O
cual se puede usar como --optimize=3
!
Donde se usa OPT_O
Ahora hacemos grep:
git grep -E '\bOPT_O\b'
que nos apunta a dos archivos:
Primero busquemos opts.c
opts.c: optimización_opciones_predeterminadas
Todos los opts.c
usos suceden en el interior: default_options_optimization
.
Hacemos grep backtrack para ver quién llama a esta función, y vemos que la única ruta de código es:
main.c:main
toplev.c:toplev::main
opts-global.c:decode_opts
opts.c:default_options_optimization
y main.c
es el punto de entrada de cc1
. ¡Bueno!
La primera parte de esta función:
- hace
integral_argument
que llama atoi
a la cadena correspondiente a OPT_O
para analizar el argumento de entrada
- almacena el valor dentro de
opts->x_optimize
donde opts
está a struct gcc_opts
.
struct gcc_opts
Después de hacer grepping en vano, notamos que esto struct
también se genera en options.h
:
struct gcc_options {
int x_optimize;
[...]
}
de donde x_optimize
viene de las lineas:
Variable
int optimize
presente en common.opt
, y que options.c
:
struct gcc_options global_options;
por lo que suponemos que esto es lo que contiene todo el estado global de la configuración y int x_optimize
es el valor de optimización.
255 es un máximo interno
in opts.c:integral_argument
, atoi
se aplica al argumento de entrada, por lo que INT_MAX
es un límite superior. Y si pones algo más grande, parece que GCC ejecuta un comportamiento indefinido de C. ¿Ay?
integral_argument
también envuelve atoi
y rechaza el argumento si algún carácter no es un dígito. Así que los valores negativos fallan con gracia.
De vuelta a opts.c:default_options_optimization
, vemos la línea:
if ((unsigned int) opts->x_optimize > 255)
opts->x_optimize = 255;
de modo que el nivel de optimización se trunca a 255
. Mientras leía opth-gen.awk
me encontré con:
# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.
y sobre el generado options.h
:
struct GTY(()) cl_optimization
{
unsigned char x_optimize;
lo que explica por qué el truncamiento: las opciones también deben reenviarse a cl_optimization
, que usa a char
para ahorrar espacio. Entonces 255 es un máximo interno en realidad.
opts.c: maybe_default_options
Volviendo a opts.c:default_options_optimization
, nos encontramos con lo maybe_default_options
que suena interesante. Lo ingresamos, y luego maybe_default_option
donde llegamos a un gran interruptor:
switch (default_opt->levels)
{
[...]
case OPT_LEVELS_1_PLUS:
enabled = (level >= 1);
break;
[...]
case OPT_LEVELS_3_PLUS:
enabled = (level >= 3);
break;
No hay >= 4
cheques, lo que indica que 3
es el más grande posible.
Luego buscamos la definición de OPT_LEVELS_3_PLUS
en 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. */
};
¡Decir ah! Este es un fuerte indicador de que solo hay 3 niveles.
opts.c: tabla_opciones_predeterminadas
opt_levels
es tan interesante, que hacemos grep OPT_LEVELS_3_PLUS
y encontramos 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 },
[...]
}
por lo que aquí es donde -On
se codifica el mapeo de optimización específico mencionado en los documentos. ¡Agradable!
Asegúrese de que no haya más usos para x_optimize
El uso principal de x_optimize
fue establecer otras opciones de optimización específicas -fdefer_pop
como se documenta en la página de manual. ¿Hay más?
Nosotros grep
, y encontramos algunos más. El número es pequeño y, tras una inspección manual, vemos que cada uso solo hace como máximo a x_optimize >= 3
, por lo que nuestra conclusión es válida.
lto-wrapper.c
Ahora vamos a la segunda aparición de OPT_O
, que estaba en lto-wrapper.c
.
LTO significa Optimización de tiempo de enlace, que, como su nombre indica, necesitará una -O
opción y estará vinculada collec2
(que es básicamente un enlazador).
De hecho, la primera línea de lto-wrapper.c
dice:
/* Wrapper to call lto. Used by collect2 and the linker plugin.
En este archivo, las OPT_O
ocurrencias parecen solo normalizar el valor de O
para pasarlo hacia adelante, así que deberíamos estar bien.
man gcc
Cygwin (12000 líneas impares) puede buscar-O
y encontrar todo lo que indican las respuestas a continuación, y más.