Einführung - WWW-Docs for TU

Werbung
Vorlesung Compilertechnik
Sommersemester 2009
Zielcodeerzeugung
M. Schölzel
Aufgabe der Zielcodeerzeugung

Erzeugung von Programmcode, der auf der
gewünschten Zielarchitektur ausgeführt werden
kann (oft Assemblercode). Das schließt ein:




Übersetzung der Zwischencodeanweisungen in
Assmeblercode (Code-Selection, Scheduling).
Registerallokation.
Erzeugung einer Laufzeitumgebung für das
Programm.
Speicherorganisation.
2
Einbettung der Zielcodeerzeugung in den
Compiler




Programmkode enthält den aus den Zwischenkodeanweisungen erzeugten
Zielcode und statisch erzeugten Zielcode (z.B. Prolog zur Initialisierung der
Laufzeitumgebung, Prozeduren zur dynamischen Speicherverwaltung)
Erzeugung statischer Daten (globale Variablen, Konstanten)
Heap speichert dynamisch erzeugte Objekte, die in einer Prozedur erzeugt
werden und auch nach dem Verlassen der Prozedur erhalten bleiben sollen.
Stack speichert Rücksprungadressen und lokale Variablen von Prozeduren.
Programmcode
Zwischencodeerzeugung
Statische Daten
3-Adress-Code
t2 := t1 + t0
t3 := a
t4 := t2 * t3
…
Zielcodeerzeugung
Heap
Stack
3
Prinzipielles Vorgehen





Für jede Anweisungsart des 3-Adress-Codes ist eine Schablone für den zu
erzeugenden Zielcode bekannt.
In dieser Schablone müssen noch die Speicherorte (i.Allg. Register), die die Werte
der Variablen des 3-Adress-Codes enthalten, eingetragen werden.
Die benötigten Werte werden durch die Registerallokation an geeigneten Orten zur
Verfügung gestellt.
Während der Zielcodeerzeugung wird in geeigneten Datenstrukturen dieser
Speicherort mitprotokolliert.
Registerallokation erzeugt auch den Zielcode zum Laden/Speichern von
Registerwerten aus dem/in den Speicher.
Registerinhalte ein-/auslagern
mov [0x1450],r4
mov r7,[0x1454]
…
t12 := a
t13 := t5 + t12
b := t13
if t11 then goto next
…
add r0,r4,r7
r?,r?,r?
r0,r?,r?
r0,r4,r?
Anweisungart identifizieren und
Schablone
erzeugen
Wert von
t5 in
Register
bereitstellen
Wert von
t12 in
Register
bereitstellen
Zielregister
für t13
bereitstellen
4
Registerallokation


Ziel: Abbildung der temporären Variablen eines
Zwischencodeprogramms auf eine beschränkte Anzahl
von Prozessorregistern.
Klassifizierung der Registerallokation:



Lokal: Auf den Basisblock beschränkt.
Global: Für Funktionen oder das gesamte Programm.
Vorgehensweise bei der Registerallokation hängt stark
von der Zielarchitektur ab:




Register-/Register-Architektur oder Register-/SpeicherArchitektur,
2-Adress- oder 3-Adress-Architektur,
Universeller Registersatz oder Spezialregistersatz,
Flache oder tiefe Registerhierarchie.
5
Fiktive Zielarchitektur

Bei den weiteren Erläuterungen betrachten wir eine Zielarchitektur
mit folgenden Eigenschaften:

32-Bit-Register:




Operationen und Bedeutung:







Stackpointer: sp
Basepointer: bp
Weitere allgemeine Register: r0,…,r15
mov
mov
mov
mov
add
add
rx,ry: ry := rx
#imm, rx: rx := imm
[addr],rx: rx := mem[addr]
rx,[addr]: mem[addr] := rx
rx,ry,rz: rz := rx + ry
rx,#imm,rz: rz := rx + imm
Dabei sind:



rx, ry, rz  {sp, bp, r0, …, r16},
imm ist ein 32-Bit-Wert,
addr ist eine 32-Bit-Speicheradresse oder addr  {sp, bp, r0, …, r16} oder
addr = bp + imm
6
Lokale Registerallokation für 3-Adress-Register/Register-Architektur: Verwaltungsstrukturen




