Давайте інтерпретуємо вихідний код GCC 5.1, щоб побачити, що відбувається-O100
оскільки на сторінці man не зрозуміло.
Ми зробимо висновок, що:
- все, що вище
-O3
, INT_MAX
є таким же, як -O3
, але це може легко змінитися в майбутньому, тому не покладайтеся на це.
- GCC 5.1 виконує невизначене поведінку, якщо ви вводите цілі числа, що перевищують
INT_MAX
.
- аргумент може мати лише цифри, або він виходить витончено. Зокрема, це виключає негативні цілі числа, як-от
-O-1
Зосередьтеся на підпрограмах
По- перше пам'ятаєте , що ПКУ просто передній кінець для cpp
, as
, cc1
, collect2
. Швидкий ./XXX --help
каже, що тільки collect2
і cc1
бери -O
, тому давайте зосередимося на них.
І:
gcc -v -O100 main.c |& grep 100
дає:
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.
так -O
було передано обом cc1
і collect2
.
O у спільних.опт
common.opt - це специфічний формат опису параметрів CLI для GCC, описаний у внутрішній документації та переведений на C opth-gen.awk та optc-gen.awk .
Він містить такі цікаві рядки:
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
які задають усі O
варіанти. Зауважте, як -O<n>
знаходиться в окремій родині від іншої Os
, Ofast
і Og
.
Коли ми будуємо, це створює options.h
файл, який містить:
OPT_O = 139, /* -O */
OPT_Ofast = 140, /* -Ofast */
OPT_Og = 141, /* -Og */
OPT_Os = 142, /* -Os */
Як бонус, поки ми жартуємо \bO\n
всередину, common.opt
ми помічаємо рядки:
-optimize
Common Alias(O)
що вчить нас, що --optimize
(подвійний тире, тому що він починається з тире -optimize
на .opt
файлі) є незадокументованим псевдонімом, для -O
якого можна використовувати як --optimize=3
!
Там, де використовується OPT_O
Тепер ми вітаємо:
git grep -E '\bOPT_O\b'
що вказує нам на два файли:
Давайте спочатку простежимо opts.c
opts.c: default_options_optimization
Всі opts.c
звичаї трапляються всередині: default_options_optimization
.
Ми повертаємося назад, щоб побачити, хто викликає цю функцію, і бачимо, що єдиний шлях коду:
main.c:main
toplev.c:toplev::main
opts-global.c:decode_opts
opts.c:default_options_optimization
і main.c
є точкою входу cc1
. Добре!
Перша частина цієї функції:
- робить,
integral_argument
яка викликає atoi
рядок, відповідний OPT_O
для розбору вхідного аргументу
- зберігає значення всередині,
opts->x_optimize
де opts
є struct gcc_opts
.
struct gcc_opts
Після марного поздоровлення ми помічаємо, що це struct
також генерується при options.h
:
struct gcc_options {
int x_optimize;
[...]
}
звідки x_optimize
походить рядки:
Variable
int optimize
присутній в common.opt
, і що options.c
:
struct gcc_options global_options;
тому ми здогадуємось, що це те, що містить всю конфігурацію глобального стану та int x_optimize
є значенням оптимізації.
255 - внутрішній максимум
в opts.c:integral_argument
, atoi
застосовується до вхідного аргументу, тому INT_MAX
є верхньою межею. А якщо поставити щось більше, здається, що GCC керує C невизначеною поведінкою. Ой?
integral_argument
також тонко загортає atoi
і відкидає аргумент, якщо будь-який символ не є цифрою. Тож негативні значення виходять витончено.
Назад opts.c:default_options_optimization
, ми бачимо рядок:
if ((unsigned int) opts->x_optimize > 255)
opts->x_optimize = 255;
щоб рівень оптимізації був усічений 255
. Читаючи, opth-gen.awk
я натрапив:
# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.
і на створеному options.h
:
struct GTY(()) cl_optimization
{
unsigned char x_optimize;
що пояснює, чому усічення: параметри також повинні бути перенаправлені на cl_optimization
, що використовує achar
для економії місця. Отже, 255 - це власне внутрішній максимум.
opts.c: можливо_default_options
Повертаючись до opts.c:default_options_optimization
, ми стикаємося з тим, maybe_default_options
що звучить цікаво. Ми входимо в нього, а потім, maybe_default_option
де доходимо до великого перемикача:
switch (default_opt->levels)
{
[...]
case OPT_LEVELS_1_PLUS:
enabled = (level >= 1);
break;
[...]
case OPT_LEVELS_3_PLUS:
enabled = (level >= 3);
break;
Немає >= 4
перевірок, що вказує на те, що 3
це найбільша можлива.
Потім ми шукаємо визначення OPT_LEVELS_3_PLUS
в 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. */
};
Га! Це сильний показник того, що існує лише 3 рівні.
opts.c: default_options_table
opt_levels
настільки цікаво, що ми жаліємось OPT_LEVELS_3_PLUS
і стикаємося 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 },
[...]
}
тому -On
тут кодується специфічне відображення оптимізації, згадане в документах. Приємно!
Переконайтесь, що для x_optimize більше не використовується
Основним способом використання x_optimize
було встановлення інших специфічних варіантів оптимізації, як -fdefer_pop
це зафіксовано на сторінці man. Чи є ще?
Ми grep
, і знайдемо ще кілька. Кількість невелика, і при ручному огляді ми бачимо, що кожне використання працює лише максимум a x_optimize >= 3
, тому наш висновок справедливий.
lto-wrapper.c
Тепер ми переходимо до другого явища OPT_O
, яке було в lto-wrapper.c
.
LTO означає оптимізацію часу зв’язку, для якої, як випливає з назви, потрібна -O
опція, і вона буде пов'язана collec2
(що в основному є лінкером).
Насправді перший рядок lto-wrapper.c
говорить:
/* Wrapper to call lto. Used by collect2 and the linker plugin.
У цьому файлі OPT_O
події, здається, лише нормалізують значення O
передачі його вперед, тому нам слід добре.
man gcc
Cygwin (12000 непарних рядків) ви можете шукати-O
і знаходити всі відповіді нижче, а потім деякі.