MIPS, registerallokering og MARS



Relaterede dokumenter
DATALOGI MASKINARKITEKTUR Blok 2 samt Reeksamination i DATALOGI MASKINARKITEKTUR Blok 1 og arkitekturdelen af DATALOGI 1E

DATALOGI 1E. Skriftlig eksamen torsdag den 3. juni 2004

Sproget Six. Til brug i rapportopgaven på kurset Oversættere. Vinter Abstract

Indhold. Maskinstruktur Kapitel 1. Assemblersprog Indledning Hop-instruktioner Input og output...

Maskinsprog. Martin Zachariasen, DIKU. Programmer og ordrer. Ordretyper. Operander og deres placering. Ordreformat. Procedurekald. Andre arkitekturer

DATALOGI 1E. Skriftlig eksamen mandag den 23. juni 2003

Eksamen Computerarkitektur 2013Q4. Niels Olof Bouvin. Studienummer Navn

Computerarkitektur Eksamen 2014Q3. Niels Olof Bouvin. Studienummer Navn

University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 2

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer

Niveauer af abstrakte maskiner

Lær Python dag 1 - modul 1

Oversættere. Vejledende løsninger til Skriftlig eksamen onsdag d. 20. april 2005

Programmering og Problemløsning, 2017

Python programmering. Per Tøfting. MacFest

Ugeseddel 4 1. marts - 8. marts

DM507 Algoritmer og datastrukturer

Eksamen dcomnet Q2/2010. Navn

AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007

Eksamen dcomnet Q2/2012. Studiekortsnummer Navn

Dat1E G2 - En enkelt/dobbelt-cyklus mikroarkitektur. Espen Højsgaard Rune Højsgaard Christian Iversen

CPUer og maskinkode DM534. Rolf Fagerberg

Eksamen dcomnet 2012Q4. Årskortsnummer Navn

Programmering og Problemløsning, 2017

Binært LAS-format Denne indstilling import Laser scan datafiler, i LAS format.

Dat1E K1-1 - En pipelinet mikroarkitektur. Espen Højsgaard Rune Højsgaard Christian Iversen

DM507 Algoritmer og datastrukturer

Interrupt - Arduino. Programmering for begyndere Brug af Arduino. Kursusaften 6 EDR Hillerød Knud Krogsgaard Jensen / OZ1QK

Hent filoplysninger fra billeder og filer

Programmering for begyndere Lektion 2. Opsamling mm

Martin Olsen. DM507 Projekt Del I. 19. marts 2012 FOTO: Colourbox

PUT og INPUT funktionerne

Programmeringscamp. Implementer funktionerne én for én og test hele tiden.

Punktskrift eller Tale Via

Skriftlig eksamen i Datalogi

Introduktion til datastrukturer. Introduktion til datastrukturer. Introduktion til datastrukturer. Datastrukturer

DM507 Algoritmer og datastrukturer

Peter Kellberg. Rundt om Danmarks Statistiks makroer. Design, Standardisering, Teknik

Divide-and-Conquer algoritmer

Klasse 1.4 Michael Jokil

Programmering C RTG

Tredjepart webservices

Computerarkitektur. - en introduktion til computerarkitektur med LINDA

Introduktion til datastrukturer. Introduktion til datastrukturer. Introduktion til datastrukturer. Datastrukturer

Kapitel 4 Løkker i C#

Vejledning INSTALLATION AF ZHC5010 BETJENINGSTRYK MED FIBARO HOME CENTER. ZHC5010 Firmware Version: 1.0

GIS. Guide til indlæsning af data i ArcGIS herunder KMS-data fra internettet

Virkefeltsregler i Java

ISCC. IMM Statistical Consulting Center. Brugervejledning til beregningsmodul til robust estimation af nugget effect. Technical University of Denmark

Programmering og Problemløsning, 2017

Abstrakte datatyper C#-version

Divide-and-Conquer algoritmer

Transkript:

MIPS, registerallokering og MARS Torben Mogensen 2011 Resumé Vi beskriver modulerne Mips.sml og RegAlloc.sml, samt hvordan de bruges sammen med MIPS-emulatoren MARS. 1 MIPS modulet Modulet Mips.sml indeholder en datastruktur for en delmængde af MIPS instruktionssættet, som beskrevet i Appendix A.10 i [1]. Typen mips beskriver udvalgte instruktioner og assemblerdirektiver: datatype mips = LABEL of string EQU of string*string GLOBL of string TEXT of string DATA of string SPACE of string ASCII of string ASCIIZ of string ALIGN of string COMMENT of string LA of string*string LUI of string*string ADD of string*string*string ADDI of string*string*string SUB of string*string*string AND of string*string*string ANDI of string*string*string OR of string*string*string ORI of string*string*string XOR of string*string*string XORI of string*string*string SLL of string*string*string SRA of string*string*string SLT of string*string*string SLTI of string*string*string BEQ of string*string*string BNE of string*string*string J of string JAL of string * string list (* label + argumentregistre *) JALR of string * string list (* hopregister + argumentregistre *) JR of string * string list (* hopregister + resultatregistre *) 1

