Clinfowiki

Kompilator ili programski prevodilac (engl. Compiler; fonetski: kompajler) je računarski program (ili niz programa) koji transformiše kod jednog programskog jezika u drugi programski jezik. Kod koji se prevodi obično se zove izvorni kod, a kod dobijen transformacijom mašinski kod.

Dijagram operacija tipičnog višejezičnog, višeciljnog kompajlera.

Najčešći razlog za prevođenje izvornog koda jeste pravljenje izvršnog programa(engl. Executable). Ime kompajler se koristi za programe koji prevode izvorni kod sa višeg programskog jezika na jezik nižeg nivoa (npr. asemblerski jezik, mašinski jezik). Program koji prevodi sa nižeg na viši programski jezik je dekompilator. Program koji prevodi sa jednog višeg programskog jezika na drugi se obično zove prevodilac jezika, prevodilac izvora u izvor, ili konvertor jezika. Preinačilac jezika je obično program koji prevodi formu izraza bez promene jezika.

Prevođenje programa često se sastoji iz više faza. To mogu da budu neki od sledećih procesa: leksička analiza, preprocesiranje, parsiranje, semantička analiza, generisanje koda i optimizacija koda.

Proces kompilacije se obično obično odvija kroz dve glavne etape:

  • analizu izvornog koda i
  • sintezu objektnog koda.

U etapi analize, izvorni program se prevodi u određenu posrednu reprezentaciju, koja je pogodna za dalje manipulisanje. U etapi sinteze se iz posredne reprezentacije dobija objektni kod.

Sa svoje strane, etapa analize se deli u tri faze:

  • leksičku analizu
  • sintaksičku analizu
  • semantičku analizu

Istorija

Za starije generacije računara, softver je godinama pisan asemblerskim jezikom. Viši programski jezici nisu pronađeni sve dok prednosti mogućnosti ponovnog korišćenja softvera na raznim vrstama CPU nisu postale značajno veće od cene pisanja kompilatora. Veoma ograničen kapacitet memorija starijih računara je takođe stvarao puno tehničkih problema pri implementaciji kompilatora.

Prvi viši programski jezik (Plankalkül) je predlozen od strane Konrada Zusa,1943. Krajem 50-ih godina prošlog veka, predloženi su mašinski nezavisni programski jezici. Prema tom predlogu, razvijeno je nekoliko eksperimentalnih kompilatora. Grejs Hoper je 1952. napisala prvi kompilator za A-0 programski jezik. Fortranov tim koji je vodio Džon Bekus iz IBM-a je generalno akreditovan sa predstavljanjem prvog kompletnog kompilatora 1957. godine. Kobol je bio rani jezik koji je kompiliran na višestrukim arhitekturama 1960. godine.

U mnogim aplikacionim domenima, ideja korišćenja viših programskih jezika je brzo prihvaćena. Zbog širenja funkcionalnosti praćenih novijim programskim jezicima i povećavanja kompleksnosti računarskih arhitektura, kompilatori su postajali sve složeniji.

Stariji računari su pisani asemblerskim jezikom. Prvi samo-domaći kompilator – sposoban da kompilira sopstveni kod u viši programski jezik- kreirali su Hart i Levin za Lisp na MIT-u 1962. godine. Još od 1970ih, implementacija kompilatora u jeziku kojeg kompilira je postala uobicajena praksa, iako su Paskal i C bili popularni izbori za implementaciju jezika. Pravljenje samo-domaćeg kompilatora je problem butstrapovanja – prvi takav kompilator za jezik mora biti kompiliran ili kompajlerom pisanim različitim jezikom ili (kao u Hart i Levinovom kompajleru za Lisp) ili pokretanjem kompilatora u interpreteru.

Kompilator u obrazovanju

