LLVM, boite à outil pour la portabilité du code

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 en l'addition du Web comme cible possible: son code IR peut être compilé en Asm.js ou WebAssembly.

Il y a cependant des inconvénients. Le runtime de LLVM n'inclut pas de garbage collector, cela doit être fourni avec le runtime du langage compilé. En outre le code intermédiaire n'est pas portable, on doit produire un code spécifique à une architecture de processeur. C'est la raison pour laquelle WebAssembly a été inventé. On qualifie aussi LLVM de cible mouvante dans la mesure ou le code qu'il produit évolue dans le temps.

En 2018 de façon inattendue, des employés quittent le projet parce qu'il est devenu trop politique et fait passer un agenda social avant la compétence, ce qui laisse planer un doute sur la future qualité du code.

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.