Может ли LTO для gcc или clang оптимизировать методы C и C++?

Если оптимизация времени компоновки (LTO) используется с gcc или clang, возможна ли оптимизация кода на границе языков C и C++?

Например, можно ли встроить функцию C в вызывающую программу C++?


person BeeOnRope    schedule 30.12.2017    source источник


Ответы (1)


Да!

Оптимизация времени компоновки обычно работает с промежуточным представлением (IR), представленным в «толстых» объектных файлах, которые могут содержать как машинный код для традиционной компоновки, так и IR для компоновки LTO.

На данном этапе языковых конструкций высокого уровня больше нет, поэтому оптимизация времени компоновки не зависит от языка.


ССАГПЗ

оптимизация времени компоновки (LTO) GCC работает с GIMPLE, одним из промежуточные представления. IR всегда не зависит от языка, поэтому любые оптимизации времени компоновки будут работать в коде, сгенерированном на любом языке.

Из документации по Параметры оптимизации GCC:

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

gcc -c -flto foo.c
g++ -c -flto bar.cc
gfortran -c -flto baz.f90
g++ -o myprog -flto -O3 foo.o bar.o baz.o -lgfortran

Обратите внимание, что последняя ссылка делается с помощью g++ для получения библиотек времени выполнения C++, а -lgfortran добавляется для получения библиотек времени выполнения Fortran. Как правило, при смешивании языков в режиме LTO следует использовать те же параметры команды link, что и при смешивании языков в обычной (не LTO) компиляции.


Вот пример, чтобы показать вам, насколько мощной является эта технология. Мы определим функцию C и вызовем ее из программы C++:

func.h

#ifndef FUNC_DOT_H
#define FUNC_DOT_H

#ifdef __cplusplus
extern "C" {
#endif

int func(int a, int b, int c);

#ifdef __cplusplus
}
#endif

#endif /* FUNC_DOT_H */

func.c

#include "func.h"

int func(int a, int b, int c)
{
    return 3*a + 2*b + c;
}

main.cpp

#include "func.h"

int main()
{
    int a = 1;
    int b = 2;
    int c = 3;

    return func(a, b, c);
}

Скомпилировать

gcc -o func.o -c -Wall -Werror -flto -O2 func.c
g++ -o main.o -c -Wall -Werror -flto -O2 main.cpp
g++ -o testlto -flto -O2 main.o func.o

Разобрать (objdump -Mintel -d -R -C testlto)

Disassembly of section .text:

00000000004003d0 <main>:
  4003d0:   b8 0a 00 00 00          mov    eax,0xa   ; 1*3 + 2*2 + 3 = 10
  4003d5:   c3                      ret

Вы можете видеть, что он не только встроил мой C func() в мой C++ main(), но и превратил все это в константное выражение!


Клэнг/LLVM

Используя тот же синтаксис, Clang может создавать «толстые» объектные файлы с LLVM IR, которые можно оптимизировать во время компоновки. См. раздел Оптимизация времени соединения LLVM.

Используя тот же тестовый код, что и выше, clang дает точно такой же результат:

00000000004004b0 <main>:
  4004b0:   b8 0a 00 00 00          mov    eax,0xa
  4004b5:   c3                      ret
person Jonathon Reinhart    schedule 30.12.2017
comment
Я полагаю, что обычные предостережения о LTO все еще применимы: AFAIK разные версии gcc могут создавать разные версии GIMPLE, которые могут не работать, если компоновщик вызывает версию компилятора, которая не использует версию, встроенную в файл .o? В действительно странном случае, когда ваши gcc и g++ были разными версиями, я думаю, это может иметь значение. - person BeeOnRope; 30.12.2017
comment
Верно - мой ответ предполагает нормальную, правильно работающую установку компилятора/компоновщика. - person Jonathon Reinhart; 30.12.2017
comment
Правильно - более реалистичным способом возникновения несоответствия версий было бы, скажем, статическое связывание, где файл .a был жиром компилятора и содержит IR, но был сгенерирован другим способом, на другой машине или в другое время, чем окончательный исполняемый файл. В этом случае GIMPLE в '.a' может быть проигнорирован, если он не совместим с локальным компилятором. Конечно, это всего лишь общая ошибка для LTO по сравнению с традиционным связыванием, и она не применяется конкретно к межъязыковой оптимизации. - person BeeOnRope; 30.12.2017
comment
У Clang, похоже, нет способа создавать толстые объектные файлы (по крайней мере, в соответствии с терминологией gcc). func.o - это просто битовый код LLVM IR, поэтому его нельзя скомпилировать ни с чем, кроме clang (предположительно, должно быть возможно и обратное, хотя в данный момент я борюсь): stackoverflow.com/questions/51259340/ - person jberryman; 06.03.2019