LLVM джит и родной - PullRequest
       47

LLVM джит и родной

18 голосов
/ 18 августа 2010

Я не понимаю, как LLVM JIT относится к нормальной компиляции без JIT, и документация не очень хорошая.

Например, предположим, что я использую интерфейс clang:

  1. Случай 1: Я компилирую файл C в нативный с помощью clang / llvm.Этот поток, который я понимаю, похож на поток gcc - я получаю свой исполняемый файл x86, который запускается.
  2. Случай 2: я компилирую в какой-то тип LLVM IR, который работает на LLVM JIT.В этом случае исполняемый файл содержит среду выполнения LLVM для выполнения IR на JIT, или как он работает?

В чем разница между этими двумя и правильны ли они?Включает ли поток LLVM поддержку как JIT, так и не JIT?Когда я хочу использовать JIT - имеет ли это смысл для такого языка, как C?

Ответы [ 4 ]

30 голосов
/ 18 августа 2010

Вы должны понимать, что LLVM - это библиотека, которая помогает вам создавать компиляторы.Clang - это просто интерфейс для этой библиотеки.

Clang переводит код C / C ++ в LLVM IR и передает его в LLVM, который компилирует его в собственный код.

LLVM также может генерироватьнативный код непосредственно в памяти, который затем можно вызывать как обычную функцию.Так, случаи 1. и 2. делятся оптимизацией и генерацией кода LLVM.

Так как же использовать LLVM в качестве JIT-компилятора?Вы создаете приложение, которое генерирует некоторый IR LLVM (в памяти), а затем используете библиотеку LLVM для генерации собственного кода (все еще в памяти).LLVM возвращает вам указатель, который вы можете вызвать позже.Никакой clang не задействован.

Однако вы можете использовать clang для перевода некоторого кода на C в LLVM IR и загрузки его в свой JIT-контекст для использования функций.

Примеры из реального мира:

Существует также Калейдоскоп учебник, который показывает, как реализовать простой язык с JIT-компилятором.

26 голосов
/ 19 сентября 2010

Сначала вы получите байт-код LLVM (IR LLVM):

clang -emit-llvm -S -o test.bc test.c 

Во-вторых, вы используете LLVM JIT:

lli test.bc

Запускает программу.

Затем, если вы хотите получить нативный, вы используете LLVM backend:

llc test.bc

Из выходных данных сборки:

as test.S
6 голосов
/ 02 августа 2013

Я предпринимаю шаги для компиляции и запуска кода JIT из почтового сообщения в сообществе LLVM.

[LLVMdev] MCJIT и учебник по калейдоскопу

Заголовочный файл:

// foo.h
extern void foo(void);

и функция для простой функции foo ():

//foo.c
#include <stdio.h>
void foo(void) {
    puts("Hello, I'm a shared library");
}

И основная функция:

//main.c
#include <stdio.h>
#include "foo.h"
int main(void) {
    puts("This is a shared library test...");
    foo();
    return 0;
}

Сборка общей библиотекииспользуя foo.c:

gcc foo.c -shared -o libfoo.so -fPIC

Создать битовый код LLVM для файла main.c:

clang -Wall -c -emit-llvm -O3 main.c -o main.bc

И запустить битовый код LLVM через jit (и MCJIT), чтобы получить желаемый вывод:

lli -load=./libfoo.so main.bc
lli -use-mcjit -load=./libfoo.so main.bc

Вы также можете передать вывод clang в lli:

clang -Wall -c -emit-llvm -O3 main.c -o - | lli -load=./libfoo.so 

Выход

This is a shared library test...
Hello, I'm a shared library

Источник, полученный из

Общие библиотеки с GCC в Linux

2 голосов
/ 20 августа 2010

Большинство компиляторов имеют внешний интерфейс, некоторый средний код / ​​структуру некоторого вида и внутренний интерфейс. Когда вы берете свою C-программу и используете clang и компилируете так, что в итоге вы получаете не-JIT x86-программу, которую вы можете просто запустить, вы все равно перешли от внешнего интерфейса к среднему и внутреннему. То же самое касается gcc, gcc переходит от внешнего интерфейса к среднему и внутреннему. Средство Gcc не является широко открытым и пригодным для использования, как LLVM.

Теперь одна вещь, которая забавна / интересна в llvm, которую вы не можете сделать с другими, или, по крайней мере, с gcc, - это то, что вы можете взять все свои модули исходного кода, скомпилировать их в байт-код llvms, объединить их в один большой байт-код файл, а затем оптимизировать все целиком, вместо оптимизации по каждому файлу или по функции, которую вы получаете с другими компиляторами, с помощью llvm вы можете получить любой уровень частичной компиляции для оптимизации программы, который вам нравится. затем вы можете взять этот байт-код и использовать llc, чтобы экспортировать его в ассемблер целей. Я обычно делаю встраивание, так что у меня есть собственный код запуска, который я обертываю вокруг этого, но теоретически вы сможете взять этот файл на ассемблере и с помощью gcc скомпилировать и связать его и запустить. gcc myfile.s -o myfile. Я полагаю, что есть способ заставить инструменты llvm сделать это и не использовать binutils или gcc, но я не потратил время.

Мне нравится llvm, потому что он всегда кросс-компилятор, в отличие от gcc, вам не нужно компилировать новый для каждой цели и разбираться с нюансами для каждой цели. Я не знаю, что я использую JIT, потому что я говорю, что использую его как кросс-компилятор и как нативный компилятор.

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

...