V ist die Menge aller Variablen im 3-Adress-Code.
Registerdeskriptor rd : {0,…,RegNum – 1}  (V  {w,r}) speichert für
jedes Register die Menge der Variablen, deren Werte sich im Register
befinden sowie deren Lese-/Schreibzustand.
Speicherdeskriptor: sd: V   speichert für jede im Speicher abgelegte
Variable die Speicheradresse (absolut für globale und relativ für lokale
Variablen).
Belegungssituationen der Verwaltungsstrukturen:



Für jede globale Variable a ist durch sd(a) immer ein Speicherplatz festgelegt.
Bei Übersetzung einer Funktion f ist außerdem für jede lokale Variable a in f
durch sd(a) eine relative Adresse festgelegt.
Für eine temporäre Variabel existiert





kein Eintrag in rd oder sd,
nur ein Eintrag in rd oder
nur ein Eintrag in sd oder
ein Eintrag in rd und sd.
Für eine Programmvariable existiert


immer ein Eintrag in sd
möglicherweise auch ein Eintrag in rd; dann befindet sich der aktuelle Wert der
Variablen im Register.
7
Hilfsfunktionen und Schnittstelle der
Registerallokation

Hilfsfunktionen für eine Variable v:











isLocal(v) = True gdw. der Speicherplatz für v im Stapel ist.
addr(v) ist die Adresse des Speicherplatzes von v oder die relative Adresse, die
während des Aufbaus der Symboltabelle festgelegt wurde.
getNextFreeLocalAddress(): Liefert die nächste freie relative Adresse im Stapel
getFreeReg(): liefert den Namen eines Registers, in das ein neuer Wert
geschrieben werden kann.
getVarInReg(v): Erzeugt den erforderlichen Zielcode, um den Wert der
Variablen v in einem Register bereitzustellen.
lockReg(r): Verhindert, dass der Inhalt des Registers r bei folgenden
Registeranforderungen ausgelagert wird.
unlockReg(r): Klar
setRegDeskr(r,x): Danach gilt (x,w) = rd(r) und für alle i: 1  i  RegNum und
i  r  (x,w)  rd(i) und (x,r)  rd(i).
delete(x,r): Danach gilt: (x,w)  rd(r) und (x,r)  rd(r).
clear(): Löscht Einträge im Speicher- und Registerdeskriptor.
saveRegs(): Sichert Register im Speicher, die aktualisierte Werte von
Programmvariablen enthalten.
8
Implementierung von getFreeReg
Eingabe: keine
Ausgabe: Name eines Registers, dessen Inhalt überschrieben werden kann
Algorithmus getFreeReg:
Falls ein i existiert mit 1  i  RegNum und rd(i) =  dann return i
Falls ein i existiert mit 1  i  RegNum und für alle (v,x)  rd(i) gilt x = r,
dann rd(i) := , return i
Wähle ein s mit 1  s  RegNum und s ist nicht gesperrt
Spill(s)
return s
Eingabe: Registernummer s
Ausgabe: Zielcode zum Auslagern des Registerwertes
Algorithmus Spill:
for each (v,w)  rd(s) do
if sd(v) undefiniert then
addr = getNextFreeLocalAddr()
outCode("mov s,[bp-addr]")
sd(v) := addr
else
if v ist global then
outCode("mov s,[sd(v)]")
else
outCode("mov s,[bp-sd(v)]")
fi
fi
od
rd(s) := 
9
Beispiel: getFreeReg

Aufruf: getFreeReg() mit Registerdeskriptor:
Registerdeskriptor:
i
rd(i)
0
Neuer Registerdeskriptor:
i
rd(i)
(t0,w), (a,r)
0
(t0,w), (a,r)
1

1

2
(t2,w), (c,r)
…
(t15,w), (p,r)
2
(t2,w), (c,r)
…
(t15,w), (p,r)
15