Konstrukcija kompilatora i optimizacija kompilatora se uči na univerzitetima kao deo nastavnog plana računarske nauke. Takvi kursevi su obično dopunjeni implementacijom kompilatora za obrazovni programski jezik. Dobro dokumentovan primer je PL/0 kompilator Niklaus Virta , koji je Virt koristio da bi podučavao konstrukciji kompilatora u 1970im. Uprkos svojoj jednostavnosti, PL/0 kompilator je imao nekoliko koncepata:

  1. Razvoj programa postepenim usavršavanjem
  2. Upotreba rekurzivnog descentnog parsera
  3. Upotreba EBNFa da bi se opisala sintaksa jezika
  4. Generator koda koji proizvodi portabilni P kod
  5. Upotreba T-dijagrama u formalnom opisivanju problema butstrapovanja

Izlaz kompilatora

Jedan metod za klasifikaciju kompilatora je preko platforme na kojoj se izvršava generisani kod koji oni proizvode. Poznata je kao ciljna platforma.

Prirodni ili domaći kompilator je onaj čiji je izlaz namenjen da se direktno pokreće na istom tipu računara i operativnog sistema na kojima se pokreće i kompilatora. Izlaz ukrštenog kompilatora je dizajniran da se pokreće na drugačijoj platformi. Ukršteni kompilatori se često koriste kada se razvija softver za ukopane sisteme koji nisu planirani za podršku softverskog razvojnog okruženja.

Izlaz kompilatora koji proizvodi kod za virtualnu mašinu (VM) može ili ne može biti izvršavan na istoj platformi kao kompilator koji ga je proizveo. Zato takvi kompilatori često nisu klasifikovani kao prirodni ili ukršteni kompilatori.

Kompilirani nasuprot interpretiranih jezika

Viši programski jezici su, pogodnosti radi, podeljeni na kompilirane jezike i interpretirane jezike. Ipak, retko postoji nešto u vezi jezika što zahteva da bude isključivo kompiliran ili isključivo interpretiran. Kategorizacija često odražava najpopularnije ili najrasprostranjenije implementacije jezika – npr. BEJZIK je interpretiran jezik i C je kompilirani, uprkos postojanju BEJZIK kompilatora i C interpretera.

Svi programski jezici mogu da se posmatraju kao interpretirani. Izvršavanje mašinskog koda bio bi samo specijalan slučaj interpretacije koju izvodi hardver centralne procesorske jedinice. Moderni trendovi kao što su pravovremena kompilacija i interpretacija bajtkoda takođe narušavaju tradicionalnu kategorizaciju.

Postoje izuzeci. Neke specifikacije jezika nagoveštavaju da implementacije moraju uključivati laku kompilaciju, npr. Komon Lisp. Ostali jezici imaju osobinu da se veoma lako implementiraju u interpreteru, ali je pisanje kompilatora znatno teže, npr. APL, SNOBOL4, i mnogi pisani jezici dozvoljavaju programima da konstruišu proizvoljan izvorni kod na početku sa operacijama regularnih stringova, i onda da izvršavaju taj kod dodajući ga specijalnoj funkciji procene. Da bi implementirali ove osobine u kompiliranim jezicima, programi obično moraju biti smešteni u početnoj biblioteci koja uključuje verziju samog kompilatora.

Kompilacija hardvera

Izlazi nekih kompajlera mogu koristiti hardver na veoma niskom nivou. Npr. Nizovi prolaza programibilnih polja (NPPP) ili struktruralno Aplikativno specifično integrativno kolo (ASIK). Za takve kompilatore se kaže da su kompilatori hardvera ili alati sinteze jer programi koje oni kompiliraju efikasno kontrolišu finalnu konfiguraciju hardvera i način njihovog delovanja; izlaz kompilacije nisu instrukcije koje su izvršene u sekvenci – samo veza tranzistora ili lookup tabele. Na primer, XST je Xilinx Synthesis Tool koji se koristi za konfigurisanje FGPA. Slični alati su dostupni iz Altera-e, Synplicity-ja, Synopsys-a i drugih prodavaca.

Dizajn kompilatora

Pristup za dizajniranje kompilatora je uzrokovan kompleksnošću potrebnog procesiranja, iskustvom osobe(a) koje ga dizajniraju i resursima koji su na raspolaganju (npr. ljudi i alat).

