Colecția GNU Compiler - GNU Compiler Collection

Colecția GNU Compiler
GNU Compiler Collection logo.svg
GCC 10.2 GNU Compiler Collection self-compilation.png
Captură de ecran a GCC 10.2 compilând propriul cod sursă
Autori originali Richard Stallman
Dezvoltatori Proiectul GNU
Eliberarea inițială 23 mai 1987 ; Acum 34 de ani ( 23.05.1987 )
Versiune stabila
11.2 / 27 iulie 2021 ; 2 luni în urmă ( 27.07.2021 )
Previzualizare lansare
11.2.0-RC / 21 iulie 2021 ; 2 luni în urmă ( 2021-07-21 )
Repertoriu
Scris in C , C ++
Sistem de operare Cross-platform
Platformă GNU și multe altele
mărimea ~ 15 milioane LOC
Tip Compilator
Licență GPLv3 + cu excepția GCC Runtime Library
Site-ul web gcc .gnu .org

Colecția de compilatoare GNU ( CCG ) este un compilator de optimizare produs de Proiectul GNU sprijinind diverse limbaje de programare , arhitecturi hardware și sisteme de operare . Free Software Foundation (FSF) distribuie CCG ca software - ul gratuit sub GNU General Public License (GNU GPL). GCC este o componentă cheie a lanțului de instrumente GNU și compilatorul standard pentru majoritatea proiectelor legate de GNU și kernel-ul Linux . Cu aproximativ 15 milioane de linii de cod în 2019, GCC este unul dintre cele mai mari programe gratuite existente. A jucat un rol important în dezvoltarea software-ului gratuit , atât ca instrument, cât și ca exemplu.

Când a fost lansat pentru prima dată în 1987 de către Richard Stallman , GCC 1.0 a fost numit GNU compilatorul C , deoarece manipulate numai limbajul de programare C . A fost extins pentru a compila C ++ în decembrie a acelui an. Front-end-urile au fost dezvoltate ulterior pentru Objective-C , Objective-C ++ , Fortran , Ada , D și Go , printre altele. Specificațiile OpenMP și OpenACC sunt, de asemenea, acceptate în compilatoarele C și C ++.

GCC a fost portat pe mai multe platforme și arhitecturi de seturi de instrucțiuni decât orice alt compilator și este utilizat pe scară largă ca instrument în dezvoltarea atât a software-ului gratuit, cât și al celui proprietar . GCC este, de asemenea, disponibil pentru multe sisteme încorporate , inclusiv cipuri bazate pe ARM și cipuri Power ISA .

Pe lângă faptul că compilatorul oficial al sistemului de operare GNU , GCC a fost adoptat ca standard de compilatorul de multe alte moderne Unix calculator sisteme de operare , inclusiv cele mai multe Linux distribuții. Majoritatea sistemelor de operare ale familiei BSD au trecut, de asemenea, la GCC la scurt timp după lansare, deși de atunci, FreeBSD , OpenBSD și Apple macOS s-au mutat în compilatorul Clang , în mare parte din motive de licențiere. GCC poate compila și coduri pentru Windows , Android , iOS , Solaris , HP-UX , AIX și DOS.

Istorie

La sfârșitul anului 1983, într - un efort de a bootstrap GNU sistemul de operare, Richard Stallman întrebat Andrew S. Tanenbaum , autorul compilatoare Kit Amsterdam ( de asemenea , cunoscut sub numele de Universitatea Liberă Compiler Kit ) pentru permisiunea de a utiliza ca software - ul pentru GNU. Când Tanenbaum l-a sfătuit că compilatorul nu era gratuit și că doar universitatea era liberă, Stallman a decis să lucreze la un alt compilator. Planul său inițial era de a rescrie un compilator existent din Laboratorul Național Lawrence Livermore de la Pastel la C, cu un ajutor de la Len Tower și alții. Stallman a scris un nou front end C pentru compilatorul Livermore, dar apoi și-a dat seama că necesită megabyți de spațiu de stivă, o imposibilitate pe un sistem Unix 68000 cu doar 64 KB și a concluzionat că va trebui să scrie un nou compilator de la zero. Niciunul dintre codurile de compilare Pastel nu a ajuns în GCC, deși Stallman a folosit front-end-ul C scris de el.

