Давайте інтерпретуємо вихідний код 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 gccCygwin (12000 непарних рядків) ви можете шукати-Oі знаходити всі відповіді нижче, а потім деякі.