Erzeugter Spillcode:
Keiner
Rückgabewert
:
r1
15
Aufruf: getFreeReg() mit Registerdeskriptor:
Registerdeskriptor:
Erzeugter Spillcode:
mov r1,[bp-sd(t1)]
mov r1,[sd(a)]
Neuer Registerdeskriptor:
i
rd(i)
(t0,w), (t16,w)
0
(t0,w), (t16,w)
1
(t1,w), (a,w)
1

2
(t2,w), (t18,w)
…
(t15,w), (t31,w)
2
(t2,w), (t18,w)
…
(t15,w), (t31,w)
i
rd(i)
0
15
Rückgabewert
:
r1
15
10
Implementierung von getVarInReg
Eingabe: Variable t
Ausgabe: Name des Registers, in dem der Wert der Variable bereitgestellt wurde
Algorithmus getVarInReg:
if x{r,w}i:1  i  RegNum und (t,x)  rd(i) then retrun i fi
if i:1  i  RegNum und rd(i) =  then
s := i
else if i:1  i  RegNum und (v,x)  rd(i): x=r then
s := i
else
Wähle ein Register i mit geringen Kosten beim Auslagern und i ist nicht gesperrt
Spill(i)
s := i
fi
fi
rd(s) := {(t,r)}
if t ist lokal then outCode("mov [bp-sd(t)],s")
else outCode("mov [sd(t)],s") fi
return s
11
Beispiel: getVarInReg

Aufruf: getVarInReg(t1) mit Registerdeskriptor:
Registerdeskriptor:
i
rd(i)
0
Neuer Registerdeskriptor:
i
rd(i)

0

1
(t1,w), (b,r)
1
(t1,w), (b,r)
2
(t2,w), (c,r)
…
(t15,w), (p,r)
2
(t2,w), (c,r)
…
(t15,w), (p,r)
15

Erzeugter Spillcode:
Keiner
Rückgabewert
:
r1
15
Aufruf: getVarInReg(t16) mit Registerdeskriptor:
Registerdeskriptor:
i
rd(i)
0
(t0,w), (a,w)
1
(t1,w), (b,w)
2
(t2,w), (c,w)
…
(t15,w), (p,w)
15
Erzeugter Spillcode:
mov r0,[bp-sd(t0)]
mov r0,[sd(a)]
mov [bp-sd(t16)],r0
Rückgabewert
:
r0
Neuer Registerdeskriptor:
i
rd(i)
0
(t16,r)
1
(t1,w), (b,w)
2
(t2,w), (c,w)
…
(t15,w), (p,w)
15
12
Übersetzung binärer/unärer Anweisungen
Eingabe: 3-Adress-Code-Anweisung x := y  z
Ausgabe: Zielcode
Algorithmus:
l := getVarInReg(y); lockReg(l);
r := getVarInReg(z); lockReg(r);
if isTemp(y) then Delete(y,l); if isTemp(z) then Delete(z,r);
t := getFreeReg(x);
unlock(l); unlock(r);
asmmnem := Assembleropertion für 
outCode("asmmnem l,r,t");
setRegDeskr(t,x)
Eingabe: 3-Adress-Code-Anweisung x :=  y
Ausgabe: Zielcode
Algorithmus:
r := getVarInReg(y); lookReg(r);
if isTemp(y) then Delete(y,r);
t := getFreeReg();
unlook(r);
asmmnem := Assembleropertion für 
outCode("asmmnem r,t");
setRegDeskr(t,x)
13
Beispiel

Übersetzung von t20 := t1 + t16; Aufruf von getVarInReg(t1) und
getVarInReg(t16):
Registerdeskriptor:
i
locked
0
(t0,w), (a,w)
0
1
(t1,w), (b,w)
0
2
(t2,w), (c,w)
…
(t15,w), (p,w)
0
15

rd(i)
Erzeugter Spillcode:
mov r0,[bp-sd(t0)]
mov r0,[sd(a)]
mov [bp-sd(t16)],r0
Rückgabewert
:
r1 für t1
r0 für t16
0
Neuer Registerdeskriptor:
i
rd(i)
0
(t16,r)
1
1
(t1,w), (b,w)
1
2
(t2,w), (c,w)
…
(t15,w), (p,w)
0
15
locked
0
Aufruf von getFreeReg()
Registerdeskriptor:
i
rd(i)
0

