Aller au contenu

Fonctionnement d'un ordinateur/Les architectures canoniques

Un livre de Wikilivres.

Les architectures que nous avons vu précédemment dans ce cours disposent de plusieurs registres pour les données, en plus du pointeur de pile, d'un program counter, et de quelques autres. Elles sont appelées des architectures à registres, terme qui trahit bien le fait qu'elles ont des registres généraux ou spécialisés pour stocker temporairement des données. Et si on leur a donné un nom, c'est parce qu'il existe des architectures qui ne sont pas dans cette catégorie.

Les architectures peuvent se classer en quatre types : architectures mémoire-mémoire, architectures à accumulateur, architectures à pile et architectures à registres. Il s'agit de ce que je vais appeler les architectures canoniques. Elles se distinguent deux points : la présence/absence de registres, la manière dont sont gérées leurs opérandes. La présence/absence de registre permet de distinguer trois classes d'architectures : celles sans registres de données, les architectures à accumulateur, et les architectures à registres. Les architectures sans registres sont elles-mêmes subdivisées en architecture mémoire-mémoire et à pile, pour des raisons qu'on expliquera dans ce chapitre.

Classe d'architecture Nombre de registres pour les données
Architecture mémoire-mémoire Aucun.
Architecture à pile/à file
Architecture à accumulateur Un registre appelé l'accumulateur.
Architecture à registres Plusieurs registres dits généraux et/ou spécialisés.

La présence de registres améliore grandement les performances, comparé aux autres architectures. Aussi, à part quelques architectures assez anciennes, toutes les architectures récentes sont des architectures à registres. Et ce qui va nous intéresser dans ce chapitre sont les autres architectures canoniques. Elles étaient autrefois utilisées sur les tous premiers ordinateurs, mais elles sont encore utilisées de nos jours sur des processeurs à base performance, typiquement dans l'embarqué. Plus précisément, nous allons voir trois types de jeu d'instruction différents, appelés architectures mémoire-mémoire, architectures à accumulateur et architectures à pile. Elles se distinguent par le fait qu'elles n'ont pas de registres adressables pour les données.

L'absence/présence de registre a de nombreuses conséquences. La première est qu'elle impacte les instructions d'accès mémoire. Toutes les architectures précédentes disposent d'instructions d'accès mémoire, mais elles ne sont pas les mêmes selon l'architecture canonique considérée. Par exemple, les instructions LOAD et STORE n'existent que sur les architectures avec au moins un registre, à savoir les architectures à accumulateur et à registre. Les autres architectures n'ont pas d'instruction LOAD/STORE.

Classe d'architecture LOAD et STORE
Architecture à pile Non
Architecture mémoire-mémoire
Architecture à accumulateur Oui, accumulateur adressé implicitement
Architecture à registres Oui, registres adressés explicitement

Une autre conséquence, assez liée à la précédente, est liée aux modes d'adressages supportés. Les architectures à registres disposent de leurs propres modes d'adressages spécifiques, qui ne sont pas supportés ailleurs. En pratique, les modes d'adressage pour les pointeurs sont radicalement différents. Le support des pointeurs demande de gérer des modes d'adressages dits indirects. La présence de registres permet d'implémenter un grand nombre de ces modes d'adressages indirects, ce qui simplifie grandement leur usage. En l'absence de registres, il est possible d'implémenter des pointeurs, avec des modes d'adressages spéciaux. Toutes les architectures canoniques supportent les pointeurs, mais les architectures mémoire-mémoire ont leurs propres modes d'adressages, idem pour les machines à pile.

Maintenant que ces préliminaires ont été abordés, voyons dans le détail ces architectures canoniques.

Les architectures mémoire-mémoire

[modifier | modifier le wikicode]
Architecture mémoire-mémoire.

Les toutes premières machines n'avaient pas de registres pour les données, pas de registres généraux. Malgré l'absence de registres pour les données, le program counter existe toujours, de même que le registre d'état et le pointeur de pile. Les instructions n'accédaient qu'à la mémoire RAM ou ROM : on parle d'architectures mémoire-mémoire.

Le défaut des architectures mémoire-mémoire est qu'elles font beaucoup d'accès mémoire, du fait de l'absence de registres. Aussi, la performance dépendait grandement de la performance de la mémoire RAM. Au début de l'informatique, le processeur et la mémoire RAM avaient des performances similaires, ce qui rendait ces architectures viables. Mais depuis que la mémoire est devenue très lente comparé au processeur, ce genre d'architectures est tombé en désuétude.

Un autre défaut de ces processeurs est que leurs instructions de calcul effectuent plusieurs accès mémoire. Une instruction dyadique fait trois accès mémoire : un par opérande, un pour enregistrer le résultat. Et il faut séquencer ces accès mémoire à l'intérieur de l'instruction, ce qui est complexe et demande d'ajouter des registres internes au processeur qui sont cachés du programmeur.

La microarchitecture d'une architecture mémoire-mémoire

[modifier | modifier le wikicode]

La microarchitecture d'une architecture mémoire-mémoire est assez simple. Elle contient une unité de calcul, des registres d’interfaçage avec la mémoire et une unité de contrôle. Elle n'a pas de registres, à part un program counter intégrés au séquenceur. La seule difficulté est d'incorporer de quoi gérer les opérations dyadiques et l'adressage mémoire indirect.

Chemin de données sans support des pointeurs
Chemin de données d'une architecture mémoire-mémoire.

La première difficulté est que les opérations dyadiques lisent deux opérandes en RAM et écrivent le résultat en RAM. Mais le bus de donnée ne permet qu'une seule lecture ou écriture à la fois. Les accès mémoire doivent donc s'exécuter l'un après l'autre et doivent être séquencés. Les opérations mémoire-mémoire dyadiques s'exécutent donc en plusieurs micro-opérations. En effet, lire deux opérandes dans la mémoire se fait forcément en deux micro-opérations mémoire consécutives, sans compter les micro-opérations pour calculer le résultat et l'enregistrer en RAM.

Le séquenceur est conçu pour, mais il reste une difficulté : un seul registre d'interfaçage ne permet que le transfert d'un opérande. Lire une seconde opérande en RAM demande de rajouter un second registre d'interfaçage, qui est relié à l'autre entrée de l'ALU. Écrire le résultat en mémoire est géré en rajoutant un troisième registre d'interfaçage, relié à la sortie de l'ALU. Le séquenceur relie le registre d’interfaçage adéquat au bus mémoire à chaque étape d'une instruction.

La seconde difficulté est la gestion des pointeurs. Pour gérer les pointeurs, ces ordinateurs utilisent le mode d'adressage absolu indirect, absolument nécessaire sur des architectures. Avec ce mode d'adressage, l'instruction incorpore une adresse mémoire, mais ce n'est pas l'adresse de la donnée voulue : c'est l'adresse du pointeur, qui lui pointe vers la donnée. Il était systématiquement supporté sur toutes les architectures mémoire-mémoire. Elles supportaient aussi ses variantes à pré– ou post-incrément, où le pointeur était incrémenté/décrémenté automatiquement à chaque accès.

Avec ce mode d'adressage, La lecture de l'opérande se fait en deux étapes. Premièrement, le processeur extrait l'adresse de l'instruction et effectue une lecture pour récupérer le pointeur. Deuxièmement, la donnée lue, le pointeur, est envoyée sur le bus d'adresse pour récupérer la donnée. Le tout se fait en faisant communiquer les registres d’interfaçage de donnée et d'adresse, quelques multiplexeurs suffisent.

Architecture mémoire-mémoire avec adressage indirect mémoire

L'exemple de l'ordinateur AT&T Hobbit

[modifier | modifier le wikicode]

Un cas intéressant d'architecture de ce genre est celle du processeur AT&T Hobbit. L'origine de ce processeur se trouve dans un projet de processeur abandonné par AT&T. Le processeur en question, la C-machine, était un processeur spécifiquement conçus pour le langage de programmation C. Et pour coller le plus possible à ce langage, le processeur n'utilisait pas de registres, seulement des adresses mémoires.

Vu que les programmes codés en C manipulent beaucoup la pile d'appel, le processeur intégrait un cache de pile, spécialisé pour les accès à la pile d'appel. Il contenait 64 données de 4 octets. Il s'agit bien d'un cache, le processeur accède à la pile d'appel en mémoire RAM, mais le cache de pile en a une copie partielle et il intercepte les accès à la RAM, c'est lui qui fournit la donnée demandée s'il la contient. L'usage d'un cache de pile est assez spécifique aux architectures mémoire-mémoire et aux architectures à pile, même s'il doit exister quelques exceptions.

Les architectures à accumulateur

[modifier | modifier le wikicode]
Architecture à accumulateur.

Les architectures à accumulateur sont centrées autour d'un registre architectural appelé l'accumulateur. Il est utilisé pour toutes les opérations arithmétiques dyadiques, mais peut être utilisé pour les instructions monadiques. Le résultat d'une instruction est automatiquement mémorisé dans l'accumulateur. Pour les instructions dyadiques, le premier opérande lu depuis l'accumulateur, le second opérande est lu depuis la mémoire RAM. En clair, toutes les instructions dyadiques sont de type load-op. Pour les instructions monadiques, elles peuvent utiliser un opérande dans l'accumulateur, ou dans la mémoire RAM, les deux sont théoriquement possibles.

L'accumulateur est adressé grâce au mode d'adressage implicite, de même que le résultat de l'opération. Par contre, les autres opérandes sont localisés avec d'autres modes d'adressage, et lues en mémoire RAM. Le résultat ainsi qu'un des opérandes sont adressés de façon implicite car dans l'accumulateur, seule la seconde opérande étant adressée directement. Grâce à l'accumulateur, une instruction ne fait qu'un seul accès mémoire maximum, ce qui rend le processeur très facile à implémenter.

L'accumulateur et le registre de multiplication

[modifier | modifier le wikicode]
IBM 701, console extérieure.

Les architectures à accumulateurs sont apparues dès les débuts de l'informatique. Les tout premiers ordinateurs étaient de type mémoire-mémoire, puis les architectures à accumulateurs sont arrivées juste après, les deux ont existé conjointement pendant un temps. A l'époque, les ordinateurs étaient gigantesques, ils prenaient une pièce de bâtiment entière dans le pire des cas, une armoire entière dans le meilleur.

Les ordinateurs étaient surtout utilisés pour du calcul scientifique ou des tâches d’ingénierie demandant beaucoup de calcul, rarement pour de la compatibilité. Les nombres flottants n'étaient pas encore apparus. A la place, les ordinateurs de l'époque géraient des nombres entiers de grande taille, de 30 bits ou plus. En conséquence, l'accumulateur faisait facilement 30 à 60 bits. Par exemple, les ordinateurs de la Série scientifique IBM 700/7000 géraient des entiers de 36 bits, l’accumulateur faisait 38 bits : 36 bits plus deux bits pour les débordements. Ce n'était pas spécifique aux architectures à accumulateur, les machines à pile de l'époque faisaient pareil.

Historiquement, les premières architectures à accumulateur ne contenaient aucun autre registre que l'accumulateur. Mais de nombreuses architectures à accumulateur ont ajouté un second registre pour les multiplication/divisions. Un exemple est celui de l'IBM 701, qui incorporait un registre accumulateur de 38 bits et un registre multiplieur de 36 bits. Le registre mémorise le multiplieur lors d'une opération de multiplication. Il mémorise donc un opérande, pas le résultat. Il peut aussi décaler le multiplieur vers la droite/gauche, ce qui est utile pour exécuter la multiplication. C'est ce qui est fait sur l'IBM 7094.

Une autre possibilité est que ce registre mémorise une partie du résultat de l'opération. Pour rappel, le résultat d'une multiplication/division est codé sur deux fois de bits que ses opérandes. Par exemple, multipliez deux opérandes de 30 bits, vous obtiendrez un résultat de 60 bits. Les 30 bits de poids faible du résultat vont dans l'accumulateur, les 30 bits de poids fort vont dans ce second registre.

L'adressage indirect avec l'accumulateur et les registres d'indice

[modifier | modifier le wikicode]

Les instructions LOAD et STORE existent bel et bien sur les architectures à accumulateur, mais n'ont pas les mêmes modes d'adressages. L'instruction LOAD copie une donnée de la RAM vers l'accumulateur, l'instruction STORE copie l'accumulateur dans une adresse. Les deux instructions n'ont pas besoin d'adresser l'accumulateur, qui est adressé de manière implicite, juste de préciser l'adresse à lire/écrire.

Les architectures à accumulateur supportaient souvent le mode d'adressage indirect mémoire. Un exemple est celui des ordinateurs Data General Nova, qui sont des architectures à accumulateur et qui supportaient ce mode d'adressage. Les deux instructions LOAD et STORE existaient en deux versions, distinguées par un bit d'indirection. Si ce bit est à 0 dans l'opcode, alors l'instruction utilise le mode d'adressage absolu normal : l'adresse intégrée dans l'instruction est celle de la donnée. Mais s'il est à 1, alors l'adresse intégrée dans l'instruction est celle du pointeur.