LW of string*string*string (* lw rd,i(rs) kodes som LW (rd,rs,i) *) SW of string*string*string (* sw rd,i(rs) kodes som SW (rd,rs,i) *) LB of string*string*string (* lb rd,i(rs) kodes som LB (rd,rs,i) *) SB of string*string*string (* sb rd,i(rs) kodes som SB (rd,rs,i) *) NOP SYSCALL Her følger en kort forklaring af instruktionerne LABEL angiver en label. EQU bruges til at definere konstanter. F.eks. bliver EQU ("x",-16") oversat til assemblerlinjen x = -16. Det andet argument er en tegnfølge, da det er muligt også at bruge hexadecimale tal (f.eks. 0xff) og symbolske konstanter (som f.eks. labels eller konstanter defineret med andre EQU er) på højresiden. GLOBL, TEXT, DATA, SPACE, ASCII, ASCIIZ og ALIGN svarer til direktiverne.globl,.text,.data,.space,.ascii,.asciiz og.align. COMMENT er en kommentar. LA er pseudoordren la, som lægger en adresse i et register. Registeret er enten et tal mellem 0 og 31 (angivet som tegnfølge uden $ foran) eller et variabelnavn, der vil blive allokeret i et numerisk register. Man kan ikke bruge de symbolske registernavne dom f.eks. v0 og a0, da alle registernavne, der ikke er tal, bliver registerallokeret. LUI...J er almindelige MIPS instruktioner. Registre angives som beskrevet herover. Talkonstanter er enten decimale tal (med muligt fortegn), hexadecimale tal eller symbolske konstanter. JAL er jal instruktionen. Udover labelen skal JAL af hensyn til registerallokatoren også have en liste args af de variable og registre, der er levende ved destinationen. Det vil typisk være argumentregistre og eventuelt registre, der bruges til globale variable. Hvis jal findes i kode, der ikke registerallokeres, bruges denne liste ikke, og den kan i givet fald lades tom. JR er jr instruktionen. Udover hopregistret skal JR af hensyn til registerallokatoren også have en liste af de registre og variabler, der er levende ved hoppet (typisk returværdiregistre og globale variabler). Hvis jr findes i kode, der ikke registerallokeres, bruges denne liste ikke, og den kan i givet fald lades tom. Bemærk, at JR er uegnet til indirekte funktionskald, brug i stedet JALR, som er beskrevet herunder. JALR er jalr instruktionen, der laver et funktionskald gennem et register. JALR (rs, args) gemmer returadressen i register 31 (link registret) og hopper til adressen i register rs. args parameteren er som beskrevet for JAL. LW, SW, LB og SB er lw, sw, lb og sb instruktionerne. Bemærk, at det konstante offset angives sidst i parameterlisten, så f.eks. lw $2,16($28) angives som LW ("2","28","16"). 2

NOP er nop, altså no operation. SYSCALL er systemkaldsinstruktionen syscall. Register $2 bruges til at angive operationen, evt. parametre angives i registrene $4 og $5 og en evt. returværdi vil lægges i $2. Se Appendix A.9 i [1] for mulige operationer. Udover de ovennævnte instruktioner definerer Mips.sml også pseudoinstruktioner, der oversættes til ORI-instruktioner: MOVE (x,y) oversættes til ORI (x,y,"0"). Registerallokatoren kan fjerne en move-instruktion, hvis x og y allokeres til samme register. LI (x,k) oversættes til ORI (x,"0",k). Et assemblerprogram vil blive bygget som en liste af instruktioner, dvs. noget af typen Mips.mips list. Mips definerer også en funktion pp list, der konverterer en liste af MIPS instruktioner til et format, der kan indlæses i MARS og SPIM. 2 Registerallokatoren Modulet RegAlloc.sml indeholder en registerallokator. Registerallokatoren arbejder med kroppen af en enkelt funktion og vil oversætte registernavne til registernumre. Registerallokatoren har typen val registeralloc : Mips.mips list -> string list -> int -> int -> int -> int -> Mips.mips list * string list * int * int registeralloc tager som argumenter en liste af MIPS instruktioner, en liste af registre, der er levende ved udgangen af koden, samt fire tal: Det mindste allokerbare register (i reglen 2), det største caller-saves register, det største allokerbare register (i reglen 25) samt antallet af allerede spillede variable. Det sidstnævnte er i reglen 0. Registre mellem de to første tal (begge inklusive) er caller-saves registre og de efterfølgende registre op til det tredje tal er callee-saves. Hvis man vil følge standardkaldkonventionen for MIPS skal tallene være 2, 15 og 25. Hvis man selv reserverer et register til f.eks. hopegepind, kan man bruge register 25 og dermed sætte grænsen for allokerbare registre til 24. registeralloc returnerer en modificeret liste af instruktioner, hvor navngivne registre er blevet erstattet med numeriske registre. For læselighedens skyld er hver modificeret instruktion efterfulgt af en kommentar, der viser den oprindelige instruktion. Udover at omdøbe registre, udkommenterer registerallokatoren MOVE instruktioner, hvor kilde- og destinationsregistrene er ens efter allokering. Udover den modificerede instruktionsliste returneres også en liste af variabler, der er levende ved indgangen til koden. Denne kan bruges til at finde fejl i koden, men vil for det meste blive ignoreret. Derefter returneres det største registernummer, der blev brugt til at allokere variabler i. Det kan bruges til i funktionsprologen at gemme præcis de callee-saves registre, der bliver brugt i funktionskroppen. 3

Til sidst returneres antallet af spillede variable. Dette tal skal bruges til at gøre plads på stakken til spillede variable. Registerallokatoren antager, at spillede variable ligger på stakken på offset 0, 4, osv. fra stakpegepinden (register 29). Hvis antallet af spillede variable f.eks. er 2, skal der gøres plads på stakken, så offset 0 og 4 kan bruges til disse. Hvis der er spillede variable, må funktionskroppen ikke flytte på stakpegepinden undtagen helt lokalt ved funktionskald, da offsets til spillede variable dermed ikke kommer til at passe længere. Spill vil sjældent forekomme, hvis registerallokatoren kan bruge 15 eller flere registre. 3 Brug af registerallokatoren ved oversættelse af funktioner Vi bruger en forenklet udgave af MIPS kaldkonventionen: Registrene $2... $15 er caller-saves og $16... $25 er callee-saves. Parametre overføres i registrene $2... $15 (efter behov) og resultatet findes efter kaldet i $2. Vi antager, at der aldrig er mere end 14 parametre, så alle parametre kan overføres i registre. Register $29 er stakpegepind. Stakken vokser nedad og stakpegepinden peger på det øverste stakelement (det med den laveste adresse). Vi bruger ikke nogen frame pointer. 3.1 Funktionskald Registerallokatoren implementerer caller-saves ved ikke at allokere variabler i callersaves-saves registre, hvis de er levend hen over et funktionskald. Så hvis vi har et kald t = f(x,y,z), kan vi oversætte det til instruktionerne [Mips.MOVE ("2","x"), Mips.MOVE ("3","y"), Mips.MOVE ("4","z"), Mips.JAL ("f",["2","3","4"]), Mips.MOVE ("t","2")] Bemærk, at listen af de registre, der bruges som parametre i kaldet, angives som et ekstra argument til JAL instruktionen. Et kald til en funktion, hvor adressen ligger i et register, oversættes med instruktionen JALR i stedet for JAL. Hvis man skal kunne håndtere flere end 14 parametre, skal de overskydende parametre lægges på stakken. 3.2 Prolog og epilog Vi antager, at vi har en definition f(x,y,z) = e og at vi har oversat kroppen e til en liste af MIPS-instruktioner i ML-variablen body, sådan at koden lægger sit resultat i variablen result. Vi skal nu tilføje kode til at overføre parametre og resultat fra funktionen: 4

val body2 = [Mips.MOVE ("x","2"), Mips.MOVE ("y","3"), Mips.MOVE ("z","4")] @ body @ [Mips.MOVE ("2","result")] Vi kalder nu registerallokatoren: val (body3,_,maxreg, spilled) = RegAlloc.registerAlloc body2 ["2"] 2 15 25 0 Bemærk, at vi angiver, at $2 skal være levende ved udgangen af koden, da den jo indeholder returværdien. body3 indholder nu den allokerede krop. Bemærk, at det er sandsynligt, at de fleste af de MOVE-instruktioner, vi tilføjede herover, (og mange andre) er elimineret af registerallokatoren. maxreg indeholder det største allokerede register. Hvis det er over 15, er der brugt callee-saves registre, der så skal gemmes i aktiveringsposten og hentes bagefter. Vi vil i samme omgang gemme returadressen i den første plads i aktiveringsposten. Lad os antage, at maxreg er 17, så vi skal gemme register $31 (returadressen), register $16 og register $17 i aktiveringsposten. Vi antager endvidere, at der ikke forekommer spill, så spilled = 0: val prologue = [Mips.LABEL "f", Mips.ADDI ("29","29","-12"), Mips.SW ("31","29","0"), Mips.SW ("16","29","4"), Mips.SW ("17","29","8")] val epilogue = [Mips.LW ("31","29","0"), Mips.LW ("16","29","4"), Mips.LW ("17","29","8"), Mips.JR ("31",[]), Mips.ADDI ("29","29","12")] val body4 = prologue @ body3 @ epilogue Bemærk, at $29 er stakpegepinden, og at den modificeres med størrelsen af aktiveringsposten. Hvis spilled er forskellig fra 0 skal stakpegepinden yderligere flyttes med 4*spilled og offsets til gemte variabler tilsvarende modificeres. Her er et eksempel, hvor spilled=2: val prologue = [Mips.LABEL "f", Mips.ADDI ("29","29","-20"), Mips.SW ("31","29","8"), Mips.SW ("16","29","12"), Mips.SW ("17","29","16")] val epilogue = [Mips.LW ("31","29","8"), Mips.LW ("16","29","12"), 5

Mips.LW ("17","29","14"), Mips.ADDI ("29","29","20"), Mips.JR ("31",[])] val body4 = prologue @ body3 @ epilogue $31 er link-registret, som indeholder returadressen. Da JR bruges i kode, der ikke registerallokeres, er listen af resultatregistre ligegyldig og derfor tom. Eksemplet herover er specifik for en bestemt funktion. En oversætter skal håndtere forskellige funktioner, og vil derfor definere kode til at konstruere prolog og epilog ud fra funktionsnavnet og værdien af maxreg. Figur 1 viser et eksempel på hvordan en sådan kode kunne se ud. Hvis man skal håndtere flere parametre, end der kan overføres i registre, skal movepars udvides, så den henter de overskydende parametre fra stakken. 4 MARS MARS er en simulator for en delmængde af MIPS. MARS er kompatibel med SPIM simulatoren, som tidligere er brugt på kurset og som er beskrevet i Appendix A.9 i [1]. MARS er et javaprogram, der kan hentes på http://courses.missouristate.edu/kenvollmar/mars/index.htm. En vejledning til at køre MARS kan findes på http://courses.missouristate.edu/kenvollmar/mars/help/marshelpintro.html. SPIM kan køres fra kommandolinjen ved at skrive java -jar Mars.jar program.asm hvorefter indata og uddata læses og skrives på standard input og output. Man kan også køre MARS interaktivt, se dokumentationen. Det kan være en god ide at lægge Mars.jar i et særskilt filkatalog og lave et alias: alias mars java -jar stinavn/mars.jar Så man man skrive mars program.asm i stedet for java -jar Mars.jar program.asm. Der er nogle ting, man skal være opmærksom på i MARS: Registrene $1, $26 og $27 er reserveret til hhv. ekspansion af pseudoordrer (f.eks. la) og til operativsystemet. Stakpegepinden ($29) er initialiseret ved start af kørsel, og man bør ikke reinitialisere den. Stakken vokser nedad i lageret (til lavere adresser) og stakpegepinden peger på topelementet. Når der skal lægges en ny værdi på stakken bør det gøres med følgende sekvens: Træk størrelsen af værdien fra stakpegepinden, gem værdien på den adresse, stakpegepinden nu peger på. Plads til globale variabler, tabeller, hob og lignende er i datasegmentet, og bør vokse opad i lageret fra datasegmentets start. 6

val SP = "29" val RA = "31" fun translatefunction(name,parameters,bodyexp,ftable) = let val body = TransExp(bodyExp,emptyTable,ftable,"2") val body2 = movepars parameters 2 @ body val (body3,_,maxreg,spilled) = RegAlloc.registerAlloc body2 ["2"] 2 15 25 0 val (savecode,restorecode,framesize) = saverestore maxreg (4*spilled+4) val prologue = [Mips.LABEL name, Mips.ADDI (SP,SP,Int.toString (~framesize)), Mips.SW (RA,SP,int.toString (4*spilled))] @ savecode val epilogue = restorecode @ [Mips.LW (RA,SP,int.toString (4*spilled)), Mips.ADDI (SP,SP,Int.toString framesize), Mips.JR (RA,[])] in prologue @ body3 @ epilogue end and movepars [] _ = [] movepars (par::pars) reg = Mips.MOVE (par,int.tostring reg) :: movepars pars (reg+1) and saverestore reg size = if reg<16 then ([],[],size) else let val args = (Int.toString reg, SP, int.tostring size) val (save,restore,size1) = saverestore (reg-1) (size+4) in (Mips.SW args :: save, Mips.LW args :: restore, size1) end Litteratur Figur 1: Oversættelse af funktion [1] David A. Patterson and John L. Hennessy. Computer Organization & Design, the Hardware/Software Interface. Morgan Kaufmann, 1998. 7