GCC a fost lansat pentru prima dată pe 22 martie 1987, disponibil de FTP de la MIT . Stallman a fost listat ca autor, dar i-a citat pe alții pentru contribuțiile lor, inclusiv Jack Davidson și Christopher Fraser pentru ideea de a folosi RTL ca limbă intermediară, Paul Rubin pentru scrierea majorității preprocesatorului și Leonard Tower pentru „părți ale parserului, RTL generatorul, definițiile RTL și descrierea mașinii Vax. " Descris drept „primul software liber accesat” de Peter H. Salus , compilatorul GNU a sosit chiar în momentul în care Sun Microsystems își separă instrumentele de dezvoltare din sistemul său de operare , vândându-le separat la un preț combinat mai mare decât pachetul anterior, care a determinat mulți dintre utilizatorii Sun să cumpere sau să descarce GCC în locul instrumentelor furnizorului. În timp ce Stallman a considerat GNU Emacs drept principalul său proiect, până în 1990, GCC a sprijinit treisprezece arhitecturi de calculatoare, a depășit mai mulți compilatori de furnizori și a fost utilizat comercial de mai multe companii.

Furcă EGCS

Întrucât GCC a fost licențiat conform GPL, programatorii care doresc să lucreze în alte direcții - în special acele interfețe de scriere pentru alte limbi decât C - au fost liberi să-și dezvolte propria furcă a compilatorului, cu condiția să îndeplinească termenii GPL, inclusiv cerințele sale de distribuire a sursei. cod . Cu toate acestea, furcile multiple s-au dovedit ineficiente și dificile, iar dificultatea de a accepta munca prin proiectul oficial GCC a fost foarte frustrantă pentru mulți, deoarece proiectul a favorizat stabilitatea față de noile caracteristici. FSF a păstrat un control atât de strâns asupra a ceea ce a fost adăugat la versiunea oficială a GCC 2.x (dezvoltată din 1992) încât GCC a fost folosit ca un exemplu al modelului de dezvoltare „catedrală” în eseul lui Eric S. Raymond The Cathedral and the Bazar .

În 1997, un grup de dezvoltatori au format Sistemul de compilare GNU Experimental / Enhanced (EGCS) pentru a îmbina mai multe furci experimentale într-un singur proiect. Baza fuziunii a fost un instantaneu de dezvoltare al GCC (realizat în jurul versiunii 2.7.2 și ulterior urmat până la versiunea 2.8.1). Fuziunile au inclus g77 (Fortran), PGCC ( P5 Pentium- GCC optimizat), multe îmbunătățiri C ++ și multe noi arhitecturi și variante ale sistemului de operare .

În timp ce ambele proiecte s-au urmărit îndeaproape schimbările reciproce, dezvoltarea EGCS s-a dovedit considerabil mai viguroasă, atât de mult încât FSF a oprit oficial dezvoltarea compilatorului GCC 2.x, a binecuvântat EGCS ca versiunea oficială a GCC și a numit proiectul EGCS drept GCC în luna aprilie 1999. Odată cu lansarea GCC 2,95 în iulie 1999, cele două proiecte au fost din nou unite. GCC a fost întreținut de atunci de un grup variat de programatori din întreaga lume sub conducerea unui comitet director.

GCC 3 (2002) a eliminat un front-end pentru CHILL din cauza lipsei de întreținere.

Înainte de versiunea 4.0, front-end-ul Fortran era g77, care suporta doar FORTRAN 77 , dar mai târziu a fost abandonat în favoarea noului front-end GNU Fortran care acceptă Fortran 95 și părți mari ale Fortran 2003 și Fortran 2008 .

Începând cu versiunea 4.8, GCC este implementat în C ++.

Suportul pentru Cilk Plus a existat de la GCC 5 până la GCC 7.