Cependant, la présence de l'accumulateur permettait d'utiliser l'adressage indirect à registre, pour gérer les pointeurs. Pour rappel, avec le mode d'adressage indirect à registre, l'adresse à lire/écrire est dans un registre. Ici, l'adresse à lire/écrire est prise dans l'accumulateur, seul registre disponible pour. L'adressage indirect est plus simple à implémenter pour l'instruction LOAD, car elle prend un seul opérande : l'adresse à lire. L'adresse à lire est placée dans l'accumulateur, la donnée lue est elle aussi chargée dans l'accumulateur. Pour l'instruction STORE, il faut fournir deux opérandes, l'adresse et la donnée à écrire, ce qui peut poser problème. Mais au pire, il est toujours possible d'utiliser l'adressage absolu ou indirect mémoire : l'adresse est dans l'instruction, la donnée à écrire dans l'accumulateur.

Les architectures à accumulateur incluent des optimisations pour les modes d'adressage base + indice et absolus indicés, qui prennent une adresse et y ajoute un indice. Pour cela, les processeurs à accumulateurs incorporent des registres d'indice, pour faciliter les calculs d'adresse mémoire. Les registres d'indice stockent des indices de tableaux. Les registres d'indice permettaient de supporter deux modes d'adressage :

  • Le mode d'adressage absolu indicé où une adresse fixe est additionnée à un indice variable. L'adresse fixe est intégrée dans l'instruction (adressage absolu), l'indice est dans un registre d'indice.
  • Le mode d'adressage base + indice, où l'adresse est dans l'accumulateur et l'indice dans le registre d'indice.

Au départ, ces processeurs n'utilisaient qu'un seul registre d'indice qui se comportait comme un second accumulateur spécialisé dans les calculs d'adresses mémoire. Le processeur supportait de nouvelles instructions qui utilisaient ce registre d'indice de façon implicite. Mais avec le temps, les processeurs finirent par incorporer plusieurs de ces registres. Les instructions de lecture ou d'écriture devaient alors préciser quel registre d'indice utiliser, en précisant un nom de registre d'indice, un numéro de registre d'indice.

Il faut préciser que les registres d'indice ont une taille bien plus petite que l'accumulateur. Ils mémorisent des indices codés sur 16 bits ou moins, alors que l'accumulateur faisait typiquement 36 à 48 bits, parfois plus. La taille classique sur les anciens mainframes était de 15 bits, parfois moins. Il n'était pas rare de tomber sur des registres d'indice de 8 ou 9 bits. Leur petite taille rendait leur implémentation matérielle peu couteuse. Et c'est ce qui explique qu'ils étaient préférés à l'usage d'un second accumulateur.

Un exemple est le cas du processeur Motorola 6809, un processeur à accumulateur qui contient deux registres d'indices nommés X et Y. L'accumulateur est noté D et fait 16 bits, il peut être parfois géré comme deux accumulateurs séparés A et B de 8 bits chacun. Il contenait aussi deux pointeurs de pile, l'un pour les programmes, l'autre pour le système d'exploitation, ainsi qu'un program counter. Le registre de page était utilisé pour l'adressage absolu, comme vu dans le chapitre sur les modes d'adressage.

6809 Internal Registers

Les processeurs IBM avaient autrefois plusieurs registres d'indice. Par exemple, l'IBM 704 avait trois registres d'indice de 15 bits. Leur contenu était soustrait de l'adresse absolue. Une instruction pouvait utiliser plusieurs registres d'indice pour adresser son opérande. Toute instruction utilisait trois bits pour sélectionner les registres d'indice utilisés. Fait assez original, lorsque plusieurs registres d'indice sont sélectionnés, le processeur n'additionne pas les trois registres à l'adresse de base. À la place, il fait un OU bit à bit entre les registres d'indice sélectionnés, et additionne le résultat à l'adresse.

Les processeurs IBM à accumulateur ont fait ça sur toute la gamme IBM 700/7000, pour des raisons de compatibilité. Les processeurs suivants avaient 7 registres d'indices, mais conservaient les trois bits pour adresser les registres d'indices. Ils pouvaient fonctionner dans deux modes. Le premier mode est plus intuitif : les trois bits précisent un registre d'indice parmi les 7, qui est additionné avec l'adresse absolue. La valeur zéro indique qu'aucun registre d'indice n'est utilisé, ce qui explique qu'il y a 7 registres d'indice et non 8. Un second mode, compatible avec l'IBM 704, fait un OU logique entre les registres d'indice sélectionnés. Seuls les trois premiers registres d'indices peuvent être sélectionnés dans ce mode. Il y a deux instructions pour changer de mode : une pour passer en mode compatible, l'autre pour le quitter pour l'autre mode.

Quelques rares processeurs à registres généraux ont utilisés des registres d'indice, en plus des registres généraux. Ce qui fait l'association que nous avons faite entre registres d'indice et architectures à accumulateur est imparfait, bien que solide sur le principe. Un exemple d'architecture de ce type sont les architectures de la série UNIVAC 1100/2200. Ils disposaient de 128 registres, qui étaient mappés en mémoire à partir de l'adresse mémoire 0, la majorité étant inaccessibles par le programmeur. Ils regroupaient 12 registres accumulateurs, 11 registres d'indice et 4 registres hybrides qui pouvaient servir soit de registre d'indice, soit de registres accumulateurs.

La microarchitecture des architectures à accumulateur

[modifier | modifier le wikicode]

L'organisation interne d'une architecture à accumulateur est très différente de celle des processeurs à registre. Elle est plus ou moins la même pour tous ces processeurs, le point important étant que le chemin de données se résume à une ALU, un registre accumulateur et le bus de données. L'ALU est reliée au registre accumulateur, ainsi qu'au bus de données pour lire la seconde opérande, comme illustré ci-dessous. Il faut aussi gérer le cas des opérations LOAD/STORE et des modes d'adressages associés, mais c'est le séquenceur qui s'en occupe.

Accumulateur.

Pour simplifier l'implémentation, la seconde opérande est mémorisée dans un registre en amont de l'ALU. Par exemple, un registre d’interfaçage mémoire ou équivalent. C'était notamment très utile si le processeur utilisait une ALU plus courte que les opérandes. La grande taille des opérandes avait des conséquences sur l'unité de calcul utilisée, ainsi que sur la communication avec la mémoire. Il arrivait que de tels processeurs utilisaient des ALU sérielles, ou du moins octet-sérielles, plutôt qu'une véritable ALU 36 bits.

