Post on 05-Apr-2015
Vorlesung CompilertechnikSommersemester 2009
Zielcodeerzeugung
M. Schölzel
2
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.
3
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.
3-Adress-Code
t2 := t1 + t0t3 := at4 := t2 * t3 …
Zielcode-erzeugung
Zwischen-code-
erzeugung
Programmcode
Statische Daten
Heap
Stack
4
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.
…t12 := at13 := t5 + t12b := t13if t11 then goto next…
add r?,r?,r?Ziel-
register für t13 bereit-stellen
Wert von t5 in
Registerbereit-stellen
Anweisung-art identi-
fizieren undSchablone erzeugen
Registerinhalte ein-/auslagern
Wert von t12 in
Registerbereit-stellen
mov [0x1450],r4
mov r7,[0x1454]
add r0,r?,r?add r0,r4,r?add r0,r4,r7
5
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-/Speicher-Architektur,
2-Adress- oder 3-Adress-Architektur, Universeller Registersatz oder Spezialregistersatz, Flache oder tiefe Registerhierarchie.
6
Fiktive Zielarchitektur
Bei den weiteren Erläuterungen betrachten wir eine Zielarchitektur mit folgenden Eigenschaften:
32-Bit-Register: Stackpointer: sp Basepointer: bp Weitere allgemeine Register: r0,…,r15
Operationen und Bedeutung: mov rx,ry: ry := rx mov #imm, rx: rx := imm mov [addr],rx: rx := mem[addr] mov rx,[addr]: mem[addr] := rx add rx,ry,rz: rz := rx + ry add 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
7
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.
8
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.
9
Implementierung von getFreeReg
Eingabe: keineAusgabe: Name eines Registers, dessen Inhalt überschrieben werden kannAlgorithmus getFreeReg:Falls ein i existiert mit 1 i RegNum und rd(i) = dann return iFalls ein i existiert mit 1 i RegNum und für alle (v,x) rd(i) gilt x = r, dann rd(i) := , return iWähle ein s mit 1 s RegNum und s ist nicht gesperrtSpill(s)return s
Eingabe: Registernummer sAusgabe: Zielcode zum Auslagern des RegisterwertesAlgorithmus 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 fiodrd(s) :=
10
Beispiel: getFreeReg
Aufruf: getFreeReg() mit Registerdeskriptor:
Aufruf: getFreeReg() mit Registerdeskriptor:
i rd(i)
(t0,w), (a,r)
(t2,w), (c,r)
0
1
2
(t15,w), (p,r)15…
i rd(i)
(t0,w), (t16,w)
(t1,w), (a,w)
(t2,w), (t18,w)
0
1
2
(t15,w), (t31,w)15…
Rückgabewert:
r1
Erzeugter Spillcode:mov r1,[bp-sd(t1)]mov r1,[sd(a)]
Neuer Registerdeskriptor:
Registerdeskriptor:
Rückgabewert:
r1
Erzeugter Spillcode:Keiner
Neuer Registerdeskriptor:
Registerdeskriptor: i rd(i)
(t0,w), (a,r)
(t2,w), (c,r)
0
1
2
(t15,w), (p,r)15…
i rd(i)
(t0,w), (t16,w)
(t2,w), (t18,w)
0
1
2
(t15,w), (t31,w)15…
11
Implementierung von getVarInReg
Eingabe: Variable tAusgabe: Name des Registers, in dem der Wert der Variable bereitgestellt wurdeAlgorithmus 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 fifird(s) := {(t,r)}if t ist lokal then outCode("mov [bp-sd(t)],s") else outCode("mov [sd(t)],s") fireturn s
12
Beispiel: getVarInReg
Aufruf: getVarInReg(t1) mit Registerdeskriptor:
Aufruf: getVarInReg(t16) mit Registerdeskriptor:
i rd(i)
(t1,w), (b,r)
(t2,w), (c,r)
0
1
2
(t15,w), (p,r)15…
i rd(i)
(t0,w), (a,w)
(t1,w), (b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
Rückgabewert:
r0
Erzeugter Spillcode:mov r0,[bp-sd(t0)]mov r0,[sd(a)]mov [bp-sd(t16)],r0
Neuer Registerdeskriptor:i rd(i)
(t16,r)
(t1,w), (b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
Registerdeskriptor:
Rückgabewert:
r1
Erzeugter Spillcode:Keiner
Neuer Registerdeskriptor:
Registerdeskriptor: i rd(i)
(t1,w), (b,r)
(t2,w), (c,r)
0
1
2
(t15,w), (p,r)15…
13
Übersetzung binärer/unärer Anweisungen
Eingabe: 3-Adress-Code-Anweisung x := y zAusgabe: ZielcodeAlgorithmus:
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 := yAusgabe: ZielcodeAlgorithmus:
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)
14
Beispiel
Übersetzung von t20 := t1 + t16; Aufruf von getVarInReg(t1) und getVarInReg(t16):
Aufruf von getFreeReg()
i rd(i)
(t0,w), (a,w)
(t1,w), (b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
Rückgabewert:
r1 für t1r0 für t16
Erzeugter Spillcode:mov r0,[bp-sd(t0)]mov r0,[sd(a)]mov [bp-sd(t16)],r0
Neuer Registerdeskriptor:i rd(i)
(t16,r)
(t1,w), (b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
Registerdeskriptor:
locked locked
0
0
0
0
1
1
0
0
i rd(i)
(b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
locked
1
1
0
0
Registerdeskriptor:
Rückgabewert:
r0
Erzeugter Spillcode:Keiner
i rd(i)
(t20,w)
(b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
locked
0
0
0
0
Zielcode:add
r1,r0,r0
Neuer Registerdeskriptor:
15
Übersetzung von Kopieranweisungen (1)
Eingabe: 3-Adress-Code-Anweisung x := y, x := k, @x := y, x := @y oder x := &yAusgabe: ZielcodeAlgorithmus:
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;
16
Beispiel
Übersetzung von t20 := z
Übersetzung von t16 := t1
i rd(i)
(t0,w), (a,w)
(z,w), (b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
Erzeugter Spillcode:keiner
Neuer Registerdeskriptor:
Registerdeskriptor:
locked
0
0
0
0
i rd(i)
(b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
locked
0
0
0
0
Registerdeskriptor:
Rückgabewert:
r0
Erzeugter Spillcode:mov [bp-sd(t1)],r0
i rd(i)
(t16,w)
(b,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
locked
0
0
0
0
Zwischencode:
Keiner
Neuer Registerdeskriptor:
i rd(i)
(t0,w), (a,w)
(z,w), (b,w), (t20,w)
(t2,w), (c,w)
0
1
2
(t15,w), (p,w)15…
locked
0
0
0
0
17
Ü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");
18
Übersetzung von Labels und Sprunganweisungen
Eingabe: 3-Adress-Code-Anweisung label:Ausgabe: ZielcodeAlgorithmus:
SaveRegs();outCode("label:");Clear();
Eingabe: 3-Adress-Code-Anweisung goto labelAusgabe: ZielcodeAlgorithmus:
SaveRegs();outCode("jmp label");
Eingabe: 3-Adress-Code-Anweisung if x then goto lAusgabe: ZielcodeAlgorithmus:
t := getVarInReg(x);Delete(x,t)SaveRegs();outCode("BNZ t,l");
… a := t7label: t8 := a …
… a := t7 goto label10label9: …
… a := t7 if t8 then goto label20 b := t9 …
Aktualisieren der Werte im Speicher.
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.
Fortsetzung der Registeralloka-tion.
Belegung der Register für jede Programmausführung
fest. Kein Sichern erforderlich.
19
Aktivierungsblock
Die Aktivierung einer Funktion durch einen Aufruf erfordert im Allgemeinen die Erzeugung eines Aktivierungsblocks im Laufzeitstapel.
Möglicher Aufbau eines Aktivierungsblocks:
Aktuelle Parameter
Rückgabewerte
Rücksprungadresse
Zugriffsverweis
Maschinenregister
Lokale Daten
Aktivierungsblock der rufenden Funktion
Lokale Daten
Temporäre Daten
…
Ausgelagerte Registerwerte im aktuellen Block
Lokale Variablen im aktuellen Block
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
Akt
ivie
rungsb
lock
der
aufg
eru
fenen F
unkt
ion
20
Übersetzung eines Funktionsaufrufs
Eingabe: 3-Adress-Code-Anweisung x := call f(t1,..,tn)Ausgabe: ZielcodeAlgorithmus:
for i := n downto 1 do p := getVarInReg(ti) outCode("push p") Delete(p,ti)od// SaveRegs() erforderlich, falls call einen Basisblock abschließtoutCode("add sp,#4,sp");outCode("call f");// Clear() erforderlich, falls call einen Basisblock abschließtr := getFreeReg()outCode("pop r") // Ergebniswert ladenrd(r) := rd(r) {(x,w)} // vorausgesetzt Stack wächst zu kleineren AdressenoutCode("add sp,#4*n,sp");
int g {…x = f(3,4);…}
t0 := 3t1 := 4t2 := call f(t0,t1)x := t2…
push r3push r2add sp,#4,spcall fpop r1add sp,#8,sp
4
undefiniert
Rücksprungadresse
Aktivierungsblock der Funktion g
bp
sp
3
spsp
Quelltext Zwischencode Zielcode Laufzeitstapel
21
Übersetzung einer Funktionsdeklaration
Eingabe: 3-Adress-Code-Anweisung Function Label:Ausgabe: ZielcodeAlgorithmus:
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){ …}
…Function f:…
push bpmov sp,bpadd sp,#16,sp
3 (Parameter b)
undefiniert
Rücksprungadresse
Aktivierungsblock der Funktion g
bp
4 (Parameter a)
spQuelltext Zwischencode Zielcode
bp der rufenden Fkt.sp bp
Lokale Variablen
sp
Laufzeitstapel
22
Übersetzung einer return-Anweisung
Eingabe: 3-Adress-Code-Anweisung return x:Ausgabe: ZielcodeAlgorithmus:
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;}
…return t20…
mov r5,[bp+8]mov bp,sppop bpreturn
4 (Parameter b)
undefiniert
Rücksprungadresse
Aktivierungsblock der Funktion g
3 (Parameter a)
Quelltext Zwischencode Zielcode
bp der rufenden Fkt.bp
Lokale Variablen
sp
sp
15
bp
sp
Laufzeitstapel
Ende der Zielcodeerzeugung
Weiter zur Optimierung