GCC a fost portat la o mare varietate de arhitecturi de seturi de instrucțiuni și este utilizat pe scară largă ca instrument în dezvoltarea software-ului gratuit și proprietar . GCC este, de asemenea, disponibil pentru multe sisteme încorporate , inclusiv cipurile Symbian (numite gcce ), ARM și Power ISA . Compilatorul poate viza o mare varietate de platforme, inclusiv console de jocuri video, cum ar fi PlayStation 2 , Cell SPE din PlayStation 3 și Dreamcast . A fost portat la mai multe tipuri de procesoare și sisteme de operare decât orice alt compilator.

Limbi acceptate

Începând cu mai 2021, lansarea recentă 11.1 a GCC include frontend-uri pentru limbaje de programare C ( gcc), C ++ ( g++), Objective-C , Fortran ( gfortran), Ada ( GNAT ), Go ( gccgo) și D ( gdc, de la 9.1), cu a OpenMP și OpenACC extensiile lingvistice paralele fiind susținute , deoarece GCC 5.1. Versiunile anterioare GCC 7 au acceptat și Java ( gcj), permițând compilarea Java în codul mașinii native.

În ceea ce privește suportul pentru versiunea lingvistică pentru C ++ și C, din moment ce GCC 11.1 ținta implicită este gnu ++ 17 , un superset de C ++ 17 , și gnu11 , un superset de C11 , cu suport standard strict, de asemenea, disponibil. GCC oferă, de asemenea, suport experimental pentru C ++ 20 și C ++ 23 viitoare .

Front-end-urile terților există pentru multe limbi, cum ar fi Pascal ( gpc), Modula-2 , Modula-3 , PL / I și VHDL ( GHDL). Există câteva ramuri experimentale care acceptă limbaje suplimentare, cum ar fi compilatorul GCC UPC pentru Unified Parallel C sau Rust .

Proiecta

Prezentare generală a conductei extinse de compilare GCC, inclusiv programe specializate precum preprocesorul , asamblatorul și linkerul .
GCC urmărește arhitectura în 3 etape tipică pentru compilatoarele multi-limbă și multi-CPU . Toți arborii programului sunt convertiți într-o reprezentare abstractă comună la „capătul mijlociu”, permițând optimizarea codului și facilitățile de generare a codului binar să fie partajate de toate limbile.

Interfața externă a GCC respectă convențiile Unix . Utilizatorii invocă un program de driver specific limbajului ( gccpentru C, g++pentru C ++ etc.), care interpretează argumentele de comandă , apelează compilatorul real, rulează asamblorul pe ieșire și apoi opțional rulează linkerul pentru a produce un binar executabil complet .

Fiecare dintre compilatoarele de limbă este un program separat care citește codul sursă și scoate codul mașinii . Toate au o structură internă comună. Un front-end per limbă analizează codul sursă în limba respectivă și produce un arbore de sintaxă abstract („copac” pe scurt).

Acestea sunt, dacă este necesar, convertite la reprezentarea de intrare a capătului de mijloc, numită formă GENERICĂ ; capătul de mijloc transformă apoi treptat programul în forma sa finală. Optimizările compilatorului și tehnicile de analiză statică a codului (cum ar fi FORTIFY_SOURCE, o directivă a compilatorului care încearcă să descopere unele depășiri de tampon ) sunt aplicate codului. Acestea funcționează pe reprezentări multiple, în principal reprezentarea GIMPLE independentă de arhitectură și reprezentarea RTL dependentă de arhitectură . În cele din urmă, codul mașinii este produs utilizând potrivirea tiparelor specifice arhitecturii bazată inițial pe un algoritm al lui Jack Davidson și Chris Fraser.

GCC a fost scris în principal în C, cu excepția unor părți ale frontului Ada . Distribuția include bibliotecile standard pentru Ada și C ++ al căror cod este în mare parte scris în aceste limbi. Pe unele platforme, distribuția include, de asemenea, o bibliotecă de execuție de nivel scăzut, libgcc , scrisă într-o combinație de C independent de mașină și cod de mașină specific procesorului , proiectat în principal pentru a gestiona operațiuni aritmetice pe care procesorul țintă nu le poate efectua direct.

