Sven Wagner
im August 2007
1. Zusammenfassung 6
2. Einleitung 8
2.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2. Zielsetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3. Aufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3. Grundlagen 11
3.1. Die Prozedur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2. Der Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3. Der Frame Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.4. Der Current Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.5. Programmbeispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
5. Das Backend 27
5.1. Die Run Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2. Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2.1. Der Task Frame . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2.2. Der Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.3. Die Header Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.4. Der von CESC generierte C Code . . . . . . . . . . . . . . . . . . . . . . . 35
5.4.1. C Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2
5.4.2. CES Prozedur Aufruf . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.4.3. Verwendung einer CES Variablen . . . . . . . . . . . . . . . . . . . 39
5.4.4. Den Storage erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.4.5. Kopie des Current Stack auf den Frame Stack . . . . . . . . . . . . 41
5.4.6. Die Print Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
7. Ausblick 47
A. CESC Handbuch 50
A.1. Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
A.1.1. Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
A.1.2. CESC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
A.1.3. Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
A.1.4. Parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
A.1.5. Run Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
A.1.6. Pretty Printer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
A.1.7. Beispielprogramm . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
B. Testumegbung 53
B.1. Dateien im Verzeichnis CESC/Tests . . . . . . . . . . . . . . . . . . . . . 53
3
Abbildungsverzeichnis
4
Listings
2.1. Pseudoprogrammbeispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5.1. Funktion aus der Run Time zur Ausfürung eines Task . . . . . . . . . . . 27
5.2. TASK_FRAME Datenstruktur aus der run_time.h . . . . . . . . . . . . . 27
5.3. Beispiel einer von CESC generierten Task Struktur im Header File . . . . 28
5.4. Beispiel zur Parameterprüfung . . . . . . . . . . . . . . . . . . . . . . . . 29
5.5. Datenstruktur des Tasks euclid(b,c;;double erg2) . . . . . . . . . . . . . . 29
5.6. STORAGE_FRAME Datenstruktur . . . . . . . . . . . . . . . . . . . . . 31
5.7. Storage Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.8. CES Programm zur Storage Erzeugung . . . . . . . . . . . . . . . . . . . . 32
5.9. Datenstruktur eines Storage für double a . . . . . . . . . . . . . . . . . . . 33
5.10. CES Prozedur printVar(double a;;) . . . . . . . . . . . . . . . . . . . . . . 34
5.11. Datenstruktur des Tasks print_Var(double a;;) . . . . . . . . . . . . . . . 35
5.12. CES Programmbeispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5
1. Zusammenfassung
Die vorliegende Diplomarbeit mit dem Titel Konzeption und Entwicklung eines
neuen Compiler „CESC“ zur Implementierung von Prozeduren als atomare
Tasks beschreibt eine Neuimplementierung von Prozeduren als atomare Tasks.
Als Grundlage für diese Diplomarbeit dient die Veröffentlichung Task Frames [1] von
Dr. Burkhard Steinmacher-Burow (IBM Deutschland Entwicklung GmbH) sowie die Po-
werPoint Präsentation The Execution of Computer Applications[2] (bei Interesse bitte
Email an steinmac@de.ibm.com). In der Arbeit Task Frames[1] wird das Konzept von
Task Frames beschrieben sowie eine grobe Implementierung von Prozeduren als atomare
Tasks. Diese Diplomarbeit greift das von Dr. Steinmacher-Burow entwickelte Konzept
auf und zeigt, dass es möglich ist die beschriebene Theorie und den praktischen Ansatz
für das Execution System in eine weiterbenutzbare Neuimplementierung umzusetzen.
Der entwickelte Compiler CESC (C with Execution System Compiler) basiert auf der
Implementierung des Compilers für die Sprache SPL von Dr. Hellwig Geisse, Professor an
der Fachhochschule Giessen-Fiedberg, der zu Lehrzwecken entwickelt wurde. Ziel war es
den Aufwand für die Entwicklung des Compilers zu minimieren, deswegen wurde auf eine
altbewährte Entwicklung im Compilerbau zurückgegriffen und eine getestete Struktur des
SPL Compilers benutzt.
Die Programmiersprache C wurde durch ein neues Sprachkonzept erweitert und als
neue Sprache CES definiert. Das C in CES steht für die Programmiersprache C, da
diese als Basis dient, um die Sprache CES zu entwickeln. Es ist weiterhin möglich die
Programmiersprache C zu benutzen. ES bedeutet Execution System. Das Execution Sys-
tem steuert die Ausführung einer Prozedur als Task, hierzu ist für die Ausführung der
atomaren Tasks eine Verwaltung nötig, die ebenfalls vom ES übernommen wird.
Der im Rahmen dieser Arbeit entwickelte Compiler CESC generiert aus CES Quelltext
einen C Quelltext. Alle in CES definierten Prozeduren werden als atomare Tasks von dem
CES Compiler übersetzt, das heisst sie werden als C Funktion implementiert. Durch eine
eigens entwickelte Run Time, die zusammen mit dem vom CES Compiler generierten C
File zum ausführbaren Programm kompiliert wird, werden die erzeugten Tasks als neu
implementierte Funktionen ausgeführt.
Nach einer Einführung in das Thema in Kapitel 2 beinhaltet die Diplomarbeit in Kapi-
tel 3 eine Beschreibung der neu definierten und verwendeten Begriffe für die Entwicklung
von CES und dem Compiler CESC. Eine genaue Beschreibung der Erweiterung von C
zu CES erfolgt in Kapitel 4. Das Kapitel 5 ist der Kern dieser Diplomarbeit. In diesem
Kapitel wird genaustens beschrieben wie die Ausführung der Tasks erfolgt und welche
Strukturen dazu nötig sind. Kapitel 6 umfasst wie der CES Compiler entwickelt wur-
de und welche Phasen zur Übersetztung nötig sind. Zusätzlich wird in diesem Kapitel
beschrieben welche Konstrukte nötig waren um das beschriebene Konzept der Neuimple-
6
mentierung durch CESC zu ermöglichen.
Das Ziel dieser Diplomarbeit ist nicht eine neue Programmiersprache zu entwickeln,
sondern das Prinzip einer Neuimplementierung einer Prozedur als atomarer Task zu
verdeutlichen.
7
2. Einleitung
Programmiersprachen beinhalten in ihrem Quellcode mitunter Befehle, Definitionen von
Funktionen und Aufrufe von Funktionen. Zur Komplexitätsbewältigung wird das Kon-
zept der Modularisierung verwendet. Das heisst, dass ein komplexes Programm durch
kleinere Teilprogramme (Funktionen) beschrieben wird. Diese können unabhängig von-
einander definiert werden, funktionieren aber als Ganzes. CES benutzt genau diese Art
von Vereinfachung. In CES ist es wie in C möglich aus mehreren Dateien ein Programm
zu erstellen. Das Teilprogramm wird in CES als CES Prozedur definiert. Die Implemen-
tierung der CES Prozedur erfolgt durch eine C Funktion.
Ein CES Quelltext ist ähnlich wie ein C Quelltext. Der Unterschied liegt in der Ausfüh-
rung von CES Prozeduren gegenüber Prozeduren oder Funktionen aus anderen Sprachen.
In anderen Sprachen wird ein Ausfruf einer Prozedur sofort ausgeführt. In CES, innerhalb
der ausführenden Prozedur, werden alle CES Prozedur Aufrufe gesammelt. Am Ende der
ausführenden Prozedur werden die gesammelten Aufurfe dem Execution System überge-
ben. Das Execution System führt dann den nächsten Aufruf aus. Der Ablauf einer CES
Prozedur ist somit ein atomarer Task, da er nicht von dem Ablauf anderer Prozeduren
abhängig ist.
Eine CES Prozedur wird ausgeführt durch einen Task Frame. Eine Prozedur aus einer
anderen Programmiersprache wird durch ein Activation Frame ausgeführt. Ein Beispiel
[1] soll nochmal den Unterschied der Ausführung zwischen dem Konzept der Activation
Frames und dem der Task Frames verdeutlichen.
1 a () { ... }
2 b() { ... }
3 c () { ... a () . . . }
4 d() { ... b() . . . c () . . . }
Listing 2.1: Pseudoprogrammbeispiel
Die Prozedur „d“ wird ausgeführt. Bei dem bekannten Konzept der Activation Frames
ist die Prozedur „d“ von Anfang bis Ende auf dem Prozedurstack vorhanden. Betrachtet
man das Konzept der Task Frames, so kann man sagen, dass ein Task durch andere
„ersetzt“ wird. Tatsächlich ist es aber so, dass das Execution System speichert in welcher
Reihenfolge die Tasks ausgeführt werden müssen. Erst wenn ein Task beendet ist, wird
der nächste Task ausgeführt. In dem aufgeführten Beispiel wird der Task d durch die
Tasks b und c „ersetzt“.
8
Abbildung 2.1.: Activation Frames[1]
2.1. Motivation
Bewährte Systeme wie zum Beispiel Seti@home oder auch diverse Datenbanken zeigen,
dass es bereits Anwendungsgebiete gibt, die das Konzept von atomaren Tasks verwenden
und erfolgreich einsetzen.
Im Hinblick auf die aktuelle Entwicklung zu Mehrprozessorsystemen entwickelt die
nachfolgende Diplomarbeit ein Execution System, dass die einzelnen Task Frames parallel
auf mehreren CPUs ausführt.
2.2. Zielsetzung
Ziel ist es Prozeduren als atomare Tasks zu implementieren. Es wurde die Program-
miersprache C erweitert und verwendet. Hierbei ist darauf geachtet worden, dass die
Programmiersprache C weiterhin verwendet werden kann. Dies war Vorrausetzung für
diese Arbeit, da es nicht Ziel ist in der vorgegebenen Zeit eine neue Sprache zu entwi-
ckeln, sondern Vorteile der Programmiersprache C nutzen zu können. Zum Beispiel soll
es weiterhin möglich sein C Bibliotheken per Include-Anleitung für eine seperate Kompi-
lierung benutzten zu können. C biete außerdem die Möglichkeit der Zeigerarithemtik mit
der einfach Referenzen gebildet werden können. Es wurde auch bewusst eine dem Autor
bekannte Programmiersprache gewählt damit ein zügiges Implementieren garantiert ist.
Zur Präsentation des Programmablaufs soll ein Pretty Printer implementiert werden, der
9
den Stack mit allen wichtigen Informationen zum Verstehen des aktuellen Status und der
zukünftigen Ausführung des Programms ausgibt. Ergebnisse dieser Diplomarbeit dienen
als Basis für die Weiterentwicklung von CES und dem Execution System (Kapitel „Das
Backend“), um einmal atomare Tasks parallel auf mehreren Prozessoren ausführen zu
können.
2.3. Aufbau
Zu Beginn der Diplomarbeit wurde ein Beispielprogramm entwickelt, welches die Aus-
gabe des CES Compilers darstellt. Hierzu wurde aus der Veröffentlichung Task Frames
[1] von Dr. Steinmacher-Burow die doch sehr einfach gehaltene Implementierung aufge-
griffen und als Basis für eine erste Testimplementierung benutzt. Es sollte klar werden
welche Codefragmente für die Neuimplementierung von Prozeduren als Tasks vom CES
Compiler generiert werden sollen. Aus diesem Beispielprogramm wurde ersichtlich wie die
Run Time implementiert werden muss, um die gleiche Funktion zu beinhalten. Daraufhin
erfolgte die Entwicklung des CES Compilers zur Generierung eines C Files, dass zusam-
men mit der Run Time von einem C Compiler übersetzt, ein ausführbares Programm
erzeugt, welches das gleiche Ergebnis liefert.
Im Konzept der Activation Frames dient der Activation Frame zur Übermittlung von
Informationen zwischen den Subfunktionen. Für diesen Mechanismus mußte eine Alter-
native geschaffen werden. Im CES dienen sogenannte Storages als Vermittler zwischen
den Subtasks.
Der Aufbau der Stacks zum Speichern und verwalten der Tasks wurde entwickelt. Die-
ser Stack ist ähnlich dem Funktionsstack in C. Passende Strukturen für den Stack und die
Storages wurden anschliessend entwickelt. Es mussten Datenstrukturen definiert werden
und welche Daten diese Strukturen enthalten müssen, um die gewünschte Funktionalität
erbringen zu können.
Der nächste Schwerpunkt der Diplomarbeit beinhaltet die Entwicklung des CES Com-
pilers. Dieser umfasst folgende Phasen: Der Scanner dient dazu den CES-Quelltext ein-
zulesen und sogenannte Tokens für den Parser zu generieren. Die im Parser definierte
kontexfreie Grammatik dient zur Überprüfung des Tokenstroms aus dem Scanner. Bei Zu-
treffen eines eingelesenen Tokenstroms in den endlichen Automaten des Parsers wird ein
vordefinierte Aktion ausgeführt. Folge hiervon ist die Erstellung eines abstrakten Syntax-
baums. Parallel kommt es zu einer Überprüfung der Semantik. Mit Hilfe des Syntaxbau-
mes werden zuerst Symboltabellen für die einzelnen Prozeduren erstellt, um anschliessend
die Codegenerierung in C zu ermöglichen. Abschließend wird das vom CES-Compiler ge-
nerierte C-File zusammen mit der Runtime als Eingabedatum für den C-Compiler einge-
geben. Das Endprodukt ist ein ausführbares Programm, dass jeden Aufruf einer Prozedur
als atomarer Task ausführt.
10
3. Grundlagen
Diese Diplomarbeit baut auf altbewährte und bekannte Grundlagen aus der Informatik
auf. Die Begriffe Prozedur und Task sind typische Begriffe aus der Informatik. In diesem
Kapitel werden die in der Arbeit verwendeten Begriffe beschrieben und erklärt um die
neu definierten Konzepte von CES verständlicher zu machen. Am Ende dieses Kapitels
verdeutlicht ein Programmbeispiel die beschriebenen Begriffe aus der vorliegenden Arbeit
in dem dazugehörigen Kontext.
11
wie bei Activation Frames möglich, dass ein Task einen anderen Task sofort aktiviert und
ausgeführt wird. Betrachtet man den Ablauf auf dem Stack so gewinnt man den Eindruck,
dass der aktuelle Task durch seine Aufrufe ersetzt wird.
12
f sp /8 Task E(;x;)
7 Storage(x)
6 Task D
5 Task C
4 Task B(;;a,b)
3 Storage(b)
2 Storage(a)
f sp_bottom /1 Task A
1 TaskA ( ; ; ) { // E l t e r n Task
2 TaskB ( ; ; ) ; // Kind Task
3 TaskC ( ; ; ) ; // Kind Task
4 }
Listing 3.2: TaskA wird ersetzt durch TaskB und TaskC
Zuerst wird TaskB und dann TaskC auf dem Current Stack erzeugt.
Danach wird der komplette Current Stack auf den Frame Stack kopiert.
3.5. Programmbeispiel
Das in diesem Abschnitt gezeigte Programmbeispiel verdeutlicht die Ausführung eines
CES Programms. Es sollte eindeutig zu erkennen sein, wo der wesentliche Unterschied
13
Frame Stack Current Stack
2 1 2 TaskB
1 TaskA 2 1 TaskC
q
2 TaskB 1 TaskB
q
1 TaskC 2 TaskC
zum Ablauf eines vergleichbaren C Programms liegt. Beide Programme haben die gleiche
Definition und das gleiche Ergebnis: Den Wert 100.0 der Variable a auf dem Monitor
auszugeben. Die Ausführung des Programms ist allerdings unterschiedlich.
Bei dem bekannten C Programm wird zuerst die Funktion main() ausgeführt. In der
main() wird eine Variable mit dem Bezeichner a definiert und an die Funktion setVal(;;)
per Call by Reference übergeben. In der Funktion setVal wird dann a der Wert 100.0
zugewiesen. Danach wird die Funktion printVar() aufgerufen, zu beachten ist, dass die
Funktion main weiterhin im Arbeitsspeicher vorhanden ist. Die printf Anweisung wird
also ausgeführt und die Funktion printVar wird beendet. Danach wird die Funktion
main() wieder reaktiviert um sich letztendlich mit dem return Befehl zu beenden.
1 #include <s t d i o . h>
2
3 void s e t V a l ( double ∗a ) {
4 p r i n t f ( " w e i s e ␣ a ␣ den ␣ wert ␣ 1 0 0 . 0 ␣ zu \n" ) ;
5 ∗a = 1 0 0 ;
6 }
7
8 void p r i n t V a r ( double a ) {
9 p r i n t f ( " Die ␣ V a r i a b l e ␣a ␣ hat ␣ den ␣Wert␣%f \n" , a ) ;
10 }
11
14
12 int main ( void ) {
13
14 double a ;
15
16 s e t V a l (&a ) ; // c a l l by r e f e r e n c e
17 p r i n t V a r ( a ) ; // c a l l by v a l u e
18
19 return 0 ;
20 }
Listing 3.3: C Programm
Bei dem CES Programm ist der Ablauf ähnlich, aber es gibt doch einen wesentlichen
Unterschied. Der CES Compiler generiert ein C File, dass die definierten Prozeduren als
Task implementiert. Das vorliegende Beispiel verdeutlicht den Ablauf bei der Ausführung
des Programms.
1 #include <s t d i o . h>
2
3 $program ( ; ; ) {
4 $ s e t V a l ( ; ; double a ) ; $
5 $printVar ( a ; ; ) ; $
6 }$
7
8 $ s e t V a l ( ; ; double a ) {
9 p r i n t f ( " w e i s e ␣ a ␣ den ␣ wert ␣ 1 0 0 . 0 ␣ zu \n" ) ;
10 $a$ = 1 0 0 . 0 ;
11 }$
12
13 $ p r i n t V a r ( double a ; ; ) {
14 p r i n t f ( " Die ␣CES␣ V a r i a b l e ␣a␣ hat ␣ den ␣Wert␣%f \n" , $a$ ) ;
15 }$
Listing 3.4: CES Programm
Abbildung 3.4 zeigt nochmal den Frame Stack während der Ausführung des CES Pro-
gramms.
Die Run Time erzeugt auf dem Frame Stack den ersten Task program(;;) und beginnt
mit der Ausführung der Prozedur program(;;).
F rameStack CurrentStack
program(; ; )
Der Task program(;;) wird auf dem Frame Stack erzeugt
15
Abbildung 3.4.: Frame Stack während der Ausführung des CES Programms
Nun wird der Task program(;;) ausgeführt. Der Kinder Task setVal(;;a) wird auf dem
Current Stack erzeugt.
F rameStack CurrentStack
Aber es erfolgt keine Ausführung von setVal(;;a)! Der in dem Task definierte
Output Parameter erzeugt als nächstes ein Storage Frame a auf dem Frame Stack der
diesen Parameter beinhaltet. Dieser Outputparameter referneziert nun auf den Storage
a. Dieser Storage ist mit dem Wert 0 initialisiert, damit die Ausgabe des Pretty Printers
keine undefinierten Zeichenketten ausgibt.
F rameStack CurrentStack
Storage(a) = 0
hQQQ
QQQ
QQQ
QQQ
Als nächstes wird der Task printVar(a;;) auf dem Current Stack erzeugt und alle Refe-
renzen werden aufgelöst, d.h. in diesem Fall wird eine Referenz von dem Input Parameter
a auf den Storage a gebildet.
16
F rameStack CurrentStack
Zuletzt werden alle von program(;;) erzeugten Tasks, die auf dem Current Stack liegen,
auf den Frame Stack kopiert. Man kann sagen, dass program(;;) durch setVal(;;a) und
printVar(a;;) ersetzt wird. Der Current Stack ist nun leer.
F rameStack CurrentStack
setV al(; ; a)
printV ar(a; ; )
O
y
Storage(a) = 0
Der Current Stack wurde auf den Frame Stack kopiert
Der Task program(;;) ist somit zu Ende und es kann der zuvor belegte Speicherbereich
freigegeben werden. Die Run Time führt den nächsten Task aus. In diesem Beispiel ist
das der Task setVal(;;a). Beim Ausführen von setVal(;;a) wird nun dem Storage a der
Wert 100.0 zugewiesen.
17
F rameStack CurrentStack
setV al(; ; a)
printV ar(a; ; )
O
y
Storage(a) = 100.0
Der Task setVal(;;a) wird ausgeführt
Danach wird der Task setVal(;;a) beendet und printVar(a;;) wird als nächstes ausge-
führt.
F rameStack CurrentStack
printV ar(a; ; )
O
Storage(a) = 100.0
Der Task printVar(a;;) wird ausgeführt.
Der Task printVar(a;;) erzeugt die Ausgabe auf dem Monitor und endet.
F rameStack CurrentStack
Storage(a) = 100.0
Der Task printVar(a;;) erzeugt die Ausgabe und endet.
Der Storage hat keine weitere Bedeutung bzw. wird nicht mehr von einem anderen
Task benötigt und kann somit freigegeben werden. Das Programm ist somit beendet. Der
Pretty Printer erzeugt die Ausgabe aus der folgenden Abbildung 3.5. Die Nummerierung
an der linken Seite der Ausgabe bezeichnet die Nummer des Frames auf dem Frame Stack.
Es kann somit nochmal der genaue Ablauf des CES Programms nachvollzogen werden.
18
*****************EXECUTE 1****************
1 program(;;)
*****************EXECUTE 2****************
3 setVal(;;double a_1=0.000000)
2 printVar(double a_1=0.000000;;)
weise a den wert 100.0 zu
*****************EXECUTE 3****************
2 printVar(double a_1=100.000000;;)
Die CES Variable a hat den Wert 100.000000
19
4. Die Sprache CES
Die Sprache CES ist eine Erweiterung der Programmiersprache C, die als neues Feature
eine Prozedur als Task implementiert. Für diese neu implementierten Prozeduren wurde
eine neue Syntax eingeführt. Es kann weiterhin die Programmiersprache C benutzt wer-
den. Im folgenden werden die neueingeführten Sprachkonzepte von CES aufgeführt und
anhand einiger Beispiele deutlich gemacht.
program.ces f oo.ces
20
4.1.2. Einordnung der Programmiersprache CES
CES gehört zu den imperativen und deklarativen Programmiersprachen, wie auch die
Programmiersprache C.
Lexikalische Einheiten
Zu Beginn der Kompilierung eines CES Quellprogrammes liegt das Programm in Form
der anschließenden lexikalischen Einheiten, auch Token genannt vor.
• Namen (Bezeichner, speziell für CES Prozedur Definition und Aufruf, sowie Varia-
blennamen)
• reservierte Wörter
• C Code
Reservierte Wörter
program Zur Vereinfachung der Implementierung beginnt ein CES Programm mit der
Ausführung der Prozedur program(;;). Andere Prozeduren können mit selbstge-
wähltem Namen bezeichnet werden. Hierbei sind gewisse Konventionen für die
Namensbildung einzuhalten (siehe Abschnitt Die CES Prozedur ). Der Name pro-
gram(;;) ist natürlich exklusiv für das Hauptprogramm reserviert und darf in einem
CES Programm nicht ein zweites Mal als Prozedur definiert werden. Die Prozedur
program(;;) dient als „Einsprung“ in das CES Programm und wird von der Run
Time im Backend erzeugt. Diese Prozedur ist der Anfangspunkt wie die Funktion
main() aus der Programmiersprache C.
parallel Dieses Schlüsselwort dient dazu Prozeduraufrufe als parallel ausführbar zu mar-
kieren. Die Möglichkeit der Funktion hierzu wurde noch nicht umgesetzt; alle CES
Programme laufen somit noch sequentiell ab. Die technische Umsetzung ist für eine
erweiterte Version von CESC und dem Execution System geplant.
21
4.1.4. Beispielprogramme
Aufbau eines CES-Programmes
Ein CES Programm besteht aus CES Prozeduren, deren Aufrufen, Variablen und C Code.
Die Prozeduren nehmen die Rolle von Haupt- und Unterprogrammen ein. Sie haben dabei
die Aufgabe Teile eines Programmes unter eigenem Namen zusammenzufassen. Mit Hilfe
des Prozedurnamens werden CES Prozeduren innerhalb eines Programmes aufgerufen.
Dabei besteht die Möglichkeit der Prozedur beim Aufruf Argumente zu übergeben. Der
Austausch von Informationen kann zum Beispiel in den CES Ausgabeparametern erfolgen
oder auch über globale C Variablen. Bei der Ausführung wird der Task durch seine Kinder
Tasks ersetzt. Wie bei C Funktionen können in CES die Prozeduren nicht geschachtelt
definiert werden. Dies bedeutet, dass es in CES nicht möglich ist eine Prozedur innerhalb
einer Prozedur zu definieren.
3 $program ( ; ; ) {
4 p r i n t f ( " Hallo , Welt ! " ) ; /∗ C Code ∗/
5 }$
Listing 4.1: CES Programm Hallo, Welt!
3 $program ( ; ; ) {
4 int a ;
5 int b ;
6 int e r g e b n i s = 0 ;
7 p r i n t f ( "\ t E u c l i d ( a , b ) \n" ) ;
8 p r i n t f ( "␣ a ␣ e i n g e b e n ␣ " ) ;
9 s c a n f ( "%d" , &a ) ;
10 p r i n t f ( "␣b␣ e i n g e b e n ␣ " ) ;
11 s c a n f ( "%d" , &b ) ;
12 $ e u c l i d ( int a , int b ; int e r g e b n i s ; ) ; $
13 $ p r i n t R e s u l t ( int e r g e b n i s ; ; ) ; $
14 }$
15 $ p r i n t R e s u l t ( int e r g ; ; ) {
16 p r i n t f ( "␣ E r g e b n i s ␣=␣%d\n" , $ e r g $ ) ;
17 }$
22
18 $ e u c l i d ( int a , int b ; int e r g ; ) {
19 i f ( $b$ == 0 )
20 $ e r g $ = $a$ ;
21 else {
22 int c = $a$ % $b$ ;
23 $ e u c l i d ( b , int c ; e r g ; ) ; $
24 }
25 }$
Listing 4.2: CES Programm Euklid’scher Algorithmus
Alle Datentypen aus C, die aus einem Bezeichner bestehen können verwendet wer-
den, zum Beispiel char, int, float, double, sowie zusammengesetzte bzw. selbstdefinierte
Datentypen aus C.
Variabeln können als Parameter oder C-Variable definiert werden. Die Sichtbarkeit
und Lebensdauer einer CES-Variable bezieht sich immer auf die CES Prozedur in der die
Variable definiert wurde. Somit vergleichbar mit dem Prinzip der lokalen Variablen aus
diversen anderen Programmiersprachen.
23
Buchstaben, Ziffern und einem „Unterstrich“ bestehen (diese Konvention wurde aus der
Programmiersprache C übernommen). Der reguläre Ausdruck hierfür ist in der scanner.l
zu finden. Die drei Arten von Parameter Input, InOutput und Output müssen durch
Semikolons getrennt werden. Durch diese Trennung können Abhängigkeiten zwischen
den Prozeduren besser deutlich gemacht werden. Beinhaltet zum Beispiel ein Task A
einen Parameter x als Ausgabeparameter, so erkennt man bei anderen definierten Tasks
welche diesen Parameter als Eingabeparameter benutzen (siehe Abbildung 4.2).
$ProzedurName(inputParamListe;inOutputParamListe;outputParamListe){...}$
int parameter
Die folgende Abbildung 4.2 soll deutlich machen in welche Richtungen Informationen
ausgetauscht werden können und welche Parameter dazu benutzt werden müssen.
OutputA / InputB
Input Parameter
Die hier definierten Parameter dürfen nur lesend benutzt werden. Es ist möglich mehrere
Parameter als Liste zu definieren.
InOutput Parameter
Auf die hier definierten Parameter darf lesend und schreibend zugegriffen werden, das
heißt es ist möglich innerhalb dieser CES Prozedur den Wert der Variablen zu verändern.
24
Output Parameter
Die hier definierten Parameter dürfen nur schreibend benutzt werden.
$ProzedurName(inputArgListe;inOutputArgListe;outputArgListe);$
Zu beachten sind die Dollarzeichen am Anfang und Ende des Prozeduraufrufs. In der
einfachen CESC Implementierung ist es nur möglich eine Variable als Argument zu über-
geben. Es ist also nicht möglich einen Ausdruck zu übergeben. Der CES Aufruf kann auch
ohne Argumente erfolgen. Sollte der Fall eintreten, dass mehr oder weniger Argumente
übergeben werden, als in der CES Prozedur definiert, so wird es eine Fehlermeldung vom
C Compiler geben. Die einfache CESC Implementierung überprüft dies nicht.
Wird dem CES Aufruf eine C Variable als Argument übergeben so muss darauf geachtet
werden den zugehörigen Datentyp davor zu setzen um dem CES Compiler mitzuteilen
welchen Datentyp diese C Variable hat. Handelt es sich bei dem Argument um eine zuvor
definierte CES Variable so ist dies nicht nötig, da dem CES Compiler diese Variable (
als Symbol) bekannt ist.
6 $ s e t V a l ( ; ; int a ) {
7 $a$ = 1 0 0 ;
8 }$
Listing 4.3: Beispiel mit Fehlerquelle
Die Ausgabe der printf Anweisung in Zeile 3 ist undefiniert. Der CES Aufruf setval(;;a)
in Zeile 6 bewirkt nur ein Erzeugen des Task Frames auf dem Current Stack. setVal(;;a)
wird nicht ausgeführt! Dies ist zu beachten.
25
4.4. Die CES Variable
Als CES Variable werden Variablen bezeichnet, die als InOutput oder nur Output Ar-
gument oder Parameter deklariert sind. Soll eine CES Variable benutzt werden, müssen
vor und nach dem Bezeichner Dollarzeichen gesetzt werden. Es ist möglich eine CES
Variable wie eine C Variable im C Code zu benutzen. Beispielsweise ist es möglich durch
Zuweisungen Werte zwischen C und CES Variablen auszutauschen oder CES Variablen
können auch als Argumente an eine C Funktion übergeben werden. Die Syntax sieht
folgendermaßen aus.
$var$
In den vorgehenden CES Quelltexten sind viele Beispiele, die eine CES Variable in C
Code benutzten.
26
5. Das Backend
Das Backend setzt sich zusammen aus der Run Time, das vom CES Compiler generierte
C Files, einem C Compiler und anderen C Entwicklungstools. Der CES Compiler wird
im nächsten Kapitel im Detail besprochen.
Der ausführbare Task ist immer eine C Funktion ohne Parameter mit dem Rückgabe-
wert void. Diese C Funktion wurde vom CES Compiler generiert. Die Codegenerierung
wird in einem späteren Abschnitt dieses Kapitels erklärt.
5.2. Datenstrukturen
5.2.1. Der Task Frame
Die Datenstruktur eines Task Frame auf dem Stack ist in der run_time.h folgendermassen
definiert:
1 typedef struct {
2 void ( ∗ func_ptr ) ( void ) ;
3 void ( ∗ p r i n t _ f u n c ) ( void ) ;
27
4 int numIn ;
5 int numInOut ;
6 int numOut ;
7 void ∗ parameter [ ARG_SIZE ] ;
8 int a n c e s t o r ; // b e n u t z t von d e r Run Time
9 char ∗name ; // b e n u t z t vom P r e t t y P r i n t e r
10 int p a r a l l e l ; // b e n u t z t vom P r e t t y P r i n t e r ( nur f ü r Ausgabe ,
k e i n e I m p l e m e n t a t i o n bzw Funktion )
11 } TASK_FRAME;
Listing 5.2: TASK_FRAME Datenstruktur aus der run_time.h
Für jeden Task wird ein taskspezifischer Frame generiert, der auf der aufgeführten
TASK_FRAME Datenstruktur aufbaut. Dieser taskspezifische Frame wird speziell in
einem eigenen Header File <Task Name>.h generiert. Da für diese taskspezifische Struk-
tur die Variablen ancestor, parallel und *name nicht verwendet werden, sind diese nicht
im eigenen Header File deklariert.
1 typedef struct {
2 void ( ∗ func_foo ) ( void ) ; // Neuimplementierung d e s Task a l s
Prozedur bzw C Funktion
3 void ( ∗ p r i n t _ f u n c ) ( void ) ; // P r i n t Funktion z u r
S e l b s t d a r s t e l l u n g f ü r den P r e t t y P r i n t e r
4 int numIn2 ; // Anzahl d e r I n p u t Parameter
5 int numInOut1 ; // Anzahl d e r InOutput Parameter
6 int numOut0 ; // Anzahl d e r Output Parameter
7 int ∗ i n 1 ; // 1 . I n p u t Parameter
8 . . . // e v e n t u e l l e z u s ä t z l i c h e I n p u t Parameter
9 int ∗ inOut1 ; // 1 . InOutput Parameter
10 . . . // e v e n t u e l l e z u s ä t z l i c h e InOutput Parameter
11 int ∗ out1 ; // 1 . Output Parameter
12 . . . // e v e n t u e l l e z u s ä t z l i c h e Output Parameter
13 // maximal ARG_SIZE Parameter
14 } Struct_foo ;
Listing 5.3: Beispiel einer von CESC generierten Task Struktur im Header File
Da Struct_foo auf der Struktur von TASK_FRAME aufbaut, kann die Run Time diese
als Task Frame TASK_FRAME benutzten.
Alle Frames auf dem Frame Stack haben die gleiche Grösse. Die Strukturgrösse wurde
konstant gehalten um eine Vereinfachung bei der Implementierung zu haben. Diese ge-
wählte Lösung bietet den Vorteil Schwierigkeiten bei der Zeigerarithmetik zu vermeiden.
Es ist also zu jedem Zeitpunkt einfach bekannt wo sich alle Informationen über einen
Task im Speicher befinden. Da der Frame Stack wie ein Array aufgebaut ist kann über
einen Index direkt auf jeden Frame zugegriffen werden. Ein weiterer Vorteil ist beim De-
bugging zu erkennen. Der Pretty Printer kann sehr leicht alle Frames ausgeben. Ziel ist es
28
nicht an Ressourcen und Performance zu sparen sondern das Prinzip und die Abfolge im
Speicher klar und deutlich zu präsentieren. Daher wurde eine einfache Struktur gewählt.
29
2 void ( ∗ f u n c _ e u c l i d ) ( void ) ; // Neuimplementierung d e s Task a l s
Prozedur bzw C Funktion
3 void ( ∗ p r i n t _ f u n c ) ( void ) ; // P r i n t Funktion z u r
S e l b s t d a r s t e l l u n g f ü r den P r e t t y P r i n t e r
4 int numIn2 ; // Anzahl d e r I n p u t Parameter
5 int numInOut1 ; // Anzahl d e r InOutput Parameter
6 int numOut0 ; // Anzahl d e r Output Parameter
7 int ∗ i n 1 ; // 1 . I n p u t Parameter
8 int ∗ i n 2 ; // 2 . I n p u t Parameter
9 int ∗ i n o u t 1 ; // 1 . InOutput Parameter
10 } Struct_euclid ;
Listing 5.5: Datenstruktur des Tasks euclid(b,c;;double erg2)
Da der Task euclid(a,b;;c) 2 Input Parameter definiert hat befindet sich hinter der
Variablen numIn die Zahl 2. So kann später bei einem Task Aufruf durch den C Com-
piler festgestellt werden ob die Definition mit der tatsächlichen Anzahl an Parameter
übereinstimmt. Würde dem Task Aufruf ein Argument fehlen $euclid(int c;erg ;) ;$ so
hätte dies folgende Fehlermeldung zur folge.
ces_program.c: In function ‘euclid’:
ces_program.c:168: structure has no member named ‘numIn1’
make: *** [gcc] Error 1
Warum diese doch etwas umständliche Art der Überprüfung von Parameter? Um die
Implementierung des CES Compiler zu vereinfachen wurde die Überprüfung an den C
Compiler weitergegeben. Die nötigen Informationen, die der C Compiler braucht ist je-
weils der Bezeichner der drei Variablen numIn, numInOut und numOut.
30
und mit dem Wert 0 einen schon ausgeführten Task anzeigt. Dies dient lediglich zur
Anzeige beim Pretty Printer. ancestor wird nicht im Frame, dass im Header File für
den zugehörigen Task generiert wurde, definiert. ancestor hat keine Auswirkung auf das
Programm, sondern dient lediglich der Ausgabe des Pretty Printers.
*****************EXECUTE 1****************
1 program(;;)
*****************EXECUTE 2****************
4 setVal(;;double a_2=0.000000)
3 printVar(double a_2=0.000000;;)
1 ANCESTOR program(;;)
*****************EXECUTE 3****************
3 printVar(double a_2=100.000000;;)
1 ANCESTOR program(;;)
Die CES Variable a hat den Wert 100.000000
Abbildung 5.1.: Pretty Printer Ausgabe mit dem „ancestor“ Task program(;;)
31
3 char ∗name ; // Name d e r V a r i a b l e
4 <type> v a l u e ; // Datentyp <t y p e > a b h ä n g i g von d e r D e f i n i t i o n
der Variable
5 } STORAGE_FRAME;
Listing 5.6: STORAGE_FRAME Datenstruktur
Die maximale Grösse eines selbstdefinierten Datentyps ist 4 Byte * ARG_SIZE. Diese
Grösse darf nicht überschritten werden. Ansonsten würden Daten vom nächsten Frame
überschrieben werden. Dieses Problem ist allerdings abhängig von der Implementierung
und nicht fundamental.
3 $program ( ; ; ) {
4 $ s e t V a l ( ; ; double a ) ; $
32
F rameStack CurrentStack
setV al(; ; a)
printV ar(a; ; )
O
y
Storage(a) = 100.0
5 $printVar ( a ; ; ) ; $
6 }$
7
8 $ s e t V a l ( ; ; double a ) {
9 $a$ = 1 0 0 . 0 ;
10 }$
11
12 $ p r i n t V a r ( double a ; ; ) {
13 p r i n t f ( " Die ␣CES␣ v a r i a b l e ␣a␣ hat ␣ den ␣Wert␣%f \n" , $a$ ) ;
14 }$
Listing 5.8: CES Programm zur Storage Erzeugung
CESC generiert aus dem CES Quelltext folgenden C Code als Storage Struktur.
1 /∗ CREATE STORAGE FOR OUTPUT ARGUMENT ’ a ’ ∗/
2 typedef struct {
3 void ( ∗ f u n c _ s t o r a g e ) ( void ) ;
4 char ∗name ;
5 double v a l u e ;
6 } Struct_storage_a_ptr ;
Listing 5.9: Datenstruktur eines Storage für double a
CESC erzeugt einen Storage für die CES Variable a. Der Scope dieser Struktur be-
zieht sich nur auf die C Funktion, in der die Struktur definiert wurde. Die Struktur ist
somit lokal. Dies ermöglicht bei verschiedenen CES Prozeduren gleiche Bezeichner für
Parameter zu benutzen. Innerhalb einer CES Prozedur dürfen keine gleichen Bezeichner
für Variablen benutzt werden; auch nicht wenn sie unterschiedliche Typen hätten.
Die Abbildung 5.3 zeigt die Ausgabe des Pretty Printers. Die Nummerierung an der
linken Seite der Ausgabe bezeichnet die Nummer des Frames auf dem Frame Stack.
33
*****************EXECUTE 1****************
1 program(;;)
*****************EXECUTE 2****************
4 setVal(;;double a_2=0.000000)
3 printVar(double a_2=0.000000;;)
1 ANCESTOR program(;;)
*****************EXECUTE 3****************
3 printVar(double a_2=100.000000;;)
1 ANCESTOR program(;;)
Die CES Variable a hat den Wert 100.000000
Abbildung 5.3.: Pretty Printer Ausgabe mit dem „ancestor“ Task program(;;)
Frame 2, der nicht vom Pretty Printer angezeigt wird, beinhaltet den Storage für die
CES Variable a. Erst bei Ausführung der Prozedur setVal bekommt a den Wert 100
zugewiesen.
Damit die Ausgabe vom Pretty Printer keine undefinierten Werte in den Storage Fra-
mes anzeigt wurde bei der Codegenerierung der Wert auf 0 initialisiert.
im CES Quelltext führt bei der Codegenerierung durch den CES Compiler zu fol-
gendem Eintrag in der ces_print.h. Der Name des Header Files wird definiert durch
34
ces_<NameDesTask>.h.
1 #i f n d e f CES_printVar_H
2 #define CES_printVar_H
4 typedef struct {
5 void ( ∗ func_printVar ) ( void ) ;
6 void ( ∗ p r i n t _ f u n c ) ( void ) ;
7 int numIn1 ;
8 int numInOut0 ;
9 int numOut0 ;
10 double ∗ i n 1 ;
11 } Struct_printVar ;
12
13 void p r i n t V a r ( void ) ;
14
15 #endif
Listing 5.11: Datenstruktur des Tasks print_Var(double a;;)
3 $program ( ; ; ) {
4 $ s e t V a l ( ; ; double a ) ; $
5 $printVar ( a ; ; ) ; $
6 }$
7
8 $ s e t V a l ( ; ; double a ) {
9 $a$ = 1 0 0 . 0 ;
10 }$
11
12 $ p r i n t V a r ( double a ; ; ) {
35
13 p r i n t f ( " Die ␣CES␣ v a r i a b l e ␣a␣ hat ␣ den ␣Wert␣%f \n" , $a$ ) ;
14 }$
Listing 5.12: CES Programmbeispiel
F rameStack CurrentStack
setV al(; ; a)
printV ar(a; ; )
O
y
Storage(a) = 100.0
Im generierten C File werden zuerst alle benötigten Standard C Bibliotheken und Hea-
der Files aus der Run Time eingebunden. Diese sind für spätere Anwendungen notwendig.
1 #include <s t d i o . h>
2 #include <s t r i n g . h>
3 #include " debug . h"
4 #include " run_time . h"
Die extern definierten Variablen aus der Run Time werden zusätzlich gebraucht. Dazu
gehören der Frame Stack Zeiger zusammen mit dem Frame Stack, Current Stack Zeiger
zusammen mit dem Current Stack und zusätzliche Zeiger auf den Anfang der beiden
Stacks.
1 extern TASK_FRAME ∗ f s p ; /∗ frame s t a c k p o i n t e r ∗/
2 extern TASK_FRAME ∗ c s p ; /∗ c u r r e n t s t a c k p o i n t e r ∗/
3 extern TASK_FRAME ∗ p t r ; /∗ t h e a c t u a l t a s k frame ∗/ \TODO
4 extern TASK_FRAME ∗ fs_bottom ; /∗ bottom o f t h e frame s t a c k ∗/
5 extern TASK_FRAME ∗ cs_top ; /∗ t o p o f t h e c u r r e n t s t a c k ∗/
Der oben gezeigte Code wird unabhängig vom CES Quelltext erzeugt.
Danach werden alle Prototypen der Print Funktionen einer jeden definierten CES Pro-
zedur erzeugt. Diese sind für die Ausgabe des Tasks durch den Pretty Printer nötig.
Die Print Funktion wird an den dazugehörigen Funktionszeiger print_func in der Task
Struktur, die im Header File erzeugt wurde, übergeben.
1 void ces_print_program ( void ) ;
36
2 void c e s _ p r i n t _ s e t V a l ( void ) ;
3 void c e s _ p r i n t _ p r i n t V a r ( void ) ;
Dann wird C Code aus dem CES Quelltext in das C File kopiert bis die erste CES
Prozedur erreicht ist. Danach wird das Header File der erreichten Prozedur durch ei-
ne include Anweisung eingebunden. Im Anschluss folgen die Header Files der von der
erreichten Prozedur aufgerufenen Prozeduren.
1 //CES Q u e l l t e x t
2 $program ( ; ; ) {
3 setVal ( . . . ) ;
4 printVar ( . . . ) ;
5 }$
7 . . .
8 }
9 . . .
Nun wird für die erreichte CES Prozedur die C Funktion generiert. Jede CES Proze-
durdefinition erzeugt eine gleichnamige C Funktionen.
In der gleichnamigen C Funktion wird zuerst der Frame Stack Zeiger kopiert. Hierzu
wird ein Zeiger definiert, der auf die passende Datenstruktur des Tasks zeigen kann. Der
Wert des Zeigers wird auf die Adresse des Frame Stack Zeigers gesetzt. Dieser Zeiger
wird später benötigt, um die Referenzen auf die Parameter des Tasks, die auf dem Frame
Stack vorhanden sind, setzen zu können.
1
9 void s e t V a l ( void ) {
10 /∗ SAVE ME s e t V a l ∗/
11 S t r u c t _ s e t V a l ∗me = ( S t r u c t _ s e t V a l ∗ ) f s p ; // Kopie d e r Adresse
a u f dem Frame S t a c k
12 ...
37
13 }
14
17 void p r i n t V a r ( void ) {
18 /∗ SAFE ME p r i n t V a r ∗/
19 S t r u c t _ p r i n t V a r ∗me = ( S t r u c t _ p r i n t V a r ∗ ) f s p ;
20 ...
21 }
Danach wird aus dem CES Quelltext weiter C Code kopiert bis die nächste CES Pro-
zedur erreicht wird. Diese Prozedur wird gleich behandelt wie die Erste. Dieser Vorgang
wiederholt sich bis zum Ende des CES quelltextes.
Zuletzt werden die Print Funktionen für die definierten Tasks generiert. Der hier aufge-
führte Quellcode ist abhängig vom Prozedurnamen und deren definierten Parameter. Auf
die Berechnung der Storage Frames für die Ausgabe wird hier nicht genauer eingegangen.
1 void c e s _ p r i n t _ s e t V a l ( void ) {
2 int ∗ value_out1 = ( ( ( S t r u c t _ s e t V a l ∗ ) p t r )−>out1 ) ;
3 int storage_index_out1 = ( ( ( char ∗ ) value_out1 −(char ∗ ) fs_bottom
) / s i z e o f (TASK_FRAME) ) +1;
4
5.4.1. C Code
Das oben beschriebene Kopieren des C Codes in das generierte C File bewirkt, dass die
Reihenfolge und die Semantik des kopierten C Codes nicht beeinflusst ist von generierten
Codefragmenten des CES Compilers.
38
2 $program ( ; ; ) {
3 ...
4 s e t V a l ( ; ; double a ) ;
5 ...
6 }$
13 ( ( S t r u c t _ s e t V a l ∗ ) c s p )−>p r i n t _ f u n c = &c e s _ p r i n t _ s e t V a l ; //
F u n k t i o n s z e i g e r a u f d i e P r i n t Funktion
14
Zuerst wird der Current Stack Zeiger auf den nächsten Frame gesetzt. Die Member
ancestor und parallel werden initialsiert. ancestor mit dem Wert 0 bedeutet dass der
task nocht nicht ausgeführt wurde. parallel mit dem Wert 0 bedeutet, dass der Task
nicht parallel ausgeführt werden kann. Der Name des Tasks wird in den Member name
geschrieben. Nun werden die Variablen für die Argumentenanzahl des Task initialisiert.
Die Funktionszeiger func_setVal und print_func werden auf die Adressen der C Funk-
tionen gesetzt. Zuletzt wird C Code generiert der Referenzen für die Argumente auf die
Storages erzeugt. Output Argument out1 auf den Storage für die Variable a. Dies ist
abhängig der Parameterdefinitionen in der CES Prozedur.
39
5 }$
Die Variable out1 ist das erste Output Argument aus dem dazugehörigen CES Task.
Diese wurde im dazugehörigen Header File setVal.h definiert. Bei lesendem Zugriff wird
dieser Code generiert.
1 void p r i n t V a r ( void ) {
2 ...
3 p r i n t f ( " Die ␣CES␣ V a r i a b l e ␣a␣ hat ␣ den ␣Wert␣%f \n" , ∗ (me−>i n 1 ) ) ;
4 ...
5 }
Die Variable in1 ist das erste Input Argument aus dem dazugehörigen CES Task setVal.
Diese Variable ist ebenfalls im Header File definiert worden.Der Zeiger me zeigt auf den
aktuellen Task Frame auf dem Frame Stack.
40
3 /∗ CREATE STORAGE FOR OUTPUT ARGUMENT ’ a ’ ∗/
4 typedef struct {
5 void ( ∗ f u n c _ s t o r a g e ) ( void ) ;
6 char ∗name ;
7 double v a l u e ; // Datentyp a b h ä n g i g von d e r D e f i n i t i o n
8 } Struct_storage_a_ptr ;
9
41
7 printf (" ; ") ;
8 p r i n t f ( "%s_%d" , ∗ ( char ∗ ∗ ) ( ( char ∗ ) ( ( ( S t r u c t _ s e t V a l ∗ ) p t r )−>
out1 ) −((( char ∗ ) &( s t o r a g e . v a l u e ) ) −((char ∗ ) &( s t o r a g e . name ) )
) ) , storage_index_out1 ) ; // Berechnung d e s S t o r a g e f ü r d i e
e r s t e Output V a r i a b l e
9 p r i n t f ( "=%d" , ∗ ( ( S t r u c t _ s e t V a l ∗ ) p t r )−>out1 ) ;
10 printf (")") ;
11 }
42
6. Der CES Compiler
Der CES Compiler übersetzt den CES Quelltext in Form einer einzigen Eingabedatei
in eine C Datei, die alle definierten Prozeduren als Task implementiert. Der Compiler
durchläuft folgenden Phasen:
lexikalische Analyse Der Scanner liest die Eingabedatei, den CES Quelltext, ein und
erzeugt einen Tokenstrom für den Parser.
syntaktische Analyse Der Parser überprüft den Tokenstrom vom Scanner und erzeugt
daraus einen abstrakten Syntaxbaum.
semantische Analyse Der abstrakte Syntaxbaum wird durchlaufen und prüft auf se-
mantische Korrektheit. Zusätzlich wird eine Symboltabelle für die Verwaltung der
definierten Variablen im Quelltext erzeugt.
Codegenerierung Aus dem abstrakten Syntaxbaum und der Symboltabelle wird C Code
generiert der die CES Prozeduren als Tasks implementiert.
Nachdem der CES Compiler das C File generiert hat wird dieses zusammen mit der Run
Time von einem C Compiler übersetzt und in ein ausführbares Programm erstellt. Es ist
möglich mit mehreren vom CESC generierten C Files und der Run Time ein ausführbares
Programm zu erzeugen.
Der Compiler CESC wurde unter dem Betriebssystem Windows XP Professional mit
Hilfe von MinGW (www.mingw.org), flex 2.5.4, bison 1.28, gcc 3.2.3 entwickelt. make
3.78.1 wurde verwendet, um die Kompilierung durch den C und CES Compiler zu steuern
und zu automatisieren.
43
dem Kompiliervorgang wird die Eingabedatei, also der CES Quelltext, analysiert und die
regulären Ausdrücke verwendet, um die Tokens für den Parser zu erzeugen. Die Datei
scanner.l ist in den Anlagen zu finden. Auf den Code der scanner.l wird in dieser Diplom-
arbeit nicht weiter eingegangen, da der Quelltext der scanner.l bekannte Konstrukte aus
dem Compilerbau aufweist und somit keine neue Konzepte beinhaltet.
CESC bietet die Möglichkeit den Tokenstrom ausgeben zu lassen (siehe Anhang A).
44
ast
_ _ _ _ _ _ _ _ _ _
listN ode
next / listN ode
_ _ _ _ _ _ _ _ _ _
value value
_ _ _ _ _ _ _ _ _ _
body
/ listN ode / listN ode
next
cCodeN ode cesDef N ode
_ _ _ _ _ _ _ _ _ _
params value value
paramN ode o paramsN ode cesCallN ode cCodeN ode
in
nn
nnnnn
nn out args
w nn inOut
n
paramN ode paramN ode argsN ode
out / argN ode
PPP
PPinOut
PPP
type in PPP
P'
typeN ode typeN ode o type
argN ode argN ode
9 TASK_FRAME ∗ p t r ;
10
45
11 extern int show_stack ;
12
13 void show_fs ( ) {
14 int frame_index ;
15
16 i f ( show_stack == 0 ) return ;
17
27 i f ( ptr−>a n c e s t o r == 1 ) p r i n t f ( "\tANCESTOR␣" ) ;
28 ( void ) ( ptr−>p r i n t _ f u n c ) ( ) ; /∗ e x e c u t e t a s k ∗/
29 i f ( ptr−>p a r a l l e l == 1 ) p r i n t f ( "\tPARALLEL␣" ) ;
30 p r i n t f ( "\n" ) ;
31 }
32 }
Listing 6.1: C Quellcode des Pretty Printers
46
7. Ausblick
Die Ergebnisse dieser Diplomarbeit zeigen, dass der theoretische Ansatz von Dr. Burk-
hard Steinmacher-Burow in eine funktionierende Implementierung umgesetzt werden
konnte. Die zu Anfangs gesteckten Ziele konnten erfolgreich erreicht werden. Der entwi-
ckelte CES Compiler erzeugt die geforderten Daten zur Implementierung von Prozeduren
als atomare Tasks. Auftretende Probleme wurden gelöst und trugen bei der Erkenntnis-
findung des Themas bei. Mit Hilfe der Veröffentlichung Task Frames [1] wurde ein leichter
Einstieg in das Thema ermöglicht. Die gewonnene Erkenntnisse dieser Arbeit bieten die
Möglichkeit Tasks parallel ausführen zu können. Um dies zu erreichen muss ein erwei-
tertes Execution System entwickelt werden, dass die Verwaltung der Tasks übernimmt.
Diese Diplomarbeit bietet ebenfalls einen pädagogischen Aspekt zur Verdeutlichung des
Themas. Die Strukturen wurden einfach gehalten. Die Pretty Printer Ausgabe des Frame
Stack kann leicht nachvollzogen werden und verdeutlicht mit einfachen Details den Ablauf
eines CES Programms. Der modulare Aufbau gibt die Möglichkeit die Implementierung
zu erweitern und zukünftige auftretende Probleme zu lösen.
Ich hoffe, dass die vorliegende Arbeit viele Menschen inspiriert und auch motiviert das
Konzept der Task Frames weiterzuentwickeln und zu nutzen.
47
Literaturverzeichnis
[1] Dr. Burkhard Steinmacher-Burow. Task Frames, 2000.
http://www-zeus.desy.de/~funnel/TSIA/index.htm.
48
Benutzte Internetquellen
www.wikipedia.org
49
A. CESC Handbuch
A.1. Dateien
A.1.1. Makefile
folgende Regeln sind möglich:
compile benutzt das Programm cesc und erzeugt aus der Datei program.ces das C Fi-
le ces_program.c. Der Name der Eingabedatei (CES Quelltext) muss immer pro-
gram.ces lauten, möchte man dieses Makefile benutzen. Der Name der Ausgabedatei
wird immer ces_program.c heissen.
print gibt das Beispielprogramm program.ces als Text auf der Konsole aus (das Pro-
gramm cat muss vorhanden sein)
gcc der C Compiler kompiliert zusammen mit der Run Time das ausführbare Programm
ces_program
fs gibt den Frame Stack während der Ausführung des Programms durch den Pretty
Printer auf der Konsole aus
ancestor zeigt zusätzlich die bereits ausgeführten Tasks auf dem Frame Stack
fstests führt nacheinander alle Testprogramme aus dem Verzeichnis Tests aus und gibt
zusätzlich den Frame Stack auf der Konsole aus
tests führt nacheinander alle Testprogramme aus dem Verzeichnis Tests aus
50
A.1.2. CESC
abstrakter Syntaxbaum
absyn.c Funktionen zur Erzeugung des abstrakten Syntaxbaumes
absyn.h Datenstrukturen für den Syntaxbaum, Prototypen
Codegenerierung
codegen.c Funktionen zur Codegenerierung
codegen.h Prototypen
Hauptprogramm CESC
main.c Das Hauptprogramm von CESC
folgende Optionen bietet CESC:
Symbole
symbol.c Funktionen zur Erzeugung von Symbolen und deren Verwaltung in einer Hash
Tabelle
symbol.h Datenstruktur eines Symbols für die Symboltabelle
Symboltabelle
table.c Funktionen zur Erstellung der Symboltabelle und zur Ausgabe der Symboltabel-
le
table.h Datenstrukturen der Symboltabelle und Definitionen von Konstanten, Prototy-
pen
Utils
utils.c Funktionen zur Fehlerausgabe, Speicherreservierung und Speicherfreigabe
utils.h Prototypen
common.h Definitionen für den eigenen Datentyp boolean
51
Semantiküberprüfung
semant.c Funktionen zur Überprüfung der Semantik
semant.h Prototypen
A.1.3. Scanner
scanner.l Scanner Eingabedatei für flex
scanner.h Datenstrukturen zur Erzeugung der Tokens
A.1.4. Parser
parser.y Parser Eingabedatei für bison
parser.h Prototypen
A.1.7. Beispielprogramm
program.ces Ein einfaches Beispielprogramm
52
B. Testumegbung
B.1. Dateien im Verzeichnis CESC/Tests
Alle Dateien von test01.ces bis test26.ces testen die richtige Definition von CES Proez-
duren und deren Verwendung durch CES Aufrufe. In der Excel Tabelle ist aufgelistet wie
der Informationsaustausch zwischen verschiedenen Prozeduren möglich ist.
53
Danksagung
Ich danke besonders Dr. Burkhard Steinmacher-Burow, der mir die Möglichkeit gegeben
hat diese sehr interessante Arbeit durchführen zu können. Ausserdem danke ich Ihm
für seine Unterstützung und den vielen wissenschaftlichen Ratschlägen zur Fertigstel-
lung dieser Diplomarbeit. Ebenfalls bedanke ich mich bei Prof. Dr. Hellwig Geisse für
seine persönliche Hilfe bei Fragen bezüglich dieser Arbeit. Der Firma IBM Deutschland
Entwicklung GmbH danke ich für die Bereitstellung der Ressourcen und der Räumlichkei-
ten. Meinem Nachfolger Jens Remus danke ich für wichtige Tipps und das Korrekturlesen
bezüglich der technischen Korrektheit dieser Arbeit. Meiner Freundin Maike Schmelter
danke ich für die Motivierung und Unterstützung bei dieser Arbeit, ohne Sie wäre es nie
möglich gewesen diese Arbeit in dem angesetzten Zeitraum zu Ende zu bringen.
54
Eidesstattliche Erklärung
Hiermit versichere ich, die vorliegende Arbeit selbstständig und unter ausschließlicher
Verwendung der angegebenen Literatur und Hilfsmittel erstellt zu haben.
Die Arbeit wurde bisher in gleicher oder ähnlicher Form keiner anderen Prüfungsbehörde
vorgelegt und auch nicht veröffentlicht.
55