Journal LLVM dans un gestionnaire de paquets ?

Posté par  (site web personnel) .
Étiquettes :
24
1
sept.
2009
Bonjour,

Aujourd'hui, comme je n'avais pas grand-chose à faire, j'ai testé llvm. Mon but n'est pas de voir s'il peut me compiler KDE, ffmpeg, GNOME ou autre, non, il y a de fortes chance qu'il n'y arrive pas.

En fait, j'ai une idée derrière la tête, idée qui va vous plaire, une bonne idée.

Le principe est simple : créer un gestionnaire de paquets, sauf que cette fois-ci, j'ai trouvé ce qui sera révolutionnaire dedans. En effet, les paquets seront petits, très petits. Ils seront rapides, très rapide également.

Comment y arriver ? C'est simple :

  • Prendre un bon gros programme
  • Le compiler avec clang, et sortir un fichier .ll
  • Petite chaîne de compilation llvm (assembleur, optimisations, bytecode)
  • Empaqueter ça, c'est à dire une représentation compacte (le bytecode), et surtout indépendante de l'architecture du processeur
  • Du côté client, on récupère le paquet. Il contient les instructions pour la suite de la compilation (donc juste les libs à passer en paramètre -l à ld)
  • Compiler le bytecode en code natif, adapté à la machine

Ça paraît trop beau pour être vrai, j'en pleure presque. Snif, fini les problèmes, on va être a des millions de kilomètres de ce que le proprio fait, de Windows, de Mac, etc. Voici la liste des avantages :

  • Des mirroirs légers, très légers : tout n'est qu'en une seule architecture, et le bytecode est largement plus petit que le code compilé !
  • Une éxécution super rapide chez le poste client, car optimisé aux petits oignons pour son CPU, quel qu'il soit (dernier Core i7, AMD Phenom, un PPC, un Cell, un ARM, etc)
  • Sans boulot supplémentaire, on supporte toutes les architectures que LLVM supporte. De plus, les paramètres -march et -mcpu de lli (le programme qui transforme le source llvm en bytecode) sont très simples à utiliser, et j'ai avec succès généré une application pour PPC sur mon x86_64. Ainsi, les LiveCD qui doivent être précompilés sont générés très facilement et rapidement

Et voici les inconvénients qu'on pourrait penser que ce système a, mais qu'il n'a pas :

  • Transformer le bytecode en assembleur est très rapide, l'assemblage aussi, et le linkage également. Le temps éventuellement perdu est de toute façon gagné en temps de téléchargement (mon fichier bytecode pour un tout petit fichier C est près de 10 fois plus petit que le binaire non-strippé, et 5x plus petit que quand il est strippé !)
  • Le statut expérimental de llvm et clang : ils marchent bien, surtout llvm. Au pire, on utilise le front-end llvm-gcc, ce qui nous apporte également le C++
  • Pas besoin de build-dependecies, tous les fichiers .h ont été parsés, et le code se trouve directement dans le fichier bytecode.

Côté inconvénients, je ne vois rien, pour le moment.

Les tests

Et oui, j'ai testé. Je suis partit d'un tout petit fichier C, le TP Plus ou Moins qu'on peut trouver ici (je n'avais pas envie de trouver un fichier C moi-même, et celui là est plus "réel" qu'un Hello Word).

Ensuite, la compilation :


