GCC, -flto, -fno-builtin и реализация пользовательских функций функций glibc - PullRequest
0 голосов
/ 23 декабря 2018

Я наблюдаю неожиданное поведение (по крайней мере, не могу найти объяснения этому) с флагами GCC -flto и jemalloc / tcmalloc.Когда используется -flto и я связываюсь с указанными выше библиотеками, malloc / calloc и друзья не заменяются реализацией je/tc malloc, вызывается реализация glibc.Как только я уберу флаг -flto, все будет работать как положено.Я пытался использовать -fno-builtin / -fno-builtin-* с -flto, но, тем не менее, он не выбирает реализацию je/tc malloc.

Как работает механизм -flto?Почему бинарный файл не выбирает новую реализацию?Как это даже связывается с -fno-builtin, когда он должен потерпеть неудачу на неразрешенном внешнем, скажем, printf?

EDIT001:
GCC 7.3
Пример кода

int main()
{
    auto p = malloc(1024);
    free(p);
    return 0;
}

Компиляция:

/ usr / bin / c ++ -O2 -g -DNDEBUG -flto -std = gnu ++ 14 -o CMakeFiles / flto.dir / main.cpp.o -c/home/user/Development/CPPJunk/flto/main.cpp

Связь:

/ usr / bin / c ++ -O2 -g -DNDEBUG -flto CMakeFiles / flto.dir / main.cpp.o -o flto -L / home / user / Development / jemalloc -Wl, -rpath, / home / user / Development / jemalloc -ljemalloc

EDIT002:
Более подходящий пример кода

#include <cstdlib>

int main()
{
    auto p = malloc(1024);
    if (p) {
        free(p);
    }

    auto p1 = new int;
    if (p1) {
        delete p1;
    }

    auto p2 = new int[32];
    if (p2) {
        delete[] p2;
    }
    return 0;
}

1 Ответ

0 голосов
/ 24 декабря 2018

Во-первых, ваш пример кода неверен.Внимательно прочитайте стандарт C11 n1570 .Если вы хотите использовать стандарт malloc, вам следует #include <stdlib.h>.

В C ++ 11 (читается n3337 ) malloc осуждаетсяи не должен использоваться (предпочитайте new).Если вы все еще хотите использовать std::malloc в C ++, вы должны #include <cstdlib> (который в GCC включает в себя <stdlib.h>)

Тогда ваш пример кода почти код C (после замены auto на void*), а не на C ++.Это может быть оптимизировано (если вы включите <stdlib.h>), даже без -flto, но только с -O3, согласно правилу как если бы , в пустую main.(Я даже написал публичный отчет, bismon-chariot-doc.pdf , в котором есть раздел §1.4.2, объясняющий на нескольких страницах, как происходит такая оптимизация).

Для оптимизацииоколо malloc и free, GCC использует некоторый __attribute__(malloc) атрибут функции в объявлении (внутри <stdlib.h>) malloc.

Как -fltoмашиностроение работает?

LTO поясняется в Внутренние компоненты GCC §25 .

Он работает с использованием некоторых внутренних ( GIMPLE -подобных и / или SSA -подобное) представление кода как во время «компиляции», так и во время «линковки» (на самом деле шаг компоновки становится другой компиляцией с оптимизацией всей программы, поэтому ваш код на практике дважды «компилируется»).

LTO всегда следует (на практике) использовать с некоторым флагом оптимизации (например, -O2 или даже -O3) как во время компиляции, так и во время компоновки.Таким образом, вы должны скомпилировать и связать с g++ -flto -O2 (нет практического смысла использовать -flto без как минимум -O2 и точно такую ​​же оптимизациюфлаги должны использоваться при компиляции и во время компоновки).

Точнее -flto также встраивает в объектные файлы некоторое внутреннее ( GIMPLE -подобное) представление исходного кода, и этотакже используется «во время соединения» (в частности, для оптимизации и вставки происходит снова при «связывании» вашей всей программы с повторным использованием ее GIMPLE).На самом деле GCC содержит некоторый внешний интерфейс LTO и компилятор с именем lto1 (в дополнение к внешнему интерфейсу C ++ и компилятору с именем cc1plus) и lto1 (когда вы связываете с g++ -flto -O2)используется во время соединения для повторной обработки этих представлений GIMPLE.

Возможно, libjemalloc имеет свои собственные заголовки и может иметь inline (или встроенные) функции.Затем вам также нужно использовать -flto -O2 при компиляции этой библиотеки из ее исходного кода (чтобы ее Gimple сохранялся в библиотеке)

Наконец, тот факт, что вызывается обычный malloc, не зависит от-flto.Это проблема компоновщика, а не компилятора.Вы можете попытаться связать -ljemalloc статически (и тогда вам лучше собрать эту библиотеку также с gcc -flto -O2; если вы не создадите ее таким образом, вы не получите оптимизацию LTO для вызовов malloc).

Вы также можете передать -v командам компиляции и компоновки, чтобы понять, что делает g++.Вы могли бы даже передать -Wl,--verbose, чтобы попросить ld (начатый с g++) быть многословным.

Обратите внимание, что LTO (и его внутренние представления) зависят от компилятора и версии.Внутреннее (Gimple & SSA ) представление немного отличается от GCC 7 & GCC 8 (а в Clang это очень отличается, поэтому, конечно, несовместимо).Динамический компоновщик ld-linux (8) не знает о LTO.

PS.Вы можете установить пакет libjemalloc-dev и добавить #include <jemalloc/jemalloc.h> в свой код.Смотрите также jemalloc (3) справочная страница.Возможно, libjemalloc можно настроить или исправить, чтобы определить символ je_malloc вместо malloc.Тогда было бы проще (для LTO) использовать je_malloc в вашем коде (чтобы избежать конфликта между несколькими malloc ELF символами).Чтобы узнать больше о символах в общих библиотеках, прочитайте статью Дреппера Как писать общие библиотеки .И, конечно, вы должны ожидать, что LTO изменит поведение линковки!

...