Un programme pour un processeur que tu n’as pas
Les téléphones, les portables et les serveurs n’ont pas les mêmes processeurs, et chacun ne comprend que ses propres instructions. RISC-V est une conception ouverte que tout le monde peut fabriquer. Cette machine n’est pas en RISC-V, mais un émulateur lui permet quand même de lancer un programme RISC-V.
À la fin de cette leçon
Remarque
Je peux suivre un court programme RISC-V, de son assembleur jusqu’à son code de sortie.
Mots-clés
- Processeur : la partie de l’ordinateur qui exécute les instructions.
- Instruction : un petit pas que le processeur sait faire, comme une addition.
- Registre : une toute petite mémoire, très rapide, dans le processeur.
- Émulateur : un programme qui se comporte comme un autre processeur.
- Code de sortie : le nombre qu’un programme rend quand il se termine.
- Cache : une petite mémoire rapide qui garde ce qui a servi récemment.
Pour commencer : ce que tu sais déjà
Remarque
Réponds dans ta tête. 1. En quoi un compilateur transforme-t-il le code source ? 2. Quelle étape assemble le code machine en un seul programme ? 3. Qu’est-ce que l’assembleur ? Aujourd’hui, tu lis de l’assembleur écrit par une personne.
Avant de commencer
Remarque
Appuie sur Démarrer le lab dans la barre en haut de cette leçon. Ce lab est une machine Ubuntu entière, rien que pour toi : elle met un peu plus de temps à démarrer qu’un conteneur. Chaque bouton Exécuter tape sa commande dans le terminal de la machine, l’une après l’autre, dans la même session. Si l’un d’eux dit que la machine démarre encore, attends quelques secondes et appuie de nouveau. Quand la leçon dit Tape ceci, le programme attend que tu lui écrives : appuie sur Ouvrir le terminal dans la barre et tape toi-même.
Un programme RISC-V, ligne par ligne
add.s contient cinq instructions. li met un nombre dans un registre : 2 dans a0 et 3 dans a1. add les additionne et garde le résultat dans a0. li a7, 93 choisit l’appel système Linux qui termine un programme, et ecall fait l’appel. Linux prend le nombre de a0 comme code de sortie.
cd ~/archAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
cat add.sAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Vérifie
Astuce
Après add a0, a0, a1, que contient a0 ? Réponse : 5, la somme de 2 et 3.
Le construire, puis le lancer sur un émulateur
L’assembleur transforme les mots en code machine, et l’éditeur de liens en fait un programme. file indique qu’il est pour RISC-V. Ce processeur ne peut pas le lancer, alors qemu-riscv64 le fait, une instruction RISC-V après l’autre. Il n’affiche rien. echo $? affiche le code de sortie laissé par le programme : 5.
riscv64-linux-gnu-as add.s -o add.oAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
riscv64-linux-gnu-ld add.o -o addAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
file addAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
qemu-riscv64 ./addAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
echo $?Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une erreur fréquente
Attention
Beaucoup pensent que echo $? affiche la dernière chose écrite par un programme. Il affiche le code de sortie, un nombre de 0 à 255 que le programme rend en se terminant. Remplace 2 et 3 par 200 et 100, reconstruis, et la réponse est 44 : 300 ne tient pas, donc le compte repart de zéro.
Les instructions sont des nombres
objdump -d montre le programme tel que le processeur le voit. Chaque ligne donne une adresse, l’instruction sous forme de nombre en hexadécimal, puis l’instruction en mots. Chaque nombre a huit chiffres hexadécimaux, car chacune de ces instructions fait 32 bits. ecall vaut 00000073.
riscv64-linux-gnu-objdump -d addAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Un cache rend la mémoire rapide en apparence
loop.c additionne deux fois un tableau de 16 millions de nombres. Ligne par ligne, le nombre suivant est juste à côté du précédent en mémoire, et le cache l’a déjà chargé. Colonne par colonne, chaque nombre est 16 Ko plus loin, donc le processeur attend la mémoire presque à chaque fois. La somme est la même, 16777216 ; le temps, non : colonne par colonne prend plusieurs fois plus longtemps.
gcc -O2 -o loop loop.cAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
./loopAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Tape ceci : regarder le processeur avancer
Dans le terminal, tape gdb ./loop. Tape break main, puis run : le programme s’arrête au début de main. Tape info registers pc pour voir le compteur ordinal (pc), l’adresse de la prochaine instruction. Tape stepi pour exécuter une seule instruction, puis regarde de nouveau pc : il a avancé. Tape quit, puis y.
Vérification finale
Astuce
Réponds sans regarder en arrière. 1. Qu’est-ce qu’un registre ? 2. Quelle commande lance un programme RISC-V sur cette machine ? 3. Qu’affiche echo $? ? 4. Pourquoi ligne par ligne est-il plus rapide ? Réponses : une toute petite mémoire rapide dans le processeur ; qemu-riscv64 ; le code de sortie de la dernière commande ; le nombre suivant est déjà dans le cache.
Pour les enseignants
Astuce
Cette leçon se fait dans un lab de type machine virtuelle : chaque élève a sa propre machine Ubuntu, et un bouton Exécuter tape dans son terminal et montre ce qui s’est affiché. Points clés : 1. Un processeur exécute des instructions qui travaillent sur des registres. 2. Un assembleur et un éditeur de liens font un programme à partir de l’assembleur, et un émulateur le lance pour un autre processeur. 3. La mémoire proche de la dernière lecture est rapide grâce au cache. Erreur fréquente : prendre le code de sortie pour ce que le programme affiche. Matériel : le lab et un navigateur. Conseil : avant ./loop, demandez à la classe de deviner quel sens est le plus rapide, et de combien. Le lab n’a pas de vérification et ne note rien ; donnez un travail à rendre de votre choix si vous voulez une note.