Le jeu d'instructions est l'ensemble des instructions-machine qu'un processeur d'ordinateur peut exécuter. Ces instructions-machines permettent d'effectuer des opérations élémentaires (addition, ET logique…) ou plus complexes (division, passage en mode basse consommation…). Le jeu d'instructions définit quelles sont les instructions supportées par le processeur. Le jeu d'instructions précise aussi quels sont les registres du processeur manipulables par le programmeur (les registres architecturaux).
Principales familles Parmi les jeux d'instructions principaux des années 1980 à 2020, nous avons, pour les microprocesseurs : Z80 (8 bits, CISC) ; 6502 (8 bits, CISC) ; x86 (16, puis 32 bits, CISC) ; x86-64 (version 64 bits de x86 introduite par AMD sous le nom AMD64 et reprise par Intel sous le nom Intel 64, aujourd'hui la plus répandue de la famille x86) ; VAX (32 bits) ; Alpha (64 bits, RISC) ; SPARC (32, puis 64 bits, RISC) ; MIPS (32 puis 64 bits, RISC) ; 68000 (16/32 puis 32 bits, CISC) ; IA-64 (architecture 64 bits EPIC d'Intel n'ayant pas percé, abandonné, CISC) ; PowerPC (32, puis 64 bits, RISC) ; ARM (16, 32, puis 64 bits, RISC) ; RISC-V (32, 64 et 128 bits, RISC), un format ouvert.
Et pour les microcontrôleurs : ARM (16 puis 32 bits, RISC) ; AVR (8 bits, RISC) ; M16C (16 bits, CISC) ; (32 bits, voir ESP32, RISC) ; RISC-V (32, 64 et 128 bits, RISC), un format ouvert.
Langage machine
Le jeu d'instructions précise non seulement les registres et instructions supportées par le processeur, mais aussi la façon dont ces instructions et les opérandes sont représentés en mémoire.
Chaque instruction-machine contient un code qui indique l'instruction à exécuter : addition, multiplication, branchement, etc. Ce code est appelé le code opération, en abrégé opcode. (Toutefois, certaines transport triggered architectures n'ont qu'une seule instruction, donc l'opcode est inutile ; mais c'est un cas très particulier.)
Une instruction, comme l'addition, s'applique généralement à des opérandes. Suivant les cas, chaque opérande est indiqué comme une valeur constante, une adresse, un registre, un décalage (offset), etc. (Toutefois, sur les machines à pile, les opérandes sont au sommet de la pile, donc à nouveau il est inutile de les indiquer.)
Cette représentation des instructions est fixée une fois pour toutes par le jeu d'instructions. Celui-ci précise quel opcode correspond à telle ou telle instruction, quels sont les opérandes permis, quels sont les modes d'adressage possibles pour chaque instruction, etc. L'ensemble de cette spécification s'appelle le langage machine.
Sur les architectures VLIW, plusieurs opcodes et opérandes peuvent être fournis en une seule instruction, permettant à celle-ci de demander l’exécution simultanée de plusieurs opérations.
Classes des jeux d'instructions
On distingue plusieurs familles de jeux d'instructions, selon la façon dont les instructions accèdent à leurs opérandes, leur codage et leur complexité. Les jeux d'instructions des processeurs diffèrent aussi dans la façon dont sont spécifiés les opérandes des instructions. Voici schématiquement représentées différentes classes.
Légende : Les registres, l'accumulateur ou la pile sont représentés dans les tons jaunes ; La mémoire centrale est représentée dans les tons mauves ; Le chemin de données (mode lecture) entre registres (ou l'accumulateur ou la pile) et l'Unité Arithmétique et Logique est en vert ; Le chemin de données (mode écriture) entre la sortie de l'Unité Arithmétique et Logique et les registres (ou l'accumulateur ou la pile) est en bleu ; Le chemin de données (mode lecture ou écriture) avec la mémoire est en violet.
« 0 adresse » thumb|right|Architecture d'un processeur « 0 adresse », dite « à pile ».
Dans cette architecture, les instructions vont directement agir sur la pile. Les opérandes sont automatiquement chargés depuis le pointeur de pile (SP, Stack Pointer), et le résultat est à son tour empilé.
L'opération A = B + C sera traduite par la séquence suivante :
PUSH B ; Empile B PUSH C ; Empile C ADD ; Additionne B et C POP A ; Stocke le haut de la pile à l'adresse A et dépile
Avec cette architecture, les instructions arithmétiques et logiques sont vraiment très courtes : leurs opérandes sont placés au sommet de la pile, et sont adressés implicitement : le processeur n'a pas besoin de préciser leur localisation dans la mémoire. En conséquence, ces instructions sont souvent constituées d'un seul et unique opcode. Ces opérations étant assez courantes, la taille moyenne d'une instruction est donc très faible. En conséquence, la taille des programmes d'une architecture « 0-adresse » est très faible comparée aux autres architectures. On résume cet état de fait en disant que la « » (la densité du code) est bonne.
Malheureusement, l'usage d'une pile pose quelques problèmes. Par exemple, il est impossible d'utiliser plusieurs fois la même donnée dans des calculs différents. Il faut dire que chaque opération arithmétique ou logique va automatiquement dépiler les opérandes qu'elle utilise. Dans ces conditions, si un opérande peut être utilisé dans plusieurs calculs, celui-ci doit être recalculé à chaque fois. Diverses instructions ont été inventées pour limiter la casse : des instructions permettant de permuter deux opérandes dans la pile, des instructions permettant de dupliquer le sommet de la pile, etc. Mais ce problème demeure.
Ce type d'architecture a été utilisé dans les calculatrices HP fonctionnant en notation polonaise inversée (post-fixée), dans les machines Burroughs de la gamme B 5000 et les miniordinateurs Hewlett-Packard de la gamme HP 3000. Il est aussi utilisé pour le FPU des processeurs x86.
« à accumulateur » thumb|right|Architecture d'un processeur avec un accumulateur ».
Sur une machine de ce type, historiquement ne disposant que d'un seul registre, appelé Accumulateur, tous les calculs se font implicitement sur celui-ci.
L'opération A = B + C sera traduite par la séquence suivante :
LOAD B ; copie le contenu de l'adresse B dans l'accumulateur ADD C ; ajoute le contenu de l'adresse C à celui de l'accumulateur, stocke le résultat dans l'accumulateur STORE A ; stocke la valeur de l'accumulateur à l'adresse A
Sur les processeurs disposant d'un accumulateur, tous les résultats d'une instruction manipulant des registres vont être écrits dans cet accumulateur. De la même manière, toute instruction va aller manipuler le contenu de cet accumulateur.
Bien évidemment, les instructions qui manipulent plusieurs opérandes vont pouvoir aller chercher ceux-ci dans la mémoire ou dans d'autres registres. Historiquement, les premières machines à accumulateur ne contenaient pas d'autres registres pour stocker les opérandes des instructions arithmétiques et logiques. De ce fait, le seul registre pouvant stocker des données était l'accumulateur.
Par la suite, certaines architectures à accumulateur incorporèrent des registres supplémentaires pour stocker des opérandes. Cela permettait de diminuer le nombre d'accès mémoire lors de la lecture des opérandes. Mais attention : ces registres ne peuvent servir que d’opérande dans une instruction, et le résultat d'une instruction ira obligatoirement dans l'accumulateur.
Les avantages et désavantages de ces machines à accumulateurs sont les mêmes que pour les machines à pile. Leur est faible. Toutefois, elle n'est pas aussi bonne que pour les machines à pile. L'opérande stocké dans l'accumulateur n'a pas besoin d'être précisé, l'accumulateur étant utilisé implicitement par ces instructions. Mais si une instruction utilise plus d'un opérande, pour les opérandes restants, chaque instruction arithmétique ou logique va devoir préciser la localisation en mémoire de celui-ci — qu'il s'agisse d'une adresse mémoire ou d'un registre. Les instructions arithmétiques et logiques sont donc plus longues, ce qui diminue la .
Réutiliser des résultats plusieurs fois est aussi très compliqué, comme sur les machines à pile. Un résultat de calcul est automatiquement stocké dans l'accumulateur, donc le contenu de l'accumulateur est écrasé à chaque instruction arithmétique et logique et réutiliser des résultats est très difficile, sauf à passer par la mémoire.
En revanche, le nombre d'accès à la mémoire diminue avec l'utilisation de l'accumulateur. Celui-ci est placé dans le processeur, et tout accès à son contenu ne passe pas par la mémoire. Avec une machine à pile, tout opérande doit être chargé implicitement depuis la mémoire (même si certaines machines à piles ont eu la bonne idée de placer le sommet de la pile dans un registre caché au programmeur).
« une adresse, registre - mémoire » thumb|right|Architecture d'un processeur « registre vers mémoire ».
Ici une instruction peut avoir comme opérande un ou plusieurs registres (typiquement un ou deux) et une adresse mémoire.
L'utilisation de registres est plus souple que l'utilisation d'une pile ou d'un accumulateur. Par exemple, une fois qu'une donnée est chargée dans un registre, on peut la réutiliser autant de fois qu'on veut tant qu'on ne l'a pas effacée. Avec une pile, cette donnée aurait été automatiquement effacée, dépilée, après utilisation.
De manière générale, le nombre total d'accès à la mémoire diminue fortement comparé aux machines à pile, grâce à une utilisation plus efficace des registres. Cela permet de gagner en performances car les registres sont souvent des mémoires bien plus rapides que la mémoire principale.
Chaque instruction manipulant des registres doit préciser quel registre lire ou écrire. Cette information est placée dans l'instruction et est codée sur quelques bits, réduisant ainsi la .
L'exemple A = B + C peut donc être traduit par la séquence :
LOAD R0, B ; copie le contenu de l'adresse B dans le registre R0 ADD R1, R0, C ; R1 = R0 + C STORE R1, A ; stocke la valeur de R1 à l'adresse A
« registre - registre » thumb|right|Architecture d'un processeur « registre vers registre ».
Si les instructions ne peuvent avoir que des registres comme opérandes, il faut deux instructions, LOAD et STORE par exemple, pour respectivement charger un registre depuis un emplacement mémoire et stocker le contenu d'un registre à une adresse donnée.
Le nombre de registres est un facteur important.
Les processeurs RISC actuels sont tous de ce type.
Ce genre de machine a une légèrement moins bonne que les architectures registre-mémoire. En effet, certaines opérations qui auraient pris une seule instruction sur une machine registre-mémoire vont utiliser plusieurs instructions sur une machine registre-registre. Par exemple, la séquence A = B + C sera traduite en :
LOAD R0, B ; charge B dans le registre R0 LOAD R1, C ; charge C dans le registre R1 ADD R2, R0, R1 ; R2 ← R0 + R1 STORE R2, A ; stocke R2 à l'adresse A
Ce qui prend une instruction de plus.
En revanche, le nombre d'accès mémoire ou la réutilisation des registres ne change pas comparé aux machines registres-mémoire.