Avec l'adressage absolu, les instructions LOAD et STORE ne font que connecter l'accumulateur au bus de données. L'instruction STORE nécessite de connecter l'accumulateur au bus de données, de manière à ce que le transfert des données se fasse de l'accumulateur vers le bus de données. L'instruction LOAD s'implémente de la même manière, sauf que le sens de transfert est inversé. Cependant, il existe une optimisation qui permet d'implémenter l'instruction LOAD sans ajouter de circuits. Pour cela, il suffit que l'unité de calcul gére les opérations pass through, à savoir des opérations qui se contentent de recopier un opérande sur la sortie. Ici, l'idée est de recopier l'opérande provenant du bus de données.

Architecture à accumulateur, microarchitecture

La présence de registres d'indice est assez simple si l'architecture se limite au mode d'adressage absolu indicé. Pour rappel, avec celui-ci, l'adresse fournie par l'instruction est additionné avec un indice. L'indice est stocké dans des registres d'indice spécialisés, regroupés ans un banc de registre dédié. L'accumulateur est à part du banc de registre, il est toujours relié directement à l'ALU.

Deux solutions sont possibles : une ALU dédiée aux calculs d'adresse, une seule ALU pour toutes les opérations. Dans le premier cas, il y a une ALU séparée associée aux registres d'indice. L'ALU et les registres d'indice sont placés en sortie du séquenceur, en-dehors du chemin de données. La sortie de l'ALU est directement connectée au bus d'adresse.

Chemin de données sans support des pointeurs, avec adressage absolu indicé

En ajoutant un multiplexeur, on peut gérer nativement l'adressage base + indice. Dans ce cas, l'accumulateur doit être connecté au bus d'adresse. Typiquement, l'accumulateur est copié dans le registre d’interfaçage pour les adresses, puis la lecture s'effectue normalement. Au final, la donnée lue est copiée dans l'accumulateur. Si on veut gérer l'ajout d'un indice à l'accumulateur, le MUX est suivi par un circuit regroupant l'unité de calcul d'adresse et les registres d'indice. Le multiplexeur choisit quelle est l'adresse de base, l'addition se fait après.

Adressage indirect sur une architecture à accumulateur

Une autre option utilise un bus interne relié à l'accumulateur, l'unité de calcul, le banc de registre d'indice, et le bus de données. Avec cette solution, l'ALU est utilisé à la fois pour calculer les adresses et pour les instructions de calcul. De plus, en reliant le bus interne au bus d'adresse, on gère naturellement les adressages indirects. Le banc de registre est monoport, car on a besoin de lire ou d'écrire un indice à la fois.

Architecture à accumulateur avec registres associés

Un exemple de processeur de ce genre est les processeurs Intel 4004 et 4040. Le processeur avait un banc de registre dédié aux registres d'indice, ainsi qu'un banc de registre pour le program counter et les pointeurs de pile. L'ALU servait pour les instructions de calcul, mais elle servait aussi à incrémenter le program counter, à incrémenter/décrémenter le pointeur de pile, et pour les calculs d'adresse avec adressage indicé.

Microarchitecture de l'Intel 4004.

Les architectures hybrides registres-accumulateur

[modifier | modifier le wikicode]

Il a existé quelques architectures qui étaient des hybrides entre architectures à accumulateur et architecture à registre. Elles ont servi de transition entre les deux, durant les années 80-90. Leur jeu d'instruction et leur microarchitecture était intermédiaire entre architecture à accumulateur et à registre. Il y en avait deux sous-types : celles qui utilisaient deux accumulateurs, celles qui couplaient un accumulateur avec plusieurs registres généraux. Nous appelerons les premiers des processeurs bi-accumulateurs, les seconds des processeurs registre-accumulateur.

Les processeurs bi-accumulateurs : le Motorola 6800

[modifier | modifier le wikicode]

Le Motorola 6800 était un processeur 8 bits qui gérait des adresses de 16 bits. Il contenait deux accumulateurs nommés A et B, de 8 bits chacun. Il disposait aussi d'un registre d'indice de 16 bits, d'un pointeur de pile de 16 bits, et d'un program coutner, lui aussi de 16 bits. Le registre d'état regroupait 6 bits, qu'on ne détaillera pas ici.

Interface et registres du MC6800.

La présence des deux accumulateurs impactait surtout les instructions dyadiques. Les instructions monadiques étaient peu touchées, elles fonctionnaient normalement. Elles avaient juste à préciser où se trouvait l'opérande : soit en RAM, soit dans le premier accumulateur, soit dans le second. Le mode d'adressage permettait de préciser la localisation de l'opérande unique, à savoir soit l'adresse mémoire, soit l'un des deux accumulateurs.

Pour les opérations dyadiques, la gestion était totalement différente. En théorie, le premier opérande est dans un accumulateur, soit A, soit B. La seconde opérande vient soit de l'autre accumulateur, soit de la mémoire RAM. Et selon les instructions, tout variait.

Les opérations logiques permettaient de faire un ET/OU/XOR entre un accumulateur et un opérande venant de la mémoire. Il y avait une instruction par accumulateur. Par exemple, l'instruction ANDA faisait un ET entre l’accumulateur A et l'opérande mémoire, l'instruction ANDB faisait pareil, mais avec l'accumulateur B. Idem avec les opérations OU (instructions ORAA et ORAB) et XOR (instructions EORA et EORB). Il n'y avait pas de possibilité de prendre les deux accumulateurs comme opérande. A l'exception de l'addition, les autres instructions arithmétiques fonctionnaient sur le même modèle : un opérande venant de la mémoire, l'autre d'un des accumulateurs.

Pour l'addition, il y avait trois instructions. Les deux premières faisaient comme pour les opérations logiques : elles additionnaient un opérande provenant de la mémoire avec un accumulateur. L'instruction ADDA sélectionnait le premier accumulateur, l'instruction ADDB sélectionnait le second accumulateur. Mais une troisième instruction, l’instruction ABA, permettait d'additionner le contenu des deux accumulateurs.

Les processeurs registres-accumulateur

[modifier | modifier le wikicode]

Le deuxième type d'hybride accumulateur-registre avait un accumulateur unique couplé à un banc de registres secondaires. Sur ces processeurs, les instructions dyadiques devaient mettre leur premier opérande dans l'accumulateur, la seconde provenait soit des registres, soit de la RAM, soit d'une constante immédiate. Les opérations monadiques pouvaient lire leur opérande depuis l'accumulateur, les autres registres ou la RAM, tout était permis. Les registres d'indices disparaissaient sur ces architectures, en théorie. Les registres généraux pouvaient être utilisés comme registre d'indice ou pour stocker des opérandes, ils étaient assez versatiles pour servir de registres d'indices.