GCC folosește multe instrumente standard în construcția sa, inclusiv Perl , Flex , Bison și alte instrumente comune. În plus, necesită în prezent să fie prezente trei biblioteci suplimentare pentru a construi: GMP , MPC și MPFR .

În mai 2010, comitetul de conducere GCC a decis să permită utilizarea unui compilator C ++ pentru a compila GCC. Compilatorul a fost destinat să fie scris mai ales în C plus un subset de caracteristici din C ++. În special, acest lucru a fost decis astfel încât dezvoltatorii GCC să poată utiliza caracteristicile generatoare și destructoare ale C ++.

În august 2012, comitetul director al GCC a anunțat că GCC folosește acum C ++ ca limbaj de implementare. Aceasta înseamnă că pentru a construi GCC din surse, este necesar un compilator C ++ care să înțeleagă standardul ISO / IEC C ++ 03 .

La 18 mai 2020, GCC s-a îndepărtat de standardul ISO / IEC C ++ 03 la standardul ISO / IEC C ++ 11 (adică este necesar pentru a compila, bootstrap, compilatorul în sine; implicit, totuși compilează versiuni ulterioare ale C ++).

Capete frontale

Front-end-urile constau în preprocesare , analiză lexicală , analiză sintactică (analiză) și analiză semantică. Obiectivele front-end-urilor compilatorului sunt fie să accepte, fie să respingă programele candidate în funcție de gramatica limbii și semantica, să identifice erori și să gestioneze reprezentări valide ale programelor pentru etapele ulterioare ale compilatorului. Acest exemplu arată etapele Lexer și parser efectuate pentru un program simplu scris în C .

Fiecare front end folosește un parser pentru a produce arborele de sintaxă abstract al unui fișier sursă dat . Datorită abstractizării arborelui de sintaxă, fișierele sursă ale oricăreia dintre diferitele limbi acceptate pot fi procesate de același back-end . GCC a început să folosească analizoare LALR generate cu Bison , dar a trecut treptat la analizoare de descriere recursivă scrise de mână pentru C ++ în 2004 și pentru C și Obiectiv-C în 2006. Începând cu 2021 toate capetele frontale folosesc analizoare de recursivitate descendentă scrise de mână. .

Până la GCC 4.0 reprezentarea arborescentă a programului nu era pe deplin independentă de procesorul vizat. Semnificația unui copac a fost oarecum diferită pentru diferite fronturi lingvistice, iar capetele frontale ar putea furniza propriile coduri de arbore. Acest lucru a fost simplificat cu introducerea GENERIC și GIMPLE, două noi forme de arbori independenți de limbă, care au fost introduse odată cu apariția GCC 4.0. GENERIC este mai complex, bazat pe reprezentarea intermediară GCC 3.x Java front end. GIMPLE este un GENERIC simplificat, în care diverse construcții sunt reduse la mai multe instrucțiuni GIMPLE. La C , C ++ și Java capetele frontale produc GENERIC direct în capătul din față. Alte front-end-uri au în schimb diferite reprezentări intermediare după analiză și le convertesc în GENERIC.

În ambele cazuri, așa-numitul „gimplifier” transformă apoi această formă mai complexă într-o formă GIMPLE mai simplă bazată pe SSA , care este limbajul comun pentru un număr mare de optimizări globale independente de limbă și arhitectură (sfera funcției).

GENERIC și GIMPLE

GENERIC este un limbaj de reprezentare intermediar folosit ca „capăt de mijloc” în timp ce compilează codul sursă în binare executabile . Un subset, numit GIMPLE , este vizat de toate capetele frontale ale GCC.

Etapa de mijloc a GCC face toată analiza și optimizarea codului , funcționând independent atât de limbajul compilat, cât și de arhitectura țintă, începând de la reprezentarea GENERIC și extinzându-l pentru a înregistra limbajul de transfer (RTL). Reprezentarea GENERICĂ conține doar subsetul constructelor de programare imperative optimizate de capătul mijlociu.

