Johdatus Alphaan

Same in English

Tässä dokumentissa kuvataan vain VICCin toteuttamiselle oleellinen osa Alphasta. Koko assembler-dokumentaatiosta on saatavissa massiivisempi Digitalin tuottama dokumentaatio, jota tarvitaan, jos haluaa toteuttaa täyden tuen debuggerille, liukuluvuille tms.

Rekisterit

Alphassa on 32 64-bittistä rekisteriä, joista viimeinen on aina arvoltaan 0.

Nimi Suositeltu käyttötapa
v0 ($0) Funktioiden paluuarvo.
t0-t11 ($1-$8,$22-$25) Laskennan tilapäismuuttujia, caller-saved.
s0-s6 ($9-$15) Callee-saved-rekistereitä.
a0-a5 ($16-$21) Argumenttien välitys
ra ($26) Funktion paluuosoite
AT ($28 tai $at) Varattu assemblerin käyttöön
gp ($29 tai $gp) Osoitin globaaliin dataan
sp ($30 tai $sp) Pino-osoitin
zero ($31) Vakio nolla

Vain callee-saved-rekisterit säilyttävät arvonsa funktiokutsujen yli. Niinpä jos kutsuttu funktio haluaa käyttää jotain rekistereistä s0-s6, on sen ensin talletettava sen arvo omaan pinokehykseensä ja luettava se sieltä takaisin ennen paluutaan.

gp on osoitin modulikohtaisen vakiodataan, ja sen arvo voi muuttua kun tehdään kutsuja eri moduleissa oleviin funktioihin. Tästä lisää myöhemmin.

Alphassa rekisteri s6 voi olla merkitykseltään myös ns. frame-osoitin, jota tarvitaan lähinnä vain jos halutaan tuotetun koodin olevan helposti debugattavaa. Muutoin se voidaan käyttää muihin tarkoituksiin. Kuvaavammat nimet v0, t0, t1 jne. saa assemblerissa käyttöönsä, jos assembler-tiedoston alussa on rivi

#include <regdef.h>

Tarvittavat käskyt

Alla r0, r1, r2 ovat (eivät välttämättä eri) rekistereitä, C vakio, L jokin symboli (label) ja mem[] vastaa muistia.

