Pourquoi l’assembleur ?
Programmer en assembleur donne accès à une connaissance intime, presque physique, du fonctionnement de l’ordinateur. Ce fut mon métier pendant quelques années de la décennie 1970, période passionnante de ma vie professionnelle, mais en France il n’est guère possible de faire une carrière décente dans ce genre d’activité. De ce fait l’assembleur et le système d’exploitation que je connais le plus intimement, encore aujourd’hui, sont ceux de la série IBM 360, aujourd’hui renommée zSeries mais restée étonnamment stable, comme j’ai pu en juger lors d’un exposé d’Ayoub Elaassal sur la sécurité de ces systèmes lors d’une réunion de l’Observatoire de la sécurité des systèmes d’information et des réseaux (cf. aussi la vidéo).
Frustré d’assembleur, j’ai néanmoins dédaigné celui des processeurs d’architecture Intel x86, archaïque et biscornue. J’ai nourri quelques espoirs pour l’architecture ARM, me suis acheté un RaspberryPi 3 et un manuel d’assembleur ARM et j’ai attaqué les exercices. La promesse de la révolution RISC, c’était la sobriété, la simplicité et la régularité, avec un jeu d’instruction réduit : ARM ne tient pas vraiment cette promesse, leur assembleur est presque aussi biscornu que celui d’Intel.
L’espoir RISC-V
Aujourd’hui mes espoirs (et ceux de beaucoup d’autres) se portent vers l’architecture libre et ouverte RISC-V, surtout depuis qu’ARM est lourdement impliqué par les mesures de boycott des entreprises chinoises par le gouvernement américain, sans parler des controverses autour du projet de son rachat par Nvidia.
L’architecture ISA (Instruction Set Architecture) de RISC-V est disponible sans frais pour tout industriel, et les premiers matériels commencent à apparaître. Les compilateurs GCC, LLVM (donc Clang), Rust et d’autres produisent du code RISC-V. Les systèmes d’exploitation Linux, FreeBSD, HarmonyOS (de Huawei) et d’autres tournent sur plate-forme RISC-V. Si on veut essayer il existe des cartes FPGA (field-programmable gate array, circuit logique programmable). Certes, pour que l’écosystème RISC-V soit en mesure de faire jeu égal avec x86 et ARM, il faudrait que se développent les logiciels de conception de circuits, et que les fonderies TSMC, Samsung, STMicro, etc., s’y mettent, ce qui demandera des investissements qui se comptent en milliards d’euros et en centaines d’ingénieurs pendant des années. En tout cas la firme chinoise Alibaba a d’ores et déjà annoncé un processeur RISC-V 64 bits (RV64GCV) 16 cœurs 2,5 GHz baptisé XuanTie 910.
Un simulateur RISC-V bien conçu
Je ne possède pas de machine dotée du processeur XuanTie 910, mais deux bienfaiteurs de l’humanité, Pete Sanderson et Kenneth Vollmar, ont écrit un simulateur en Java, que l’on peut télécharger ici, d’une utilisation pratique et agréable. Il est distribué sous forme d’une arcchive jar
exécutable, ce qui donne l’occasion de se remettre à l’esprit le mode de déploiement d’une application Java, dont il faut bien dire qu’il est puissant, sûr, commode, quand même autre chose que du Python...
On peut charger dans le simulateur son code assembleur, l’assembler, on voit le code machine (instructions de 32 bits), la valeur des registres et des différents drapeaux, celle des champs de la section .data
, que l’on peut afficher en décimal, hexadécimal ou Ascii, puis on peut lancer l’exécution pas à pas ou directe. Il accepte les appels système Linux. Que demande le peuple ?
Cette expérience me rappelle combien l’agencement des bits dans les mots selon l’option Big Endian est bien plus pratique que l’option Little Endian, malheureusement retenue par les concepteurs de RISC-V. Je crains être un des derniers Big Endianers.
Un livre bien écrit
En fait j’avais décidé de me mettre à l’assembleur RISC-V en découvrant que la dernière édition du livre classique de David A. Patterson et John L. Hennessy était intitulée Computer Organization and Design - RISC-V Edition, ce qui donnait à penser (à juste titre) qu’elle comporterait une description de cette architecture et des exercices de programmation. David Patterson, professeur à Berkeley, est l’architecte principal des processeurs SPARC de Sun (maintenant Oracle) et RISC-V, John Hennessy, professeur à Stanford, est l’architecte principal des processeurs MIPS. Tous les deux se sont vu décerner le prix Turing 2017 pour leurs contributions éminentes tant aux architectures RISC qu’à l’élaboration théorique de leurs principes. Quand ces deux-là parlent de processeurs, on peut leur faire confiance. Leur livre, qui s’adresse à des presque débutants, est bien écrit et très pédagogique. Je me suis attelé aux exercices.
Comment débuter
Mes débuts en assembleur RISC-V m’ont ramené aux recommandations que je donne à mes étudiants : pour commencer, contentez-vous de recopier un programme vu en cours ou dans le bouquin, compilez-le et faites-le tourner, vous verrez que déjà là vous allez rencontrer des problèmes, et que les résoudre vous en apprendra beaucoup. Le plus difficile pour un débutant dans un programme, ce sont les cinq ou dix premières lignes, les cinq dernières, et toutes celles où il y a des interactions avec le monde extérieur, telles que lecture, écriture, appels système. Le reste, c’est ce que le cours raconte, et sauf algorithmes très subtils c’est plus facile.
C’est bien ce que j’ai rencontré : le code recopié directement du livre ne fonctionnait pas tel quel. Pour savoir ce qui clochait j’ai couru le Web. Par exemple le blog de Stephen Smith, qui m’a mis le pied à l’étrier. Son programme Hello World m’a donné ces fameuses lignes de début et de fin, ainsi que les appels système Linux.
Première version de strcpy
Sur les traces de Patterson et Hennessy, voici ma première version de strcpy
. Je sais que ce programme n’est pas optimal, mal écrit, pas structuré, il serait plus efficace avec des pointeurs qu’avec des tableaux, c’est un premier jet à peu près illisible, ce sera mieux pour la version suivante ci-dessous (pour lire le code voici une carte de référence du jeu d’instructions) :
Le même, structuré, avec des pointeurs
Le programme ci-dessus a plusieurs défauts : il utilise les indices pour parcourir les chaînes, ce qui est légitime mais moins efficace que l’utilisation de pointeurs. Surtout, le texte de la fonction strcpy
proprement dite est mélangé avec celui des données de test et de leur manipulation. Il faut séparer les choses, en d’autres termes structurer le programme.
Un assembleur moderne, en principe, transforme les indices du précédent en pointeurs de celui qui vient, mais à la main c’est plus amusant.
Encore mieux grâce à Samuel Tardieu et à Twitter
Samuel Tardieu a lu mon code et répondu : « Plutôt que t’embêter à sauvegarder et restaurer x19, je te suggère d’utiliser un registre “caller-save” comme x28. Si jamais l’appelant de ta fonction l’utilise, c’est à lui de le sauvegarder. Cela économise quatre instructions dont deux accès mémoire à la pile. » Un accès mémoire, en 2021, c’est le temps de 100 à 200 instructions. Ce qui donne :
Emmanuel Lazard enrichit le programme
Publier un programme est utile, on reçoit des améliorations substantielles, dont celles d’Emmanuel Lazard, qui améliorent la structuration et la généralité du code :