Aperçu du pipeline de compilation étendu de GCC, y compris des programmes spécialisés tels que le préprocesseur, l’assembleur et l’éditeur de liens.
GCC suit l’architecture à 3 étages typique des compilateurs multilingues et multi-CPU. Toutes les arborescences de programme sont converties en une représentation abstraite commune au « milieu », permettant l’optimisation du code et les fonctionnalités de génération de code binaire d’être partagées par toutes les langues.,
L’interface externe de GCC suit les conventions Unix. Les utilisateurs invoquent un programme de pilote spécifique à la langue (gcc
pour C, g++
pour C++, etc.), qui interprète les arguments de commande, appelle le compilateur réel, exécute l’assembleur sur la sortie, puis exécute éventuellement l’éditeur de liens pour produire un binaire exécutable complet.
Chacun des compilateurs de langage est un programme distinct qui lit le code source et produit le code machine. Tous ont une structure interne commune., Un frontal par langue analyse le code source dans cette langue et produit un arbre syntaxique abstrait (« tree » en abrégé).
Ceux-ci sont, si nécessaire, convertis en représentation d’entrée de l’extrémité médiane, appelée forme GÉNÉRIQUE; l’extrémité médiane transforme alors progressivement le programme vers sa forme finale. Les optimisations du compilateur et les techniques d’analyse de code statique (telles que FORTIFY_SOURCE, une directive du compilateur qui tente de découvrir certains débordements de tampon) sont appliquées au code., Ceux-ci travaillent sur plusieurs représentations, principalement la représentation GIMPLE indépendante de l’architecture et la représentation RTL dépendante de l’architecture. Enfin, le code machine est produit en utilisant une correspondance de motifs spécifique à l’architecture basée à l’origine sur un algorithme de Jack Davidson et Chris Fraser.
GCC a été écrit principalement en C, à l’exception de certaines parties du frontal Ada. La distribution comprend les bibliothèques standard pour Ada, C++ et Java dont le code est principalement écrit dans ces langages., Sur certaines plates-formes, la distribution comprend également une bibliothèque d’exécution de bas niveau, libgcc, écrite dans une combinaison de C indépendant de la machine et de code machine spécifique au processeur, conçue principalement pour gérer les opérations arithmétiques que le processeur cible ne peut pas effectuer directement.
GCC utilise de nombreux outils standard dans sa construction, y compris Perl, Flex, Bison et d’autres outils courants. De plus, il nécessite actuellement la présence de trois bibliothèques supplémentaires pour la construction: GMP, MPC et MPFR.
En mai 2010, le comité directeur de GCC a décidé d’autoriser l’utilisation d’un compilateur C++ pour compiler GCC., Le compilateur était destiné à être écrit principalement en C plus un sous-ensemble de fonctionnalités de C++. En particulier, cela a été décidé pour que les développeurs de GCC puissent utiliser les destructeurs et les fonctionnalités génériques de C++.
En août 2012, le comité directeur de GCC a annoncé que GCC utilise désormais C++ comme langage d’implémentation. Cela signifie que pour construire GCC à partir de sources, un compilateur C++ est requis qui comprend la norme ISO/IEC C++03.,
Avant endsEdit
les extrémités Avant sont constitués de prétraitement, l’analyse lexicale, analyse syntaxique (parsing) et l’analyse sémantique. Les objectifs des frontaux du compilateur sont d’accepter ou de rejeter les programmes candidats en fonction de la grammaire et de la sémantique du langage, d’identifier les erreurs et de gérer les représentations de programme valides aux étapes ultérieures du compilateur. Cet exemple montre les étapes de lexer et d’analyseur effectuées pour un programme simple écrit en C.,
Chaque frontal utilise un analyseur de produire de l’arbre de syntaxe abstraite d’une source donnée de fichier. En raison de l’abstraction de l’arborescence syntaxique, les fichiers source de l’une des différentes langues prises en charge peuvent être traités par le même back-end. GCC a commencé à utiliser des analyseurs LALR générés avec Bison, mais est progressivement passé à des analyseurs de descente récursive écrits à la main pour C++ en 2004, et pour C et Objective-C en 2006. À partir de 2021, tous les frontaux utilisent des analyseurs de descente récursive écrits à la main.
Jusqu’à ce que GCC 4.,0 la représentation arborescente du programme n’était pas totalement indépendante du processeur ciblé. La signification d’un arbre était quelque peu différente pour différents frontaux de langue, et les frontaux pouvaient fournir leurs propres codes d’arbre. Cela a été simplifié avec l’introduction de GENERIC et GIMPLE, deux nouvelles formes d’arbres indépendants du langage qui ont été introduites avec l’avènement de GCC 4.0. Le GÉNÉRIQUE est plus complexe, basé sur le GCC 3.x Représentation intermédiaire du frontal Java. GIMPLE est un GÉNÉRIQUE simplifié, dans lequel diverses constructions sont réduites à plusieurs instructions GIMPLE., Les frontaux C, C++ et Java produisent des GÉNÉRIQUES directement dans le frontal. D’autres frontaux ont à la place des représentations intermédiaires différentes après l’analyse et les convertissent en GÉNÉRIQUES.
Dans les deux cas, le soi-disant « gimplifier » convertit alors cette forme plus complexe en la forme GIMPLE plus simple basée sur SSA qui est le langage commun pour un grand nombre de puissantes optimisations globales (portée de fonction) indépendantes du langage et de l’architecture.,
GENERIC and GIMPLEEdit
GENERIC est un langage de représentation intermédiaire utilisé comme « intermédiaire » lors de la compilation du code source en binaires exécutables. Un sous-ensemble, appelé GIMPLE, est ciblé par toutes les extrémités avant de GCC.
L’étape intermédiaire de GCC effectue toute l’analyse et l’optimisation du code, travaillant indépendamment du langage compilé et de l’architecture cible, en partant de la représentation GÉNÉRIQUE et en l’élargissant au langage de transfert d’enregistrement (RTL)., La représentation GÉNÉRIQUE ne contient que le sous-ensemble des constructions de programmation impératives optimisées par l’extrémité médiane.
Lors de la transformation du code source en GIMPLE, les expressions complexes sont divisées en un code à trois adresses à l’aide de variables temporaires. Cette représentation a été inspirée par la représentation SIMPLE proposée dans le compilateur McCAT par Laurie J. Hendren pour simplifier l’analyse et l’optimisation des programmes impératifs.,
OptimizationEdit
L’optimisation peut se produire pendant n’importe quelle phase de compilation; cependant, la majeure partie des optimisations sont effectuées après l’analyse syntaxique et sémantique du front-end et avant la génération de code du back-end; ainsi, un nom commun, bien que quelque peu contradictoire, pour cette partie du compilateur est »
L’ensemble exact des optimisations GCC varie d’une version à l’autre au fur et à mesure de son développement, mais inclut les algorithmes standard, tels que l’optimisation des boucles, le filetage de saut, l’élimination des sous-expressions communes, la planification des instructions, etc., Les optimisations RTL sont moins importantes avec l’ajout d’optimisations globales basées sur SSA sur les arbres GIMPLE, car les optimisations RTL ont une portée beaucoup plus limitée et ont moins d’informations de haut niveau.
Certaines de ces optimisations effectuées à ce niveau incluent l’élimination du code mort, l’élimination de la redondance partielle, la numérotation des valeurs globales, la propagation constante conditionnelle clairsemée et le remplacement scalaire des agrégats. Des optimisations basées sur la dépendance de tableau telles que la vectorisation automatique et la parallélisation automatique sont également effectuées. L’optimisation guidée par profil est également possible.,
Retour endEdit
Le back-end de GCC est en partie spécifié par des macros de préprocesseur et des fonctions spécifiques à une architecture cible, par exemple pour définir son endianness, sa taille de mot et ses conventions d’appel., La partie avant de l’arrière-plan les utilise pour aider à décider de la génération RTL, donc bien que RTL de GCC soit nominalement indépendant du processeur, la séquence initiale d’instructions abstraites est déjà adaptée à la cible. À tout moment, les instructions RTL réelles formant la représentation du programme doivent être conformes à la description de la machine de l’architecture cible.
Le fichier de description de la machine contient des modèles RTL, ainsi que des contraintes d’opérande et des extraits de code pour produire l’assemblage final., Les contraintes indiquent qu’un modèle RTL particulier peut s’appliquer uniquement (par exemple) à certains registres matériels, ou (par exemple) autoriser des décalages d’opérandes immédiats d’une taille limitée (par exemple 12, 16, 24, … décalages de bits,etc.). Lors de la génération RTL, les contraintes pour l’architecture cible donnée sont vérifiées. Pour émettre un extrait donné de RTL, il doit correspondre à un (ou plusieurs) des modèles RTL dans le fichier de description de la machine, et satisfaire aux contraintes de ce modèle; sinon, il serait impossible de convertir le RTL final en code machine.,
Vers la fin de la compilation, le RTL valide est réduit à une forme stricte dans laquelle chaque instruction fait référence à de vrais registres de machine et à un motif du fichier de description de machine de la cible. Former un RTL strict est une tâche compliquée; une étape importante est l’allocation des registres, où de vrais registres matériels sont choisis pour remplacer les pseudo-registres initialement attribués. Ceci est suivi d’une phase de » rechargement »; tous les pseudo-registres qui n’ont pas été affectés à un vrai registre matériel sont « déversés » dans la pile, et RTL pour effectuer ce déversement est généré., De même, les décalages trop importants pour s’intégrer dans une instruction réelle doivent être décomposés et remplacés par des séquences RTL qui obéiront aux contraintes de décalage.
Dans la phase finale, le code machine est construit en appelant un petit extrait de code, associé à chaque modèle, pour générer les instructions réelles à partir du jeu d’instructions de la cible, en utilisant les registres finaux, les décalages et les adresses choisis pendant la phase de rechargement. L’extrait de génération d’assemblage peut être juste une chaîne, auquel cas une simple substitution de chaîne des registres, des décalages et/ou des adresses dans la chaîne est effectuée., L’extrait de génération d’assembly peut également être un court bloc de code C, effectuant un travail supplémentaire, mais renvoyant finalement une chaîne contenant le code assembly valide.
Autres featuresEdit
Certaines fonctionnalités de GCC incluent:
- Link-time optimization optimise les limites des fichiers objets pour améliorer directement le binaire lié. L’optimisation Link-time repose sur un fichier intermédiaire contenant la sérialisation d’une représentation Gimple incluse dans le fichier objet. Le fichier est généré à côté du fichier objet lors de la compilation source., Chaque compilation source génère un fichier objet séparé et un fichier d’aide link-time. Lorsque les fichiers objets sont liés, le compilateur est exécuté à nouveau et utilise les fichiers d’assistance pour optimiser le code à travers les fichiers objets compilés séparément.
- Les plugins peuvent étendre directement le compilateur GCC. Les plugins permettent à un compilateur stock d’être adapté à des besoins spécifiques par du code externe chargé en tant que plugins. Par exemple, les plugins peuvent ajouter, remplacer ou même supprimer des passes intermédiaires opérant sur des représentations Gimple., Plusieurs plugins GCC ont déjà été publiés, notamment le plugin GCC Python, qui lie avec libpython, et permet d’invoquer des scripts Python arbitraires depuis l’intérieur du compilateur. L’objectif est de permettre aux plugins GCC d’être écrits en Python. Le plugin MELT fournit un langage Lisp de haut niveau pour étendre GCC.
- Mémoire transactionnelle C++ lors de la compilation avec-fgnu-tm.
- À partir de GCC 10, les identifiants permettent l’encodage UTF-8 (Unicode), c’est-à-dire que le code source C utilise l’encodage UTF-8 par défaut.