TIE448 Kääntäjätekniikka, syksy 2009 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 27. lokakuuta 2009
Sisällys
Sisällys
Seuraava deadline Vaihe D tiistai 10.11. klo 10 välikielen generointi
Kääntäjän rakenne lähdeohjelma SELAAJA sanasjono JÄSENTÄJÄ VÄLIKOODIN GENEROIJA KOHDEKOODIN GENEROIJA rakennepuu välikoodi kohdeohjelma TARKASTAJA SOKERINPILKKOJA OPTIMOIJA OPTIMOIJA
Sisällys
Koko ja tasaus Jokaisella tietotyypillä on takapään päättämä koko (kuinka monta tavua tarvitaan yhden tietotyypin muuttujan esittämiseen) ja tasaus (engl. alignment) (eli millä jaollisesta osoitteesta tietotyypin muuttuja alkaa. Esimerkiksi SYSV ABI:ssa (mm. Linux): char int long osoitin koko tasaus koko tasaus koko tasaus koko tasaus IA32 1 1 4 4 4 4 4 4 MIPS 1 1 4 4 4 4 4 4 PowerPC 1 1 4 4 4 4 4 4 PPC64 1 1 4 4 8 8 8 8 Sparc 1 1 4 4 8 8 8 8 AMD64 1 1 4 4 8 8 8 8 Välikielen generoinnissa pitää muuttujan, tietue-kentän ym. tyypistä poimia koko ja tasaus, ja kuljettaa niitä tietoja väliesityksen mukana. Esim. mallikääntäjässä takapäällä on metodit describetype useille tyypeille, ja niitä käyttäen TypeDescriber-visitori; jokaisella välikielen muuttujalla on koko- ja tasaus-kentät.
Kun lausekkeessa esiintyy nimi, se voi viitata moneen eri asiaan: globaaliin muuttujaan paikalliseen muuttujaan globaaliin aliohjelmaan paikalliseen aliohjelmaan (jos lähdekieli tukee) luokan metodiin (luokan sisällä, jos lähdekielessä implisiittinen this) Generoitava koodi riippuu symbolitaulun sisällöstä. Yksi hyvä tapa: symbolitaulussa olevalla oliolla on metodi generateaccess, joka generoi ko. nimen arvon hakemisen.
Paikallisen muuttujan esittely Paikallisen muuttujan esitystapa riippuu takapään tekemistä valinnoista. Kannattaa siis tehdä niin, että paikallisen muuttujan esittelyssä annetaan takapään päättää. Esimerkkikääntäjässä muuttuja esitetään abstraktina operandina, josta takapää perii oman toteutuksensa. public Void visit(localvariable st){ Variable v = r.addlocalvariable(st.type.accept(new TypeDescriber(tgt))); if (st.initializer!= null) { Rand iv = st.initializer.accept(new EvalGen(tgt, p, r, symtab)); r.addtriple(op.copy, v, iv); } symtab.declare(st.name, new RandAccess(v)); return null; }
Sisällys
Taulukon esitystapa Taulukko on samantyyppisten otusten jono, jossa otukset ovat saman muistialueen sisällä peräkkäin. Moniulotteinen taulukko voidaan esittää joko muuttamalla se yksiulotteiseksi (kuten C), tai sitten taulukkoviittausten taulukkona (kuten Java). Olennaista on huolehtia, että jokaisella alkiolla on tarpeeksi tilaa (vähintään tietotyyppinsä koon verran) ja jokainen alkio alkaa tasauksella jaollisesta osoitteesta. Käytännössä siis alkion koko taulukossa on koko % tasaus!= 0? koko + (tasaus - koko % tasaus) : koko;
Taulukon indeksointi Yksiulotteiset taulukot Taulukon indeksissä i oleva alkio alkaa osoitteesta b + (i i ) d missä ensimmäisen alkion indeksi on i, b on taulukon alun osoite ja d on taulukon alkion koko taulukossa (tasaus huomioon otettuna)
Taulukon indeksointi Moniulotteiset taulukot Taulukon indeksissä (i 1,..., i m ) oleva alkio alkaa osoitteesta [ ] m b + (i k ik m )d n l k=1 l=k+1 missä kunkin dimension k ensimmäinen indeksi on i k b on taulukon alun osoite, d on taulukon alkion koko taulukossa (tasaus huomioon otettuna), ja n l on dimension l alkioiden lukumäärä.
Taulukon indeksointi Edellä esitetty moniulotteisen taulukon indeksin kaava olettaa, että koko indeksointi tehdään yhdessä operaatiossa (kuten esim. Pascalissa) ja että koko taulukko esitetään yhtenä muistialueena (toisin kuin Javassa). C:n kaltaisessa kielessä sen sijaan jokaista dimensiota indeksoidaan erikseen (esim. a[i][j], ei a[i, j]). Tällöin kukin indeksointi voidaan hoitaa yksiulotteisen indeksoinnin kaavalla...... kunhan muistetaan alkion kokoa laskiessa, että taulukon alkio voi olla taulukko. Tällä tavalla ero C:n ja Javan taulukoiden välillä on helppo: tähdätäänkö kunkin indeksoinnin jälkeen aina vaiko ei. Huomaa, että kaavoissa on joka tapauksessa paljon käännösaikaisia vakioita!
Sisällys
Tietueet Tietue koostuu nimetyistä kentistä, joilla jokaisella on oma tyyppinsä. Tietueelle pitää konstruoida symbolitaulu, joka yhdistää kentän sen sijaintiin tietueessa. Kentät sijoitetaan tietueeseen yleensä esittelyjärjestyksessä. Kentän sijainnin tulee olla aina jaollinen kentän tyypin tasauksella! Näin tietueeseen voi jäädä tyhjiä pätkiä...... ja tietueen kenttien yhteenlaskettu koko voi olla vähemmän kuin tietueen koko!
Metoditaulu myös: virtuaalitaulu, vtable Luokalle, joka sisältää ainakin yhden virtuaalimetodin, tulee konstruoida metoditaulu. Metoditaulu on globaali vakio, ja sen osoite laitetaan jokaiseen olioon ensimmäiseksi kentäksi. Oletetaan ensiksi, että luokkaa ei ole peritty mistään luokasta: Tällöin kullekin metodille annetaan sijainti metoditaulussa, yleensä esittelyjärjestyksessä. Metodille varataan yleensä tilaa taulussa absoluuttisen hyppyosoitteen vaatiman tilan verran. Luokan metoditauluun laitetaan kunkin ei-abstraktin metodin kohdalle hyppyosoite ko. metodin toteuttavaan aliohjelmaan.
Aliluokan metoditaulu Jos luokka on peritty jostain (yhdestä) luokasta: Yläluokassa määriteltyjen metodien sijainniksi laitetaan sama kuin yläluokan metoditaulussa. Luokan määrittelemät metodit, joita ei yläluokassa ole, lisätään metoditauluun yläluokan metodien jälkeen. Jos luokka ei ylimäärittele jotain yläluokan metodia, ko. metodin kohdalle laitetaan sama osoite kuin yläluokan metoditaulussa on. Luokan metoditauluun laitetaan kunkin sen määrittelemän ei-abstraktin metodin kohdalle hyppyosoite ko. metodin toteuttavaan aliohjelmaan. Moniperintä on huomattavasti hankalampaa.
Downcast-tyyppimuunnokset Jos lähdeohjelmassa muutetaan luokkatyyppi alityypikseen,...... tulee välikieleen lisätä sen tarkistus, että olio on todellä ko. alityyppiä. Tätä varten metoditaulun ensimmäiseen kohtaan kirjataan yläluokan metoditaulun osoite (tai muutoin nollaosoite). Tyyppimuunnoksen (T)E kohdalla sitten tehdään seuraavaa: 1. Olkoon m E-olion metoditaulun osoite. 2. Jos m on nollaosoitin, hylätään tyyppimuunnos (heitetään poikkeus, kaadetaan ohjelma tms.). 3. Jos m on T:n metoditaulun osoite, hyväksytään tyyppimuunnos. 4. Muutoin sijoitetaan m:ään m:n ensimmäisen alkion arvo (yläluokan metoditaulun osoite) ja hypätään kohtaan 2.
Metodin dynaaminen sidonta Kun tulee tarve selvittää, mihin pitää hypätä metodikutsussa, toimitaan seuraavasti: Selvitetään käännösaikana luokan symbolitaulusta, mikä on metodin sijainti metoditaulussa. Haetaan olion metoditaulusta (jonka osoite löytyy olion alusta) ko. kohdalta hyppyosoite. Käytännössä kannattaa tehdä niin, että symbolitaulussa olevassa oliossa on genaccess-metodi, joka tekee yo. asiat. Tällöin nimiviittauksen kohdalla ei tarvitse iffitellä onko kyseessä metodi vai ei.
Sisällys
Välikoodin generoija ei tarvitse tietää aliohjelmasta sen parametrien määrää ym. Ne oletetaan olevan ok tyyppitarkastuksen pohjalta. Argumentit pitää laskea yksi kerrallaan. Laskujärjestys riippuu kielestä: vasemmalta oikealle ja oikealta vasemmalle ovat tavallisimmat. Välikielessä ei kannata esittää kutsua monimutkaisesti. Esim. kolmiosoitekoodissa riittää hyvin jokin seuraavanlainen sekvenssi kutsulle f (x, y): yp := ARG y xp := ARG x, yp res := CALL f, xs Tällöin kutsu ja parametrit muodostavat puun, jossa kutsu on ensimmäisenä ja parametrit lineaarisesti järjestyksessä oikeassa alipuussa.
esimerkkikääntäjässä public Rand gencall(call call) { EvalGen eg = new EvalGen(tgt, p, r, symtab); ArrayList<Rand> args = new ArrayList<Rand>(); for (Expression arg : call.args) { Rand ra = arg.accept(eg); args.add(ra); } Rand fr = call.fun.accept(eg); Rand rest = null; for (Rand arg : new ReverseList<Rand>(args)) { rest = r.addtriple(op.arg, arg, rest); } return r.addtriple(op.call, fr, rest); }
Sulkeuma Mallikääntäjässä aliohjelman nimen mainitseminen tuottaa sulkeuman tietueen, joka sisältää aliohjelman hyppyosoitteen, aliohjelman tarvitseman staattisen linkin (jos paikallinen aliohjelma) ja this-osoittimen (jos metodi tai metodin paikallinen aliohjelma) ja sulkeuman tulkitseminen kutsussa jätetään takapään huoleksi.
Sisällys
Seuraava deadline Vaihe D tiistai 10.11. klo 10 välikielen generointi