Kääntäjätekniikka
Luento 3 (21.3.2017)
Tämän luennon aiheet
- administrivia
- aliohjelmien teoriaa
- pinolokaalit muuttujat
- rekursiiviset aliohjelmat
- häntäkutsun poisto
- ensiluokkaiset aliohjelmat
Yksityiskohdat tänään vain AMD64 SysV ABI:n mukaisesti.
Esimerkkikoodit
Yousourcessa (huomaa branch!)
Kurssin tilannepäivitys
C-kirjastofunktioiden yhteenveto
Lisäys suositeltujen kirjojen listaan
Kurssin opiskelijan löytö:
- Dick Grune, Kees van Reeuwijk, Henri E. Bal, Ceriel J.H. Jacobs ja Koen Langendoen: Modern Compiler Design. 2nd edition. New York, NY: Springer, 2012. doi:10.1007/978-1-4614-4699-6
Harjoitustön tilanne
- Suunnitelman ohjeellinen deadline oli eilen. (Vielä ehtii!)
- Tee Yousourceen tai Githubiin git-varasto (repository)
- jos se ei ole julkinen, anna lukuoikeus AJK:lle ja VT:lle (Yousourcessa antkaij ja aleator).
- Laita suunnitelma esimerkkiohjelmineen gitiin
- Tägää suunnitelma.
- Laita harjoitustyösi tiedot Korppiin.
- Laita URLiksi varaston pääsivu, älä sen pull-osoitetta (haluan copypastettaa sen selaimeen!)
- Seuraava ohjeellinen deadline on kahden viikon kuluttua 3.4.2017 (jäsennin valmis)
Aliohjelmien teoriaa
Aliohjelman aktivaatio
- Kun aliohjelmaa kutsutaan, sen paikallisista muuttujista (myös parametrit) muodostetaan uudet esiintymät.
- Näiden paikallisten muuttujien esiintymien kokoelmaa kutsutaan aliohjelman aktivaatioksi.
- Aktivaatio pääsääntöisesti tuhotaan aliohjelman suorituksen päättyessä.
- Aliohjelmasta voi olla samanaikaisesti useita aktivaatioita.
Aktivaatioiden samanaikaisuus
- Säikeet
- Eri säikeissä voi olla samanaikaisesti sama aliohjelma käynnissä.
- Rekursio
- Jos aliohjelma kutsuu itseään (vaikka epäsuorasti), on siitä samassa säikeessä olemassa yhtä aikaa useita aktivaatioita.
callcc
(Scheme, ML)- ensiluokkaiset aliohjelmat
Aktivaation tulee tietää
- aliohjelman saamat argumentit
- aliohjelman paikallisten muuttujien tähän aktivaatioon liittyvät esiintymät
- Mihin osoitteeseen tulee hypätä aliohjelman tultua valmiiksi? (paluuosoite)
- Mihin aliohjelma-aktivaatioon siirrytään aliohjelman tultua valmiiksi? (dynaaminen linkki)
- Jos kyseessä on aliohjelman A sisäinen aliohjelma B, missä A:n aktivaatiossa esiintyvät B:n tarvitsemat A:n paikalliset muuttujat? (staattinen linkki)
- Jos kyseessä on olion metodi, osoitin olion ilmentymään (
this
,self
)
Nämä tiedot sisältävää tietorakennetta sanotaan aktivaatiotietueeksi (activation record)
Aktivaatiopino
- Saman säikeen aliohjelma-aktivaatiot muodostavat (yleensä) pinon
- ... toteutettuna yksinkertaisesti linkittynä listana (linkkinä dynaaminen linkki)
- Aliohjelmaa kutsuttaessa sille muodostetaan aktivaatiotietue, joka lisätään pinon päällimmäiseksi.
- Aliohjelman päättyessä sen aktivaatiotietue on pinon päällimmäisenä; poistetaan se pinosta ja tuhotaan se.
- Tavallisesti aktivaatiopino rakennetaan niin, että aktivaatiotietueet ovat fyysisestikin päällekäin muistissa.
- Näin aktivaatiotietueen luominen ja tuhoaminen on erittäin nopeaa.
- Jos on useita säikeitä, jokaisella on oma aktivaatiopinonsa.
AMD64 SysV ABI:
Perustyyppisten parametrien välitys
- totuusarvot, kokonaisluvut (max 64 bittiä) ja osoittimet:
- rdi, rsi, rdx, rcx, r8, r9
- liukuluvut (max 64 bittiä):
- xmm0–xmm7
- jos kutsuttava on
stdarg
-funktio (esim.printf
),al
:ssä oltava yläraja liukulukurekisterien käytölle (0–8)
- rekisterit otetaan käyttöön argumenttilistaa vasemmalta oikealle läpikäymällä
- jos kaikki argumentille sopivat rekisterit on jo käytetty, argumentti välitetään pinossa
AMD64 SysV ABI:
Tietueiden parametrivälitys
- tietue välitetään kokonaan pinossa, jos
- kooltaan yli 32 tavua tai
- kentät eivät noudata alignment-vaatimuksia tai
- mikään osa tietueesta välitettäisiin mistään syystä pinossa
- muuten tietue jaetaan 8 tavun osiin, jotka välitetään erikseen:
- jos ainakin yksi osassa olevista kentistä välitetään kokonaisluvun tavoin, koko osa välitetään kokonaisluvun tavoin
- muuten koko osa välitetään liukuluvun tavoin
- esimerkkejä: struct-amd64.S + struct-c.c; struct2-amd64.S + struct2-c.c; struct3-amd64.S + struct3-c.c
AMD SysV ABI:
Paluuarvon välitys
- paluuarvon luokittelu kuten parametreille
- pinossa välitettävä paluuarvo käsitellään erikseen:
- kutsuja varaa paluuarvolle tilaa
- paluuarvon tilan osoite välitetään rdi-rekisterissä aliohjelmalle
- palatessa rax:ssa tulee olla rdi:ssä välitetty osoite
- totuusarvot, kokonaisluvut ja osoittimet palautetaan rekistereissä
rax
jardx
- liukuluvut palautetaan rekistereissä
xmm0
jaxmm1
- kaksi rekisteriä varataan, jotta 16-tavuinen (joskus jopa 32-tavuinen) tietue voidaan palauttaa rekistereissä
Välihuomautus: Staattinen linkki ja this
- sekä staattinen linkki että this tulee välittää parametreina normaaliin tapaan
AMD64 SysV ABI:
Pinon käyttö aliohjelmakutsussa
- pinossa välitettävät argumentit laitetaan pinoon argumenttilistaa oikealta vasemmalle läpi käyden
- argumentin koko pyöristetään ylöspäin niin, että se on jaollinen 8 tavulla
- ensimmäisen argumentin
- osoite tulee olla 8 tavulla mutta ei 16 tavulla jaollinen
- tulee olla pinossa päällimmäisenä kun call-käsky aloitetaan
- kutsuja poistaa argumentit pinosta
AMD64 SysV ABI:
Pinokehys
- engl. stack frame
- aliohjelman käytössä oleva osa pinosta
- on melkein sama asia kuin aktivaatiotietue
- pinoargumentit kuuluvat aktivaatiotietueeseen mutta eivät pinokehykseen
- alkaa paluuosoitteesta ja päättyy pino-osoittimen rsp osoittamaan kohtaan
- huomaa: pino kasvaa alaspäin
- jos aliohjelma ei kutsu muita aliohjelmia, se saa käyttää myös 128 tavua rsp:n jälkeen (alapuolelta)
- paluuosoitteen osoitteen tulee olla 16 tavulla jaollinen
- kutsujan vastuulla huolehtia tästä
- kutsuttava saa olettaa tämän pätevän
AMD64 SysV ABI:
Aliohjelman prologi ja epilogi
Luodaan ja puretaan pinokehys:
push rbp
mov rbp, rsp
sub rsp, n
...
mov rsp, rbp
pop rbp
ret
missä n on paikallisten muuttujien tarvitsema tila tavuina (16:lla jaollinen).
- sub voidaan korvata jonolla
push
-käskyjä- huolehdi, että rsp:n muutos on 16:lla jaollinen
- muistissa oleva data kannattaa pushata
- ei tarvitse lukea rekisteriin
AMD64: Muistiosoitukset
Useimmat käskyt sallivat, että yksi operandeista viittaa muistiin. Muistiosoituksen yleinen muoto on
[r1 + s * r2 + d]
- hakasulkeissa olevan lausekkeen arvo on muistiosoite, josta data luetaan
- r1 ja r2 ovat kokonaislukurekistereitä
- s on 1, 2, 4 tai 8 (vakio)
- d on enintään 32-bittinen etumerkillinen vakio
- enintään kaksi termeistä voi puuttua
- muistiosoituksen eteen pitää tavallisesti kirjoittaa
byte ptr
,word ptr
,dword ptr
taiqword ptr
kertomaan operandin koko (8, 16, 32 tai 64 bittiä), jos käskyllä ei ole myös rekisterioperandia
Digressio: LEA-käsky (AMD64)
LEA rekisteri, [lauseke]
- sijoittaa rekisteriin lausekkeen arvon
- lauseke on mikä vain joka kepaa muistiosoitukseksi
- mutta
LEA
ei lataa mitään muistista!
- mutta
- voi käyttää kokonaislukuaritmetiikkaan muistiosoitelausekkeen yleisen muodon rajoissa
- voi myös käyttää osoitteen laskemiseksi rekisteriin
AMD64 SysV ABI:
Kehysosoitin
- rbp-rekisteri on kehysosoitin, engl. frame pointer
- 1. pinoargumentti löytyy osoitteesta rbp+16
- jos 1. pinoargumentti oli 64 bittiä leveä, 2. pinoargumentti löytyy osoitteesta rbp+24
- paikalliset muuttujat löytyvät negatiivisista offseteista rbp:n suhteen
- ei pakollinen jos paikallisten muuttujien tarvitseman tilan koko on vakio
- tällöin argumentit ja paikalliset muuttujat indeksoidaan rsp:n suhteen
AMD64 SysV ABI:
Paikalliset muuttujat
- paikalliset muuttujat, jotka eivät mahdu rekistereihin tai joilla tarvitsee olla osoitin, tallennetaan pinoon
- myös callee save -rekisterien tallennukset
- paikallisille muuttujille varattu osa pinokehyksestä alkaa rbp:stä ja päättyy rsp:hen
- kannattaa jakaa suunnitelmallisesti eri muuttujille
- paikalliseen muuttujaan viitataan
[rbp-n]
, missä n on muuttujan offset suhteessa rbp:hen
Huomioita häntäkutsun poistosta
- muuttaa pinoa syövän rekursion pinoon koskemattomaksi iteraatioksi
- mutta ei rajoitu vain rekursion tapaukseen
- mahdollista vain, jos kutsun paluuarvo välitetään sellaisenaan ja välittömästi kutsujan kutsujalle
- joskus häntäkutsun poiston jälkeen kehyksen rakentaminen ja purkaminen voidaan jättää tekemättä (esimerkki)
- jotkin lähdekielet vaativat, että jokainen häntäkutsu on poistettava
- tällöin kieli ei välttämättä tarvitse erillisiä silmukkarakenteita
Standardi ABI ei sovellu kaikkeen
Seuraavat temput eivät onnistu standardi-ABIen kanssa:
- häntäkutsun poisto, kun kutsuja ottaa vähemmän tavuja pinoargumentteina kuin kutsuttava
- osittain kutsutut aliohjelmat ja ensiluokkaiset aliohjelmat
Näitä tarvitsevan kääntäjän tulee määritellä oma ABI.
- voi olla fiksua tarjota ohjelmoijalle mahdollisuus määritellä, että jokin aliohjelma noudattaa poikkeuksellisesti standardi-ABIa
Vapaaehtoinen harjoitustehtävä
Kirjoita ensin C-kielellä yksinkertaisesti linkitetyn listan (alkiotyyppinä long
) käsittelykirjasto (add_front
, remove_first
, map
– missä map
ottaa parametrinaan yksiparametrisen funktion ja listan ja kutsuu ko. funktiota jokaiselle listan alkiolle) ja sitten sitä käyttäen testipääohjelma, joka käyttäen map
-funktiota laskee listan alkioiden summan.
Sen jälkeen kirjoita sama assemblynä.
These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.