Как избежать пролога функции gcc? - PullRequest
12 голосов
/ 29 марта 2011

В последнее время я сталкивался с множеством функций, в которых gcc генерирует действительно плохой код на x86.Все они соответствуют шаблону:

if (some_condition) {
    /* do something really simple and return */
} else {
    /* something complex that needs lots of registers */
}

Думайте о простом кейсе как о чем-то настолько маленьком, что половина или больше работы тратится на отправку и получение регистров, которые вообще не будут изменены.Если бы я писал asm вручную, я бы сохранял и восстанавливал регистры сохраненных вызовов в сложном случае и вообще не касался указателя стека в простом случае.

Есть ли способзаставить gcc быть немного умнее и сделать это самому?Желательно с параметрами командной строки, а не безобразными хаки в источнике ...

Редактировать: Чтобы конкретизировать, вот что-то очень похожее на некоторые из функций, с которыми я имею дело:

if (buf->pos < buf->end) {
    return *buf->pos++;
} else {
    /* fill buffer */
}

и еще один:

if (!initialized) {
    /* complex initialization procedure */
}
return &initialized_object;

и еще один:

if (mutex->type == SIMPLE) {
    return atomic_swap(&mutex->lock, 1);
} else {
    /* deal with ownership, etc. */
}

Правка 2: Я должен был упомянуть для начала:эти функции не могут быть встроены.У них есть внешняя связь, и они - код библиотеки.Если их включить в приложение, это приведет к возникновению всевозможных проблем.

Ответы [ 5 ]

2 голосов
/ 08 сентября 2012

Я бы сделал это так:

static void complex_function() {}

void foo()
{
    if(simple_case) {
        // do whatever
        return;
    } else {
        complex_function();
    }
}

Мой компилятор настаивает на встраивании complex_function (), в этом случае вы можете использовать для него атрибут noinline.

2 голосов
/ 29 марта 2011

Обновление

Чтобы явно подавить встраивание для одной функции в gcc, используйте:

void foo() __attribute__ ((noinline))
{
  ...
}

См. Также Как я могу сказать gcc невстроить функцию?


Подобные функции будут регулярно автоматически вставляться, если не скомпилировано -O0 (отключить оптимизацию).

В C ++ вы можете подсказывать компилятору, используя ключевое слово inline

Если компилятор не примет вашу подсказку, вы, вероятно, используете слишком много регистров / ветвей внутри функции.Ситуация почти наверняка разрешается путем извлечения «сложного» блока в его собственную функцию.


Обновление Я заметил, что вы добавили тот факт, что они являются внешними символами.(Пожалуйста, обновите вопрос этой важной информацией).Ну, в некотором смысле, с внешними функциями все ставки выключены.Я не могу поверить, что gcc по определению встроит всю сложную функцию в крошечную функцию , просто , потому что она вызывается только оттуда.Возможно, вы можете дать пример кода, который демонстрирует поведение, и мы можем найти подходящие флаги оптимизации, чтобы исправить это?

Также , это C или C ++?В C ++ я знаю, что обычным делом является включение встроенных тривиальных функций принятия решений (в основном в качестве членов, определенных в объявлении класса).Это не приведет к конфликту связей, как с простыми (внешними) функциями Си.

Также вы можете иметь определенные функции шаблонов, которые будут идеально встроены во все модули компиляции, не приводя к конфликтам ссылок.

Я надеюсь, что вы используете C ++, потому что он даст вам массу вариантов здесь.

1 голос
/ 30 марта 2011

Возможно обновить вашу версию gcc? 4.6 был только что выпущен. Насколько я понимаю, у него есть возможность "частичного встраивания". То есть легко интегрируемая внешняя часть функции встроена, а дорогая часть превращается в вызов. Но я должен признать, что сам пока не пробовал.

Редактировать: Утверждение, на которое я ссылался из ChangeLog:

Частичное встраивание теперь поддерживается и включен по умолчанию при -O2 и выше. Функцией можно управлять через -fpartial-встраивание.

Частично встроенные функции разделения с короткий горячий путь, чтобы вернуться. Это позволяет более агрессивное встраивание горячих путь , ведущий к лучшей производительности и часто к уменьшению размера кода (потому что холодные части функций не Дублированный).

...

Инлайнинг при оптимизации по размеру (либо в холодных регионах программы или при компиляции с -Os) был улучшено, чтобы лучше обрабатывать программы на C ++ с большим штрафом за абстракцию, приводит к уменьшению и ускорению кода.

0 голосов
/ 30 марта 2011

Поскольку это внешние вызовы, возможно, gcc рассматривает их как небезопасные и сохраняет регистры для вызова функции (трудно понять, не видя регистры, которые он сохраняет, включая те, которые вы говорите «не используются»). «). Из любопытства, происходит ли это чрезмерное разлитие регистров при отключенной оптимизации?

0 голосов
/ 29 марта 2011

Я бы, вероятно, реорганизовал код, чтобы поощрить встраивание простого случая.Тем не менее, вы можете использовать -finline-limit, чтобы заставить gcc рассмотреть встраивание больших функций, или -fomit-frame-pointer -fno-exceptions, чтобы минимизировать кадр стека.(Обратите внимание, что последний может прервать отладку и привести к тому, что исключения C ++ будут плохо себя вести.)

Вероятно, вы не сможете получить много пользы от настройки опций компилятора, и вам придется провести рефакторинг.

...