1
1
(b,w)
1
2
(t2,w), (c,w)
…
(t15,w), (p,w)
0
15
locked
0
Erzeugter Spillcode:
Keiner
Rückgabewert
:
r0
Zielcode:
add r1,r0,r0
Neuer Registerdeskriptor:
i
rd(i)
0
(t20,w)
0
1
(b,w)
0
2
(t2,w), (c,w)
…
(t15,w), (p,w)
0
15
locked
14
0
Übersetzung von Kopieranweisungen (1)
Eingabe: 3-Adress-Code-Anweisung x := y, x := k, @x := y, x := @y oder x := &y
Ausgabe: Zielcode
Algorithmus:
Für x := y:
if ein i und ein k  {r,w} ex. mit (y,k)  rd(i) then
rd(i) := rd(i)  {(x,w)};
else
i := getVarInReg(y)
rd(i) := rd(i)  {(x,w)};
fi
if isTemp(y) then Delete(y,i)
Für x := k:
r := getFreeReg()
outCode("mov #k,r")
setRegDesk(r,x); return;
Für x := &y:
r := getFreeReg()
if isLocal(y) then outCode("mov bp,r"); outCode("add r,#addr(y),y");
else outCode("mov #addr(y),r")
setRegDesk(r,x); return;
15
Beispiel

Übersetzung von t20 := z
Registerdeskriptor:
i
locked
Neuer Registerdeskriptor:
i
rd(i)
locked
0
(t0,w), (a,w)
0
0
(t0,w), (a,w)
0
1
(z,w), (b,w)
0
1
(z,w), (b,w), (t20,w)
0
2
(t2,w), (c,w)
…
(t15,w), (p,w)
0
2
0
0
15
(t2,w), (c,w)
…
(t15,w), (p,w)
15

rd(i)
Erzeugter Spillcode:
keiner
0
Übersetzung von t16 := t1
Registerdeskriptor:
i
rd(i)
0

