INFO0004 – Structure des
langages de programmation
Génération de code; liaison et exécution
Justus H. Piater
Résumé – Analyse
2/51
Analyse sémantique
arbre de syntaxe abstraite
Analyse syntaxique
flux de jetons
Analyse lexicale
fichier texte
arbre annoté
Compilation
Liaison
ExécutionInterprétation
Programmation
Analyse
Résumé – Génération de code
3/51
arbre annoté
Création de code intermédiaire
pseudo- instructions en blocs élémentaires
Amélioration
pseudo- instructions en blocs élémentaires
Génération de code
code assembleur
Amélioration
code assembleur
Assemblage
code binaire
Compilation
Linkage
ExécutionInterprétation
Programmation
Analyse
Le code intermédiaire
Code intermédiaire
Le code intermédiaire 5/51
ressemble au code assembleur:
code linéaire
registres (nombre infini)
données brutes
indépendant d’une machine quelconque
pour effectuer des optimisations
pour réduire au minimum le code spécifique à la
machine cible
Fibonacci
Le code intermédiaire 6/51
// compute result – the nth Fibonacci number
void main() {
int n, fib0, fib1, temp, result;
n = 8;
fib0 = 0;
fib1 = 1;
while (n > 0) {
temp = fib0;
fib0 = fib1;
fib1 = fib0 + temp;
n = n – 1;
}
result = fib0;
}
Fibonacci: code intermédiaire
Le code intermédiaire 7/51
Cf. UASM
Block 0:
LoadC v0, 8
Store n, v0
LoadC v1, 0
Store fib0, v1
LoadC v2, 1
Store fib1, v2
Block 1:
Load v12, n
LoadC v13, 0
v11 := v12 > v13
If v11 Goto Block 2
Goto Block 3
Block 2:
Load v3, fib0
Store temp, v3
Load v4, fib1
Fibonacci: code in-
termédiaire (suite)
Le code intermédiaire 8/51
Store fib0, v4
Load v6, fib0
Load v7, temp
v5 := v6 + v7
Store fib1, v5
Load v9, n
LoadC v10, 1
v8 := v9 – v10
Store n, v8
Goto Block 1
Block 3:
Load v14, fib0
Store result, v14
Représenter le code intermédiaire
Le code intermédiaire 9/51
Chaque instruction du code intermédiaire est une
structure de données qui encode:
le type de l’instruction (Load, :=, …)
le type de chaque opérande (registre virtuel, variable
globale, variable locale, ou constante)
l’opérateur (le cas échéant)
des informations sémantiques supplémentaires – tout
ce qui est utile pour l’amélioration et génération du
code
Créer du code intermédiaire
Le code intermédiaire 10/51
Formellement: spécification par une grammaire
attribuée (attribute grammar)
Pour nous: Très similaire à l’interprétation du code
(voir les méthodes sémantiques):
On parcourt l’arbre de syntaxe abstraite;
à chaque nœud, on crée des instructions.
En vue d’une amélioration du code par la suite, on ne
réutilise jamais des registres; un nouveau registre
virtuel est prévu chaque fois.
Les blocs élémentaires
Le code intermédiaire 11/51
Pour gérer des sauts, il faut insérer des étiquettes.
Le morceau de code entre une étiquette et le prochain
saut constitue un bloc élémentaire (basic block).
Nous allons couper le code en blocs élémentaires (pas
essentiel ici, mais pour l’amélioration du code plus
tard).
Ainsi, on crée un graphe de flot de contrôle (control
flow graph).
Assignment
Le code intermédiaire 12/51
BasicBlock* generate(const Assignment* s, BasicBlock* bb) {
bb–>vec.push_back(new MStore(s–>target,
generate(s–>source, bb)));
return bb;
}
Il faut passer le bloc pour savoir où ajouter le code.
La source est un registre virtuel.
Il faut renvoyer le bloc parce qu’une instruction peut
rompre le bloc élémentaire actuel.
Note
Cette implémentation fonctionne parce qu’ici, une
expression ne rompt jamais le bloc élémentaire
actuel.
Binary
Le code intermédiaire 13/51
VirtualRegister* generate(const Binary* e, BasicBlock* bb) {
VirtualRegister* target = newRegister();
bb–>vec.push_back(new MBinary(e–>op, target,
generate(e–>term1, bb),
generate(e–>term2, bb) ));
return target;
}
Loop
Le code intermédiaire 14/51
BasicBlock* generate(const Loop* s, BasicBlock* bb) {
BasicBlock* bbTest = basicBlocks–>getNewBasicBlock();
bb–>vec.push_back(new MGoto(bbTest));
BasicBlock* bbBodyBeg = basicBlocks–>getNewBasicBlock();
BasicBlock* bbBodyEnd = generate(s–>body, bbBodyBeg);
bbBodyEnd–>vec.push_back(new MGoto(bbTest));
VirtualRegister* test = generate(s–>test, bbTest);
BasicBlock* bbNext = basicBlocks–>getNewBasicBlock();
bbTest–>vec.push_back(new MCondGoto(test, bbBodyBeg));
bbTest–>vec.push_back(new MGoto(bbNext));
return bbNext;
}
La génération du code assembleur
Allocations familières
La génération du code assembleur 16/51
arbre annoté
Création de code intermédiaire
Génération de code
code assembleur
Allocation des registres
Allocation des variables locales
Allocation des variables globales
Allocation des constantes
pseudo- instructions en blocs élémentaires
Allocation des registres
La génération du code assembleur 17/51
Équivalent à la coloration de graphe (NP-hard)
L’idée: Nœuds sont registres virtuels, arêtes
connectent des nœuds simultanément actifs. Couleurs
sont registres réels.
Faisons-le pour Fibonacci en regardant les plages
actives (live ranges) des registres virtuels…
Simplification bloc-par-bloc: Sans plages actives
inter-blocs élémentaires, un algorithme d’allocation de
ressources convient.
Allocation de ressources
La génération du code assembleur 18/51
Problème: Nous avons un ensemble de tâches.
Chaque tâche a une heure de début et une heure de
fin avec . Chaque tâche doit être exécutée
sur une machine; une machine ne peut exécuter
qu’une tâche à la fois. Deux tâches et ne sont pas
en conflit si ou . Il s’agit d’exécuter toutes
les tâches sur un nombre minimal de machines.
Algorithme
La génération du code assembleur 19/51
Temps d’exécution?
Algorithm resourceAllocation( ):
Input: A set of tasks with start and end times and .
Output: A minimal, nonconflicting allocation of machine
for all tasks in .
// minimal number of machines
while do
remove from the task with smallest
if there is a machine with no task conflicting with then
schedule task on machine
else
// add a new machine
schedule task on machine
Correction
La génération du code assembleur 20/51
Proposition: L’algorithme resourceAllocation() est
correct.
Preuve (par contradiction): Supposons que
l’algorithme trouve une solution avec machines,
alors qu’il existe une solution avec machines. Soit
la première tâche allouée sur la machine . Quand
l’algorithme traita la tâche , toutes les machines
contenaient des tâches en conflict avec et aussi (par
le même argument) entre elles, ce qui implique la
présence dans de tâches en conflit entre elles.
1 / 13 100%
La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !