LLVM, le graal de la programmation

Depuis toujours l'idéal de la programmation est un langage portable et proche du code natif en vitesse d'exécution.

Logo LLVM émergeant d'un ordinateur

LLVM apporte cela avec le code IR (Intermediate Representation) qu'il produit. Ce langage object est assez similaire à un langage d'assemblage et les optimisations apportées sur son code, qui seraient difficiles à produire et maintenir sur un langage de haut niveau, lui apportent la vitesse requise. L'IR est ensuite exécuté par un compilateur Just In Time qui le traduit presque directement en code machine.

LLVM (Low Level Virtual Machine) est plus qu'une machine virtuelle, c'est aussi une infrastructure de compilateur, un ensemble d'outils écrits en C++ et fonctionnant sur les systèmes Unix et Linux ainsi que sur Windows. Ces outils fonctionnent avec le bitcode (et non bytecode) LLVM qui est l'empaquetage du code IR dans un module distribuable.
Les outils se composent du compilateur Clang qui supporte quatre langages et produit du le bitcode (incluant l'IR) ou un exécutable binaire, d'un optimiseur de code, du debogueur LLDB, d'un éditeur de liens, de machines virtuelles JIT, d'un interpréteur.
Il a été créé par l'Université de l'Illinois et obtenu la contribution d'Apple qui a notamment écrit Clang.

LLVM accorde une nouvelle jeunesse aux anciens programmes écrits en C, C++ ou autres langages (tous les langages statiquement typés peuvent être compilés en IR). Il permet de les exécuter sur de nouveaux systèmes. Ils peuvent être convertis en JavaScript (avec Emscriptem) et fonctionner dans les navigateurs. Ou grâce à Portable Native Client, une fois traduits en IR, fonctionner aussi sur tout système...
LLVM est donc une alternative à Java avec l'option navigateur en plus et il a pour concurrent Ams.js en tant qu'objet à distribuer mais Asm.js est aussi un complément de son écosystème puisqu'il peut être produit à partir du code IR.

Il y a cependant au moins un inconvénient. Le runtime de LLVM n'inclut pas de garbage collector, cela doit être fourni avec le runtime du langage compilé.

Diagramme du fonctionnement de LLVM

Diagramme du fonctionnement de LLVM

LLVM permet de générer du bitcode à partir de nombreux langages statiquement typés: C et Objective C avec CLang, Java, ADA, Fortran avec GCC et d'autres langages avec d'autres compilateurs dès lors qu'ils supportent le bitcode en sortie.

Ce bitcode est optimisé puis il peut être utilisé directement par une machine virtuelle LLVM. Avec une édition de lien et une compilation statique, il peut devenir un binaire exécutable. Et on peut aussi recourir à Emscriptem pour le convertir JavaScript ou Asm.js, ce qui permet de faire tourner le programme dans le navigateur.

LLVM inclut une machine virtuelle JIT qui est utilisée par Mono, Julia et de nombreux autres projets.

Différence entre le code Java et LLVM

Le meilleur moyen de voir la différence entre les codes qui sont produits, est par l'exemple:

Soit une simple fonction en C ou en Java à compiler:

int arith(int x, int y, int z)
{    
    return(x * y + z);  
}

LLVM produit ce code IR:

define i32 @arith(i32 %x, i32 %y, i32 %z) 
{  
   entry:    
   %tmp = mul i32 %x, %y    
   %tmp2 = add i32 %tmp, %z    
   ret i32 %tmp2  
}

Alors que Java produit ce bytecode (on peut le voir avec l'outil javap du JDK):

public class demo.Demo {
  public static int arith(int, int, int);
    Code:
       0: iload_0
       1: iload_1
       2: imul
       3: iload_2
       4: iadd
       5: ireturn
}

On voit que le bytecode, outre qu'il est plus proche du langage machine, utilise une pile pour stocker les données et effectuer des opérations dessus alors que l'IR utilise des registres et des zones de mémoire.

Et différence entre bitcode et bytecode

Quelle est la différence entre le bitcode et l'IR? Pourquoi parle-t-on de bitcode et non de bytecode comme c'est le cas pour Java?

Dans les deux cas, le code est exécuté par une machine virtuelle, JIT ou non. Le nom de bytecode vient de ce que le jeu d'instruction était codé à l'origine sur un octet (un byte en anglais). Ce n'est plus forcément le cas, mais le flux reste un flux d'octets (bytestream) tandis que l'on utilise le terme bitcode, pour marquer le fait que le flux est exprimé en bits (bitstream), et donc non en octets mais en unités de tailles variables.

L'IR (Intermediate Representation) est le langage conçu pour une machine virtuelle ou un compilateur, et il est encapsulé dans un fichier que l'on appelle dans le cas de LLVM, le bitcode. Celui-ci est encodé en flux de bits (bitstream) composé de blocs (blocks) et d'enregistrements (records).

Outils

Sur Windows, on peut utiliser Visual Studio ou Eclipse CDT avec le plugin LLVM. QtCreator peut aussi utiliser Clang.