Käsky Semantiikka Toteutus
lda r0, L r0 = L; Makro
ldiq r0, C r0 = C; Makro
ldq r0, C(r1) r0 = mem[r1 + C]; Käsky
stq r0, C(r1) mem[r1 + C] = r0; Käsky
addq r0, r1, r2 r2 = r0 + r1; Käsky
addq r0, C, r1 r1 = r0 + C; Käsky
s4addq r0, r1, r2 r2 = 4*r0 + r1; Käsky
s4addq r0, C, r1 r1 = 4*r0 + C; Käsky
s8addq r0, r1, r2 r2 = 8*r0 + r1; Käsky
s8addq r0, C, r1 r1 = 8*r0 + C; Käsky
subq r0, r1, r2 r2 = r0 - r1; Käsky
subq r0, C, r1 r1 = r0 - C; Käsky
s4subq r0, r1, r2 r2 = 4*r0 - r1; Käsky
s4subq r0, C, r1 r1 = 4*r0 - C; Käsky
s8subq r0, r1, r2 r2 = 8*r0 - r1; Käsky
s8subq r0, C, r1 r1 = 8*r0 - C; Käsky
absq r0, r1 r1 = (r0 < 0) : -r0 : r0; Käsky
absq C, r0 r0 = (C < 0) : -C : C; Käsky
negq r0, r1 r1 = -r0; Käsky
negq C, r0 r0 = -C; Käsky
mulq r0, r1, r2 r2 = r0 * r1; Käsky
mulq r0, C, r1 r1 = r0 * C Käsky
divq r0, r1, r2 r2 = r0 / r1; Makro
divq r0, C, r1 r1 = r0 / C; Makro
remq r0, r1, r2 r2 = r0 % r1; Makro
remq r0, C, r1 r1 = r0 % C; Makro
sll r0, r1, r2 r2 = r0 << r1; Käsky
sll r0, C, r1 r1 = r0 << C; Käsky
sra r0, r1, r2 r2 = r0 >> r1; Käsky
sra r0, C, r1 r1 = r0 >> C; Käsky
mov r0, r1 r1 = r0; Käsky
cmpeq r0, r1, r2 r2 = (r0 == r1); Käsky
cmpeq r0, C, r1 r1 = (r0 == C); Käsky
cmplt r0, r1, r2 r2 = (r0 < r1); Käsky
cmplt r0, C, r1 r1 = (r0 < C); Käsky
cmple r0, r1, r2 r2 = (r0 <= r1); Käsky
cmple r0, C, r1 r1 = (r0 <= C); Käsky
br L goto L; Käsky
beq r0, L if (r0 == 0) goto L; Käsky
bne r0, L if (r0 != 0) goto L; Käsky
blt r0, L if (r0 < 0) goto L; Käsky
ble r0, L if (r0 <= 0) goto L; Käsky
bgt r0, L if (r0 > 0) goto L; Käsky
bge r0, L if (r0 >= 0) goto L; Käsky
cmoveq r0, r1, r2 if (r0 == 0) r2 = r1; Käsky
cmoveq r0, C, r2 if (r0 == 0) r2 = C; Käsky
cmovne r0, r1, r2 if (r0 != 0) r2 = r1; Käsky
cmovne r0, C, r2 if (r0 != 0) r2 = C; Käsky
cmovlt r0, r1, r2 if (r0 < 0) r2 = r1; Käsky
cmovlt r0, C, r2 if (r0 < 0) r2 = C; Käsky
cmovle r0, r1, r2 if (r0 <= 0) r2 = r1; Käsky
cmovle r0, C, r2 if (r0 <= 0) r2 = C; Käsky
cmovgt r0, r1, r2 if (r0 > 0) r2 = r1; Käsky
cmovgt r0, C, r2 if (r0 > 0) r2 = C; Käsky
cmovge r0, r1, r2 if (r0 >= 0) r2 = r1; Käsky
cmovge r0, C, r2 if (r0 >= 0) r2 = C; Käsky

Alpha osaa lukea muistista dataa vain kahdeksalla jaollisista osoitteista, muutoin kyseessä on ns. alignment-virhe. Sama pätee tietysti myös muistiin kirjoituksiin.

Ylläolevan listan käskyt addq, subq, negq ja mulq voi korvata käskyillä addqv, subqv, negqv ja mulqv, jos haluaa tarkistaa ylivuodot.

Makrot voivat vastata useita Alpha-käskyjä. Käskyt divq ja remq käyttävät rekistereitä t9-t12. Todellisuudessa myös moni muu käsky saattaa laventua useammaksi käskyksi, varsinkin jos niiden vakio-argumentti on yli 8-bittinen. Apuna käytetään tällöin AT-rekisteriä, jota käyttäjän siis ei tulisi hyödyntää.

Oikein innokas optimoija saattaa kaivata muitakin käskyjä, esimerkiksi bittioperaatioita ja fetch-käskyä. Niiden dokumentaatio löytyy Alphan assembler-manuaalista.

Funktiot ja muuttujat

Funktion funktio toteutus alkaa Alphan assemblerilla seuraavasti:
        .text
        .align 3
        .globl funktio
        .ent funktio
funktio:
        ldgp gp,0(pv)
funktio..ng:

Alphassa pino (yleensä) kasvaa ylhäältä, suuremmista muistiosoitteista, alaspäin, kohti pienempiä muistiosoitteita. Koska VICCissä kaikki argumentit mahtuvat aina kuuteen argumenttirekisteriin, ei tarvetta välittää argumentteja pinossa tule. Niinpä pinoon tarvitsee varata tilaa vain funktion paikallisille muuttujille ja callee-saved -rekisterien ja ra:n tallettamiseen. Jos funktio ei kutsu muita funktioita, ei käytä callee-saved -rekistereitä ja kaikki sen käyttämät muuttujat mahtuvat tilapäisrekistereihin, ei pinoa tarvitse laisinkaan käyttää. Jos funktio kutsuu muita funktioita, pitää pino-osoitinta muuttaa tämän funktion pinokehyksen verran (vaikka 32 tavua):

        lda sp, -32(sp)

Kaikki funktion käyttämät callee-saved -rekisterit pitää tallettaa pinokehykseen:

        stq s0, 8(sp)
        stq s1, 16(sp)

Jos funktio kutsuu muita funktioita, pitää sen tallettaa paluuosoite pinokehykseen:

        stq ra, 0(sp)

Kun funktio haluaa kutsua toista funktiota, pitää sen ensin tallettaa omat tilapäismuuttujansa (mikäli niissä on jotain funktiokutsun jälkeen tarpeellista tietoa) pinokehykseen ja sijoittaa kutsun argumentit argumenttirekistereihin. Jos kutsuttava funktio kutsuttu on VICC-funktio, varsinainen kutsu on yksinkertaisesti

        bsr kutsuttu..ng

Huomaa, että VICCissä ei ole modulijärjestelmää, joten gp:n muuttumista ei tapahdu. Jos kutsuttu funktio on kirjastofunktio (toisessa käännösyksikössä), on kutsu

        jsr kutsuttu
        ldgp gp, 0(ra)

Tilapäismuuttujien lisäksi myös argumenttirekisterien arvo voi muuttua kutsun aikana. Paluuarvot ovat rekisterissä v0, johon myös funktion tulee jättää oma paluuarvonsa. Kun funktio päättyy, pitää luotu pinokehys purkaa ja mahdollisesti muuttunut ra ja callee-saved -rekisterit lukea takaisin:

        ldq ra, 0(sp)
        ldq s0, 8(sp)
        ldq s1, 16(sp)
        lda sp, 32(sp)

Lopulta funktio palaa:

        ret zero,(ra),1
        .end funktio

Funktioiden ulkopuolella voi assemblerin käskeä luomaan muuttujia ja vakioita. Esimerkkinä merkkijonovakio foo ja taulukko bar : ARRAY [2] OF Int

        .data
foo:
        .ascii "...\0"
        .align 3
bar:
        .comm bar 16

Tähän liittyy seuraava helpottava korjaus VICCin määrittelyyn: Taulukkoa ei voi sijoittaa toiseen taulukkoon. Taulukon välittäminen argumenttina tapahtuu viittauksena, eli välitetään argumenttirekisterissä taulukon alkuosoite, ja vastaavasti taulukkotyyppisille paluuarvoille.

Taulukko paikallisena muuttujana tarkoittaa taulukon käyttämän tilan vaaraamista pinosta.

Assemblerissa risuaita # kommentoi pois loppurivin, mutta koska assembler ajetaan cpp:n läpi, ei risuaita voi olla rivin ensimmäinen merkki.

Esimerkkejä

Eräs esimerkki Alphan assemblerista on implementaatio kotitehtävänä olleesta lausekääntäjästä, joka pinokoodin sijasta tuottaakin Alphan ymmärtämään assembleria, joskin hyvin epäoptimaalista sellaista. Kyseinen koodi on haettavissa anonymous ftp:llä sauna.cs.hut.fi:stä shar-tiedostosta pub/tik76.149/kotialpha.shr.

Muita esimerkkejä saa esimerkiksi Alphan assembler-manuaaleista ja kääntämällä Alphoissa sopivia C-ohjelman pätkiä optiolla -S.

Joitakin koneläheisiä optimointeja