În transformarea codului sursă în GIMPLE, expresiile complexe sunt împărțite într-un cod cu trei adrese folosind variabile temporare . Această reprezentare a fost inspirată de simplă reprezentare propusă în compilatorul McCAT de Laurie J. Hendren pentru simplificarea analiza și optimizarea de programe imperative .

Optimizare

Optimizarea poate avea loc în timpul oricărei faze de compilare; cu toate acestea, cea mai mare parte a optimizărilor se efectuează după sintaxă și analiza semantică a front-end-ului și înainte de generarea de cod a back-end-ului; astfel, un nume comun, deși oarecum contradictoriu, pentru această parte a compilatorului este „capătul mijlociu”.

Setul exact al optimizărilor CCG variază de la lansare pentru a elibera cum se dezvolta, dar include algoritmi standard, cum ar fi optimizarea bucla , filetare salt , eliminare comună subexpression , programarea de instruire , și așa mai departe. De RTL optimizările sunt mai puțin importante , cu adăugarea la nivel mondial optimizări SSA bazate pe GIMPLE copaci, ca optimizări RTL au un domeniu de aplicare mult mai limitat, și au mai puține informații la nivel înalt.

Unele dintre aceste optimizări efectuate la acest nivel includ eliminarea codului mort , eliminarea parțială a redundanței , numerotarea valorii globale , propagarea constantă condițională rară și înlocuirea scalară a agregatelor . De asemenea, sunt realizate optimizări bazate pe dependență de matrice, cum ar fi vectorizarea automată și paralelizarea automată . Este posibilă și optimizarea ghidată de profil .

Back end

Back-end-ul GCC este parțial specificat de macro-urile preprocesorului și funcțiile specifice unei arhitecturi țintă, de exemplu pentru a defini endianitatea , dimensiunea cuvântului și convențiile de apelare . Partea frontală a back-end-ului le folosește pentru a decide generarea RTL, deci, deși RTL-ul GCC este nominal independent de procesor, secvența inițială de instrucțiuni abstracte este deja adaptată țintei. În orice moment, instrucțiunile RTL efective care formează reprezentarea programului trebuie să respecte descrierea mașinii a arhitecturii țintă.

Fișierul de descriere a mașinii conține modele RTL, împreună cu constrângeri de operand și fragmente de cod pentru a genera asamblarea finală. Constrângerile indică faptul că un anumit model RTL s-ar putea aplica (de exemplu) numai anumitor registre hardware sau (de exemplu) să permită compensări imediate de operandi de o dimensiune limitată (de exemplu, 12, 16, 24, ... compensări de biți etc.) ). În timpul generării RTL, constrângerile pentru arhitectura țintă dată sunt verificate. Pentru a emite un anumit fragment de RTL, acesta trebuie să se potrivească cu unul (sau mai multe) dintre modelele RTL din fișierul de descriere al mașinii și să satisfacă constrângerile pentru acel model; în caz contrar, ar fi imposibil să se convertească RTL-ul final în codul mașinii.

Spre sfârșitul compilării, RTL valabil este redus la o formă strictă în care fiecare instrucțiune se referă la registre reale ale mașinii și la un model din fișierul de descriere a mașinii țintă. Formarea RTL strictă este o sarcină complicată; un pas important este alocarea registrelor , unde sunt alese registre hardware reale pentru a înlocui pseudo-registrele atribuite inițial. Aceasta este urmată de o fază de „reîncărcare”; orice pseudo-registre cărora nu li s-a atribuit un registru hardware real sunt „vărsate” în stivă și se generează RTL pentru a efectua această deversare. În mod similar, compensările care sunt prea mari pentru a se potrivi într-o instrucțiune reală trebuie să fie despărțite și înlocuite cu secvențe RTL care vor respecta constrângerile de compensare.