0
1
(b,w)
0
2
(t2,w), (c,w)
…
(t15,w), (p,w)
0
15
locked
0
Erzeugter Spillcode:
mov [bp-sd(t1)],r0
Rückgabewert
:
r0
Zwischencode
:
Keiner
Neuer Registerdeskriptor:
i
rd(i)
0
(t16,w)
0
1
(b,w)
0
2
(t2,w), (c,w)
…
(t15,w), (p,w)
0
15
locked
0
16
Übersetzung von Kopieranweisungen (2)
Für @x := y:
l := getVarInReg(x); lockReg(l);
r := getVarInReg(y);
unlock(l);
if(isTemp(y) then Delete(y,r);
if(isTemp(x) then Delete(x,l);
outCode("mov r,[l]");
Für x := @y:
r := getVarInReg(y); lockReg(r);
l := getFreeReg()
unlock(r);
if(isTemp(y) then Delete(y,r);
rd(l) := rd(l)  {(x,w)}
outCode("mov [r],l");
17
Übersetzung von Labels und
Sprunganweisungen
Eingabe: 3-Adress-Code-Anweisung label:
Ausgabe: Zielcode
Algorithmus:
SaveRegs();
outCode("label:");
Clear();
Aktualisieren der Werte im
Speicher.
…
a := t7
label:
t8 := a
…
Eingabe: 3-Adress-Code-Anweisung goto label
Ausgabe: Zielcode
Algorithmus:
…
SaveRegs();
outCode("jmp label");
a := t7
goto label10
label9:
…
Eingabe: 3-Adress-Code-Anweisung if x then goto l
Ausgabe: Zielcode
Algorithmus:
t := getVarInReg(x);
Delete(x,t)
SaveRegs();
outCode("BNZ t,l");
Einsprung von verschiedenen
Position möglich; Belegung der
Register unklar.
Sprung zu einer Position an
der der Registerdeskriptor
gelöscht wird; Aktualisieren der
Wert eim Speicher nötig.
Hier wird die Registerallokation
fortgesetzt.
Sprung zu einer Position an
der der Registerdeskriptor
gelöscht wird; Aktualisieren der
Wert eim Speicher nötig.
…
a := t7
if t8 then goto label20
b := t9
Fortsetzung der
Registeralloka-tion. Belegung
…
der Register für jede
Programmausführung fest.
18
Aktivierungsblock

Temporäre Daten
Lokale Daten
Ausgelagerte Registerwerte im aktuellen Block
Lokale Variablen im aktuellen Block
…
Aktivierungsblock der aufgerufenen Funktion

Die Aktivierung einer Funktion durch einen Aufruf erfordert im Allgemeinen
die Erzeugung eines Aktivierungsblocks im Laufzeitstapel.
Möglicher Aufbau eines Aktivierungsblocks:
Lokale Daten
Maschinenregister
Zugriffsverweis
Rücksprungadresse
Rückgabewerte
Aktuelle Parameter
Lokale Variablen in Blöcken, die den aktuellen Block enthalten
Gesicherter Maschinenstatus der rufenden Funktion (z.B. Registerinhalte, Statusregister)
Verweis auf den Aktivierungsblock der rufenden Funktion
Adresse des rufenden call-Befehls
Rückgabewert, falls vorhanden (kann sich aber auch in einem Register befinden)
Aktuelle Parameter der aufgerufenen Funktion
Aktivierungsblock der
rufenden Funktion
19
Übersetzung eines Funktionsaufrufs
Eingabe: 3-Adress-Code-Anweisung x := call f(t1,..,tn)
Ausgabe: Zielcode
Algorithmus:
for i := n downto 1 do
p := getVarInReg(ti)
outCode("push p")
Delete(p,ti)
od
// SaveRegs() erforderlich, falls call einen Basisblock abschließt
outCode("add sp,#4,sp");
outCode("call f");
// Clear() erforderlich, falls call einen Basisblock abschließt
r := getFreeReg()
outCode("pop r")
// Ergebniswert laden
rd(r) := rd(r)  {(x,w)}
// vorausgesetzt Stack wächst zu kleineren Adressen
outCode("add sp,#4*n,sp");
int g {
…
x = f(3,4);
…
}
t0
t1
t2
x
…
:=
:=
:=
:=
3
4
call f(t0,t1)
t2
push r3
push r2
add sp,#4,sp
call f
pop r1
add sp,#8,sp
sp
sp
Zwischencode
Zielcode
undefiniert
3
4
sp
bp
Quelltext
Rücksprungadresse
Aktivierungsblock der
Funktion g
20
Laufzeitstapel
Übersetzung einer Funktionsdeklaration
Eingabe: 3-Adress-Code-Anweisung Function Label:
Ausgabe: Zielcode
Algorithmus:
outCode("push bp")
outCode("mov sp,bp");
// aktuelle Parameter sind über positive Offsets
// größer gleich 12 erreichbar
// lokale Variablen mit negativen Offsets
outCode("add sp,#frameSize,sp")
int f(int a, int b)
{
…
}
Quelltext
…
Function f:
…
push bp
mov sp,bp
add sp,#16,sp
sp
Lokale Variablen
sp bp
sp
Zwischencode
Zielcode
bp der rufenden Fkt.
Rücksprungadresse
undefiniert
4 (Parameter a)
3 (Parameter b)
Aktivierungsblock der
Funktion g
bp
21
Laufzeitstapel
Übersetzung einer return-Anweisung
Eingabe: 3-Adress-Code-Anweisung return x:
Ausgabe: Zielcode
Algorithmus:
r := getVarInReg(x)
outCode("mov r,[bp+8]");
SaveRegs();
outCode("mov bp,sp");
outCode("pop bp");
outCode("return");
int f(int a, int b)
{
…
return 15;
}
Quelltext
…
return t20
…
Zwischencode
mov r5,[bp+8]
mov bp,sp
pop bp
return
sp
Lokale Variablen
sp bp
sp
Zielcode
bp der rufenden Fkt.
Rücksprungadresse
undefiniert
15
3 (Parameter a)
4 (Parameter b)
Aktivierungsblock der
Funktion g
bp
22
Laufzeitstapel
Ende der Zielcodeerzeugung
Weiter zur Optimierung
Herunterladen