Подтвердить что ты не робот

LLVM jit и родной

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

lli test.bc

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

Затем, если вы хотите получить native, вы используете LLVM-сервер:

llc test.bc

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

as test.S

Ответ 3

Я предпринимаю шаги для компиляции и запуска 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

Ответ 4

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

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

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

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