Nous sommes souvent dit que le matériel ne se soucie pas de la langue dans laquelle un programme est écrit car il ne voit que le code binaire compilé, mais ce n'est pas toute la vérité. Par exemple, considérons l'humble Z80; ses extensions au jeu d'instructions 8080 incluent des instructions comme CPIR qui est utile pour numériser des chaînes de style C (terminées par NULL), par ex. pour exécuter strlen()
. Les concepteurs doivent avoir identifié que l'exécution de programmes C (par opposition à Pascal, où la longueur d'une chaîne est dans l'en-tête) était quelque chose pour laquelle leur conception était susceptible d'être utilisée. Un autre exemple classique est le LISP Machine .
Quels autres exemples y a-t-il? Par exemple. instructions, nombre et type de registres , modes d'adressage, qui font qu'un processeur particulier favorise les conventions d'un langage particulier? Je suis particulièrement intéressé par les révisions de la même famille.
Les réponses existantes se concentrent sur ISA changements. Il y a aussi d'autres changements matériels. Par exemple, C++ utilise couramment vtables pour les appels virtuels. à partir du Pentium M , Intel a un composant "prédicteur de branche indirecte" qui accélère les appels de fonction virtuelle.
Le jeu d'instructions Intel 8086 comprend une variante de "ret" qui ajoute une valeur au pointeur de pile après popping l'adresse de retour. Ceci est utile pour de nombreuses implémentations Pascal où l'appelant d'une fonction poussera des arguments sur la pile avant de faire un appel de fonction et les fera disparaître ensuite. Si une routine accepterait par exemple valeur de quatre octets de paramètres, il pourrait se terminer par "RET 0004" pour nettoyer la pile. En l'absence d'une telle instruction, une telle convention d'appel aurait probablement exigé que le code insère l'adresse de retour dans un registre, met à jour le pointeur de pile, puis passe à ce registre.
Fait intéressant, la plupart des codes (y compris les routines du système d'exploitation) sur le Macintosh d'origine utilisaient la convention d'appel Pascal malgré l'absence d'instructions de facilitation dans le 68000. L'utilisation de cette convention d'appel a permis d'économiser 2 à 4 octets de code sur un site d'appel typique, mais nécessitait un supplément 4-6 octets de code sur le site de retour de chaque fonction qui a pris des paramètres.
Un exemple est MIPS, qui a à la fois add
et addu
pour intercepter et ignorer le débordement respectivement. (Aussi sub
et subu
.) Il avait besoin du premier type d'instruction pour des langues comme Ada (je pense - je n'ai jamais réellement utilisé Ada cependant) qui traitent des débordements explicitement et le second tapez pour les langages comme C qui ignorent les débordements.
Si je me souviens bien, le CPU réel a des circuits supplémentaires dans l'ALU pour garder une trace des débordements. Si le seul langage dont les gens se souciaient était le C, il n'en aurait pas besoin.
Personne ne semble avoir mentionné jusqu'à présent que les progrès de l'optimisation du compilateur (où le langage de base est largement hors de propos) ont entraîné le passage des jeux d'instructions CISC (qui étaient largement conçus pour être codés par les humains) aux jeux d'instructions RISC (qui étaient en grande partie conçu pour être codé par des compilateurs.)
La série Burroughs 5000 a été conçue pour prendre en charge efficacement ALGOL, et l'iAPX-432 d'Intel a été conçu pour exécuter efficacement Ada. L'Inmos Transputer avait sa propre langue, l'Occam. Je pense que le processeur Parallax "Propeller" a été conçu pour être programmé en utilisant sa propre variante de BASIC.
Ce n'est pas un langage, mais le jeu d'instructions VAX-11 a une seule instruction pour charger un contexte de processus, qui a été conçu à la demande de l'équipe de conception VMS. Je ne me souviens pas des détails, mais ISTR a mis tellement d'instructions à mettre en œuvre qu'il a mis une limite supérieure sérieuse sur le nombre de processus qu'ils pouvaient planifier.
L'ordinateur central de la série Z d'IBM est le descendant de l'IBM 360 des années 1960.
Plusieurs instructions ont été mises en place pour accélérer spécifiquement les programmes COBOL et Fortran. L'exemple classique étant le BXLE
- "Branch on Index Low Or Equal" qui est la plupart d'une boucle Fortran for
ou un COBOL PERFORM VARYING x from 1 by 1 until x > n
encapsulé dans une seule instruction.
Il existe également toute une famille d'instructions décimales compressées pour prendre en charge l'arithmétique décimale à virgule fixe commune dans les programmes COBOL.
La famille Motorola 68000 a introduit certains mode d'adressage automatique qui ont rendu la copie de données via le processeur très efficace et compacte.
[Exemple mis à jour]
c'était du code c ++ qui a influencé l'assembleur 68000
while(someCondition)
destination[destinationOffset++] = source[sourceOffset++]
implémenté dans l'assembleur conventionnel (pseudocode, j'ai oublié les commandes de l'assembleur 68000)
adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
move akku,(adressRegister1)
move (adressRegister2), akku
increment(adressRegister1, 1)
increment(adressRegister2, 1)
}
avec le nouveau mode d'adresse, il est devenu quelque chose de similaire à
adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
move akku,(adressRegister1++)
move (adressRegister2++), akku
}
seulement deux instructions par boucle au lieu de 4.
Les premiers processeurs Intel avaient les fonctionnalités suivantes, beaucoup d'entre eux sont désormais obsolètes en mode 64 bits:
L'indicateur de signe, trouvé dans le registre d'état de nombreux processeurs, existe pour effectuer facilement l'arithmétique signée ET non signée.
Le jeu d'instructions SSE 4.1 introduit des instructions pour le traitement des chaînes, à la fois comptées et terminées à zéro (PCMPESTR, etc.)
De plus, je pouvais imaginer qu'un certain nombre de fonctionnalités au niveau du système ont été conçues pour prendre en charge la sécurité du code compilé (vérification des limites de segment, portes d'appel avec copie de paramètres, etc.)
Certains ARM processeurs, principalement ceux des appareils mobiles, incluent (d) l'extension Jazelle, qui est un interpréteur JVM matériel; il interprète Java bytecode directement. Jazelle-aware La JVM peut utiliser le matériel pour accélérer l'exécution et éliminer une grande partie du JIT, mais le recours au logiciel VM est toujours assuré si le bytecode ne peut pas être interprété sur la puce.
Les processeurs avec une telle unité incluent l'instruction BXJ, qui place le processeur en "mode Jazelle" spécial, ou si l'activation de l'unité a échoué, elle est simplement interprétée comme une instruction de branchement normale. L'unité réutilise les registres ARM pour conserver l'état JVM.
Le successeur de la technologie Jazelle est ThumbEE
Autant que je sache, c'était plus courant dans le passé.
Il y a ne session de questions dans laquelle James Gosling a dit qu'il y avait des gens qui essayaient de fabriquer du matériel qui pourrait mieux gérer le bytecode JVM, mais ensuite ces gens trouveraient un moyen de le faire avec des génériques "communs" "intel x86 (peut-être en compilant le bytecode d'une manière intelligente).
Il a mentionné qu'il y a un avantage à utiliser la puce générique populaire (comme Intel) car elle a une grande entreprise qui jette d'énormes sommes d'argent sur le produit.
La vidéo mérite d'être regardée. Il en parle à la minute 19 ou 20.
Le processeur Intel iAPX a été spécialement conçu pour les langues OO. Cependant, cela n'a pas vraiment fonctionné.
Le iAPX 432 ( architecture Intel Advanced Processor) était la première conception de microprocesseur 32 bits d'Intel, introduite en 1981 en tant que ensemble de trois circuits intégrés. Il devait être la conception principale d'Intel pour les années 80, mettant en œuvre de nombreuses fonctionnalités avancées de gestion multitâche et de mémoire. Le design a donc été appelé un Micromainframe ...
L'iAPX 432 a été "conçu pour être entièrement programmé dans des langages de haut niveau" , avec Ada étant principal et pris en charge - programmation orientée objet et garbage collection directement dans le matériel et microcode . La prise en charge directe de diverses structures de données était également destinée à permettre la mise en œuvre de systèmes d'exploitation modernes pour l'iAPX 432 en utilisant beaucoup moins de code de programme que pour les processeurs ordinaires. Ces propriétés et fonctionnalités ont abouti à une conception matérielle et microcode beaucoup plus complexe que la plupart des processeurs de l'époque, en particulier les microprocesseurs.
En utilisant la technologie des semi-conducteurs de son époque, les ingénieurs d'Intel n'ont pas pu traduire la conception en une première implémentation très efficace. Avec le manque d'optimisation dans un compilateur Ada prématuré, cela a contribué à des systèmes informatiques plutôt lents mais coûteux, effectuant des tests de référence typiques à environ 1/4 de la vitesse de la nouvelle puce 80286 à la même fréquence d'horloge (au début de 1982).
Cet écart de performances initial par rapport au profil 8086 plutôt bas et à bas prix était probablement la principale raison pour laquelle le plan d'Intel de remplacer ce dernier (appelé plus tard x86) par l'iAPX 432 a échoué. Bien que les ingénieurs aient vu des moyens d'améliorer une conception de nouvelle génération, l'iAPX 432 Architecture de capacité commençait désormais à être davantage considéré comme une surcharge d'implémentation que comme le support simplifiant qu'il était censé être.
Le projet iAPX 432 a été un échec commercial pour Intel ...
J'ai fait une recherche de page rapide et il semble que personne n'ait mentionné les processeurs développés spécifiquement pour exécuter Forth . Le Forth langage de programmation est basé sur la pile, compact et utilisé dans les systèmes de contrôle.
Le 68000 avait MOVEM qui était le plus adapté pour pousser plusieurs registres sur la pile en une seule instruction, ce que de nombreuses langues attendaient.
Si vous avez vu MOVEM (MOVE Multiple) précédant JSR (Jump SubRoutine) tout au long du code, vous saviez généralement que vous aviez affaire à du code C conforme.
MOVEM autorisait l'incrémentation automatique du registre de destination, permettant à chaque utilisation de continuer l'empilement sur la destination, ou la suppression de la pile en cas de décrémentation automatique.
Lorsque le coprocesseur numérique 8087 a été conçu, il était assez courant que les langues effectuent toutes les mathématiques à virgule flottante en utilisant le type de précision le plus élevé, et arrondissent le résultat uniquement pour une précision inférieure lors de son affectation à une variable de précision inférieure. Dans la norme C d'origine, par exemple, la séquence:
float a = 16777216, b = 0.125, c = -16777216;
float d = a+b+c;
promouvoir a
et b
en double
, les ajouter, promouvoir c
en double
, l'ajouter, puis stocker le résultat arrondi à float
. Même s'il aurait été plus rapide dans de nombreux cas pour un compilateur de générer du code qui effectuerait des opérations directement sur le type float
, il était plus simple d'avoir un ensemble de routines à virgule flottante qui ne fonctionneraient que sur le type double
, ainsi que les routines à convertir vers/depuis float
, que d'avoir des ensembles de routines distincts pour gérer les opérations sur float
et double
. Le 8087 a été conçu autour de cette approche de l'arithmétique, effectuant toutes les opérations arithmétiques en utilisant un type à virgule flottante de 80 bits [80 bits a probablement été choisi parce que:
Sur de nombreux processeurs 16 et 32 bits, il est plus rapide de travailler avec une mantisse 64 bits et un exposant séparé que de travailler avec une valeur qui divise un octet entre la mantisse et l'exposant.
Il est très difficile d'effectuer des calculs qui sont exacts avec la pleine précision des types numériques que l'on utilise; si l'on essaie par exemple calculer quelque chose comme log10 (x), il est plus facile et plus rapide de calculer un résultat qui est précis à moins de 100ulp d'un type 80 bits que de calculer un résultat qui est précis à moins de 1ulp d'un type 64 bits, et d'arrondir l'ancien Le résultat à une précision de 64 bits donnera une valeur de 64 bits qui est plus précise que celle-ci.
Malheureusement, les futures versions du langage ont changé la sémantique du fonctionnement des types à virgule flottante; alors que la sémantique 8087 aurait été très agréable si les langages les avaient pris en charge de manière cohérente, si les fonctions f1 (), f2 (), etc. renvoient le type float
, de nombreux auteurs de compilateurs se chargeraient de faire long double
un alias pour le type double 64 bits plutôt que pour le type 80 bits du compilateur (et ne fournit aucun autre moyen de créer des variables 80 bits), et pour évaluer arbitrairement quelque chose comme:
double f = f1()*f2() - f3()*f4();
de l'une des manières suivantes:
double f = (float)(f1()*f2()) - (extended_double)f3()*f4();
double f = (extended_double)f1()*f2() - (float)(f3()*f4());
double f = (float)(f1()*f2()) - (float)(f3()*f4());
double f = (extended_double)f1()*f2() - (extended_double)f3()*f4();
Notez que si f3 et f4 renvoient respectivement les mêmes valeurs que f1 et f2, l'expression d'origine doit clairement retourner zéro, mais la plupart de ces dernières expressions ne le peuvent pas. Cela a conduit les gens à condamner "l'extra précision" du 8087 même si la dernière formulation serait généralement supérieure à la troisième et - avec un code qui utilisait correctement le type double étendu - serait rarement inférieure.
Dans les années qui ont suivi, Intel a répondu à la tendance du langage (à mon humble avis) à forcer les résultats intermédiaires à être arrondis à la précision des opérandes en concevant leurs processeurs ultérieurs de manière à favoriser ce comportement, au détriment du code qui gagnerait à utiliser une plus grande précision sur les calculs intermédiaires.
L'architecture AVR d'Atmel est entièrement conçue dès le départ pour être adaptée à la programmation en C. Par exemple, cette note d'application développe davantage.
OMI, cela est étroitement lié à l'excellent rockets4kids'es réponse , les premiers PIC16 étant développés pour la programmation directe des assembleurs (40 instructions au total), les familles ultérieures ciblant C.