Kompilator za relativno jednostavan jezik koji je napisala jedna osoba, može biti jedno parče softvera. Kada je izvorni jezik veliki i složen i kada je zahtevan visoko-kvalitetan izlaz, dizajn može biti podeljen u broj relativno nezavisnih faza ili prolaza. Imati odvojene faze znači da razvoj može biti podeljen na male delove i dat različitim ljudima. Tada je takođe puno lakše zameniti jednu fazu poboljšanom ili ubaciti nove faze kasnije (npr. dodatne optimizacije).

Ideja o podeli procesa kompilacije na faze (ili prolaze) je pobedila zahvaljujući Projektu produkcije kvaliteta kompilatora na univerzitetu Karnegi Melon. Ovaj projekat je uveo termine front end, middle end (danas retko korišćen) i back end.

Svi osim najmanjih kompilatora imaju više od dve faze. Ipak, ove faze su obično posmatrane kao deo front end-a ili back end-a. Tačka na kojoj se ova dva kraja susreću je uvek diskutabilna. front end je generalno razmatran da bude tamo gde se obavljaju sintaksičko i semantičko procesiranje zajedno sa pomeranjem u niži nivo reprezentacije (nego izvorni kod).

Middle end je obično dizajniran da izvede optimizaciju na formi različitoj od izvornog ili mašinskog koda. Ova nezavisnost izvornog (mašinskog) koda ima za cilj da omogući da generičke optimizacije budu podeljene između verzija kompilatora podržavajući razne jezike i ciljne procesore.

Back end uzima izlaz iz sredine. Može da izvodi više analiza, transformacija i optimizacija koje su za neki poseban računar. Potom, generiše kod za poseban procesor i OS.

Ovaj front-end/middle/back-end pristup daje mogućnost kombinovanja front end-ova za različite jezike sa back end-ovima za različite centralne procesorske jedinice. Praktični primeri ovog pristupa su GNU kompajler kolekcija, LLVM i Amsterdam kompilator alat koji imaju višestruke front end-ove, podeljene analize i višestruke back end-ove.

Jednoprolazni nasuprot višeprolaznog kompilatora

Klasifikacija kompilatora prema broju prolaza ima svoju pozadinu u ograničenju hardverskih resursa računara. Kompilacija uključuje puno posla i stariji računari nisu imali dovoljno memorije da bi sadržali program koji bi radio sav pomenuti posao. Zato su kompilatori bili podeljeni na manje programe od kojih je svaki imao prolaz preko izvora (ili neke njegove reprezentacije) izvodeći neke od zahtevanih analiza i translacija.

Sposobnost kompilacije u jednom prolazu je često viđena kao prednost jer pojednostavljuje posao pisanja kompilatora i jednoprolazni kompilatori su generalno brži od višeprolaznih kompilatora. Mnogi jezici su dizajnirani tako da mogu biti kompilirani u jednom prolazu (npr. Paskal).

U nekim slučajevima dizajniranje osobine jezika može zahtevati da kompilator izvede više od jednog prolaza preko izvora. Npr. uzimajući u obzir pojavljivanje deklaracije u 20. liniji izvora što utiče na pomeranje pojavljivanja iskaza u liniji 10. U ovom slučaju, prvi prolaz treba da sakupi informacije o pojavljivanjima deklaracija posle iskaza na koje utiču sa pomeranjem događaja za vreme sledećeg prolaza.

Nedostatak kompilacije u jednom prolazu je što ona ne može da omogući mnoge od dobrih optimizacija potrebnih za generisanje visoko – kvalitetnog koda. Može biti teško da se prebroji koliko tačno faza optimizirajući kompilator pravi. Npr. različite faze optimizacije mogu analizirati jedan izraz više puta ali samo jednom neki drugi izraz.