$ clang-cc test.c -emit-llvm -o - | llvm-as | opt -std-compile-opts > bytecode.bc
$ clang -o binaire_llvm test.c
$ ./binaire_llvm
Quel est le nombre ? 4
C'est moins !
[ ... donc ça marche ;-) ... ]
$ gcc -o binaire_gcc test.c
$ ./binaire_gcc
Quel est le nombre ? 3
C'est plus !
[ ... Lui aussi marche ... ]
$ strip --strip-all binaire_gcc # Soyons honnête sur la comparaison
$ strip --strip-all binaire_llvm
$ llc -o bytecode_asm.s bytecode_llvm.bc # Maintenant, compileation du bytecode
$ clang -o bytecode_bin bytecode_asm.s
$ ./bytecode_bin
Quel est le nombre ?
[ ... c'est bon aussi ... ]
$ ls -l
5104 sep 1 12:50 binaire_gcc
5112 sep 1 12:50 binaire_llvm
5128 sep 1 13:12 bytecode_bin
1116 sep 1 12:48 bytecode_llvm.bc


On voit donc que le bytecode est 5 fois plus petit que le résultat compilé et strippé.

Intéressant non ? Plus qu'à tester sur de gros programmes (ce que je vais faire).

PS: J'ai refait le même test avec un code C plus gros (le TP du pendu :P ), et le bytecode fait 3472 octets, alors que le fichier binaire le plus petit (celui de llvm) fait 5536 octets. Je vais tester avec encore plus gros.

PS 2: (décidément, linuxfr down me permet de bien tester) son TP du sokoban est remarquable : je compile chaque fichier .c en byte code, puis utilise llvm-ld pour générer tout.bc, puis lie le tout en un fichier sokoban. Tout.bc pèse à peine 5264 octets, alors que sokoban, strippé, pèse 11416 octets ! La version sokoban de gcc pèse 12904 octets. LLVM est le grand gagnant !

Encore un PS (j'ai eu le temps): marche à la perfection avec Cream, un navigateur web en GTK 2. Super et rapide : Cream. 63k pour le fichier bytecode, et 77k pour le binaire. Ca me paraît petit comme gain, peut-être que GTK met beaucoup de données qui ne sont pas des binaires dans son code.
  • # opt de taille

    Posté par  (site web personnel) . Évalué à 4.

    Est-ce que dans les deux cas les optimisations pour la taille du code produit sont les mêmes ? J'avais souvenir en ayant fait le même genre de tests que gcc de base était moins violent, d'où un code bien moins compact. Mais, toujours de souvenir, en rajoutant un -Os, l'écart avait disparu entre les 2.
    Je parle de tests que j'ai fais il y a bien 1année...

    (PS: je reste dubitatif sur ton hypothèse qui dit "les build-deps == ensemble de .h". Tu zappes un paquet de chose là, ne serait-ce que les bibliothèques liées statiquement)
    • [^] # Re: opt de taille

      Posté par  (site web personnel) . Évalué à 3.

      Je n'ai pas fait de recherches sur l'optimisation du côté de LLVM, j'ai recopié la doc. C'est donc égal à un -O0.

      Pour les bibliothèques liées statiquement, hors le fait que je n'aime pas, il devrait être possible de les fournir dans le paquet, puis d'utiliser "clang -o binaire fichiers.s -L. -llib_statique", ou quelque-chose comme ça. C'est un domaine jamais exploré, je vais voir ce que je peux trouver.
      • [^] # Re: opt de taille

        Posté par  (site web personnel) . Évalué à 3.

        Quand on fais du profiling ou de la comparaisons, il faut toujours se mettre dans le cas final. C-à-d avec toutes les options d'optimisation active des 2 coté.

        Sinon c'est inutile.

        (Les comparaisons peuvent s'inverser au final)
  • # petit ordi et grosse connexion

    Posté par  (site web personnel) . Évalué à 3.

    > Le temps éventuellement perdu (à la compile) est de toute
    > façon gagné en temps de téléchargement

    Moi j'ai pas un ordi véloce, mais j'ai une grosse connexion adsl (à 300m du dslam)
    La compile de firefox est extrêmement plus longue que le téléchargement/installe de ce dernier ...

    cependant, c'est super interessant ton concept
  • # Pas très écolo

    Posté par  . Évalué à 2.

    Tu économises (un peu) de bande passante mais en contrepartie tu fais compiler N fois un programme alors qu'auparavant il était compilé une fois par architecture.

    Sinon le bitcode llvm n'est pas toujours portable d'une architecture à l'autre.

    Par contre les blocks/libdispatch de Apple 10.6 seraient intéressants à porter sous linux, des volontaires ?

    http://arstechnica.com/apple/reviews/2009/08/mac-os-x-10-6.a(...)
    • [^] # Re: Pas très écolo

      Posté par  (site web personnel) . Évalué à 1.

      LLVM n'est pas encore stable (c'est d'ailleurs embêtant si la compatibilité du bytecode d'une version à une autre est cassée), mais il me semble qu'ils assurent que leur bytecode est indépendant de l'architecture.

      À mon avis, le problème se situera plutôt du côté des applis "mal codées" (ce qui est relatif) qui ne sont déjà que difficilement portables comme ça (donc celles qui n'utilisent pas des sizeof(void *), ou qui ne font pas attention à l'endianess, etc).

      À la limite, on pourra proposer des paquets spéciaux dépendants d'une architecture qui pose problème pour ces paquets (si foobar marche sur tout sauf sur ARM, on propose le paquet précompilé pour ARM).

      Ensuite, la compilation est faite : le plus difficile et le plus lourd est la transformation des sources vers le bytecode (parsage des fichiers d'en-tête, optimisations, etc). Du côté client, il ne faut que générer le fichier assembleur, l'assembleur et le lier.

      Pour le test de Cream, la phase "compilation sur le serveur" prend environ 20 secondes (peut-être plus, je ne m'en souviens plus), et compiler le bytecode en code natif prend moins d'une seconde. À mon avis, compiler une vingtaine de gros paquets devrait prendre moins de 30 secondes, en tous cas moins d'une minute.

      Compiler KDE depuis les sources, ça prend combien de temps ? 3h chez moi. On y gagne donc si le bytecode s'assemble en moins de 3h (bon, ce n'est pas instantané, mais on installe bien moins souvent un programme qu'on ne l'utilise).
      • [^] # Re: Pas très écolo

        Posté par  . Évalué à 5.

        Ensuite, la compilation est faite : le plus difficile et le plus lourd est la transformation des sources vers le bytecode (parsage des fichiers d'en-tête, optimisations, etc). Du côté client, il ne faut que générer le fichier assembleur, l'assembleur et le lier.

        C'est faux, il reste bien évidemment toutes les optimisations archi-dépendantes (code selection, coalescing, spilling et regalloc, scheduling, etc.) et/ou les optimisations inter-procédurales. Sinon ton système n'apporterait aucun gain (pas de spécialisation).


        Sinon à mon avis quitte à se taper du bitcode, autant faire de la compil dynamique et faire de l'optimisation "runtime".
      • [^] # Re: Pas très écolo

        Posté par  (site web personnel) . Évalué à 2.

        > il me semble qu'ils assurent que leur bytecode est indépendant de l'architecture.

        Non, ce n'est pas le cas. Enfin, le bitcode est plus ou moins indépendant de l'architecture, mais c'est la chaine de compilation en partant du C qui ne l'est pas. Pour prendre un exemple caricatural, comment représenterait-t-on

        printf("%d\n", sizeof(int));

        en bitcode indépendant de l'architecture ?

        Pour les détails, cf. par exemple http://markmail.org/message/chucijjh6ki4jz5c
        • [^] # Re: Pas très écolo

          Posté par  (site web personnel) . Évalué à 4.

          (et si tu penses à écrire un langage avec l'indépendance de l'architecture en tête depuis le source, dans l'optique de pouvoir distribuer les applis sous forme de byte-code, j'ai un scoop : ça s'appelle Java ...)
    • [^] # Re: Pas très écolo

      Posté par  (site web personnel) . Évalué à 2.

      Pas très écolo, c'est vite dit. Il faudrait pouvoir comparer le bilan énergétique des compilation et des téléchargements. Chaque octet transféré sur le réseau consomme de l'énergie, et c'est très loin d'être négligeable.
      Il y avait un article (peut-être légèrement orienté, mais intéressant tout de même) à ce sujet dans le n°58 de La Décroissance (avril 2009) : http://www.ladecroissance.net/?chemin=journal&numero=58 (non lisible en ligne).
  • # Sympa ce journal

    Posté par  . Évalué à 4.

    J'ai appris ce qu'était LLVM, (Low Level Virtual Machine) allez hop pour la route :
    http://llvm.org/

    et j'ai découvert Cream en bonus :)

    Sinon le but général du journal, ce nouveau gestionnaire de paquet, a l'air très prometteur, comme tu dis c'est trop beau pour être vrai !

    Surtout tiens nous au courant des tests !!!!
    Je plussoie \o/
    • [^] # Re: Sympa ce journal

      Posté par  (site web personnel) . Évalué à 2.

      Merci :) .

      La gestion des architectures sera toujours gardées, car il y aura bien des paquets (ne fusse que le kernel) qui ne passeront pas avec LLVM, et qui auront besoin de GCC, ou de fichiers non-binaires mais dépendants de l'architecture (il me semble que ça existe, genre des dumps mémoire dépendants de la taille des pages).

      Et oui, j'ai oublié de présenter LLVM. Toutes mes excuses à ceux qui se sont sentis perdus.
    • [^] # Re: Sympa ce journal

      Posté par  (Mastodon) . Évalué à 2.

      Oui, à l'identique.

      Mais n'ayant pas les connaissances pour évaluer lvvm vs gcc, te surtout pas l'envie de rentrer dans un début philosophique me dépassant (et sachant que des argumentaires techniques peuvent y être mûs par la seule volonté de valoriser une vision philosophique). Ayant encore moins les notions permettant de jauger la qualité, les résultats, les tenants et aboutissants du bytecode vs compilé, je nepeux que me contenter de dire :

      La solution "architecture" me semble belle. Et ça me plait :) Donc pas la vision purement technique sur les moyens d'y arriver mais la finalité du projet : disposer de 'binaires' initiaux très 'légers', très 'portables', et très 'indépendant'. La phase finale étant assurée par la machine cliente. Je trouve cela beau et formidable.

      Un seul bémol, sur l' argumentaire :
      Une éxécution super rapide chez le poste client, car optimisé aux petits oignons pour son CPU, quel qu'il soit (dernier Core i7, AMD Phenom, un PPC, un Cell, un ARM, etc)
      Là j' ai des doutes. D' une part parceque les bibliothèques sont toujours ce qu' elles sont : des binaires classiquement installés. Ce qui, je présumé, grève significativement l' argument, pour une partie des fonctions d' une bonne partie des binaires logiciels fait de cette nouvelle manière.

      Il faudrait peut être que cela soit, plutôt que de suite vouloir faire un gestionnaire de paquet, une distribution faite sur ce mode. Proposer un "stage 1" à-la-Gentoo, qui permet d' installer une distribution avec ce mode de fonctionnement d'installation. Le but n'étant pas de faire une nouvelle distro, mais de valider totalement ce -futur- gestionnaire de paquet, en validant son fonctionnement de manière quasi-globale.

      Si j'ai pas trop faux, et de toutes façons dans tout les cas, je suppose que tu as besoin d'aide ? Perso je peux pas apporter grand chose, peut être une cotisation supplémentaire chez tuxfamily.org pour que tu ai une super archi d'une part, et peut être d'autre part une machine ?

      mes deux cents.
      cordialement.
      • [^] # Re: Sympa ce journal

        Posté par  (site web personnel) . Évalué à 2.

        J'ai un serveur dédié qui tourne et qui me rapporte :) (j'ai justement fait un don de 30$ ce matin à initng, objet de mon prochain journal).

        Je suis content que l'idée te plaise. En effet, avoir des optimisations spécifique au processeur est un gros plus. Ce qui fait que le amd64 est plus rapide que le i386 n'est pas spécialement le passage en 64bit, mais simplement le fait que amd64 est plus récent, et que pour être compatible amd64, un processeur doit supporter plus d'instructions, qu'un i386 ne supporte pas. Ainsi, on peut utiliser l'argument -mtune de GCC.

        Les bibliothèques utiliseront également LLVM, ce sera appliqué à tous les paquets (si ça marche). Ainsi, si je teste sur un Core i7, les bibliothèques multimédia par exemple pourront utiliser les instructions dernier cri SSE4, alors que le paquet Debian, amd64 générique, ne dépasse pas SSE2.

        Il y a aussi de subtiles différences entre les processeurs AMD et Intel. Une certaine instruction sera plus rapide sur un proc AMD qu'une autre, alors que ça peut être l'inverse sur du Intel. LLVM, dans sa phase de compilation en binaire, va générer le code optimal pour le processeur.

        J'ai également comme idée un support d'un genre de "USE flags" : du côté serveur, on compile le programme avec toutes les combinaisons de USE. Dans le paquet, on place les fichiers bytecode en deux parties :

        * Un gros fichier bytecode (créé avec llvm-link) pour ce qui ne change pas en fonction des USE
        * De petits bytecode pour ce qui est dépendant des USE/architectures (au cas où des #ifdef traineraient)

        À l'installation, en fonction des choix de l'utilisateur, on peut choisir les fichiers bytecode à lier, puis à compiler.

        Tout ceci n'est qu'une idée. Il me semble que personne encore n'a pensé à utiliser LLVM pour ça, c'est un sujet très intéressant. Tout ce que je peux demander à ceux qui veulent voir ceci réalisé est de contribuer à LLVM et Clang (le code est simple et beau, on s'y retrouve vite), en particulier pour le C++ (j'ai envie d'avoir KDE compilé avec Clang :-° ).
        • [^] # Re: Sympa ce journal

          Posté par  . Évalué à 1.

          Heu non, le gros avantage apporté par amd64, c'est qu'il y a deux fois plus de registres généraux qu'en x86.
  • # Bytecode vraiment portable ?

    Posté par  (site web personnel) . Évalué à 4.

    Ça marche comment en C avec les macros style #ifdef WIN32 ? C'est normalement pris en compte par le pré-processeur, donc perdu dans le bytecode (que le code soit présent dans le bytecode ou pas, la condition est perdue)...
    • [^] # Re: Bytecode vraiment portable ?

      Posté par  (site web personnel) . Évalué à 1.

      J'ai vu plein de #ifdef, dans le code de KDE. Jamais un #ifdef n'est sur une architecture, seulement sur un OS.

      Logram étant une distrib GNU/Linux, les #ifdef sont bons :) .

      Pour les paquets qui sont vraiment dépendants d'une architecture, soit on cherche un hack, soit on utilise la bonne vieille méthode.
      • [^] # Re: Bytecode vraiment portable ?

        Posté par  (Mastodon) . Évalué à 8.

        Jamais un #ifdef n'est sur une architecture, seulement sur un OS.

        Ça, c'est faux.

        Il y a plusieurs cas où ça arrive qu'on teste sur l'architecture :
        - code assembleur inline
        - test d'endianness (c'est pas un test d'architecture au sens strict, mais ça revient au même)
        - 32 ou 64 bits (idem qu'au dessus)
  • # Dépendances?

    Posté par  . Évalué à 4.

    # Pas besoin de build-dependecies, tous les fichiers .h ont été parsés, et le code se trouve directement dans le fichier bytecode.

    Donc tu risques d'avoir des redondances fâcheuses (et lourdes). Si je télécharge des programmes KDE - par exemple - via un gestionnaire de paquets lambda, il ne téléchargera que le paquet (éventuellement les trucs nécessaires au runtime, mais seulement la première fois). Si je fais de même avec Gentoo - par exemple - il récupérera les dépendances nécessaires à la compilation, mais une sule fois.

    Par contre, avec ton truc, _chaque_ programme récupéré par le gestionnaire de paquets va se trimballer une grosse partie des kdelibs (dans mon exemple) avec lui. Ce qui est bien, mais pas top.

    Ce qui n'enlève rien à l'intérêt de ton concept. Une solution qui permettrait - au moins partiellement - de limiter le problème que je soulève serait de faire un diff binaire (xdelta?) entre les différents paquets "logiquement" liés en amont, pour ensuite morceler les téléchargements. Genre "tu as besoin de digikam et d'amarok, alors je te fournis d'abord la partie commune - kdelibs, qt &co - puis les parties spécifiques et tu te chargeras de tout regrouper". On peut même envisager un cache côté client pour ces mêmes parties communes. Si ta technique est vraiment aussi économe en place que tu le dis, le cache ne sera pas _très_ couteux.
    • [^] # Re: Dépendances?

      Posté par  . Évalué à 2.

      Pourquoi ? Ça changerait rien de faire l'édition des liens avec les bibliothèques compilées une fois le bytecode du programme compilé en natif, j'imagine ...
    • [^] # Re: Dépendances?

      Posté par  (site web personnel) . Évalué à 6.

      Je crois, mais je peux me tromper, que tu n'as pas très bien compris le principe du bytecode.

      Ce bytecode est très proche du binaire, juste un niveau au dessus, pour être portable. Voici comment se déroule la création d'un paquet :

      * Compilation de chaque fichier .c ou .cpp dans un fichier .ll
      * Compilation de chaque fichier .ll en un fichier .bc

      Si on devait s'arrêter ici, on aurait effectivement plein de redondance, mais ce n'est pas ce qui est fait :

      * Utilisation de llvm-link pour lier tous les fichiers .bc en un seul gros fichier .bc

      Voilà, c'est là que tout s'est joué. Le bytecode est comme de l'assembleur : les bibliothèques ne sont pas inclues, on ne perd pas de place. On a juste des "call glib_machin_truc" ou "call machin::truc", mais sans rien d'autres.

      Une fois le paquet récupérer, c'est là que la liaison est faite :) . C'est à ce moment qu'on dit «glib_machin_truc est un appel vers la fonction blabla dans la lib machin», donc c'est lié dynamiquement.

      Par exemple, voici le Makefile utilisé pour compiler Cream :


      SOURCES=main.b bookmarks.b callbacks.b command.b CreamView.b download.b favicon.b ftplib.b history.b ini.b keybindings.b
      BINARY=cream
      LIBS=-lwebkit-1.0 -lgtk-x11-2.0
      INCLUDES=-I/usr/include/gtk-2.0 -I/usr/include/webkit-1.0/ -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/libsoup-2.4 -I. -I.. -DPREFIX=\"/usr/local\" -DLOCALEDIR=\"/usr/local/share/locale\" -D_REENTRANT

      all: $(SOURCES)
      llvm-ld -o all -s $(SOURCES)
      # Ici on met dans un paquet, les lignes suivantes sont côté client.
      llc -o all.s all.bc
      clang -o $(BINARY) all.s $(LIBS)

      %.b:%.c
      clang-cc $*.c $(INCLUDES) -emit-llvm -o - | llvm-as | opt -std-compile-opts > $*.b

      clean:
      rm -f *.b

      distclean: clean
      rm -f all.* $(BINARY)


      Et quand je liste le contenu du dossier, on voit ceci :


      63564 sep 1 14:36 all.bc
      10772 sep 1 14:35 bookmarks.b
      19560 sep 1 14:35 callbacks.b
      10484 sep 1 14:35 command.b
      23392 sep 1 14:35 CreamView.b
      5824 sep 1 14:35 download.b
      3432 sep 1 14:35 favicon.b
      23176 sep 1 14:35 ftplib.b
      10256 sep 1 14:35 history.b
      5808 sep 1 14:36 ini.b
      7524 sep 1 14:36 keybindings.b
      17804 sep 1 14:35 main.b


      On voit qu'il y a des données qui sont perdues entre la somme des tailles des fichiers .b, et la taille du .bc. Ce sont les répétitions qui sont éliminées.
  • # LLVM IR n'est pas portable

    Posté par  . Évalué à 2.

    Loin de là.

    Voir la faq llvm : http://llvm.org/docs/FAQ.html => Can I compile C or C++ code to platform-independent LLVM bitcode?

    En pratique, tous les modules ont un "target datalayout" et un "target triple".
    Exemple :
    target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
    target triple = "i386-pc-linux-gnu"
    • [^] # Re: LLVM IR n'est pas portable

      Posté par  (site web personnel) . Évalué à 2.

      Bonjour,

      C'est possible, mais alors ceci m'étonne :


      $ llc -o - bc.bc -march=ppc32


      Donc en ppc32 :


      .file "bc.bc"


      .text
      .global main
      .type main, @function
      .align 2
      main:
      mflr 0
      stw 0, 4(1)
      stwu 1, -16(1)
      lis 3, .L.str@ha
      la 3, .L.str@l(3)
      creqv 0, 0, 0
      cror 6, 0, 0
      bl printf
      li 3, 0
      addi 1, 1, 16
      lwz 0, 4(1)
      mtlr 0
      blr
      .size main,.-main
      .section .rodata.str1.1,"aMS",@progbits,1
      .L.str: # '@.str'
      .asciz "Salut !"


      Maintenant, testons l'ARM, avec le même fichier :


      .file "bc.bc"
      .eabi_attribute 20, 1
      .eabi_attribute 21, 1
      .eabi_attribute 23, 3
      .eabi_attribute 24, 1
      .eabi_attribute 25, 1


      .text
      .globl main
      .align 2
      main:
      str lr, [sp, #-4]!
      ldr r0, .LCPI1_0
      bl printf
      mov r0, #0
      ldr lr, [sp], #+4
      bx lr
      .LBB1_1:
      .LCPI1_0:
      .long .L.str

      .size main, .-main
      .type .L.str,%object
      .section .rodata.str1.1,"aMS",%progbits,1
      .L.str: @ @.str
      .size .L.str, 8
      .asciz "Salut !"


      et pour finir, le x86_64


      .file "bc.bc"


      .text
      .align 16
      .globl main
      .type main,@function
      main: # @main
      .LBB1_0: # %entry
      subq $8, %rsp
      movl $.L.str, %edi
      xorb %al, %al
      call printf
      xorl %eax, %eax
      addq $8, %rsp
      ret
      .size main, .-main
      .type .L.str,@object
      .section .rodata.str1.1,"aMS",@progbits,1
      .L.str: # @.str
      .asciz "Salut !"
      .size .L.str, 8

      .section .note.GNU-stack,"",@progbits


      Pour un truc pas portable, ça passe assez bien. À mon avis, cette en-tête est plutôt là pour dire «J'ai été compilé sur cette arch, donc je risque d'être dépendant de ça. Tu connais les différences entre les deux, corrige-les».
      • [^] # Re: LLVM IR n'est pas portable

        Posté par  . Évalué à 3.

        Un exemple tout bête (et valide !) :
        cat ir.c

        int take_ptr(long ptr) {
        return *(int*)ptr;
        }


        clang ir.c -emit-llvm -o - | llvm-as | opt -O3 | llvm-dis

        ; ModuleID = ''
        target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
        target triple = "i386-pc-linux-gnu"
        %struct.__block_descriptor = type { i32, i32 }
        %struct.__block_literal_generic = type { i8*, i32, i32, i8*, %struct.__block_descriptor* }

        define i32 @take_ptr(i32 %ptr) nounwind {
        entry:
        %conv = inttoptr i32 %ptr to i32* ; <i32*> [#uses=1]
        %tmp1 = load i32* %conv ; [#uses=1]
        ret i32 %tmp1
        }


        Note le "long -> i32" qui ne sera pas valide sur une architecture 64 bits. :(
        • [^] # Re: LLVM IR n'est pas portable

          Posté par  (site web personnel) . Évalué à 2.

          Ça c'est pas LLVM, c'est celui qui a codé son programme qui l'a mal fait.

          Même avec un GCC normal, on ne sait pas le compiler out-of-the-box sur d'autres architectures.

          LLVM est en fait une sorte de GCC qui sort sous forme de bytecode son interprétation au niveau moyen (donc plus du code source, pas du binaire). Si on sait compiler un fichier source avec GCC sur n'importe quelle architecture, on saura utiliser le fichier bytecode LLVM ainsi. Sinon, même GCC aurait nécessité un hack dans le programme.

          Il me semble que tout cela a été bien nettoyé depuis le passage au x86_64, il me semble même que Debian a fait pas mal de boulot là-dessus (faut dire que Debian est les architectures, c'est une belle histoire d'amour :-° ).
          • [^] # Re: LLVM IR n'est pas portable

            Posté par  (site web personnel) . Évalué à 2.

            Sauf que pleins de programme en C ou C++ bien portable font ce genre de chose en savant ce qu'ils font (en utilisant des macro, ou des templates du C++)

            En C tu vera beaucoup de

            MaStruct *plop = (MaStruct *)malloc(sizeof(MaStruct));

            Et si MaStruct contiens des pointeurs sa taille sera différente selon la platforme.
            Et donc le bytecode ne sera pas portable malgré la portabilité de l'application.
            • [^] # Re: LLVM IR n'est pas portable

              Posté par  (site web personnel) . Évalué à 4.

              Yeessssss :D

              Rah, Clang est vraiment le meilleur : cet exemple est portable, super, magnifique !

              Code C:


              #include <stdio.h>
              #include <stdlib.h>

              typedef struct {
              void *buffer;
              int machin;
              } maStruct;

              int main(int argc, char **argv)
              {
              maStruct *s = (maStruct *)malloc(sizeof(maStruct));

              s->machin = 3;
              s->buffer = s;

              printf("Le buffer est à l'adresse %ld", s->buffer);

              return s->buffer == s;
              }


              On compile en bytecode, puis on lance :


              /usr/local/bin/llc -o - -march=x86 test.bc


              Et un petit extrait :


              movl $8, (%esp)
              call malloc


              Remarque le "$8", la taille de ma structure, contenant un pointeur.

              Maintenant, sans recompiler le code C, je génère l'assembleur pour x86-64 :


              /usr/local/bin/llc -o - -march=x86-64 test.bc


              Et j'obtiens ceci :


              movl $16, %edi
              call malloc


              Remarque le $16 : ça marche, yeah :D . Le reste est bien en pur 64-bit (ici, on utilise %edi car malloc attend un int, pas un int64).

              Bref, +1 pour Clang et LLVM, ils savent ce qu'ils font :) .

              PS: J'ai pas vérifié avec llvm-gcc, je n'arrive pas à le compiler.
              • [^] # Re: LLVM IR n'est pas portable

                Posté par  . Évalué à 1.

                Je crois que tu n'as pas vraiment compris le problème. Si dans l'ir tu lui donnes un "i32*" ou "i8*" alors llc à la compilation demande aux classes qui encapsulent les target-datalayout quelle est la taille d'un pointeur.
                Et effectivement en changeant l'architecture avec -march=XXX, tu changes le target-datalayout utilisé.
                Le problème vient du fait qu'un "long" (le type c) n'a pas la même taille sur 32 et 64 bits.

                Si tu prends le code suivant :

                int main() {
                printf("%d\n", sizeof(long)/sizeof(int));
                }

                Sur 32 bits tu vas avoir le résultat "1" sur 64 bits le résultat sera "2".

                En générant l'ir, clang | opt résumera l'appel à
                printf("%d\n" , 1); sur 32 bits
                et
                printf("%d\n" , 2); sur 64 bits

                Donc clang lors de la génération de l'ir prend les dimensions de la plateforme (long=4 ou long=8) et génère l'ir. Ca veut dire que l'ir qu'il soit généré avec clang sur 32 ou 64 bits n'aura pas la même tête si il y a des "long" ou autres trucs de ce genre dans ton code.

                Le premier exemple que j'ai donné est un bon exemple : il est portable en c et pas en llvm.
        • [^] # Re: LLVM IR n'est pas portable

          Posté par  (Mastodon) . Évalué à 2.

          Tout ce que montre ton exemple, c'est que le bytecode LLVM ne sera pas plus portable que le code source qui l'a généré, ce qui est une évidence.

          (et un développeur qui écrirait un code pareil aujourd'hui devrait être viré immédiatement)
          • [^] # Re: LLVM IR n'est pas portable

            Posté par  . Évalué à 1.

            Ce bout de code en c est portable. Il marchera sur x86 _et_ sur x86_64.
            si tu fais un gcc ir.c sur 32 bits le code généré fonctionnera exactement de la même façon que si tu faisais un gcc ir.c sur 64 bits. C'est donc du code portable.

            Par contre si tu prends ton ir.bc que tu l'exécutes sur 64 bits, ça va être bizarre : le bytecode lui n'est pas portable.

            Ce code est pas particulièrement sale, le cast d'un pointeur en long est chose commune (par exemple gestion mémoire du kernel).
            • [^] # Re: LLVM IR n'est pas portable

              Posté par  . Évalué à 7.

              Moi, un truc me chiffonne. Je sais que je ne suis pas un Dieu du code, en fait je ne connait que l'interprété et bidouille si peu que pas en C.

              On prend un code, par exemple celui dans le tread, certains le trouve crade et pas portable, digne à virer celui qui le fait, et d'autres qu'il n'est pas particulièrement sale.

              Qui croire ? les deux ? Aucun ? on s'en fout ? 42 ? Obiwan ?
  • # Intéressant

    Posté par  (site web personnel, Mastodon) . Évalué à 2.

    Tests intéressants. Pour ma part, le problème qui me saute aux yeux est tout de même de tout baser sur un unique compilateur. Je pense à cette liste, sûrement non exhaustive, d'inconvénients:

    - Tu as testé sur quelques exemples, mais comme tu le dis, à ce que je comprends, llvm est expérimental. Peut-être que sur des bouts de code utilisant des fonctionnalités de langage très particulières, llvm peut montrer des limites? Cela n'est qu'une hypothèse basé sur le fait que llvm est apparemment expérimental (cf tes dires) et que les dévs eux-même semblent surtout destiner ce compilo/VM à des chercheurs (cf. section "LLVM Audience" de la page "Features" du site, où la plupart des lignes désignent chercheurs ou développeurs, hormis la dernière pour un certain type d'utilisateurs finaux).

    - Et si le développement de LLVM s'arrête? Ton idée est entièrement dépendante de ce produit. Tout autre gestionnaire de paquet marche avec n'importe quel compilateur.

    - Apparemment LLVM ne supporte vraiment que C et C++ pour l'instant (et qques autres en développement). C'est beaucoup étant donné que ce sont les langages les plus utilisés, historiquement. Mais on sait que de nos jours, il y a foultitude plus de langages et que ces autres langages sont de plus en plus utilisés, étant donné qu'on considère de plus en plus le gain de temps apporté par les langages de haut niveau.
    Une "solution" serait que les programmes utilisant un langage non supportés seraient en binaire dans tes repositories, et ceux en C/C++ en bytecode. Néanmoins je ne connais pas les statistiques sur le pourcentage de programme non C/C++, mais cela ne m'étonnerait pas que ce soit plus de la moitié des programmes existants. Et dans ce cas, on réduit énormément les intérêts principaux de ton gestionnaire de paquet et de ses dépôts de programme très petits.
    Évidemment si à l'avenir, LLVM se met à supporter tous les langages (boulot titanesque), cela n'est plus un problème.

    Sinon une petite question: puisque LLVM est avant tout une VM, peut-être n'y aurait-il même pas besoin de compiler en langage machine les programmes chargés, non? Si on considère que qqun utilisant ton gestionnaire de fichier a de toutes façons LLVM d'installé, il peut directement exécuter les fichiers en bytecode, d'où gain de place, de temps, etc. Ensuite je ne sais pas, peut-être que les programmes interprétés ne sont pas suffisamment performants, mais j'ai l'impression de comprendre tout de même que LLVM (qui se définit comme Low Level Virtual Machine) prétend avoir des binaires interprétés plutôt performants. Donc peut-être est-il intéressant de s'arrêter à ce niveau (bytecode) de compilation?

    Quoiqu'il en soit, tiens nous au courant des avancées de l'idée...

    Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

    • [^] # Re: Intéressant

      Posté par  . Évalué à 2.

      Apparemment LLVM ne supporte vraiment que C et C++ pour l'instant (et qques autres en développement).

      Via llvm-gcc, il me semble que llvm supporte les mêmes languages que gcc.

      Sinon une petite question: puisque LLVM est avant tout une VM

      En fait le nom est assez trompeur, llvm se veut plutot un "compiler toolkit", d'ailleurs le mode just-in-time n'est pas extremement performant (du moins comparé à l'état de l'art, hotspot).
    • [^] # Re: Intéressant

      Posté par  (Mastodon) . Évalué à 1.

      Apparemment LLVM ne supporte vraiment que C et C++ pour l'instant (et qques autres en développement)

      Il y a en fait 2 front-ends (la partie qui va lire le code) :
      - gcc
      - CLang

      Avec le frontend gcc, ça supporte les mêmes langages que gcc, avec les mêmes extensions non-standard.

      CLand est un parseur indépendant réécrit de zéro, qui supporte relativement bien C, mais pour le reste, c'est pas encore ça (mais vu qu'Apple travaille sur LLVM, on peut s'attendre à avoir rapidement un bon support Objective-C)
    • [^] # Re: Intéressant

      Posté par  (site web personnel) . Évalué à 5.

      llvm est expérimental

      Pour corriger ce que dit l'auteru du journal, on ne peut pas vraiment dire que llvm soit encore vraiment expérimental. Il est relativement proche de ce que 'lon pourrait appeller un programme stable, et encore plus dans le cas de la 2.6 qui va bientot sortir, le freeze c'est produit il y a quelques jours.

      Le code intermédiaire devrait être parfaitement compatible dans le sens ou un code actuel devrait rester executable par toutes les futures version de llvm, ce qui n'empeche pas à l'avenir d'ajouter de nouvelles instruction si celles-ci sont nécéssaire.

      LLVM est même suffisament stable pour que Apple ait choisit de le mettre au coeur de son système, et contribu pas mal au passage. LLVM est une des brique éssentielle du fameux OpenCL.

      Et si le développement de LLVM s'arrête? Ton idée est entièrement dépendante de ce produit. Tout autre gestionnaire de paquet marche avec n'importe quel compilateur.

      Vu le projet il y a relativement peut de chance que le dévellopement s'arrete, mais même si c'était le cas, c'est du logiciel libre et il est possible de reprendre le dev.

      Apparemment LLVM ne supporte vraiment que C et C++ pour l'instant

      LLVM seul ne supporte absolument rien ;-)

      C'est juste la spécification d'un code intermédiaire et tout un tas de bibliothèques et outils pour le manipuler et notament le copiler en code natif. Il y a pas mal de projet de compilateurs qui produisent ce code intermédiaire au lieu de produire directement du code natif.

      C'est le cas par exemple de clang qui est un copilateur destiner à compiler du C ainsi que d'autre langage dérivés. Et pour CLang, en effet, il supporte actuellement très bien le C et produit du code très performant. L'objective-C est relativeent bien supporté, je sais pas le staus exacte mais là aussi c'est un truc ou apple contribue pas mal. Par contre pour le C++, pour l'instant c'est encore très expérimental et de nombreuse choses ne sont pas implémentée.

      Par contre il existe aussi llvm-gcc qui est un bacend pour gcc. C'est-à-dire que c'est une modification de gcc qui lui permet de produire du code llvm au lieu de code pour un processeur. Donc ça permet d'avoir un copilateur qui supporte éxactement les même langages que gcc.

      Et il y a différents autres compilos disponibles, même parfois pour des langages auquels on ne penserais as du style un compilateur pour lua.

      Sinon une petite question: puisque LLVM est avant tout une VM, peut-être n'y aurait-il même pas besoin de compiler en langage machine les programmes chargés, non?

      Comme tu le dis après, L'objectif de llvm c'est quand même de produire du code très performant, et les perfs du code interprété sont quand même pas térrible à côter de celle du binnaire. Et vu le coût du JIT autant ne pas s'en priver.

      Et un dernier petit point pour l'auteur du journal. L'argument de ca prend moins de place est loin d'être pertinent, dans la grosse majoritée des pacquet, à mon avis, ce qui prend de la place c'est pas les binnaires mais les donnés. De plus les mesures sont à faire sur le code une foi compressé car à tous les formats de paquets que je connais compressent le tout.
      Avant de penser à des optimisation sur la taille du code, il vaudrais mieux faire quelques stat pour savoir s'il y a quelque chose à gagner.

      Voilà, j'éspère avoir clarifier quelques points sur llvm et le reste.
  • # Courgette

    Posté par  (site web personnel) . Évalué à 6.

    Un pointeur vers le système de paquetage / mise à jour de logiciels de Google, intitulé Courgette. Il est utilisé pour Chrome notamment :

    http://dev.chromium.org/developers/design-documents/software(...)

    C'est assez costaud, et il y a le code source dans les sources de Chrome.

    http://src.chromium.org/viewvc/chrome/trunk/src/courgette/

    Avis aux amateurs qui rêvent de créer leur système de paquet ultra performant...
  • # simpas l'idée mais ca ne sufit pas pour un getionnaire de paquet

    Posté par  . Évalué à 1.

    C'est pas pour casser (je trouve l'idée interessante mais il manque des choses) mais un gestionnaire de paquet le fourni pas que des logiciel et de plus tout les logiciel ne sont pas en c / c++:
    -certain paquet d'un gestionnaire ne contienne que des fichier text (doc, script etc...) et la y'a rien a compiler...
    - pour les programme java tu fais comment???
    - impossible de faire un paquet pour une appli sans les soucres, je sais si c'est pas libre ca pue. Mais bon en entreprise (pour des raison X ou Y, la n'est pas le sujet) on a pas que du libre et la on ne peut pas l'empaqueter, c'est dommage.

    Voila, sinon l'idée est belle...
  • # Avantages et inconvénients

    Posté par  . Évalué à 2.

    L'avantage face à une distribution telle que gentoo serait de pouvoir profiter des optimisations de l'architecture sans compiler, ce qui est déjà pas mal.

    Mais l'avantage de pouvoir mettre pleins de drapeaux au moment de la compilation part en fumée.

    Envoyé depuis mon lapin.

    • [^] # Re: Avantages et inconvénients

      Posté par  . Évalué à 4.

      Non, justement, pour profiter des optimisations dépendants de l'architecture il *faut* compiler !
      • [^] # Re: Avantages et inconvénients

        Posté par  . Évalué à 2.

        Oui, mais une premiere passe est déjà faite. Tu "compile" à partir du bytecode.
        • [^] # Re: Avantages et inconvénients

          Posté par  . Évalué à 2.

          Et pour une compil optimisé t'as testé quelle part se déroule dans le backend et quelle part pour les optimisations génériques ? (avec llvm-gcc et clang+llvm)

          A mon avis tu vas voir que si tu lances pas gcc (qui est plutot vieux et lent :) tu gagnes pas énorme à ne pas faire le backend.
    • [^] # Re: Avantages et inconvénients

      Posté par  . Évalué à 3.

      Ne pourrait on pas ajouter la possibilité d'avoir des genre de #ifdef dans le bytecode ? ou bien est-il déjà trop tard dans l'optimisation pour pouvoir utiliser ce genre de procédé ?
  • # De la portabilité de Clang et LLVM

    Posté par  (site web personnel) . Évalué à 3.

    Bonjour,

    Beaucoup d'entre vous se sont inquiétés de la portabilité du bytecode généré par Clang, et utilisé par LLVM.

    Il faut dire que le problème est réel, et la doc de Clang très claire, avec un bon exemple : sizeof(void *) est codé en dur dans le fichier .ll

    Enfin, c'est ce qui devrait être fait. En effet, dans ce document[1], il est cité quelque-part un warning très intéressant, vraiment très intéressant :


    portability.c:4:11: note : sizeof(wchar_t) varies between targets, source is not 'portable'


    La ligne de commande d'appel est également très intéressante :


    clang -arch ppc -arch linux -fsyntax-only portability.c


    Vous avez bien vu, deux architectures sont données. C'est à approfondir, car ça pourrait permettre de dire à Clang «Génère du code portable pour toutes ces architectures», et il trouvera automatiquement ce qui ne va pas (et donc permettra de hacker le code source, et de soumettre un patch :) ).

    Affaire à suivre, mais en tous cas, c'est intéressant.

    [1] : http://llvm.org/devmtg/2007-05/09-Naroff-CFE.pdf
  • # Et la compression dans tout ça ?

    Posté par  . Évalué à 4.

    Je me pose une question, quand même. Étant donné que tous les paquets actuels sont compressés, soit avec gzip, soit avec lzma, qu'est-ce que ça donne si tu passes les binaires au compresseur ?
    J'ai dans l'idée qu'il se peut que le bytecode passe moins bien la compression que le binaire, auquel cas, la différence pourrait devenir ridicule.
    • [^] # Re: Et la compression dans tout ça ?

      Posté par  (site web personnel) . Évalué à 3.

      Effectivement, ça mérite un test :

      * sokoban.bc : 10296 octets (tiens, il devait être plus petit, il faut dire que j'ai un peu changé la chaîne de compilation pour utiliser clang à la place de clang-cc)
      * sokoban (éxécutable) : 11360 octets

      Maintenant, je compresse les deux en LZMA

      * sokoban_bin.lzma : 3755 octets
      * sokoban_bc.lzma : 8212 octets

      Effectivement, ça calme :/ . Plus qu'à tester sur de plus gros programmes, LLVM est vraiment intéressant et on sait en faire quelque-chose.

      Mouais, mais un truc n'est pas bien allé avec le bytecode, il est largement plus gros que ce que je dis dans le journal, ce n'est pas normal (j'ai pourtant utilisé opt, donc ça devrait être bon). Je vais refaire des tests.
    • [^] # Re: Et la compression dans tout ça ?

      Posté par  . Évalué à 2.

      moi l'avantage que je vois, c'est le mélange entre facilitée de ne pas avoir à compiler avec tous les problèmes de dépendances des fichiers dev et la 'performance' d'un code compilé en natif sur une machine propre.

      Par exemple, lors de l'installation du système de package-compilation on met les bonnes options de compilations un fois pour toute, avec la possibilité de faire du sur mesure assez pointu, et tous les paquets bytecode seront personnalisés à la compilation, sans se prendre la tête et sans avoir des probs de dépendances de fichiers dev et sans passer X heures et sans gérer les problèmes de compilations exotiques parce que gcc a changé depuis l'écriture du soft.

      Maintenant cela impose probablement plus de rigueur dans l'écriture des softs, mais est-ce un mal ? il pourrait par exemple y avoir un 'truc' qui 'pré-compile' le code et qui trouve les choses qui ne sont pas portable, ce qui aiderait grandement les programmeurs du dimanche.

      Mais bon, je m'avance un peu car je ne suis pas du tout un pro de l'écriture/compilation, mon expérience s'arrète à faire des poc bien crade\w code de débutant pour montrer ce que j'aimerais avoir et laisser un vrai dev faire le taff. (Et non je ne désire pas faire de poc dans un autre langage car c'est de la triche)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.