Почему исполняемый файл такой большой?(Почему не удаляется мертвый код?) - PullRequest
19 голосов
/ 24 февраля 2012

Компиляция и связывание этого файла приводит к 1-КиБ исполняемый файл :

#pragma comment(linker, "/Entry:mainCRTStartup") // No CRT code (reduce size)
#pragma comment(linker, "/Subsystem:Console")    // Needed if avoiding CRT

#define STRINGIFIER(x)    func##x
#define STRINGIFY(x)      STRINGIFIER(x)
#define G   int STRINGIFY(__COUNTER__)(void) { return __COUNTER__; }

int mainCRTStartup(void) { return 0; }  // Does nothing

#if 0
    // Every `G' generates a new, unused function
    G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
    G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
#endif

При изменении #if 0 на #if 1) выходной размер удваивается до 2 КиБ.

Похоже, что это происходит со всеми версиями Visual C ++ на сегодняшний день, хотя мои параметры командной строки содержат все возможные оптимизации:

/Ox /MD /link /fixed /OPT:ICF /OPT:REF

и, в частности, я не включил отладочную информацию.

Кто-нибудь знает, почему /OPT:REF не заставляет компоновщик удалить неиспользуемые функции?

1 Ответ

20 голосов
/ 24 февраля 2012

В общих чертах ... компилятор генерирует код в «объектных записях», который содержит набор кода сборки и вспомогательную информацию.Компоновщик связывает эти записи объектов вместе, чтобы создать исполняемый файл.

Часто компилятор создает одну запись объекта для всего исходного файла.В этом случае компоновщик может принять решение только о ссылке во всей записи объекта или нет.Поскольку в записи объекта есть хотя бы одна функция, она должна ссылаться на все из них.

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

Из документации Microsoft для / OPT :

/ OPT: REF

LINK по умолчанию удаляет пакетные функции без ссылок.Объект содержит упакованные функции (COMDAT), если он был скомпилирован с параметром / Gy.Эта оптимизация называется транзитивным устранением COMDAT.Чтобы переопределить это значение по умолчанию и оставить COMDAT без ссылки в программе, укажите / OPT: NOREF.Вы можете использовать параметр / INCLUDE, чтобы переопределить удаление определенного символа.

Опция компилятора /Gy включает связывание на уровне функций.

Для справки, эта функция также существует в gcc:

-ffunction-section
-fdata-section

Поместить каждую функцию или элемент данных в свои собственныераздел в выходном файле, если цель поддерживает произвольные разделы.Имя функции или имя элемента данных определяет имя раздела в выходном файле.

Используйте эти параметры в системах, где компоновщик может выполнять оптимизацию для улучшения местоположения ссылки в пространстве команд.Большинство систем, использующих объектный формат ELF и процессоры SPARC, работающие под управлением Solaris 2, имеют компоновщики с такой оптимизацией.В будущем в AIX могут появиться эти оптимизации.

Используйте эти параметры только тогда, когда это дает значительные преимущества.Если вы укажете эти параметры, ассемблер и компоновщик создадут более крупные объектные и исполняемые файлы, а также будут работать медленнее.Вы не сможете использовать «gprof» на всех системах, если вы укажете эту опцию, и у вас могут возникнуть проблемы с отладкой, если вы укажете и эту опцию, и -g.

И сопутствующийопция в ld:

- gc-section

Включить сбор мусора из неиспользуемых разделов ввода.Он игнорируется для целей, которые не поддерживают эту опцию.Эта опция не совместима с -r или --emit-relocs.Поведение по умолчанию (не выполнение этой сборки мусора) можно восстановить, указав --no-gc-section в командной строке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...