La microarchitecture de ces processeurs est très similaire à celle d'un processeur avec des registres d'indices. Plus précisément, avec un processeur avec un bus interne, relié à l'unité de calcul, au banc de registres et au bus de données/adresse. La seule différence est que le banc de registre contient des registres généraux et non des registres d'indices. Le banc de registre était monoport, car n'avait aucune raison d'être multiport. Pour les instructions dyadiques, seules à lire deux opérandes, il ne servait que pour la seconde opérande.

Architecture à accumulateur avec registres associés

Sur les architectures Von Neumann, le bus interne au processeur est utilisé pour la lecture des instructions. Il est donc relié sur l'entrée du séquenceur. S'il y a un registre d'instruction, il est connecté au bus interne.

Single bus organization

Un exemple ce processeur de ce type est celui des premiers processeurs Intel, des processeurs 8 bits, à savoir le 8008 et le 8080. Les processeurs Intel 8 bits avaient plusieurs registres : l'accumulateur A, et six registres nommés B, C, D, E, H et L. Les six registres sont regroupés dans un banc de registre à part de l'accumulateur.

Vu que les adresses font 16 bits, l'accumulateur 8 bits ne peut pas être utilisé pour l'adressage indirect. La solution a été de concaténer les deux registres H et L pour donner une adresse de 16 bits. Une conséquence est que le banc de registre avait deux ports : un pour le relier à l'accumulateur et au bus de données, l'autre pour le connecter au bus d'adresse. Afin de profiter au mieux de ce port d'adresse, le program counter et les pointeurs de pile étaient dans le même banc de registre.

Microarchitecture de l'Intel 8080.

Le Z80 est un processeur inspiré du design des processuers Intel 8 bits, mais avec pas mal d'améliorations. Le processeur Z80 avait un registre accumulateur nommé A, six registres de données nommés B, C, D, E, H et L, un registre d'état F. Le processeur Z80 avait des registres séparés ou les interruptions. Tous les registres précédents avaient une copie séparée accessible uniquement pour les interruptions, via fenêtrage de registres. Les registres pour interruptions sont nommés A', B', C', D', E', H', L', F'. En plus des registres précédents, le Z80 incorporait un pointeur de pile SP et deux registres d'indice nommés X et Y, qui n'étaient pas dupliqués pour les interruptions.

Le Z80 incorporait des instructions pour échanger le contenu des deux ensembles de registres, accumulateur inclus. L'instruction EXX échangeait le contenu des registres B, C, D, E, H et L et B', C', D', E', H' et L'. L'instruction EX échangeait les registres A,F avec les registres A',F'. Ces instructions permettaient d'utiliser les registres d'interruption pour les programmes et réciproquement. Cela permettait de doubler le nombre de registres pour les programmes, si les interruptions n'étaient pas utilisées.

A l'intérieur du processeur, tous les registres étaient regroupés dans un banc de registre unique, sauf les deux accumulateurs qui étaient séparés des autres registres. Pour l'accumulateur et le registre d'état, le choix entre registre normal et registre d'interruption se faisait par une bascule appelée la bascule A. Une bascule similaire était utilisée pour le banc de registres, appelons-la la bascule INT. Les instructions d'échange de registres du Z80 ne faisaient pas de copies, elles ne faisaient que modifier les bascules INT et A. L'instruction EX inverse la bascule A, l'instruction EXX inverse le contenu de la bascule I.

Il est aussi possible d'échanger le contenu des registres DE et HL avec une instruction dédiée, ce qui est là encore fait par deux bascules : une par ensemble de registres.

Microarchitecture du Z80.

Les architectures à pile

[modifier | modifier le wikicode]
Exemple d'une pile/file d'opérandes, chaque opérande étant codée sur 4 Bytes.

Les dernières architectures que nous allons voir sont les machines à pile, des jeux d'instructions qui gèrent nativement une pile semblable à la pile d'appel, sauf qu'elle mémorise les opérandes des calculs et leur résultat. Une instruction de calcul prend les opérandes qui sont au sommet de la pile. L'instruction dépile automatiquement les opérandes qu'elle utilise et empile son résultat. Le processeur peut copier des opérandes dans la pile ou en retirer grâce à des instructions PUSH et POP qu'on analysera sous peu.

Les instructions d'accès mémoire d'une machine à pile

[modifier | modifier le wikicode]

Les opérandes sont ajoutés ou retirés de la pile grâce à deux instructions nommées PUSH et POP.

  • L'instruction PUSH permet d'empiler une donnée. Elle prend l'adresse de la donnée à empiler, charge la donnée, et met à jour le pointeur de pile.
  • L'instruction POP dépile la donnée au sommet de la pile, la stocke à l'adresse indiquée dans l'instruction, et met à jour le pointeur de pile.
Instruction Push.
Instruction Pop.

Un défaut lié à l'absence des registres est qu'il est impossible de réutiliser une donnée chargée dans la pile. Vu qu'une instruction dépile ses opérandes, on ne peut pas les réutiliser. Ceci dit, certaines instructions ont été inventées pour limiter la casse : on peut notamment citer l'instruction DUP, qui copie le sommet de la pile en deux exemplaires. On peut aussi citer l'instruction SWAP, qui échange deux données dans la pile. Mais ces instructions sont des opérations à faire en plus, comparé aux autres architectures. Les autres classes d'architectures n'ont pas à copier des données dans une pile, les empiler, et les déplacer avant de les manipuler.

Instruction Dup.
Instruction Swap.

Les modes d'adressage sur les machines à pile

[modifier | modifier le wikicode]