Podela kompilatora na manje programe je tehnika koju koriste istraživači zainteresovani za stvaranje korektnih kompilatora. Dokazivanje korektnosti grupe malih programa često zahteva manje napora nego dokazivanje korektnosti jednog većeg, ekvivalentnog programa.

Dok tipični višeprolazni kompilator izbacuje mašinski kod iz svog poslednjeg prolaza, postoji nekoliko drugih tipova:

  • Izvor – u – izvor kompilator je tip kompilatora koji koristi jezik visokog nivoa kao ulaz, a izlaz je takođe jezik visokog nivoa. Npr. automatski paralelizujući kompilator će učestalo uzimati programski jezik visokog nivoa kao ulaz i onda transformisati kod i pribeležiti ga sa paralelnim kodom (npr. OpenMP) ili konstrukcije jezika (npr. Fortranovi DOALL iskazi).
  • Fazni kompilator koji kompilira u asemblerski jezik teorijske mašine kao neke Prolog implementacije
  • Pravovremeni kompilator koji koriste Smalltalk i Java sistemi, i takođe Microsoft .Net - ov Običan intermedijalni jezik (CIL)
    • Aplikacije se isporučiju u bajtkodu koji je kompiliran u prirodni mašinski kod baš pre izvršenja.

Front end

Front end analizira izvorni kod da bi napravio unutrašnju reprezentaciju programa koja se zove intermedijalna reprezentacija ili IR. On takođe uređuje tabelu simbola, strukturu podataka koja mapira svaki simbol u izvornom kodu u povezane informacije kao što su lokacija, tip i cilj. Ovo je urađeno u nekoliko faza u koje se uključuju neke od sledećih:

  1. Rekonstrukcija linije. Jezici koji skupljaju svoje ključne reči ili dozvoljavaju neograničen broj belina unutar identifikatora zahtevaju fazu pre parsiranja u kojoj se konvertuje ulazna sekvenca karaktera u kanonsku formu spremnu za parsiranje. analiza naniže, rekurzivni spust parseri korišćeni 1960ih su čitali izvor karakter po karakter i nisu zahtevali posebnu fazu za tokeniziranje. Atlas autokod i Imp (i neke implementacije Algola i Korala 66) su primeri skupljenih jezika čiji bi kompilatori imali fazu rekonstrukcija linije.
  2. Leksička analiza deli izvorni kod na male delove koji se zovu tokeni. Svaki token je atomična jedinica jezika, npr. ključna reč, identifikator ili ime simbola. Sintaksa tokena je tipičan regularni jezik tako da bi se konačni automat konstruisan za dati regularni izraz mogao koristiti za prepoznavanje jezika. Ova faza se takođe zove leksička ili skenirajuća, a softver koji vrši leksičku analizu zove se leksički analizator ili skaner.
  3. Preprocesiranje. Neki jezici, npr. C, zahtevaju fazu preprocesiranja koja podržava makro supstituciju i uslovnu kompilaciju. Faza preprocesiranja se odvija pre sintaksičke ili semantičke analize; npr. u slučaju jezika C, preprocesor pre upravlja leksičkim tokenima nego sintaksičkim formama. Ipak, neki jezici kao što je Šeme podržavaju makro supstitucije zasnovane na sintaksičkim formama.
  4. Sintaksička analiza obuhvata parsiranje sekvence tokena da bi identifikovao sintaksičku strukturu programa. Zadatak sintaksičke analize je da se utvrdi da li je program u skladu sa gramatičkim pravilima programskog jezika u kome je pisan. Ova faza pravi drvo parsiranja koje zamenjuje linearne sekvence tokena drvolikom strukturom nastalom prema pravilima formalne gramatike koja definiše sintaksu jezika. Drvo parsiranja je često analizirano, proširivano i tranformisano u kasnijim fazama u kompilatoru. Sintaksički analizator ili parser ovo drvo prosleđuje semantičkom analizatoru.
  5. Semantička analiza ja faza u kojoj kompajler dodaje semantičke informacije u drvo parsiranja i gradi tabelu simbola. Ova faza izvodi semantičke provere kao npr. proveru tipa (provera tipa grešaka) ili povezivanje objekata (povezivanje referenci na promenljive i funkcije sa njihovim definicijama) ili jasna dodela, odbijanje neispravnih programa ili izbacivanje upozorenja. Semantička analiza često zahteva kompletno drvo parsiranja što znači da ova faza prati fazu parsiranja i nailazi na fazu generisanja koda iako je često moguće obuhvatiti više faza jednim prolazom preko koda u implementaciji kompilatora.