În faza finală, codul mașinii este construit apelând un mic fragment de cod, asociat fiecărui model, pentru a genera instrucțiunile reale din setul de instrucțiuni al țintei , utilizând registrele finale, compensările și adresele alese în timpul fazei de reîncărcare. Fragmentul de generare a ansamblului poate fi doar un șir, caz în care se efectuează o simplă înlocuire a șirului de registre, decalaje și / sau adrese în șir. Fragmentul de generare a ansamblului poate fi, de asemenea, un bloc scurt de cod C, efectuând unele lucrări suplimentare, dar în cele din urmă returnând un șir care conține codul de asamblare valid.

Biblioteca standard C ++ (libstdc ++)

Proiectul GCC include o implementare a bibliotecii standard C ++ numită libstdc ++, licențiată sub licența GPLv3 cu o excepție pentru a lega aplicația sursă închisă atunci când sursele sunt construite cu GCC. Versiunea actuală este 11.

Alte caracteristici

Unele caracteristici ale GCC includ:

Optimizare link-time
Optimizarea timpului de legătură optimizează peste limitele fișierelor obiect pentru a îmbunătăți direct binele legate. Optimizarea timpului de legătură se bazează pe un fișier intermediar care conține serializarea unor reprezentări Gimple incluse în fișierul obiect. Fișierul este generat alături de fișierul obiect în timpul compilării sursă. Fiecare compilație sursă generează un fișier obiect separat și un fișier de asistență în timp de legătură. Când fișierele obiect sunt legate, compilatorul este executat din nou și folosește fișierele de ajutor pentru a optimiza codul între fișierele obiect compilate separat.
Pluginuri
Pluginurile extind direct compilatorul GCC. Pluginurile permit ca un compilator de stocuri să fie adaptat nevoilor specifice prin codul extern încărcat ca pluginuri. De exemplu, plugin-urile pot adăuga, înlocui sau chiar elimina treceri de capăt mijlociu care funcționează pe reprezentări Gimple . Au fost deja publicate mai multe pluginuri GCC, în special:
  • Plugin-ul Python, care se leagă de libpython și permite invocarea scripturilor Python arbitrare din interiorul compilatorului. Scopul este de a permite pluginurilor GCC să fie scrise în Python.
  • Pluginul MELT oferă un limbaj de tip Lisp la nivel înalt pentru a extinde GCC.
Suportul pluginurilor a fost odată o problemă controversată în 2007.
Memorie tranzacțională C ++
Limbajul C ++ are o propunere activă pentru memoria tranzacțională. Poate fi activat în GCC 6 și mai nou atunci când compilați cu -fgnu-tm.
Identificatori Unicode
Deși limbajul C ++ necesită suport pentru caracterele Unicode non-ASCII din identificatori , caracteristica a fost acceptată doar de la GCC 10. Ca și în cazul gestionării existente a literelor șirului, se presupune că fișierul sursă este codificat în UTF-8 . Funcția este opțională în C, dar a fost disponibilă și de la această modificare.

Arhitecturi

GCC compilează Hello World pe Windows

Familiile de procesoare țintă GCC începând cu versiunea 11.1 includ:

Procesoarele țintă mai puțin cunoscute acceptate în versiunea standard au inclus:

Procesoarele suplimentare au fost acceptate de versiunile GCC menținute separat de versiunea FSF:

GCJ compilatorul Java poate viza fie o arhitectură nativ limbaj mașină sau mașină virtuală Java e Java bytecode . Când retargeting GCC pe o nouă platformă, bootstrapping este adesea folosit. Motorola 68000, Zilog Z80 și alte procesoare sunt, de asemenea, vizate în versiunile GCC dezvoltate pentru diverse calculatoare grafice programabile Texas Instruments, Hewlett Packard, Sharp și Casio.

Licență

GCC este licențiat sub licența GNU General Public License versiunea 3. Excepția Runtime GCC permite compilarea de programe proprietare (în plus față de software-ul gratuit) cu GCC. Acest lucru nu afectează termenii licenței codului sursă GCC.

Vezi si

Referințe

Lecturi suplimentare

linkuri externe

Oficial

Alte