overzicht van GCC ’s uitgebreide compilatiepijplijn, inclusief gespecialiseerde programma’ s zoals de preprocessor, assembler en linker.
GCC volgt de 3-traps architectuur die kenmerkend is voor multi-language en multi-CPU compilers. Alle programmabomen worden geconverteerd naar een gemeenschappelijke abstracte representatie aan de “middle end”, waardoor code-optimalisatie en binaire code generatie faciliteiten gedeeld kunnen worden door alle talen.,
de externe interface van GCC volgt Unix-conventies. Gebruikers roepen een taalspecifiek stuurprogramma aan (gcc
voor C, g++
voor C++, enz.), die Commando-argumenten interpreteert, de eigenlijke compiler aanroept, de assembler op de uitvoer uitvoert, en dan optioneel de linker uitvoert om een volledig uitvoerbaar binair programma te produceren.
elk van de taal compilers is een afzonderlijk programma dat broncode leest en machinecode uitvoert. Alle hebben een gemeenschappelijke interne structuur., Een per-taal front-end ontleedt de broncode in die taal en produceert een abstracte syntaxis boom (“tree” in het kort).
deze worden, indien nodig, geconverteerd naar de invoerweergave van het middle end, GENERIC form genaamd; Het middle end transformeert dan geleidelijk het programma naar zijn uiteindelijke vorm. Compiler optimalisaties en statische code analyse technieken (zoals FORTIFY_SOURCE, een compiler richtlijn die probeert te ontdekken wat buffer overflows) worden toegepast op de code., Deze werken op meerdere representaties, meestal de architectuur-onafhankelijke Gimple representatie en de architectuur-afhankelijke RTL representatie. Tot slot, machine code wordt geproduceerd met behulp van architectuur-specifieke patroon matching oorspronkelijk gebaseerd op een algoritme van Jack Davidson en Chris Fraser.
GCC werd voornamelijk geschreven in C, met uitzondering van delen van het ada-front-end. De distributie bevat de standaardbibliotheken voor Ada, C++ en Java waarvan de code meestal in die talen is geschreven., Op sommige platforms bevat de distributie ook een low-level runtime library, libgcc, geschreven in een combinatie van machine-onafhankelijke C en processor-specifieke machinecode, voornamelijk ontworpen om rekenkundige bewerkingen aan te kunnen die de doelprocessor niet direct kan uitvoeren.
GCC gebruikt veel standaard tools in zijn build, waaronder Perl, Flex, Bison en andere veelgebruikte tools. Daarnaast vereist het momenteel drie extra bibliotheken aanwezig te zijn om te bouwen: GMP, MPC, en MPFR.
In mei 2010 besloot de GCC-stuurgroep het gebruik van een C++ – compiler toe te staan om GCC samen te stellen., De compiler was bedoeld om te worden geschreven meestal in C plus een subset van functies van C++. In het bijzonder werd dit besloten zodat de ontwikkelaars van GCC de destructors en generics-functies van C++konden gebruiken.
in augustus 2012 kondigde de GCC-stuurgroep aan dat GCC nu C++ als implementatietaal gebruikt. Dit betekent dat om GCC van bronnen te bouwen, een C++ compiler nodig is die de ISO/IEC C++03 Standaard begrijpt.,
Front endsEdit
Front ends bestaan uit preprocessing, lexicale analyse, syntactische analyse (parsing) en semantische analyse. De doelen van compiler front ends zijn het accepteren of afwijzen van kandidaat-programma ‘ s volgens de taal grammatica en semantiek, het identificeren van fouten en omgaan met geldige programma representaties naar latere compiler stadia. Dit voorbeeld toont de lexer en parser stappen uitgevoerd voor een eenvoudig programma geschreven in C.,
elke front-end gebruikt een parser om de abstracte syntaxisstructuur van een bepaald bronbestand te produceren. Als gevolg van de syntaxis boom abstractie, bronbestanden van een van de verschillende ondersteunde talen kunnen worden verwerkt door dezelfde back-end. GCC begon met LALR parsers gegenereerd met Bison, maar geleidelijk overgestapt op handgeschreven recursieve-descent parsers voor C++ in 2004, en voor C en Objective-C in 2006. Vanaf 2021 gebruiken alle front-ends handgeschreven recursieve-descent parsers.
tot GCC 4.,0 de boomweergave van het programma was niet volledig onafhankelijk van het doel van de processor. De Betekenis van een boom was iets anders voor verschillende taal front ends, en front ends konden hun eigen boom codes. Dit werd vereenvoudigd met de introductie van GENERIC en GIMPLE, twee nieuwe vormen van taalonafhankelijke bomen die werden geïntroduceerd met de komst van GCC 4.0. GENERIC is complexer, gebaseerd op de GCC 3.x Java front end ‘ s intermediate representation. GIMPLE is een vereenvoudigde generiek, waarin verschillende constructies worden verlaagd naar meerdere Gimple instructies., De C, C++ en Java front-ends produceren generiek direct in de front-end. Andere front-ends in plaats daarvan hebben verschillende intermediaire representaties na parsing en converteren deze naar generiek.
in beide gevallen converteert de zogenaamde “gimplifier” deze meer complexe vorm naar de eenvoudiger SSA-gebaseerde GIMPLE-vorm die de gemeenschappelijke taal is voor een groot aantal krachtige taal – en architectuuronafhankelijke globale (function scope) optimalisaties.,
GENERIC en Gimpledit
GENERIC is een intermediaire representatietaal die wordt gebruikt als een” middle end ” tijdens het compileren van broncode in uitvoerbare binaire bestanden. Een deelverzameling, genaamd GIMPLE, is gericht op alle front-ends van GCC.
de middelste fase van GCC doet alle codeanalyse en-optimalisatie, onafhankelijk van zowel de gecompileerde taal als de doelarchitectuur, beginnend bij de generieke representatie en uitbreidend tot register transfer language (RTL)., De generieke representatie bevat alleen de subset van de imperatieve programmeerconstructies die zijn geoptimaliseerd door het middeneinde.
bij het omzetten van de broncode naar GIMPLE worden complexe expressies gesplitst in een drie-adrescode met behulp van tijdelijke variabelen. Deze voorstelling werd geïnspireerd door de eenvoudige voorstelling die Laurie J. Hendren in de McCAT-compiler voorstelde voor het vereenvoudigen van de analyse en optimalisatie van imperatieve programma ‘ s.,
OptimizationEdit
optimalisatie kan optreden tijdens elke fase van compilatie; echter, het grootste deel van de optimalisaties worden uitgevoerd na de syntaxis en semantische analyse van de front-end en voor de code generatie van de back-end; dus een veel voorkomende, hoewel enigszins tegenstrijdige, naam voor dit deel van de compiler is de “middle end.”
de exacte set van GCC optimalisaties varieert van release tot release naarmate het zich ontwikkelt, maar bevat de standaard algoritmen, zoals lus optimalisatie, jump threading, common subexpression elimination, instruction scheduling, enzovoort., De RTL-optimalisaties zijn van minder belang met de toevoeging van globale SSA-gebaseerde optimalisaties op Gimple-bomen, omdat RTL-optimalisaties een veel beperkter bereik hebben en minder informatie op hoog niveau hebben.
sommige van deze optimalisaties die op dit niveau worden uitgevoerd, omvatten dode code-eliminatie, gedeeltelijke redundantie-eliminatie, globale waardenummering, schaarse conditionele constante propagatie en scalaire vervanging van aggregaten. Array dependence based optimalisaties zoals automatische vectorisatie en automatische parallellisatie worden ook uitgevoerd. Profielgestuurde optimalisatie is ook mogelijk.,
Back endEdit
De back-end van de GCC wordt gedeeltelijk gespecificeerd door preprocessor macro ‘ s en functies die specifiek zijn voor een doelarchitectuur, bijvoorbeeld om zijn endianness, woordgrootte en aanroepconventies te definiëren., Het voorste deel van de back-end gebruikt deze om te helpen beslissen RTL generatie, dus hoewel GCC ‘ s RTL nominaal processor-onafhankelijk is, is de initiële reeks van abstracte instructies al aangepast aan het doel. Op elk moment moeten de eigenlijke RTL-instructies die de programmaweergave vormen, voldoen aan de Machinebeschrijving van de doelarchitectuur.
het machinebeschrijvingsbestand bevat RTL-patronen, samen met operand-beperkingen, en codefragmenten om de eindmontage uit te voeren., De beperkingen geven aan dat een bepaald RTL-patroon alleen van toepassing is (bijvoorbeeld) op bepaalde hardwareregisters, of (bijvoorbeeld) onmiddellijke operand offsets van slechts een beperkte omvang toestaan (bijvoorbeeld 12, 16, 24, … bit offsets, enz.). Tijdens het genereren van RTL worden de beperkingen voor de gegeven doelarchitectuur gecontroleerd. Om een bepaald fragment van RTL uit te geven, moet het overeenkomen met een (of meer) van de RTL patronen in het machinebeschrijvingsbestand, en voldoen aan de beperkingen voor dat patroon; anders zou het onmogelijk zijn om de uiteindelijke RTL om te zetten in machine code.,
aan het einde van de compilatie wordt geldige RTL gereduceerd tot een strikte vorm waarin elke instructie verwijst naar echte machine registers en een patroon uit het machinebeschrijvingsbestand van het doel. Het vormen van strikte RTL is een ingewikkelde taak; een belangrijke stap is register allocation, waarbij echte hardware registers worden gekozen om de aanvankelijk toegewezen pseudo-registers te vervangen. Dit wordt gevolgd door een” herladen ” fase; alle pseudo-registers die niet aan een echt hardware register zijn toegewezen, worden ‘gemorst’ op de stack, en RTL om dit morsen uit te voeren wordt gegenereerd., Ook offsets die te groot zijn om in een daadwerkelijke instructie te passen, moeten worden opgesplitst en vervangen door RTL-sequenties die de offset-beperkingen zullen gehoorzamen.
in de laatste fase wordt de machinecode gebouwd door een klein stukje code aan te roepen, geassocieerd met elk patroon, om de echte instructies te genereren uit de instructieset van het doel, met behulp van de laatste registers, offsets en adressen die tijdens de herlaadfase zijn gekozen. Het Assembly-generation fragment kan slechts een string zijn, in welk geval een eenvoudige string vervanging van de registers, offsets, en/of adressen in de string wordt uitgevoerd., De assembly-generation fragment kan ook een kort Blok C code, het uitvoeren van wat extra werk, maar uiteindelijk het retourneren van een string met de geldige assembly code.
andere featuresEdit
sommige features van GCC zijn:
- Link-time optimization optimaliseert over de grenzen van objectbestanden om het gekoppelde binaire bestand direct te verbeteren. Link-time optimalisatie is gebaseerd op een tussenbestand dat de serialisatie bevat van sommige Gimple-representaties die in het objectbestand zijn opgenomen. Het bestand wordt gegenereerd naast het objectbestand tijdens het compileren van de broncode., Elke broncompilatie genereert een afzonderlijk objectbestand en een link-time helper-bestand. Wanneer de objectbestanden worden gekoppeld, wordt de compiler opnieuw uitgevoerd en gebruikt hij de hulpbestanden om de code te optimaliseren over de afzonderlijk gecompileerde objectbestanden.
- Plugins kunnen de GCC compiler direct uitbreiden. Plugins kunnen een voorraad compiler worden aangepast aan specifieke behoeften door externe code geladen als plugins. Plug-ins kunnen bijvoorbeeld middle-end passes toevoegen, vervangen of zelfs verwijderen die werken op Gimple-representaties., Verschillende GCC plugins zijn al gepubliceerd, met name de GCC Python plugin, die linkt naar libpython, en het mogelijk maakt om willekeurige Python scripts aan te roepen vanuit de compiler. Het doel is om GCC plugins te schrijven in Python. De MELT plugin biedt een high-level Lisp-achtige taal om GCC uit te breiden.
- C++ transactioneel geheugen bij het compileren met-fgnu-tm.
- vanaf GCC 10 staan identifiers de UTF-8 (Unicode)-codering toe, d.w.z. C-broncode gebruikt standaard UTF-8-codering.