2010-08-18 19 views
18

Non capisco come JIT LLVM si relaziona con la normale compilazione JIT e la documentazione non è buona.LLVM jit e nativo

Per esempio supponiamo che io uso il clang front-end:

  1. Caso 1: compilo il file C a nativa con clang/LLVM. Questo flusso che capisco è come gcc flow: ottengo il mio eseguibile x86 e funziona.
  2. Caso 2: compilo in una sorta di LLVM IR eseguito su JV LLVM. In questo caso l'eseguibile contiene il runtime LLVM per eseguire l'IR su JIT, o come funziona?

Qual è la differenza tra questi due e sono corretti? Il flusso LLVM include il supporto per JIT e non JIT? Quando voglio usare JIT - ha senso per un linguaggio come C?

risposta

30

Devi capire che LLVM è una libreria che ti aiuta a compilare i compilatori. Clang è semplicemente un frontend per questa libreria.

Clang converte il codice C/C++ in LLVM IR e lo passa a LLVM, che lo compila nel codice nativo.

LLVM è anche in grado di generare codice nativo direttamente in memoria, che può quindi essere chiamato come una funzione normale. Quindi il caso 1. e 2. condividono l'ottimizzazione e la generazione del codice di LLVM.

Quindi, come si usa LLVM come compilatore JIT? Si crea un'applicazione che genera alcuni LLVM IR (in memoria), quindi si utilizza la libreria LLVM per generare codice nativo (ancora in memoria). LLVM ti restituisce un puntatore che puoi chiamare in seguito. Nessun clang coinvolto.

È tuttavia possibile utilizzare clang per tradurre un codice C in LLVM IR e caricarlo nel contesto JIT per utilizzare le funzioni.

Real World esempi:

C'è anche il tutorial Kaleidoscope che mostra come implementare un linguaggio semplice con compilatore JIT.

+0

Quindi, per usare LLVM come JIT che c'è bisogno di collegarlo nella vostra applicazione, giusto? Ci sono applicazioni che lo fanno? – zaharpopov

2

La maggior parte dei compilatori ha un front-end, un codice/struttura di qualche tipo e il back-end. Quando si prende il programma C e si usa clang e si compila in modo tale che si finisca con un programma x86 non-JIT che si può semplicemente eseguire, si è passati da frontend a middle a backend. Lo stesso vale per gcc, gcc passa da frontend a middle thing e backend. La cosa intermedia di Gccs non è ampia e utilizzabile come quella di LLVM.

Ora una cosa che è divertente/interessante su llvm, che non puoi fare con gli altri, o almeno gcc, è che puoi prendere tutti i moduli del tuo codice sorgente, compilarli in un bytecode di llvms, unirli in un unico grande il file bytecode, quindi ottimizza l'intera cosa, anziché l'ottimizzazione per ogni file o funzione che ottieni con altri compilatori, con llvm puoi ottenere qualsiasi livello di ottimizzazione del programma da compilare a tuo piacimento. allora puoi prendere quel codice bytecode e usare llc per esportarlo nell'assembler dei bersagli. Io di solito lo faccio in modo embedded, quindi ho il mio codice di avvio che mi avvolge, ma in teoria dovresti essere in grado di prendere quel file assembler e con gcc compilarlo e collegarlo ed eseguirlo. gcc myfile.s -o miofile.Immagino che ci sia un modo per ottenere gli strumenti llvm per farlo e non dover usare binutils o gcc, ma non mi ci è voluto del tempo.

Mi piace llvm perché è sempre un compilatore incrociato, a differenza di gcc non è necessario compilarne uno nuovo per ogni obiettivo e gestire le sfumature per ciascun target. Non so che ho alcun uso per la cosa JIT è quello che sto dicendo che lo uso come cross compilatore e come compilatore nativo.

Quindi il tuo primo caso è il fronte, il centro, la fine e il processo è nascosto da te, inizi con la fonte e ottieni un binario, fatto. Il secondo caso è se capisco la parte anteriore e quella centrale e si fermano con un file che rappresenta il centro. Quindi il medio verso la fine (il processore di destinazione specifico) può avvenire appena in tempo in fase di esecuzione. La differenza è che il backend, l'esecuzione in tempo reale della seconda lingua del secondo caso, è probabilmente diverso dal backend del caso uno.

+4

sia gcc che icc hanno un lto e un ipo che "ottimizzano il tutto", quindi non è una caratteristica unica di llvm. Inoltre, llvm non è un semplice "cross-compiler", è un compilatore, che supporta molti obiettivi nel singolo binario. È un po 'meglio del semplice cross-compiler, è un compilatore universale - funziona per qualsiasi target. – osgx

+0

Mi piace quel termine "compilatore universale". grazie per l'aggiornamento, non sapevo che avresti potuto ottenere gcc per farlo, qualcosa con cui giocare un giorno. –

22

In primo luogo, si ottiene LLVM bytecode (LLVM IR):

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

In secondo luogo, si utilizza LLVM JIT:

lli test.bc 

che esegue il programma.

Poi, se si desidera ottenere nativa, si utilizza LLVM backend:

llc test.bc 

Dalla uscita di montaggio:

as test.S 
5

sto prendendo i passaggi per compilare ed eseguire il JIT'ed codice da un messaggio di posta nella comunità LLVM.

[LLVMdev] MCJIT and Kaleidoscope Tutorial

file di intestazione:

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

e la funzione per un semplice foo() funzione:

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

e la funzione principale:

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

Creare la libreria condivisa usando foo.c:

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

Generare il codice di bit LLVM per il principale.file C:

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

ed eseguire il codice binario che LLVM attraverso JIT (e MCJIT) per ottenere il risultato desiderato:

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

È inoltre possibile reindirizzare l'output clang in lli:

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

Uscita

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

fonte ottava ained da

Shared libraries with GCC on Linux