Cela pourrait être une sorte de question étrange.
Un gars qui écrit un compilateur C ++ (ou n'importe quel langage non VM): doit-il être capable de lire / écrire du langage machine brut? Comment ça marche?
EDIT: Je fais spécifiquement référence aux compilateurs qui compilent en code machine, pas en un autre langage de programmation.
compiler
machine-code
Aviv Cohn
la source
la source
Réponses:
Non pas du tout. Il est parfaitement possible (et souvent même préférable) que votre compilateur émette du code d'assembly à la place. L'assembleur se charge ensuite de créer le code machine réel.
Soit dit en passant, votre distinction entre l'implémentation non VM et l'implémentation VM n'est pas utile.
Pour commencer, l'utilisation d'une machine virtuelle ou d'une précompilation pour coder une machine n'est que différentes façons d'implémenter un langage; dans la plupart des cas, un langage peut être implémenté en utilisant l'une ou l'autre stratégie. J'ai dû utiliser une fois un interpréteur C ++ .
De plus, de nombreuses machines virtuelles comme la JVM ont toutes deux un code machine binaire et un assembleur, tout comme une architecture ordinaire.
Le LLVM (qui est utilisé par les compilateurs Clang) mérite une mention spéciale ici: il définit une machine virtuelle pour laquelle les instructions peuvent être représentées soit par du code d'octets, un assemblage textuel ou une structure de données qui le rend très facile à émettre à partir d'un compilateur. Donc, même si cela serait utile pour le débogage (et pour comprendre ce que vous faites), vous n'auriez même pas besoin de connaître le langage d'assemblage, uniquement sur l'API LLVM.
La bonne chose à propos du LLVM est que sa VM n'est qu'une abstraction et que le code d'octet n'est généralement pas interprété, mais JITted de manière transparente à la place. Il est donc tout à fait possible d'écrire un langage qui est effectivement compilé, sans jamais avoir à connaître le jeu d'instructions de votre CPU.
la source
Non. Le point clé de votre question est que la compilation est un terme extrêmement large. La compilation peut se produire de n'importe quelle langue vers n'importe quelle langue. Et le code assembleur / machine n'est que l'un des nombreux langages pour la cible de compilation. Par exemple, les langages Java et .NET comme C #, F # et VB.NET se compilent tous en une sorte de code intermédiaire au lieu d'un code spécifique à la machine. Peu importe qu'il s'exécute ensuite sur VM, le langage est toujours compilé. Il existe également une option pour compiler dans un autre langage, comme C. C est en fait une cible de compilation très populaire et de nombreux outils le font. Et enfin, vous pouvez utiliser un outil ou une bibliothèque pour faire le dur travail de production de code machine pour vous. il y a par exemple LLVM qui peut réduire l'effort nécessaire pour créer un compilateur autonome.
De plus, votre modification n'a aucun sens. C'est comme demander "Est-ce que chaque ingénieur doit comprendre comment fonctionne le moteur? Et je pose des questions sur les ingénieurs travaillant sur les moteurs." Si vous travaillez sur un programme ou une bibliothèque qui émet un code machine, vous devez le comprendre. Le fait est que vous n'avez pas à faire une telle chose lors de l'écriture du compilateur. Beaucoup de gens l'ont fait avant vous, vous devez donc avoir de sérieuses raisons de le refaire.
la source
Classiquement, un compilateur comprend trois parties: l'analyse lexicale, l'analyse et la génération de code. L'analyse lexicale décompose le texte du programme en mots-clés, noms et valeurs de langue. L'analyse montre comment les jetons qui proviennent de l'analyse lexicale sont combinés dans des déclarations syntaxiquement correctes pour la langue. La génération de code prend les structures de données produites par l'analyseur et les traduit en code machine ou en une autre représentation. De nos jours, l'analyse lexicale et l'analyse syntaxique peuvent être combinées en une seule étape.
Il est clair que la personne qui écrit le générateur de code doit comprendre le code machine cible à un niveau très profond, y compris les jeux d'instructions, les pipelines de processeur et le comportement du cache. Sinon, les programmes produits par le compilateur seraient lents et inefficaces. Ils peuvent très bien être capables de lire et d'écrire du code machine comme représenté par des nombres octaux ou hexadécimaux, mais ils écrivent généralement des fonctions pour générer le code machine, se référant en interne aux tableaux d'instructions machine. Théoriquement, les gens qui écrivent le lexer et l'analyseur peuvent ne rien savoir de la génération du code machine. En fait, certains compilateurs modernes vous permettent de brancher vos propres routines de génération de code qui pourraient émettre du code machine pour certains CPU dont les rédacteurs de lexers et d'analyseurs n'ont jamais entendu parler.
Cependant, dans la pratique, les rédacteurs de compilateurs à chaque étape en savent beaucoup sur les différentes architectures de processeur, ce qui les aide à concevoir les structures de données dont l'étape de génération de code aura besoin.
la source
Il y a longtemps, j'ai écrit un compilateur qui convertissait entre deux scripts shell différents. Cela n'allait pas du tout près du code machine.
Une écriture de compilateur doit comprendre leur sortie , mais ce n'est souvent pas du code machine.
La plupart des programmeurs n'écriront jamais un compilateur qui génère du code machine ou du code assembleur, mais des compilateurs personnalisés peuvent être très utiles sur de nombreux projets pour produire d'autres sorties.
YACC est un compilateur de ce type qui ne génère pas de code machine….
la source
Vous n'avez pas besoin de commencer par une connaissance détaillée de la sémantique de vos langages d'entrée et de sortie, mais vous feriez mieux de terminer par une connaissance extrêmement détaillée des deux, sinon votre compilateur sera anormalement bogué. Donc, si votre entrée est C ++ et votre sortie est un langage machine spécifique, vous devrez éventuellement connaître la sémantique des deux.
Voici quelques-unes des subtilités de la compilation de C ++ en code machine: (juste au-dessus de ma tête, je suis sûr qu'il y en a plus que j'oublie.)
Quelle sera la taille
int
? Le choix «correct» ici est un art, basé à la fois sur la taille naturelle du pointeur de la machine, les performances de l'ALU pour différentes tailles d'opérations arithmétiques et les choix faits par les compilateurs existants pour la machine. La machine a-t-elle même une arithmétique 64 bits? Si ce n'est pas le cas, l'ajout d'entiers 32 bits doit se traduire par une instruction tandis que l'ajout d'entiers 64 bits doit se traduire par un appel de fonction pour effectuer l'ajout 64 bits. La machine a-t-elle des opérations d'ajout 8 bits et 16 bits ou devez-vous simuler celles avec des opérations 32 bits et un masquage (par exemple le DEC Alpha 21064)?Quelle est la convention d'appel utilisée par les autres compilateurs, bibliothèques et langages de la machine? Les paramètres sont-ils poussés de droite à gauche ou de gauche à droite sur la pile? Certains paramètres vont-ils dans les registres tandis que d'autres vont sur la pile? Les entiers et flottants sont-ils dans différents espaces de registre? Les paramètres alloués au registre doivent-ils être traités spécialement lors des appels varargs? Quels registres sont sauvegardés par l'appelant et lesquels sont sauvegardés par l'appelé? Pouvez-vous effectuer des optimisations d'appel feuille?
Que fait chacune des instructions de changement de vitesse de la machine? Si vous demandez de décaler un entier 64 bits de 65 bits, quel est le résultat? (Sur de nombreuses machines, le résultat est le même que le décalage d'un bit, sur d'autres le résultat est "0".)
Quelle est la sémantique de cohérence de la mémoire de la machine? C ++ 11 a une sémantique de mémoire très bien définie qui impose des restrictions sur certaines optimisations dans certains cas, mais permet des optimisations dans d'autres cas. Si vous compilez un langage qui n'a pas de sémantique mémoire bien définie (comme toutes les versions de C / C ++ avant C ++ 11, et de nombreux autres langages impératifs), vous devrez inventer la sémantique mémoire au fur et à mesure, et généralement vous voudrez inventer la sémantique de la mémoire qui correspond le mieux à la sémantique de votre machine.
la source