Les instructions de calcul manipulent les opérandes qui sont au sommet de la pile. Les opérandes sont retirés de la pile par l'instruction et le résultat est placé au sommet de la pile. C'est du moins le cas sur les machines à pile dites "pures", mais d'autres architectures permettent de préciser la position des opérandes dans la pile. Une instruction peut indiquer qu'elle veut utiliser les opérandes situés 2, 3 ou 5 cases sous le sommet de la pile. Si les opérandes sélectionnés ne sont pas toujours retirés de la pile (tout dépend de l'architecture), le résultat est lui toujours placé au sommet de la pile. Ces jeux d'instruction n'utilisent pas la pile comme une mémoire LIFO, ce qui lui vaut le nom d'architecture à pseudo-pile.

Les machines à pile que je viens de décrire ne peuvent manipuler que des données sur la pile. Toutefois, des machines à pile plus évoluées ajoutent des instructions load-op, qui lisent la seconde opérande en mémoire RAM, avec une adresse mémoire ou un mode d'adressage pour les pointeurs. L'avantage est que la seconde opérande n'a pas à être placée sur la pile pour être utilisée. Mais de telles instructions sont assez peu utilisées.

Sur les machines à pile, l'adressage indirect utilise le sommet de la pile. L'adresse à lire est placée au sommet de la pile, elle est envoyée sur le bus d'adresse. Pour l'instruction STORE indirecte, la donnée à écrire est placée sous l'adresse, donnée et adresse sont dépilées par l'instruction STORE. Pour l'instruction LOAD indirecte, l'adresse à lire est dépilée au sommet de la pile, la donnée lue est empilée au sommet de la pile. Il est possible d'implémenter un adressage de type base + indice, il suffit de placer l'adresse de base et l'indice au sommet de la pile.

La microarchitecture d'une machine à pile

[modifier | modifier le wikicode]

L'implémentation d'une architecture à pile est assez complexe, mais elle a l'avantage de ne pas utiliser beaucoup de registres. Elle se contente d'un program counter, d'un pointeur de pile qui indique l'adresse du sommet de la pile en RAM, d'un registre d'état, et de quelques registres facultatifs. Les architectures à pile peuvent être implémentées de plusieurs manières, la plus simple utilisant une architecture mémoire-mémoire améliorée, la plus complexe utilisant une architecture à registres sous-jacente.

La première implémentation utilise une architecture mémoire-mémoire sous-jacente, toute la difficulté de l'implémentation étant déportée dans le séquenceur. La pile d'opérande est placée en mémoire RAM, les instructions lisent leurs opérandes en RAM et empilent le résultat en RAM. Les instructions PUSH et POP font des copies en mémoire RAM pour empiler et dépiler les données. Pour cela, elles passent par l'intermédiaire d'un registre d’interfaçage, comme sur les architectures mémoire-mémoire.

Architecture à pile basée sur une architecture mémoire-mémoire

Le séquenceur est adapté de manière à gérer une pile d'opérande, les instructions PUSH/POP/autres, et quelques autres détails. Le pointeur de pile est géré par le séquenceur, au même titre que les registres d'indice. Il est stocké à côté ou dans le séquenceur, souvent à côté du program counter, et il est incrémenté/décrémenté par l'unité de calcul d'adresse. Placer le pointeur de pile dans le séquenceur n'est pas spécifique des architectures à pile. De nombreuses architectures CISC faisaient pareil. Mais nous détaillerons cela dans le prochain chapitre, dans la section sur le program counter.

Organisation interne d'une architecture à pile

Une telle implémentation intègre souvent un cache de pile, qui mémorise les données au sommet de la pile. Il s'agit réellement d'un cache, dans le sens où il contient une copie du sommet de la pile d'appel, qui est aussi en RAM. La pile est donc en RAM, totalement, et seules les portions utilisées de la pile sont maintenues dans le processeur. La gestion du débordement se fait en utilisant le remplacement des lignes de cache naturellement incorporé dans tout cache digne de ce nom.

Une autre implémentation ajoute un registre pour mémoriser l'opérande au sommet de la pile. Le registre agit comme un registre accumulateur. En effet, le sommet de la pile est le dernier résultat calculé par une instruction. De plus, il sera utilisé comme opérande de la prochaine opération. Du moins, tout cela est valable tant qu'on n'utilise pas d'instruction DUP ou SWAP, qui changent la position des opérandes dans la pile. L'implémentation n'est ni plus ni moins qu'une architecture à accumulateur au séquenceur modifié.

Implémentation d'une machine à pile avec une machijne à accumulateur

Une dernière implémentation place tout ou partie de la pile dans le processeur, pas seulement le sommet de la pile. Le sommet de la pile est stocké dans une mémoire tampon de type LIFO, construite en améliorant un banc de registre. Selon l'implémentation, la LIFO permet de lire une ou plusieurs opérandes. Si elle ne permet de lire qu'un seul opérande, l'intérieur du processeur ressemble à une architecture à accumulateur, avec des registres en amont et aval de l'ALU. Sinon, l'intérieur du processeur ressemble à une architecture LOAD-STORE avec des circuits en plus.

Si jamais la LIFO est totalement remplie, le processeur peut gérer la situation de plusieurs manières. Avec la première, les données au bas de la pile débordent en mémoire RAM. Si la LIFO est déjà pleine au moment d'un PUSH, l'opérande au fond de la pile est alors envoyé en mémoire RAM. Reste qu'implémenter la gestion du débordement de la pile est quelque peu complexe. Une autre solution est de déléguer les débordements au logiciel. Un PUSH dans pune pile pleine déclenche une exception matérielle, dont la routine gère la situation.

Machine à pile avec LIFO intégrée au CPU

Les avantages de l'absence de registres

[modifier | modifier le wikicode]

Pour comprendre pourquoi les architectures à registre dominent actuellement, il faut les comparer aux autres architectures canoniques. Et la différence principale concerne le nombre de registres. De ce point de vue, on peut distinguer trois classes d'architectures : celles sans registres de données, les architectures à accumulateur, et les architectures à registres.

Classe d'architecture Nombre de registres pour les données
Architecture mémoire-mémoire Aucun.
Architecture à pile/à file
Architecture à accumulateur Un registre appelé l'accumulateur.
Architecture à registres Plusieurs registres dits généraux et/ou spécialisés.

La présence de registres réduit grandement le nombre d'accès mémoire à effectuer, ce qui améliore grandement la performance. Un bon moyen de s'en rendre compte est de regarder le nombre d'accès mémoire par instruction. Il est d'autant plus élevé en absence de registres. L'usage d'un accumulateur réduit le nombre d'accès mémoire par instruction, avoir plusieurs registres généraux le réduit encore plus. Au début de l'informatique, mémoire et processeur étaient tout aussi rapides, ce qui fait que les architectures à registre n'avaient pas d'avantage évident en termes de performances. Mais depuis, l’absence de registres est devenu un désavantage global.

Classe d'architecture Nombre d'accès mémoire par opération dyadique
Architecture à pile Trois accès mémoire par opération : un par opérande, un pour le résultat
Architecture mémoire-mémoire
Architecture à accumulateur Un accès mémoire par instruction, pour lire la seconde opérande
Architecture à registres Zéro si les opérandes sont dans les registres, un pour les opérations load-op, LOAD et STORE.

L'absence de registre a cependant des avantages, voyons lesquels.

La rapidité des interruptions/appels de fonction

[modifier | modifier le wikicode]

L'absence de registre a des avantages pour la gestion des procédures. Pas besoin de sauvegarder les registres du processeur à chaque appel de fonction, ni de les restaurer à la fin. La gestion de la pile est ainsi grandement simplifiée : pas de sauvegarde/restauration des registres signifie pas besoin d'instructions pour échanger des données entre registres et cadres de pile. Les programmes avec beaucoup d'appels de fonction économisent ainsi beaucoup d’instructions, ils étaient plus petits. L'avantage est d'autant plus important que les procédures/fonctions sont petites (une large partie des instructions est alors dédiée à la sauvegarde/restauration des registres) et nombreuses.

En conséquence, les architectures mémoire-mémoire et les architectures à pile ont un avantage pour ce qui est des appels de fonction. L'avantage concerne aussi les interruptions, qui sont des appels de fonction comme les autres. Ce qui fait que les architectures sans registres de données sont parfois utilisés comme processeurs pour gérer les entrées-sorties, vu que ces processeurs doivent fréquemment gérer des interruptions matérielles. Ils sont aussi très adaptés pour des processeurs faible performance dans l'embarqué, dans des cas d'utilisation où les interruptions matérielles sont fréquentes.

Les architectures à accumulateur sont aussi dans ce cas. Avec elles, il n'y a qu'un seul registre accumulateur en plus comparé aux architectures sans registres, ce qui ce qui rend la sauvegarde/restauration des registres très rapide. Et encore, dans quelques situations, l'accumulateur n'a pas à être sauvegardé. Et tout cela vaut aussi pour les interruptions. Aussi, beaucoup de processeurs embarqués à faible performance, qui doivent gérer un grand nombre d'entrées-sorties, sont des architectures à accumulateur. Leur simplicité de conception, leur facilité de programmation, leur parfaite adaptation aux interruptions fréquentes, les rend idéaux pour ce genre d'applications.

La densité de code

[modifier | modifier le wikicode]

Au début de l'informatique, la mémoire avait des performances similaires à celles du processeur, mais elle était de petite taille. Aussi il était avantageux d'avoir des programmes assez petits. Les architectures se distinguaient sur la densité de code, à savoir la taille des programmes. La taille d'un programme dépend de deux choses : le nombre d'instructions et la taille de celles-ci. Et ces deux paramètres sont influencés par l'architecture, ainsi que par le caractère CISC/RISC. La taille des instructions est aussi influencée par les modes d'adressage disponibles.

Les architectures 0, 1, 2 et 3 références

[modifier | modifier le wikicode]

Pour ce qui est de comparer la densité de code des architectures canoniques, il est intéressant de parler de la distinction entre architectures 0, 1, 2 et 3 adresses. Elle est liée aux opérations dyadiques, à savoir celles à deux opérandes. Elles ont besoin de préciser trois choses : la localisation des deux opérandes et l'endroit où ranger le résultat. Opérandes et résultat peuvent être adressés soit explicitement, soit implicitement. Par implicitement, on veut dire avec le mode d'adressage implicite, alors que explicite signifie qu'on doit préciser un numéro/nom de registre, une adresse mémoire, ou toute autre référence pour localiser l'opérande/résultat. Et le nombre de référence par instruction dyadique varie grandement suivant l'architecture utilisée.

Notons que ce qui nous intéresse ici est le cas des instructions dyadiques uniquement. La raison est que les autres instructions ont un encodage similaire. L'encodage des branchements est globalement le même d'une architecture à l'autre. Comme autre exemple, les instructions d'accès mémoire utilisent souvent l'adressage absolu, ce qui demande juste une adresse mémoire et un opcode, sur toutes les architectures canoniques. Les instructions d'accès mémoire PUSH et POP sont dans ce cas, pareil pour les instructions LOAD et STORE, pour les instructions pour copier une donnée dans l'accumulateur, etc.

Les architectures à zéro référence sont les architectures à pile. Elles n'ont pas besoin de préciser la localisation de leurs opérandes, qui sont au sommet de la pile et sont adressées implicitement, sauf pour Pop et Push. Quelques architectures à pile ont des instructions annexes qui précisent l'adresse mémoire du second opérande, mais elles sont assez peu utilisées, la majorité des instructions adressent toutes les opérandes implicitement.

Les architectures à une référence correspondent aux architectures à accumulateur. Les instructions dyadiques lisent un opérande depuis la mémoire, la seconde est lue depuis l'accumulateur. Seule la première est adressée explicitement, alors que l'accumulateur est adressé implicitement. Le résultat de l'opération est enregistré dans l'accumulateur, là aussi avec un adressage implicite.

Les architectures à deux et trois références regroupent les autres architectures à registre et mémoire-mémoire.

Les architectures à deux références adressent les opérandes explicitement, mais le résultat est adressé implicitement. Le résultat d'une instruction est stocké à l'endroit indiqué par la référence du premier opérande. Pour le dire autrement, l'opérande sera remplacé par le résultat de l'instruction. Avec cette organisation, les instructions ne précisent que deux opérandes, pas le résultat. Mais la gestion des instructions est moins souple, vu qu'un opérande est écrasé.

Les architectures à trois références permettent de préciser le registre de destination du résultat explicitement, ou son adresse mémoire sur les architectures mémoire-mémoire. Ce genre d'architectures permet une meilleure utilisation des registres, mais les instructions deviennent plus longues que sur les architectures à deux références.

La taille des instructions

[modifier | modifier le wikicode]

L'intérêt de cette distinction 0/1/2/3 adresses est une question de taille des instructions. La raison est intuitive : encoder une référence pour adresser un opérande demande quelques bits. Plus il y a d'opérandes adressés explicitement, plus il faut ajouter de bits à une instruction pour ça, plus les instructions sont longues. Mais il faut aussi tenir compte de la taille des références. Un numéro/nom de registre est bien plus court qu'une adresse, par exemple. Et le nombre d'opérandes à adresser ne fait pas tout : il faut aussi tenir compte du mode d'adressage. Voyons ce qu'il en est pour chaque type d'architecture.

Les architectures à zéro référence ont les instructions dyadiques les plus courtes, car elles adressent toutes leurs opérandes implicitement. Elles sont aussi appelées architectures à zéro adresse par abus de langage. Les instructions se limitent généralement à un opcode, du moins pour les instructions de calcul et les branchements.

Les architectures à une référence utilisent une adresse mémoire pour adresser l'opérande en RAM. Elles sont aussi appelées architectures à une adresse par abus de langage. Les instructions dyadiques utilisent donc un opcode couplé à une adresse mémoire. L'adresse mémoire est généralement assez longue, plus que l'opcode.

Les autres architectures sont toutes à deux ou trois références. Par contre, la nature de ces références change d'une architecture à l'autre.

  • Sur les architectures LOAD-STORE, les instructions dyadiques utilisent des numéros de registres, mais aucune adresse. Aussi, encoder les 2/3 registres adressés prend autant, voire moins de place qu'une adresse. Elles ont donc les instructions les plus courtes des trois, leurs instructions sont globalement de même taille que sur les architectures à accumulateur, voire un peu moins.
  • Les architectures mémoire-mémoire encodent deux/trois adresses mémoires en plus de l'opcode. Elles ont les instructions les plus longues des trois.
  • Les architectures à registre sont intermédiaires entre les deux cas précédents. Leurs instructions ont la même taille que sur une architecture LOAD-STORE dans le meilleur des cas, si opérandes et résultat vont dans les registres. Pour les instructions load-op, leur taille est légèrement supérieure à celle d'une architecture à accumulateur, vu qu'elles encodent une adresse et un registre. Les architectures devant encoder deux ou trois adresses sont très rares.

Si on fait le résumé, le classement en termes de longueur d'instruction est le suivant, en partant des instructions les plus courtes vers les plus longues.

  • Architectures à pile ;
  • Architectures LOAD-STORE ;
  • Architectures à registres ;
  • Architectures à accumulateur ;
  • Architectures mémoire-mémoire.

Le nombre d'instructions d'un programme

[modifier | modifier le wikicode]

Les développements précédents nous parlent de la taille des instructions, mais il faut aussi tenir compte du nombre d'instructions. Et ce nombre est fortement influencé par l'architecture du processeur. Les architectures canoniques ne sont pas égales niveau accès mémoire. Pour faire la même chose, le nombre d'instructions mémoire ne sera pas le même sur les architectures canoniques. Et le nombre d'instructions peut compenser l'effet de la taille des instructions dyadiques. Si on a des instructions dyadiques très courtes, cela peut être compensé par des un plus grand nombre d'instructions d'accès mémoire. L'étude de la densité de code demande donc de regarder comment se manifeste ce compromis pour les architectures canoniques.

Les machines à pile ont un nombre d'instructions par programme plus élevé que sur les autres architectures, en grande partie à cause des instructions mémoire. Une bonne partie des instructions sont des instructions Pop et Push. De plus, les programmes utilisent souvent des instructions pour dupliquer/copier des données dans la pile, chose qui n'est pas nécessaire sur les autres architectures. La situation est légèrement améliorée sur les architectures à pile qui supportent des instructions load-op, mais pas de quoi sensiblement changer la donne.

Les architectures à registre ont en théorie un désavantage en termes de nombre d'instructions, lié à la gestion des procédures. Elles ont besoin de sauvegarder les registres lors d'un appel de fonction, et de les restaurer quand la fonction termine. Cela implique des échanges avec la mémoire réalisée avec des instructions LOAD-STORE. Plus les fonctions sont fréquentes et petites, plus le désavantage est important. Le nombre d'instructions augmente donc assez rapidement, mais reste en-deça de celui des architectures à pile. Les architectures LOAD-STORE ont un léger désavantage en plus, car les instructions load-op sont émulées avec une instruction LOAD suivie d'une instruction de calcul.

Les architectures mémoire-mémoire sont celles qui ont le moins d'instructions par programme. Comme les architectures à accumulateur, elles se passent d'un paquet d'opérations nécessaires sur les autres machines. Pas besoin de sauvegarder/restaurer les registres lors d'un appel de fonction, la gestion de la pile d'appel est simplifiée. Les architectures à accumulateur sont proches des architectures mémoire-mémoire pour les mêmes raisons, avec cependant un léger désavantage : elles doivent sauvegarder l'accumulateur lors des appels de fonction, et le restaurer après. Mais le désavantage est vraiment mineur.

Pour résumer :

  • Les architectures mémoire-mémoire utilisent moins d'instructions par programme que les autres, elles se contentent du strict minimum.
  • Les architectures à pile utilisent en plus des instructions PUSH et POP, ainsi que d'autres instructions pour manipuler la pile.
  • Les architectures à registre ajoutent des instructions de sauvegarde/restauration des registres lors d'un appel de fonction, idem pour les architectures LOAD-STORE.
  • Les architectures LOAD-STORE utilisent en plus des instructions LOAD-STORE pour copier les opérandes dans les registres et écrire les résultats en RAM, mais seulement si besoin.
Instructions d'accès mémoire hors opérandes Instructions pour gérer les opérandes/résultats
Architecture mémoire-mémoire
Architecture à pile Instructions PUSH et POP, ainsi que d'autres instructions pour manipuler la pile.
Architecture à registres Instructions de sauvegarde/restauration des registres lors d'un appel de fonction
Architecture LOAD-STORE Instructions LOAD-STORE pour lire les opérandes et enregistrer les résultats.
Architecture à accumulateur Rares copies de l'accumulateur en mémoire RAM.

Les architectures CISC ont un avantage en termes de densité de code, par rapport aux architectures RISC. Le fait qu'elles intègrent des instructions complexes est un avantage. Les processeurs RISC doivent émuler ces instructions complexes à partir d'une suite d'opérations plus simples. Il n'est pas garanti que les instructions CISC soient plus rapides que leur équivalent RISC, tout dépend de leur implémentation matérielle. L'avantage est surtout que le programme prend moins d'instructions pour faire la même chose, ce qui augmente la densité de code.

La conclusion : la densité de code finale

[modifier | modifier le wikicode]

Si on compare les 5 types d'architectures précédentes, on s’aperçoit que c'est surtout la taille des instructions qui compte en premier lieu, suivi par le nombre d'instruction, et enfin par le caractère 0/1/2/3 opérandes. Les machines à pile ont la meilleure densité de code, suivies par les architectures à registre, puis par les architectures LOAD-STORE, puis par les architectures à accumulateur, et enfin par les architectures mémoire-mémoire.

Les architectures à pile ont des programmes avec plus d'instructions, mais cela est plus que compensé par le fait que ce sont des architectures à zéro adresse, où les instructions sont très petites. Les architectures à registre ont une bonne densité de code, bien qu'inférieure à celle des architectures à pile, du fait que de la petite taille de leurs instructions dépasse l'effet des instructions liées aux appels de fonction. Les architectures LOAD-STORE viennent en troisième position, car leurs instructions sont aussi longues que celles à registres, mais qu'elles ont le désavantage des instructions LOAD-STORE. Enfin, les architectures sans registres viennent en dernier du fait de leur encodage désastreux, qui encodent des adresses mémoires. Les architectures à accumulateur sont en quatrième position, car ce sont des architectures 1-adresse, là où les architectures mémoire-mémoire sont de type 2/3 adresses.