Back end

Izraz back end se ponekad meša sa generatorom koda zbog preklopljene funkcionalnosti generisanja asemblerskog koda. Neke literature koriste middle end da bi razlikovale faze generičke analize i optimizacije u back end-u od mašinski zavisnih generatora koda. Glavne faze back end-a uključuju sledeće:

  1. Analiza: u ovoj fazi se skupljaju programske informacije iz intermedijalne reprezentacije izvedene iz ulaza. Tipične analize su analize prolaznih podataka za pravljenje upotrebno-definisanih lanaca, zavisnih analiza, alijas analiza, pokaznih analiza, iskejp analiza itd. Ispravna analiza je osnova za bilo koju kompjutersku optimizaciju. Graf pozivanja i graf kontrole toka se često prave za vreme faze analize.
  2. Optimizacija: reprezentacija intermedijalnog jezika je transformisana u funkcionalno ekvivalentne ali brže (ili manje) forme. Popularne optimizacije su inlajn proširenje, eliminacija mrtvog koda, množenje konstanti, transformacija petlje, alokacija registara ili čak automatska paralelizacija.
  3. Generisanje koda: transformisani intermedijalni jezik se prevodi u izlazni jezik, obično u prirodni mašinski jezik sistema. Ovo uključuje resursne i memorijske odluke, kao što su odlučivanje koje promenljive smestiti u registre i memoriju kao i biranje i vremensko planiranje odgovarajućih mašinskih instrukcija zajedno sa njihovim povezanim adresnim modovima (videti takođe Seti-Ulman algoritam).

Kompilatorska analiza je preduslov za bilo koju kompilatorsku optimizaciju i oni su tesno povezani. Npr. zavisna analiza je ključna za transformaciju petlje.

U dodatku, cilj kompilatorske analize i optimizacije se puno menja, od malog baznog bloka do proceduralnog / funkcijskog nivoa ili čak do čitavog programa (interproceduralna optimizacija). Očigledno, kompilator bi mogao obavljati bolji posao koristeći širi pogled. Ali taj širi pogled nije besplatan: velike ciljne analize i optimizacije su veoma skupe u pogledu vremena kompilacije i memorijskog prostora; ovo je naročito tačno za interproceduralne analize i optimizacije.

Postojanje interproceduralnih analiza i optimizacija je zajedničko u modernim komercijalnim kompilatorima iz HPa, IBMa, SGIa, Intela, Microsofta i Sun Microsystemsa. Otvoreni izvor GCC je kritikovan duže vremena zbog nedostatka moćne interproceduralne optimizacije, ali sve više dobija na važnosti. Još jedan dobar kompilator otvorenog izvora sa punom analizom i optimizacijom infrastrukture je Open64, koji koriste mnoge organizacije u istraživačke i komercijalne svrhe.

Zbog zahteva za dodatnim vremenom i prostorom za kompilatorske analize i optimizacije, neki kompilatori ih preskaču po default-u. Korisnici moraju koristiti opcije kompilacije da bi jasno saopštili kompilatoru koje optimizacije treba omogućiti.

Povezane tehnike

Asemblerski jezik je niži jezik, program koji ga kompilira je poznat kao asembler, čiji se inverzni program zove disasembler. Program koji prevodi niži programski jezik u viši je dekompilator. Program koji prevodi viši programski jezik u drugi, takođe viši jezik obično se naziva prevodilac jezika, prevodilac izvora u izvor, jezički konvertor ili preinačilac jezika.

Povezano

Izvori