Vorlesung Compilertechnik Sommersemester 2009 Zielcodeerzeugung M. Schölzel.

Slides:



Advertisements
Ähnliche Präsentationen
Algorithmentheorie 08 – Dynamische Programmierung (1)
Advertisements

der Universität Oldenburg
der Universität Oldenburg
Vorlesung Compilertechnik Sommersemester 2009
Vorlesung Compilertechnik Sommersemester 2008
CPI Der einzelne Befehl braucht immer noch 5 Zyklen (stimmt nicht ganz, einige brauchen weniger!) Was verbessert wird, ist der Durchsatz = #Befehle /
Eine dynamische Menge, die diese Operationen unterstützt,
10.2 Wechselseitiger Ausschluss in Hardware
Vorlesung Compilertechnik Sommersemester 2009 Optimierung M. Schölzel.
Befehlssatz und Struktur
Timm Grams Hochschule Fulda Fachbereich Elektrotechnik und Informationstechnik Rekursive Funktionen in C © Timm Grams, Fulda, (korr.: )
3. Kapitel: Komplexität und Komplexitätsklassen
10. Grundlagen imperativer Programmiersprachen
(kleine!) Java Einführung Mittwoch, Heute Ziel: erstes Java-Programm erstellen Von der Aufgabenstellung bis zur Lösung Grundlagen Einfache.
der Universität Oldenburg
SAP R/3 - Speichermanagement
Gliederung Motivation / Grundlagen Sortierverfahren
Java: Objektorientierte Programmierung
Java: Dynamische Datentypen
Indirekte Adressierung
FH-Hof Indirekte Adressierung Richard Göbel. FH-Hof Einfache Speicherung von Daten Eine "einfache" Deklaration definiert direkt eine Speicherplatz für.
Strukturen. In einer Struktur kann eine beliebige Anzahl von Komponenten (Daten) mit unterschiedlichen Datentypen (im Gegensatz zu Feldern) zusammengefaßt.
ARRAY oder FELD oder VEKTOR
Polymorphie (Vielgestaltigkeit)
Dynamischer Speicher. In einer Funktion wird z.B. mit der Deklaration int i; Speicher auf dem sogenannten Stack reserviert. Wenn die Funktion verlassen.
WS Algorithmentheorie 13 - Kürzeste (billigste) Wege Prof. Dr. Th. Ottmann.
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Tobias Lauer.
Union-Find-Strukturen
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (05 – Elementare Datenstrukturen) Prof. Th. Ottmann.
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
6 Folgen (Teil II - Datenstrukturen und Algorithmen)
Imperative Programmierung
PKJ 2005/1 Stefan Dissmann Ausblick Es fehlen noch: Möglichkeiten zum Strukturieren größerer Programme Umgang mit variabler Zahl von Elementen Umgang mit.
PKJ 2005/1 Stefan Dissmann Rückblick auf 2005 Was zuletzt in 2005 vorgestellt wurde: Klassen mit Attributen, Methoden und Konstruktoren Referenzen auf.
Zusammenfassung Vorwoche
Vorlesung, Wintersemester 2009/10M. Schölzel 1 Optimierungstechniken in modernen Compilern Einführung.
Optimierungstechniken in modernen Compilern
Optimierungstechniken in modernen Compilern
Programmiermethodik SS2007 © 2007 Albert Zündorf, University of Kassel 1 4. Methodenentwurf Gliederung: 1. Einführung 2. Objektdiagramme zur Analyse von.
Programmierung 1 - Repetitorium WS 2002/2003 Programmierung 1 - Repetitorium Andreas Augustin und Marc Wagner Homepage:
Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Kapitel 2: Datenstrukturen
Rekursion Richard Göbel.
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Javakurs FSS 2012 Lehrstuhl Stuckenschmidt
BIT – Schaßan – WS 02/03 Basisinformationstechnologie HK-Medien Teil 1, 11.Sitzung WS 02/03.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure
Einführung in die Programmierung Wintersemester 2009/10 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Grundlagen der Informatik 4 Lehrstuhl für Betriebssysteme 1 Wie werden Funktionen realisiert? Beispiel: int maximum(int x, int y) { int j = x; if (y >
Vom Umgang mit Daten. public void myProgram() { int[] saeulenWerte = new int[world.getSizeX()]; for (int i = 0; i < saeulenWerte.length; i++) { saeulenWerte[i]
Institut für Wirtschaftsinformatik – Software Engineering, JKU Linz 1 Algorithmen und Datenstrukturen SS 2005 Mag.Th. Hilpold u. Dr. A.Stritzinger Institut.
Prof. Dr.-Ing. Franz-Josef Behr
Programmieren in Assembler
BMEVIEEA100 Grundlagen der Programmierung
Hochschule Fulda – FB ET Sommersemester 2014
Code-Optimierung Philipp Bergener Seminar „Übersetzung künstlicher Sprachen“
Mag. Thomas Hilpold, Universität Linz, Institut für Wirtschaftsinformatik – Software Engineering 1 Algorithmen und Datenstrukturen 1 SS 2002 Mag.Thomas.
Wann ist eine Funktion (über den natürlichen Zahlen) berechenbar?
Funktionen. Aufgabe : Eingabe zweier Zahlen ---> Minimum bestimmen Dann nochmals Eingabe zweier Zahlen ---> Minimum bestimmen.
C++ FÜR cOMPUTERSPIELENTWICKLER
Funktionen (Zweck und Eigenschaften) Funktionen sind Unterprogramme, die einen bestimmten Zweck erfüllen Sie zerlegen Probleme in kleine, abgeschlossene.
Dr. Wolfram Amme, Funktionale Programmierung, Informatik II, FSU Jena, SS Funktionale Programmierung.
Praktische Informatik 1
Rekursion – Speicherverwaltung
Programmieren in C Wie speichert C
Vom HW-Automaten zum Prozessor
Implementieren von Klassen
 Präsentation transkript:

Vorlesung Compilertechnik Sommersemester 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 + t0 t3 := a t4 := 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 := a t13 := t5 + t12 b := t13 if t11 then goto next … add r?,r?,r? Ziel- register für t13 bereit- stellen Wert von t5 in Register bereit- stellen Anweisung- art identi- fizieren und Schablone erzeugen Registerinhalte ein-/auslagern Wert von t12 in Register bereit- 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: 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 od rd(s) :=

10 Beispiel: getFreeReg Aufruf: getFreeReg() mit Registerdeskriptor: i rd(i) (t0,w), (a,r) (t2,w), (c,r) (t15,w), (p,r)15 … i rd(i) (t0,w), (t16,w) (t1,w), (a,w) (t2,w), (t18,w) (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) (t15,w), (p,r)15 … i rd(i) (t0,w), (t16,w) (t2,w), (t18,w) (t15,w), (t31,w)15 …

11 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 rd(s) := {(t,r)} if t ist lokal then outCode("mov [bp-sd(t)],s") else outCode("mov [sd(t)],s") fi return 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) (t15,w), (p,r)15 … i rd(i) (t0,w), (a,w) (t1,w), (b,w) (t2,w), (c,w) (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) (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) (t15,w), (p,r)15 …

13 Ü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)

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) (t15,w), (p,w)15 … Rückgabewert: r1 für t1 r0 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) (t15,w), (p,w)15 … Registerdeskriptor: locked i rd(i) (b,w) (t2,w), (c,w) (t15,w), (p,w)15 … locked Registerdeskriptor: Rückgabewert: r0 Erzeugter Spillcode: Keiner i rd(i) (t20,w) (b,w) (t2,w), (c,w) (t15,w), (p,w)15 … locked Zielcode: add r1,r0,r0 Neuer Registerdeskriptor:

15 Übersetzung von Kopieranweisungen (1) Eingabe: 3-Adress-Code-Anweisung x := y, x := := y, x 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;

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) (t15,w), (p,w)15 … Erzeugter Spillcode: keiner Neuer Registerdeskriptor:Registerdeskriptor: locked i rd(i) (b,w) (t2,w), (c,w) (t15,w), (p,w)15 … locked Registerdeskriptor: Rückgabewert: r0 Erzeugter Spillcode: mov [bp-sd(t1)],r0 i rd(i) (t16,w) (b,w) (t2,w), (c,w) (t15,w), (p,w)15 … locked Zwischencode: Keiner Neuer Registerdeskriptor: i rd(i) (t0,w), (a,w) (z,w), (b,w), (t20,w) (t2,w), (c,w) (t15,w), (p,w)15 … locked

17 Übersetzung von Kopieranweisungen (2) := 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 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: Zielcode Algorithmus: SaveRegs(); outCode("label:"); Clear(); Eingabe: 3-Adress-Code-Anweisung goto label Ausgabe: Zielcode Algorithmus: SaveRegs(); outCode("jmp label"); Eingabe: 3-Adress-Code-Anweisung if x then goto l Ausgabe: Zielcode Algorithmus: t := getVarInReg(x); Delete(x,t) SaveRegs(); outCode("BNZ t,l"); … a := t7 label: t8 := a … a := t7 goto label10 label9: … 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 Aktivierungsblock der aufgerufenen Funktion

20 Ü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 := 3 t1 := 4 t2 := call f(t0,t1) x := t2 … push r3 push r2 add sp,#4,sp call f pop r1 add sp,#8,sp 4 undefiniert Rücksprungadresse Aktivierungsblock der Funktion g bp sp 3 QuelltextZwischencodeZielcodeLaufzeitstapel

21 Ü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) { … } … Function f: … push bp mov sp,bp add sp,#16,sp 3 (Parameter b) undefiniert Rücksprungadresse Aktivierungsblock der Funktion g bp 4 (Parameter a) sp QuelltextZwischencodeZielcode bp der rufenden Fkt. spbp Lokale Variablen sp Laufzeitstapel

22 Ü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; } … return t20 … mov r5,[bp+8] mov bp,sp pop bp return 4 (Parameter b) undefiniert Rücksprungadresse Aktivierungsblock der Funktion g 3 (Parameter a) QuelltextZwischencodeZielcode bp der rufenden Fkt. bp Lokale Variablen sp 15 bp sp Laufzeitstapel

Ende der Zielcodeerzeugung Weiter zur OptimierungOptimierung