TLC Complet
TLC Complet
(TLC)
Fabrice Lamarche
[email protected]
ESIR
1
Historique
• 1946 : Création de l’ENIAC (Electronic Numerical Integrator and Computer)
– P. Eckert et J. Marchly
• Quelques chiffres
– 17468 tubes à vide
– 7200 diodes
– 1500 relais
– 70000 résistances
– 10000 condensateurs
– 30 tonnes
– Occupe une surface de 67m2
– Consomme 150 Kilowatts
• Performances
– Horloge à 100KHz
– 5000 additions / seconde
– 330 multiplications / seconde
– 38 divisions / seconde
2
Historique
• ENIAC
– 30 Unités autonomes
• 20 accumulateurs 10 digits
• 1 multiplicateur
• 1 Master program capable de gérer des boucles
– Mode de programmation
• Switchs
• Câblage des unités entre elles
3
Historique
• Ceci est un programme sur l’ENIAC…
4
Historique
• 1950 : Invention de l’assembleur par Maurice
V. Wilkes de l’université de Cambridge
– Avant : programmation en binaire
5
Historique
1950
Fortran, COBOL, Lisp
1960
Basic, Logo, Apl, Pl/1
1970
Pascal, Prolog, SmallTalk, C, Awk, Ada, Rexx
1980
Dbase, C++, Eiffel, Perl, Tcl/Tk, Mathematica, Maple
1990
Java, Javascript, Php, Mysql, VisualBasic
2000
Java 2, Php 4.3.3, Perl 5.8.1, C#
2010
6
Les langages de programmation
• Langages de programmation compilés
– Génération de code exécutable i.e. en langage machine
– Ex : C / C++
• Les langages de programmation interprétés
– Un langage interprété est converti en instructions exécutables par la machine
au moment de son exécution
– Ex : PHP, Javascript, Ruby
• Les langages P-code
– Langages à mi-chemin entre l’interprétation et la compilation
– Java
• La phase de compilation génère du « byte code »
• Le « byte code » est interprété par une machine virtuelle lors de l ’exécution
– Exécution plus rapide que les langages interprétés
– Exécution plus lente que les langages compilés
– Machine virtuelle pour chaque processeur => portabilité du code compilé
7
Catégories de langages de
programmation
• Programmation impérative
– la programmation impérative est un paradigme de programmation qui décrit
les opérations en termes de séquences d'instructions exécutées par
l'ordinateur pour modifier l'état du programme.
– Programmation procédurale
• Paradigme de programmation basé sur le concept d'appel procédural. Une
procédure, aussi appelée routine, sous-routine ou fonction contient simplement
une série d'étapes à réaliser. N'importe quelle procédure peut être appelée à
n'importe quelle étape de l’exécution du programme, incluant d'autres procédures
voire la procédure elle-même
– Programmation objet
• Paradigme de programmation qui consiste en la définition et l'interaction de
briques logicielles appelées objets ; un objet représente un concept, une idée ou
toute entité du monde physique. Il possède une structure interne, un
comportement et sait communiquer avec ses pairs. Il s'agit donc de représenter ces
objets et leurs relations ; la communication entre les objets via leur relation permet
de réaliser les fonctionnalités attendues, de résoudre le ou les problèmes.
8
Catégories de langages de
programmation
• Programmation fonctionnelle
– La programmation fonctionnelle est un paradigme de programmation qui
considère le calcul en tant qu'évaluation de fonctions mathématiques et
rejette le changement d'état et la mutation des données. Elle souligne
l'application des fonctions, contrairement au modèle de programmation
impérative qui met en avant les changements d'état.
– Ex: CAML
• Programmation logique
– La programmation logique est une forme de programmation qui définit les
applications à l'aide d'un ensemble de faits élémentaires les concernant et de
règles de logique leur associant des conséquences plus ou moins directes. Ces
faits et ces règles sont exploités par un démonstrateur de théorème ou
moteur d'inférence, en réaction à une question ou requête.
– Ex : PROLOG
9
Pourquoi TLC ?
• Culture de l’ingénieur
– Vous utilisez des compilateurs, des interpréteurs etc…
• Savez vous comment ils fonctionnent ?
– Qu’est-ce qu’un langage ?
– Comment le définir ?
– Comment le reconnaitre ?
– Comment l’interpréter, le compiler ?
11
THÉORIE DES LANGAGES
12
Motivation historique
• Reconnaissance automatique de structure
dans des séquences finies
– Structure : loi, régularité
– Séquence : agencement spatial ou temporel
• Avant / après, gauche / droite, dessus / dessous…
13
Exemples
• Télécommunications : séquences de signaux
(Shanon 1916-2001)
14
Exemples
• Protocoles :
– Séquences de symboles formant des messages
– Séquences de messages
2 niveaux d’analyse
• Langages de programmation
– Séquences de caractères pour former des mots
– Séquences de mots
2 niveaux d’analyse
15
Exemple : le morse
• Suite de traits et de points forme des lettres
• Les silences séparent les lettres et les mots
– Jamais deux silences à suivre
Trait
Point
Trait,
Point
Séparateur de lettres
Séparateur de mots
16
Exemple : grammaire de formes
• Description de la structure d’une forme géométrique
• Le « flocon de Koch »
17
Théorie des langages
• Etudie les moyens de caractériser des langages
– Ces moyens sont eux-mêmes des langages
– Notion de métalangage
18
Applications (1)
• Spécification syntaxique
– Le langage est le plus souvent infini
• Point de vue extensionnel
– Trouver une description formelle finie
• Point de vue intentionnel
• description => (description)
• (description) = langage engendré
• Difficultés
– Sur-générer :
– Sous-générer :
19
Applications (2)
• Vérification syntaxique pour la conformité
– Langages de programmation, protocoles réseau,
format de fichier…
20
Applications (3)
• Analyse syntaxique
– Recherche du sens (sémantique)
• Difficultés
– Ambiguïté syntaxique
• Jean a vu Pierre avec ses lunettes
• « ses » réfère à Jean ou à Pierre ?
– Ambiguïté sémantique
• Cet avocat véreux est dégoutant
• Quel « avocat », le fruit ou l’homme de loi ?
21
Notions de base
22
Lettre
• Utilisation d’un alphabet (ou V)
– Ensemble fini de lettres ou de symboles
– On parle aussi de vocabulaire
• Symboles génériques
– Chiffre, ponctuation, majuscule….
– Correspond à des classes de symboles
23
Mot
• Un mot m (ou w)
– Suite finie de symboles appartenant à
– On note le mot vide
24
Langage
• Un langage est un ensemble de mots
(possiblement vide ou infini)
25
Attention
• : le langage vide
– Aucun mot
• : le mot vide
– Un mot, aucune lettre
26
Opérations sur les mots
• Concaténation (noté )
27
Opérations sur les mots
•
– =
• est un élément neutre
–
• La concaténation est associative
– En général :
• La concaténation n’est pas commutative
28
Opérations sur les langages
• Somme de langages : (ou
) ) )
Ex :
• Propriétés
– =
–
29
Opérations sur les langages
• Produit de langages : (ou
) ) )
Ex :
• Propriétés
– =
–
30
Opérations sur les langages
• Fermeture de Kleene :
) )
Ex :
31
Conclusion
• Les mots sont des séquences de lettres
– Concaténation
32
Expressions régulières
33
Motivation
• Expression de langages en termes de langages
plus simples
35
Expression régulières (RE)
• Définition plus formelle
– Soit ∑ un alphabet
– Si ∑, a est une RE
– est une RE
– est une RE
– est une RE si est une RE
– est une RE si et sont des RE
– est une RE si et sont des RE
36
Expressions régulières
• Langage engendré et propriétés
37
Variations de notations
• Objectif : simplifier la description
• [abc]=(a|b|c)
• [a-z] = (a | b | c | … | z)
• )
•
n fois
38
Exemples
• Expression régulière correspondant à un entier
– 0 | [1..9][0..9]*
39
Les limites des langages réguliers
• Les expressions régulières construisent des langages
dits réguliers sur la base de trois opérations
– La concaténation, l’union, la fermeture
40
Conclusion
• Description qui emploie les opérations sur les langages
– Construire un langage complexe à partir de langages simples
41
Les automates à états finis
Reconnaissance d’expressions
régulières
42
Automate fini déterministe
• Un automate fini déterministe est donné par un
quintuplet (S, ∑, δ, s0, SF)
– S est un ensemble fini d’états
– ∑ est un alphabet fini
– δ : S x ∑ -> S est la fonction de transition
– s0 est l’état initial
– SF est l’ensemble des états finaux
• Automate déterministe
– l’état courant + un caractère défini un unique état suivant.
• La structure de l’automate ainsi que ses transitions
définissent des mots reconnus sur l’alphabet ∑
43
Automate fini déterministe
• Automate reconnaissant le mot ‘fee’
f e e
S0 S1 S2 S3
44
Automate fini déterministe
• Automate reconnaissant les mots ‘fee’ et ‘feu’
f e e
S0 S1 S2 S3
u
S4
45
Automate fini déterministe
• Possibilité de reconnaitre des mots de
longueur infinie
– Rebouclage sur un état
0-9
1-9
S0 S3 Que reconnait cet automate ?
0
S4
46
Automates finis non déterministes
• Automates pouvant atteindre plusieurs états en lisant
un seul caractère
• Deux représentations possibles
– On autorise plusieurs transitions sur un même caractère
• δ devient une fonction de S x ∑ -> 2S
– On autorise les Ԑ-transitions
• Transitions sur le mot vide
47
Automates finis non déterministes
a
Ԑ a b
S0 S1 S2 S3
48
Des expressions régulières aux
automates non déterministes
Minimisation
Code d’analyseur
lexical
Expressions Automates finis
régulières déterministes
Construction Déterminisation
de Thompson Automates finis
non
déterministes
49
Des expressions régulières aux
automates non déterministes
• Pour construire un automate reconnaissant un
langage régulier, il suffit d’avoir un mécanisme
qui reconnait
– La concaténation
– L’union
– La fermeture
50
Des expressions régulières aux
automates non déterministes
a
S0 S1 Automate reconnaissant a
b
S2 S3 Automate reconnaissant b
c
S4 S5 Automate reconnaissant c
51
Des expressions régulières aux
automates non déterministes
b
Ԑ S2 S3 Ԑ
S6 S7 Automate reconnaissant
b|c
c
Ԑ S4 S5 Ԑ
52
Des expressions régulières aux
automates non déterministes
Ԑ
b
Ԑ S2 S3 Ԑ
Ԑ
Ԑ
S8 S6 S7 S9
c
Ԑ S4 S5 Ԑ
53
Des expressions régulières aux
automates non déterministes
a
S0 S1
Ԑ
Ԑ
b
Ԑ S2 S3 Ԑ
Ԑ
Ԑ
S8 S6 S7 S9
c
Ԑ S4 S5 Ԑ
55
Algorithme de déterminisation
• s0 : état initial de l’automate
• Ԑ-fermeture(S) : collecte tous les états accessibles par Ԑ-transition depuis les états contenus
dans S
• Δ(S,c) : collecte tous les états atteignables depuis les états de S en lisant le caractère c
q0 <- Ԑ-fermeture(s0)
initialiser Q avec q0
WorkList <- q0
Tant que WorkList non vide
Prendre qi dans WorkList Exercice
Pour chaque caractère c de ∑ Déterminisez l’automate
q <- Ԑ-fermeture(Δ(qi, c)) reconnaissant a(b|c)*
Δ[qi,c] <- q
Si q n’est pas dans Q
Ajouter q à WorkList
Ajouter q à Q
Fin si
Fin pour
Fin tant que
56
Algorithme de déterminisation
• Déterminisation de l’automate pour a(b|c)*
b
b q2
a
q0 q1
c b
c
q3
57
Algorithme de minimisation
• S : ensemble des états de l’automate
• SF : ensemble des états finaux de l’automate
P <- { SF, S- SF }
Tant que P change
T <- ensemble vide
Pour chaque ensemble p de P Exercice
T <- T union Partition(p) Minimisez l’automate
P <- T reconnaissant a(b|c)*
Fin tant que
Partition(p)
Pour chaque c de ∑
Si c sépare p en { p1,…,pk } return { p1,…,pk }
Return p
58
Algorithme de minimisation
• Minimisation de l’automate pour a(b|c)*
b
b q2 b
a a
q0 q1 s0 s1
c b
c
c
q3
59
Algorithme de reconnaissance
• Codage de l’automate par une table de transition
60
Conclusion
• Les expressions régulières décrivent ce qui doit être
reconnu
• Méthodologie
– Spécifier avec une expression régulière
– Transformer l’expression régulière en automate
– Reconnaitre avec l’automate
61
Analyse lexicale
• Les langages réguliers sont utilisés pour décrire les jetons (ou tokens) reconnus lors
de l’analyse lexicale
– Constantes numériques
– Chaines de caractères
– Mots clefs du langage
– Identifiants
– …
62
Les classes de grammaires
La hiérarchie de Chomsky
(formalisée en 1959)
63
Grammaire : définition
64
Les grammaires : formalisation
• La grammaire d’un langage est constituée de quatre
objets
– T est l’alphabet terminal
• Ensemble des symboles qui constituent les phrases à reconnaitre (Cf. sections
précédentes)
– N est l’alphabet non terminal
• Un non-terminal est une variable syntaxique désignant des ensembles de
chaines de symboles terminaux
– S est un symbole particulier de N, l’axiome
• Symbole non-terminal qui désigne l’intégralité du langage
– P est un ensemble de règles de production (ou de dérivation)
• Leur forme générale est la suivante :
avec ∗ ∗ et ∗
65
Les grammaires : formalisation
• En fonction de la nature des règles de production,
plusieurs classes de langages peuvent être identifiées
67
Grammaires contextuelles (type 1)
• Règles de la forme avec
–
–
–
69
Grammaires régulières (type 3)
• Règles de la forme
et avec et
et avec et
On ne peut pas autoriser les deux types de règles
simultanément au risque de sortir des langages réguliers
70
Grammaire à choix fini (type 4)
• Règles de la forme avec et
71
Les langages de programmation usuels
• La description d’un programme est un texte
– Une simple suite de caractères
72
Les langages de programmation usuels
1. Analyse lexicale
– Découpe du texte en petits morceaux appelés jetons
(tokens) correspondant à des suites de caractères
– Chaque jeton est une unité atomique du langage
• Mots clés, identifiants, constantes numériques…
– Les jetons sont décrits par un langage régulier
• Description via des expression régulières
• Reconnaissance via des automates à état finis
73
Les langages de programmation usuels
2. Analyse syntaxique
– Analyse de la séquence de jetons pour identifier la
structure syntaxique du langage
– S’appuie sur une grammaire algébrique définissant la
syntaxe du langage
– Produit généralement un arbre syntaxique qui pourra être
analysé et transformé par la suite
– Détection des erreurs de syntaxe
• Constructions ne respectant pas la grammaire
74
Chaine de compilation
• Analyse lexicale
– Découpe du texte en petits morceaux appelés jetons
(tokens)
– Chaque jeton est une unité atomique du langage
• Mots clés, identifiants, constantes numériques…
– Les jetons sont décrits par un langage régulier
• Détection via des automates à état finis
• Description via des expression régulières
75
Chaine de compilation
• Analyse syntaxique
76
La hiérarchie de chomsky (1/2)
• Extraction de 5 classes de grammaires
– Générales (type 0)
• Règles de la forme : avec
• Génère un très grand nombre de mots
• Temps pour savoir si un mot appartient ou non à la grammaire n’est
pas forcément fini
– Appartenance : temps fini
– Non appartenance : possibilité de bouclage sans réponse
– Indécidable…
– Contextuelles (type 1)
• Règles de la forme avec
• Contextuelles car le remplacement d’un non terminal peut dépendre
des éléments autour de lui
77
La hiérarchie de chomsky (2/2)
– Hors contexte (type 2)
• Règles de la forme , avec
• Les éléments terminaux sont traités individuellement, sans prise
en compte du contexte
• Reconnaissance des langages algébriques
– Régulières (type 3)
• Règles de la forme et et avec
• Reconnaissance des langages rationnels (reconnus par automates
à états finis)
– A choix fini(type 4)
• Règles de la forme avec
• Classe très restreinte…
78
Les grammaires algébriques
L’analyse syntaxique
79
Grammaire algébrique (hors contexte)
Définition
• Définition: une grammaire algébrique est un quadruplet
• G = (N, T, S, P) où:
– N est l’alphabet non terminal
– T est l’alphabet terminal
– S est un symbole particulier de N, l’axiome
– P est un ensemble de règles de production (ou de
dérivation), de la forme:
A w, avec AN et w(N T)*
80
Grammaire algébrique
Langage engendré
• Dérivations
• m m’ si m = uAv et m’=uwv et A->w P
• Langage engendré
• L(G) = { m T*, S m }
• Langage algébrique:
– Définition: un langage L est dit algébrique (ALG) ou "context free"
(CFL) si il existe une grammaire algébrique G, tel que L = L(G)
– Propriété des langages algébriques
• la partie gauche d’une règle est un non-terminal
81
Grammaire algébrique
Arbre de dérivation syntaxique
• Un mot m est reconnu par une grammaire algébrique s’il
existe un arbre de dérivation syntaxique
– Les nœuds internes contiennent des symboles non terminaux
– Les feuilles contiennent des symboles terminaux
– La racine est le symbole initial
– Dans un parcours infixe, les feuilles donnent le mot m
– Si un nœud interne étiqueté X a des sous-arbres t1…tn alors
X -> t1…tn est une règle de la grammaire
82
Grammaire algébrique
Exemple
• Exemple de grammaire décrivant des expressions simples (sans
parenthèses)
– N = {EXP}, S = {EXP}, T = {var, cst, +, *}
– P={ EXP
EXP -> var,
EXP -> cst, EXP + EXP
EXP -> EXP + EXP,
EXP -> EXP * EXP
– } EXP * EXP cst
83
Grammaire algébrique
Exemple
• Exemple de grammaire décrivant des expressions parenthésées
– N = {EXP}, S = {EXP}, T = {var, cst, +, *, (, ) }
– P={
EXP -> var,
EXP -> cst,
EXP -> EXP + EXP,
EXP -> EXP * EXP,
EXP -> ( EXP )
– }
84
Grammaire algébrique
Alphabet et langage de programmation
• L'alphabet non terminal N correspond
– soit à des constructions sémantiques du langage de programmation: programme,
déclaration, instruction, boucle, etc..
– soit à des constructions syntaxiques: liste, etc..
• L'alphabet terminal T correspond aux jetons (unités lexicales) renvoyées par l'analyseur
lexical:
– mots clés,
– identificateurs,
– séparateurs,
– opérateurs,
– …
85
Un exemple de texte structuré
• Exemple de deux adresses postales
Fabrice Lamarche Bernard Truc
IRISA 25 avenue Bidule
263 avenue du général Leclerc 35000 Rennes
35062 Rennes CEDEX
86
Notations usuelles des grammaires
• BNF : Backus Naur Form ou Backus Normal Form
– Description de grammaires algébriques ou context free
• Notations
– Définition d’une règle : opérateur ::=
• Non terminal ::= expression
– Non terminaux : <non-terminal>
• Identifiant entre < >
– Terminaux : "terminal"
• Chaine de caractères entre " "
– Alternative : opérateur |
• "toto" | <regle>
• signifie une alternative entre toto et ce qui est reconnu par le non terminal <regle>
87
Grammaire en notation BNF
La grammaire pour les adresses
<adresse> ::= <identite> <adresse-rue> <code-ville>
<identite> ::= <nom-personne> <EOL> |
<nom-personne> <EOL> <nom-entreprise> <EOL>
<nom-personne> ::= <prenom> <nom>
<prenom> ::= <initiale> "." | <mot>
<nom> ::= <mot>
<nom-entreprise> ::= <mot> | <mot> <nom-entreprise>
<adresse-rue> ::= <numero> <type-rue> <nom-rue> <EOL>
<type-rue> ::= "rue" | "boulevard" | "avenue"
<nom-rue> ::= <mot> | <mot> <nom-rue>
<code-ville> ::= <numero> <ville> <EOL> | <numero> <ville> "CEDEX" <EOL>
<ville> ::= <mot> | <mot> <ville>
88
Grammaires en notation BNF
• Certaines choses sont « longues » à décrire et pas forcément lisibles
– Rendre quelque chose d’optionnel
• <rule-optional> ::= <element> | ""
• Rq : "" est le mot vide
– Répétition d’un élément 0 à n fois
• <rule-repeat> ::= <element> <rule-repeat> | ""
• Utilisation de la récursivité et du mot vide
– Répétition d’un élément 1 à n fois
• <rule-repeat-one> ::= <element> <rule-repeat>
• Utilisation de deux règles car pas de groupement
89
Notations usuelles des grammaires
Grammaires en notation EBNF
• EBNF : Extended BNF
– Même expressivité que les grammaire BNF
– Plus facile à écrire et à comprendre
• Notations :
– Définition d’une règle : operateur =
– Fin de règle : symbole ;
– Alternative : symbole |
– Elément optionnel : utilisation de [ … ]
– Répétition de 0 à n fois : { … }
– Groupement : utilisation de ( … )
– Terminal : "terminal" ou ‘terminal’
– Commentaire : (* commentaire *)
90
Exemple BNF vs EBNF
• Exemple: langage de déclaration de plusieurs variables de
type int ou float.
91
La grammaire pour les adresses
La grammaire en notation EBNF
adresse = identite adresseRue codeVille ;
identite = nomPersonne EOL [nomEntreprise EOL] ;
nomPersonne = prenom nom ;
prenom = initiale "." | mot ;
nom = mot ;
nomEntreprise = mot {mot} ;
adresseRue = numero typeRue nomRue EOL ;
typeRue = "rue" | "boulevard" | "avenue" ;
nomRue = mot {mot} ;
codeVille = numero ville ["CEDEX"] EOL ;
ville = mot {mot} ;
92
La notation graphique
Diagrammes de syntaxe
• Exemple sur la règle de déclaration de méthode en Java
93
Notations usuelles des grammaires
Variante basée sur expressions régulières
• Simple d’utilisation
• Notation similaire à celle utilisée pour l’analyseur lexical
• Notation utilisée dans certains logiciels
– Ex : ANTLR qui sera utilisé en TP.
• Notations
– Alternative : symbole |
– Groupement : utilisation de ( … )
– Répétition 0 à n fois : utilisation de *
– Répétition 1 à n fois : utilisation de +
– Elément optionnel : utilisation de ?
94
La grammaire pour les adresses
Variante EBNF basée exp. régulières
adresse = identite adresseRue codeVille ;
identite = nomPersonne EOL (nomEntreprise EOL)? ;
nomPersonne = prenom nom ;
prenom = initiale "." | mot ;
nom = mot ;
nomEntreprise = mot+ ;
adresseRue = numero typeRue nomRue EOL ;
typeRue = "rue" | "boulevard" | "avenue" ;
nomRue = mot+ ;
codeVille = numero ville "CEDEX"? EOL ;
ville = mot+ ;
95
Exercice
96
Reconnaissance du langage
• Une fois la grammaire écrite, il faut la reconnaitre
– L’analyse ascendante
• Construction de l’arbre de dérivation syntaxique à partir des
feuilles
• Utilisée dans des logiciels type YACC
– L’analyse descendante
• Construction de l’arbre de dérivation syntaxique à partir de la
racine
• Utilisée dans des logiciels type ANTLR(utilisé en TP)
97
L’analyse ascendante
• Aussi appelée analyse LR
– Il analyse l'entrée de gauche à droite (Left to right) et produit une dérivation à
droite (Rightmost derivation)
– On parle d’analyseur LR(k) où k est le nombre de symboles anticipés et non
consommés utilisés pour prendre une décision d’analyse syntaxique
98
L’analyse descendante
• Aussi appelée analyse LL
– Il analyse l'entrée de gauche à droite (Left to right) et en construit une dérivation à gauche (Leftmost
derivation)
– On parle d’analyseur LL(k) où k est le nombre de symboles anticipés et non consommés utilisés pour
prendre une décision d’analyse syntaxique
• Construit (implicitement) un arbre de racine le symbole initial dont les feuilles forment le préfixe de
m
99
Limites de l’analyse descendante
• On ne peut pas toujours décider facilement de la règle à appliquer
– Combien de terminaux faut-il explorer pour déterminer la règle à appliquer ?
– Si k terminaux à explorer : analyseur LL(k)
– La plupart du temps, on s’intéresse aux grammaires LL(1)
• Les grammaires récursives gauches (X::=Xm) ne sont pas LL(1)
• Par contre, les grammaires récursives droite (X::=mX) le sont
• Remarque : le plus souvent, il est possible de passer d’une grammaire LL(k) à une grammaire LL(1)
• Remarque 2 : de nos jours, grâce à certains outils (ANTLR par exemple), il n’est plus nécessaire
d’effectuer cette transformation
100
Vers les arbres de syntaxe abstraite
• Arbre de dérivation syntaxique
– Résulte du parcours de la grammaire lors de l’analyse syntaxique
– Possède beaucoup d’informations « inutiles »
• Lexèmes servant à désambigüiser l’analyse
• Lexèmes servant à identifier les priorités sur les opérations
– Exemple : parenthèses dans une expression numérique
• Délimitation de début / fin de bloc
• …
• Arbre de syntaxe abstraite
– Plus compacte que l’arbre de dérivation syntaxique
– Représente la sémantique du langage
– Supprime les informations « inutiles »
• Ex : Une syntaxe sous forme d’arbre représente implicitement les priorités des
opérations, pas besoin de parenthèses
– S’obtient par réécriture de l’arbre de dérivation syntaxique
101
Retour sur les adresses
Arbre de dérivation syntaxique
• Adresse exemple :
Fabrice Lamarche
IRISA INRIA Rennes
265 avenue du General Leclerc
35042 Rennes CEDEX
102
Retour sur les adresses
Arbre de syntaxe abstraite
• Adresse exemple :
Fabrice Lamarche
IRISA INRIA Rennes
265 avenue du General Leclerc
35042 Rennes CEDEX
103
Retour sur les adresses
Arbre de dérivation syntaxique
• Adresse exemple :
Bernard Truc
25 avenue Bidule
35000 Rennes
104
Retour sur les adresses
Arbre de syntaxe abstraite
• Adresse exemple :
Bernard Truc
25 avenue Bidule
35000 Rennes
105
ANTLR
Un compilateur de compilateurs
106
Introduction
• Un outils pour la reconnaissance de langages
– Ecrit par Terence Parr en Java
107
Introduction
• Compilateur de compilateur
– Fichier source
• Une description du langage
– Grammaires EBNF
• Éventuellement annotée
– Association de différents types d’opérations aux règles de la grammaire
– Produit
• Le code pour l’analyseur lexical
• Le code pour l’analyseur syntaxique
– Le code produit peut être intégré dans les applications
108
Introduction
• Supporte les grammaires LL(*)
– Ne supporte que les grammaires récursives à droite
– LL(k) : Regarde k lexèmes en avance pour lever des
ambigüités
– LL(*) : Regarde autant de lexèmes que nécessaire pour
lever les ambigüités
• Avantage du LL(k)
– Améliore la lisibilité des règles de la grammaire
– Simplifie la description
– Simplifie l’ajout de nouvelles règles
109
Introduction
• Trois grands types d’utilisation
1. Implémentation de « validateurs »
– Valident si un texte en entrée respecte une grammaire
– Pas d’action spécifique effectuée
2. Implémentation de « processeurs »
– Valide un texte en entrée
– Effectue les actions correspondantes
– Les actions peuvent inclure
• Des calculs (langages de script ou avoisinés)
• De la mise à jour de base de données
• L’initialisation d’une application à partir d’un fichier de configuration
• …
3. Implémentation de « traducteurs »
– Valide un texte
– Le transforme dans un autre format
• Ex : Java vers C++
110
Etapes de développement avec ANTLR
• Ecriture d’une grammaire
• Remarque : dans les exemples et dans les TP, le langage Java sera utilisé
111
Structure d’un fichier de grammaire
• grammar nom-grammaire ;
– nom-gramaire : Nom donné à la grammaire
– Doit correspondre au nom du fichier contenant la grammaire avec l’extension « .g »
• options-de-grammaire [optionnel]
– Options permettant de décrire ce qui doit être généré par ANTLR
• attributs-methodes-lexer-parser [optionnel]
– Ensemble des attributs et méthodes ajoutés aux classes d’analyseurs lexical et syntaxique
• lexemes
– Ensemble des lexèmes utilisés dans la grammaire
• regles
– Ensemble des règles définissant la grammaire
112
Les options des grammaires
• Syntaxe :
option
{
name = value ;
}
113
Ajout d’informations pour
les classes et fichiers générés
• Ajout d’un en-tête au fichier généré
– @lexer::header { … }
• Signale des instructions à ajouter à l’en-tête du fichier contenant l’analyseur lexical
– @parser::header { … }
• Signale des instructions à ajouter à l’en-tête du fichier contenant l’analyseur syntaxique
– Utile pour des inclusions de classes ou définition de package par exemple
114
Définition des lexèmes
• La syntaxe de définition des lexèmes est la suivante
– NomLexeme : expression-regulière ;
• Où
– NomLexeme est l’identifiant d’un lexème
• Cet identifiant DOIT commencer par une majuscule
– expression-regulière est une expression régulière décrivant les mots
associés au lexème
• ‘x’ : le caractère ‘x’
• ‘a’..’t’ : l’ensemble des caractères dans l’intervalle [‘a’; ‘t’]
• a|b : décrit l’alternative entre a et b
• . : n’importe quel caractère
• ~ : tous les caractères saufs ceux à droite de ~
• ( ) : sert à regrouper des éléments
• + : répétition 1 à n fois de l’élément à gauche de +
• * : répétition 0 à n fois de l’élément à gauche de *
• ? : rend optionnel l’élément à gauche de ?
115
Définition des lexèmes
• Exemples
116
Définition des lexèmes
• Par défaut, l’analyseur lexical possède deux canaux de sortie
pour les lexèmes
– DEFAULT : le canal de sortie par défaut. Le lexème est consommé par
l’analyseur syntaxique
– HIDDEN : lorsqu’un lexème est sorti sur ce canal, il n’est pas traité par
l’analyseur syntaxique
• Utile pour les commentaires par exemple
117
Définition des lexèmes
• Exemples d’actions
– $channel = HIDDEN ;
• Provoque la sortie sur le canal HIDDEN. Le lexème n’est donc
pas traité par l’analyseur syntaxique
– skip() ;
• Le lexème qui vient d’être reconnu est simplement omis
118
Définition des règles
• Les règles sont décrites via la syntaxe EBNF inspirée par
les expression régulières
• Syntaxe :
regle : corps-regle ;
• Où
– regle est le nom de la règle
• Commence forcément par une minuscule
– corps-regle est une expression décrivant ce qui doit être
reconnu
119
Définition des règles
• Syntaxe du corps de la règle
– identifiant
• Si commence par une majuscule, réfère à un lexème préalablement
déclaré qui doit être reconnu
• Si commence par une minuscule, réfère à une règle qui doit être
reconnue
– ‘chaine’ : reconnait le mot ‘chaine’
• Utilisé pour les mots clefs du langage par exemple
• Ajoute « automatiquement » un lexème prioritaire
– a | b : reconnait soit la sous-règle a, soit la sous règle b
– a* : reconnait 0 à n fois la sous-règle a
– a+ : reconnait 1 à n fois la sous-règle a
– a? : rend optionnelle la reconnaissance de la sous règle a
– () : sert à regrouper des sous-règles
120
Définition de règles
• Exemple de définition de règles
121
Définition de règles
• Vu d’ANTLR, une règle correspond à une méthode
de l’analyseur syntaxique
122
Définition et utilisation des règles
• Syntaxe des règles
regle [ Type1 param1, Type2 param2] returns [TypeR1 id1, TypeR2 id2]
@init { /* code en initialisation de règle */ }
@after { /* code en fin de règle */ }
: corps-règle { /*code en cours de règle */ } ;
123
Exemple : la calculatrice en notation
polonaise inverse en ~20 lignes
grammar calculatrice;
@parser::members
{ java.util.Stack<Integer> m_stack = new java.util.Stack() ; }
INT : '0'..'9'+ ;
expression
@after
{ System.out.println("Value on top: "+ m_stack.pop()) ; } :
( v = value { m_stack.push($v.val) ; }
| v = operation[m_stack.pop(), m_stack.pop()] { m_stack.push($v.val) ; }
)* ';' ;
• Attributs prédéfinis
– $r.text : String, le texte reconnu par la règle
– $r.start : Token, le premier token à reconnaitre
– $r.stop : Token, le dernier token à reconnaitre
– $r.tree : Object, l’arbre AST généré par la règle
127
Attributs des tokens
• Les tokens possèdent des attributs accessibles via
– $NomToken.attribut, accès direct à l’attribut
– a=NomToken et $a.attribut, récupération valeur de retour
et accès à l’attribut
• Attributs prédéfinis
– $t.text : String, le texte associé au token t
– $t.line : int, le numéro de ligne du premier caractère du
token (commence à 1)
– $t.pos : int, la position de ce caractère sur la ligne
(commence à 0)
128
Le code généré
• Lors de la compilation, deux fichiers sont générés :
– Un « lexer » (analyseur lexical)
– Un « parser » (analyseur syntaxique)
129
Le code généré pour l’analyseur
syntaxique
• Les valeurs de retour des règles
– Une classe générée par règle
– Cette classe contient un attribut publique par valeur de retour de règle
– Une référence vers l’arbre de syntaxe abstrait (si présent)
• Accessible via getTree()
– Nom normalisé : nomRegle_return
130
Arbres de syntaxe abstraits
• ANTLR propose des fonctionnalités pour la
construction d’arbres de syntaxe abstraits
131
Structure des arbres de syntaxe
abstraits
• En ANTLR un arbre de syntaxe abstrait :
– Est composé de nœuds
– Ces nœuds peuvent avoir de 0 à n fils
– Les étiquettes associées aux nœuds sont des lexèmes
– Rq : à partir des lexèmes, possibilité de récupérer le texte associé et
donc de l’interpréter
132
Exemples d’arbres de syntaxe abstraits
+ • Arbre correspondant à l’expression 3+4*5
• Encode la priorité des opérateurs
3 *
• Notation en ANTLR
4 5 – ^(‘+’ 3 ^(‘*’ 4 5))
133
Construction d’arbres de syntaxe abstraits
à partir de règles de réécriture
• Manière recommandée de construire des arbres de syntaxe
abstraits (AST)
• Notation :
rule : « alt1 » -> « créer-ceci-suivant-alt1 »
| « alt2 » -> « créer-ceci-suivant-alt2 »
…
| « alt2 » -> « créer-ceci-suivant-alt2 »
;
• Remarques :
– les règles peuvent générer 1 AST ou une liste d’AST
– Par défaut : génération d’une liste d’AST correspondant aux éléments
de la règle (si omission de l’opérateur -> )
134
Exemples simples
• stat: 'break' ';' -> 'break‘
– Renvoie un AST composé d’un nœud avec l’étiquette ‘break’
– Supprime le ‘;’
135
Les lexèmes ‘imaginaires’
• La règle précédente :
– decl : ‘var’ ID ‘:’ type -> ^(VARDEF type ID)
– Utilise un lexème nommé VARDEF
– Ce lexème
• n’a pas d’expression régulière associée
• n’est pas présent dans la grammaire
• Est ajouté pour donner de la sémantique à une opération
136
AST et règles de réécriture
• Collecte d’une suite d’éléments
– list : ID (',' ID)* -> ID+
• Retourne la liste des AST correspondant aux identifiants ID
– formalArgs : formalArg (',' formalArg)* -> formalArg+
• Retourne la liste des AST retournés par la règle formalArg
– decl : 'int' ID (',' ID)* -> ^('int' ID+) ;
• Retourne un AST avec ‘int’ comme étiquette et un ensemble de fils
correspondant à la liste des identifiants
– compilationUnit : packageDef? importDef* typeDef+
-> ^(UNIT packageDef? importDef* typeDef+) ;
• Retourne un AST ayant comme étiquette UNIT
• Un fils optionnel contenant l’AST retourné par packageDef
• Une suite de 0 à n fils contenant les AST retournés par importDef
• Une suite de 1 à n fils contenant les AST retournés pas typeDef
137
AST et règles de réécriture
• Duplication de nœuds et d’arbres
– dup : INT -> INT INT ;
• Retourne deux AST désignant le même lexème
– decl : 'int' ID (',' ID)* -> ^('int' ID)+ ;
• Retourne 1 à n AST ayant ‘int’ pour étiquette et un AST contenant
le token ID comme fils
– decl : type ID (',' ID)* -> ^(type ID)+ ;
• Retourne 1 à n AST en duplicant l’AST retourné par type et en
ajoutant ID comme fils
– decl : modifier? type ID (',' ID)* -> ^(type modifier? ID)+ ;
• Retourne 1 à n AST comme dans la règle précédente mais ajoute
l’AST retourné par modifier comme sous arbre si celui-ci est
présent
138
AST et règles de réécriture
• Choix des arbres en fonction de conditions
– Possibilité d’ajouter des conditionnelles en tête des
règles de réécriture
– Choix de la règle en fonction du résultat de la
conditionnelle
139
AST et règles de réécriture
• Possibilité d’utiliser des labels dans les règles de réécriture
140
AST et règles de réécriture
• Règles de réécriture dans les sous-règles
Ifstat :
'if' '(' equalityExpression ')' s1=statement
('else' s2=statement -> ^('if' ^(EXPR equalityExpression) $s1 $s2)
| -> ^('if' ^(EXPR equalityExpression) $s1)
)
;
– Retourne deux arbres différents en fonction de la présence de ‘else’
– La présence du ‘else’ indique la règle de réécriture à utiliser
141
AST et règles de réécriture
• Parfois une simple réécriture n’est pas suffisante
– Exemple
+
expr : INT ('+’ INT)*
À l’analyse de 1+2+3 nous souhaitons générer + 3
^(’+’ ^(’+’ 1 2) 3)
1 2
142
Comment utiliser un AST
• Par défaut, ANTLR utilise
– org.antlr.runtime.tree.CommonTree pour représenter les AST
– org.antlr.runtime.Token pour représenter les lexèmes
• Lorsque les AST sont générés, les types de retour des règles
contiennent une méthode getTree() renvoyant l’AST
– Attention l’arbre est renvoyé via une référence sur Object
– (CommonTree)valRet.getTree() pour convertir en CommonTree
– Pas très beau mais possibilité d’inclure ses propres classes d’AST
• Cf. livre sur ANTLR
The Definitive ANTLR Reference: Building Domain-Specific Languages
Terence Parr, ISBN: 978-0-9787-3925-6
143
org.antlr.runtime.tree.CommonTree
• Manipulation du lexème associé à l’arbre
– Token getToken()
• Récupération du lexème
– int getType()
• Récupération du type du lexème
144
Conclusion
• ANTLR est un outils puissant
– Description des grammaires LL(*) en EBNF
– Paramètres et valeurs de retour pour les règles
– Association de code aux règles
– Gestion des arbres de syntaxe abstraits
– Règles de réécriture
– Simple d’utilisation étant donné sa puissance…
146
Compilation
147
La compilation
• Un compilateur est un programme informatique qui traduit un
langage, le langage source, en un autre, appelé le langage cible, en
préservant la sémantique du texte source
GL OS
150
Les compilateurs
• Un logiciel parmi les plus sûrs
– Composantes très automatisées
• Analyse lexicale, syntaxique
– Des composantes bien formalisées
• Analyse sémantique, analyse statique
– Une approche très régulière
• Génération de code
• La compilation
– Une théorie qui marche
– NB : ce qui est théorisé marche le mieux…
151
Qu’est-ce qu’un compilateur ?
152
153
Programme source
Front end
Analyse lexicale
Suite de tokens
Analyse syntaxique
Arbre de syntaxe abstraite
Analyse sémantique
Table des
Arbre de syntaxe abstraite enrichi Erreurs
symboles
Génération de code
intermédiaire
Code intermédiaire (ex. code 3 adresses)
Middle end
Optimisation de code
Code intermédiaire optimisé
Back end
Génération de code
Code objet
154
Chaine de compilation
• Analyse lexicale
– Découpe du texte en petits morceaux appelés jetons
(tokens)
– Chaque jeton est une unité atomique du langage
• Mots clés, identifiants, constantes numériques…
– Les jetons sont décrits par un langage régulier
• Détection via des automates à état finis
• Description via des expression régulières
155
Chaine de compilation
• Analyse syntaxique
156
Chaine de compilation
• Analyse sémantique
– Ajout d’information sémantique à l’arbre d’analyse
– Construction de la table des symboles
• Ex : noms de fonction, de variables etc…
– Réalise des vérifications sémantiques
• Vérification de type
• Vérification de la déclaration des variables, des fonctions
• Vérifie que les variables sont initialisées avant utilisation
• …
– Emission d’erreurs et / ou d’avertissements
– Rejet des programmes « incorrects »
157
Chaine de compilation
• Génération de code intermédiaire
– Code indépendant de la machine cible
– Généralement : utilisation du code 3 adresses
• Nombre « infini » de registres
159
Chaine de compilation
• Génération de code
– Transformation du code intermédiaire en code machine
– Connaissance des particularités du processeur
• Nombre de registres
• Utilisation du jeu d’instruction du processeur
– Ex : instructions vectorielles de type SSE
• Ordonnancement des instructions
160
Avantage de la structuration
front / middle / back end
• Front end
– Dépendant du langage source
– Un front end par langage
• Back end
– Transforme le code intermédiaire (IR) en langage cible
– Un back end par langage cible
• Ex : IR -> assembleur x86-64, IR -> assembleur ARM etc…
• Middle end
– Produit une IR optimisée à partir d’une IR
– L’IR est indépendante du langage source et du langage cible !
• En conclusion :
– Au tout départ, N langages sources et M langages cibles = NxM développements de
compilateurs
– Compilateurs modernes, N langages sources et M langages cibles = production de N front ends
et M back ends i.e. N+M développements et réutilisation du middle end !
161
Un compilateur moderne : LLVM
• Front ends pour Ada, C, C++, D, Delphi, Haskell, Julia,
Objective-C, Rust, swift…
162
Analyse sémantique
GRAMMAIRES À ATTRIBUTS
163
Introduction
• Analyse syntaxique
– Permet de vérifier que les mots appartiennent à la grammaire
– Ne présume en rien des propriétés sémantiques du langage
164
Notion d’attribut
• Pour tout symbole terminal ou non terminal de la grammaire
– On associe zéro, un ou plusieurs attributs
– Un attribut est une information jugée utile en cours du processus de
compilation
• Valeur, type de donnée, numéro de ligne, de colonne dans le fichier source…
– L’attribut est définit par un nom et un type (le type de la donnée qu’il porte)
165
Règles sémantiques
• On associe à chaque règle de production une
règle sémantique
( + Exp Exp )
Exp.v=Exp1.v*Exp2.v
v=2 v=10
nb ( * Exp Exp )
v=2 v=5
nb nb
168
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
v=? v=?
169
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
v=? v=?
170
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
v=2 v=?
Exp.v = valeur(nb)
nb
171
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
v=2 v=?
nb ( * Exp Exp )
v=? v=?
172
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
v=2 v=?
nb ( * Exp Exp )
v=? v=?
173
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
v=2 v=?
nb ( * Exp Exp )
v=2 v=?
Exp.v = valeur(nb)
nb
174
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
v=2 v=?
nb ( * Exp Exp )
v=2 v=5
nb nb
175
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
v=?
( + Exp Exp )
Exp.v=Exp1.v*Exp2.v
v=2 v=10
nb ( * Exp Exp )
v=2 v=5
nb nb
176
Exemple de grammaire attribuée
• Texte en entrée : (+ 2 (* 2 5))
Exp
Exp.v=Exp1.v+Exp2.v
v=12
( + Exp Exp )
Exp.v=Exp1.v*Exp2.v
v=2 v=10
nb ( * Exp Exp )
v=2 v=5
nb nb
177
Grammaires attribuées
• Dans l’exemple précédent, les attributs sont
synthétisés
– Les attributs du nœud parent dépendent des
attributs des nœuds fils
178
Exemple déclaration /utilisation
Règles de production Règles sémantiques
179
Exemple déclaration /utilisation
Règles de production Règles sémantiques
180
Exemple déclaration /utilisation
S { ok = …}
u { use=…}
181
Exemple déclaration /utilisation
S { ok = …}
u { use=…}
182
Evaluation des attributs
• Deux difficultés
183
Dépendances circulaires
• A B C {B.x = C.x ; C.y = B.y}
• B b {B.y = b.text}
• B ε {B.y = B.x}
• C ε {C.x = C.y} A
B{xy} C{xy}
b { text } ε
184
Dépendances circulaires
• A B C {B.x = C.x ; C.y = B.y}
• B b {B.y = b.text}
• B ε {B.y = B.x}
• C ε {C.x = C.y} A
B{xy} C{xy}
ε ε
185
Détecter la circularité
Après construction de l’arbre
• Algorithme de tri topologique
– Entrée : Toutes les instances d’attributs, ensemble A
• Complexité : O(Card(A))
186
Détecter la circularité
Après construction de l’arbre
• Semble efficace
• Mais…
– … que faire en cas d’échec de l’analyse ?
• Echec dû à une grammaire mal formée
– Cela ne regarde pas l’utilisateur final !
187
Détecter la circularité
Avant la construction de l’arbre
• Ne doit pas dépendre du mot en entrée
• Si hérité et synthétisé
Risque de circularité
Pas de circularité
190
Bilan des tests de non circularité
• Tri topologique
– Grammaire attribuée et mot connus
• Algorithme de Knuth
– Grammaire attribuée connue
– Conclusion pour tout mot
• Critères de non-circularité
– Sous-langage de Grammaire attribuée connu
– Conclusion pour toute Grammaire attribuée du sous-
langage et tout mot
191
Méthodologie des grammaires
attribuées : étude
• Identifier ce qu'on veut calculer
192
Méthodologie des grammaires
attribuées : réalisation
• Lister les attributs synthétisés
• Tester la non-circularité
193
Mise en œuvre en ANTLR
• A ributs synthé sés ≡ résultat d’appel de
fonction
– Exemple
expr returns [int v] : term '+' expr
{$v = $term.v + $expr.v} ;
194
Mise en œuvre en ANTLR
• A ributs hérités ≡ paramètres d'appel de
procédure
– Exemple
expr [TS ts] returns [int v] :
term [ts] '+' expr [ts] {$v = $term.v + $expr.v} ;
195
Construction de l’arbre vs évaluation
• Analyse par descente récursive
197
Conclusion
• La grammaire attribuée est l'outil de base pour les
calculs dirigés par la syntaxe
• Concevoir en termes de
– Grammaire abstraite
– Attribut synthétisé/hérité
198
ANALYSE SEMANTIQUE
199
Limites des grammaire algébriques
• Comment prévenir la définition multiple d’une même
fonction ?
• Etc…
200
Analyse sémantique
• Objectif : assurer que le programme a un sens
201
Challenges
• Rejeter le plus grand nombre de programmes
incorrects
• Le faire rapidement…
202
Analyse sémantique : implémentation
• Grammaires à attributs
– Augmenter les règles pour faire la vérification durant le parsing
• Complexe et peu lisible
• Peu modulaire et extensible
203
Overview du chapitre
• Vérification de portée
– Comment dire à quoi réfère un identifiant ?
– Comment stocke-t-on cette information ?
• Vérification de type
– Comment dire si des expressions ont le bon type ?
– Comment dire si les fonctions ont des paramètres
valides ?
204
Analyse sémantique
205
Qu’est qu’un symbole ?
• Le même symbole dans un programme peut faire
référence à des choses totalement différentes.
– Ex en java:
public class A
{
char A;
A A(A A)
{
A.A = ‘A’
return A( (A) A ) ;
}
}
206
Qu’est qu’un symbole ?
• Le même symbole dans un programme peut faire
référence à des choses totalement différentes.
– Ex en java:
public class A
{
char A;
A A(A A)
{
A.A = ‘A’
return A( (A) A ) ;
}
}
207
Notion de portée
• La portée d’une entité est l’ensemble des endroits dans
un programme où le symbole associé à l’entité réfère à
cette entité
• Questions :
– A quoi cela ressemble-t-il ?
– Quelle opération doit-elle définir ?
– Comment l’implémenter ?
209
Table des symboles : intuition
01: int x = 137;
02: int z = 42;
03: int function(int x, int y)
04: {
05: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
06: {
07: int x, z;
08: z=y;
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
210
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y)
04: {
05: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
06: {
07: int x, z;
08: z=y;
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
211
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
06: {
07: int x, z;
08: z=y;
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
212
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
06: {
07: int x, z; x 03
08: z=y; y 03
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
213
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
06: {
07: int x, z; x 03
08: z=y; y 03
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
214
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z=y; y 03
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
215
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z=y; y 03
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
216
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z=y; y 03
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
217
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x=z;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
218
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
219
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
220
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: } y 11
19: }
221
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x<<", "<<y<<", "<<z<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: } y 11
19: }
222
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: } y 11
19: }
223
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; x 07
14: }
15: std::cout<<x<<", "<<y<<", "<<z<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: } y 11
19: }
224
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; x 07
14: }
15: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: } y 11
19: }
225
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; x 07
14: }
15: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; z 07
16: }
17: std::cout<<x<<", "<<y<<", "<<z<<std::endl;
18: }
19: }
226
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; x 07
14: }
15: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl; z 07
16: }
17: std::cout<<x@07<<", "<<y@03<<", "<<z@07<<std::endl;
18: }
19: }
227
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z; x 03
08: z@07=y@03; y 03
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl;
14: }
15: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl;
16: }
17: std::cout<<x@07<<", "<<y@03<<", "<<z@07<<std::endl;
18: }
19: }
228
Table des symboles : intuition
Symbole Ligne
01: int x = 137;
02: int z = 42; x 01
03: int function(int x, int y) z 02
04: {
05: std::cout<<x@03<<", "<<y@03<<", "<<z@02<<std::endl;
06: {
07: int x, z;
08: z@07=y@03;
09: x@07=z@07;
10: {
11: int y=x@07;
12: {
13: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl;
14: }
15: std::cout<<x@07<<", "<<y@11<<", "<<z@07<<std::endl;
16: }
17: std::cout<<x@07<<", "<<y@03<<", "<<z@07<<std::endl;
18: }
19: }
229
Remarque
• Dans l’exemple précédent
– La table contient les déclarations de variables
– Les numéros de ligne de déclaration
• De façon usuelle
– La table contient tous les symboles (variables,
fonctions…)
– Le type de l’entité associée au symbole
– D’autres informations utiles à la compilation…
230
Implémentation 1 : pile
• Une implémentation typique : une pile le tables de correspondance
• Les opérations :
– Entrée dans un contexte : push d’une nouvelle table
– Sortie d’un contexte : pop de la table en sommet de pile
– Insertion de symbole : ajout d’une entrée dans la table en sommet de pile
– Recherche d’un symbole : parcours des tables depuis le sommet de la pile (du
plus récent au plus ancien) pour rechercher la première occurrence du
symbole
231
Utilisation de la table des symboles
• Pour traiter une portion de programme qui crée un contexte (bloc,
déclaration de fonction, déclaration de classe…)
– Entrer dans un nouveau contexte
– Ajouter toutes les déclarations de variables dans la table
– Traiter le corps du block / de la fonction / de la classe
– Sortir du contexte
232
Une autre vision
01: int x;
02: int y;
03: int function(int x, int y)
04: {
05: int w,z;
06: {
07: int y;
08: }
09: {
10: int w;
11: }
12: }
233
Une autre vision
Global
01: int x;
x 01
02: int y;
03: int function(int x, int y) y 02
04: {
05: int w,z;
06: {
07: int y;
08: }
09: {
10: int w;
11: }
12: }
234
Une autre vision
Global
01: int x;
x 01
02: int y;
03: int function(int x, int y) y 02
04: {
function
05: int w,z;
x 03
06: {
y 03
07: int y;
08: }
09: {
10: int w;
11: }
12: }
235
Une autre vision
Global
01: int x;
x 01
02: int y;
03: int function(int x, int y) y 02
04: {
function
05: int w,z;
x 03
06: {
y 03
07: int y;
08: } Bloc 04-12
09: { w 05
10: int w;
z 05
11: }
12: }
236
Une autre vision
Global
01: int x;
x 01
02: int y;
03: int function(int x, int y) y 02
04: {
function
05: int w,z;
x 03
06: {
y 03
07: int y;
08: } Bloc 04-12
09: { w 05
10: int w;
z 05
11: }
12: } Bloc 06-08
y 07
237
Une autre vision
Global
01: int x;
x 01
02: int y;
03: int function(int x, int y) y 02
04: {
function
05: int w,z;
x 03
06: {
y 03
07: int y;
08: } Bloc 04-12
09: { w 05
10: int w;
z 05
11: }
12: } Bloc 06-08 Bloc 09-11
y 07 w 10
238
Implémentation 2 : spaghetti stacks
• La table des symbole est un arbre de tables de
correspondances (associées à des contextes)
239
ANALYSE SEMANTIQUE
VERIFICATION DE TYPE
240
Qu’est ce qu’un type ?
• Définition sujette à débat…
241
Types de vérification de type
• Vérification de type statique
– Analyse effectuée au moment de la compilation
– Prouve l’absence d’erreur avant l’exécution
242
Les systèmes de typage
• Les systèmes de typage forts
– N’autorisent jamais une erreur de typage
– Ex: Java, Python, ADA, Haskell…
243
La guerre des types
• Débat sans fin sur le meilleur système de typage
244
Ingrédients du typage
• Un langage de termes
• Un langage de types
• Un système de typage
– Quel type a quel terme ?
245
Termes et types
• Les termes dénotent de valeurs
– On les évalue pour obtenir leur dénotation
• Les programmes…
247
Langage de types
• Pas forcément explicite dans le langage source
– Si explicite, on peut garder la syntaxe abstraite, sinon en
inventer une
– Situation intermédiaire possible
• Lisp, Scheme
– Complètement implicite
• ML, Javascript, Scala
– Explicite optionnel, tous les types ont une expression
• C, C++, Java
– Explicite obligatoire
248
Langage de types
249
Types atomiques
• bool, int, float, char…
• unit
– Type des termes avec une seule valeur possible
– Utile en fonctionnel pour des fonctions ne
renvoyant pas vraiment de valeur (void en C…).
• void
– Le type des termes avec valeur pas représentable
250
Type tableau
• array(Ind, Val)
– Retourne un terme de type Val quand on l’indexe
avec un terme de type Ind
• Ex :
– en C, float t[12] dénote array(int, float)
251
Le type fonction
• fun(From, To)
– Retourne un terme de type To
– Quand on l’appelle avec un terme de type From
• Ex : fun(float, int)
– En C : int f(float)
– En ML : int -> float
252
Le type structure
• struct(A1:T1, …, Ai:Ti, …, An:Tn)
– Retourne un terme de type Ti
– Quand on lui applique le sélecteur Ai
253
Le type union
• union(A1:T1, …, Ai:Ti, …, An:Tn)
– Retourne un terme de type Ti
– Quand on lui applique le sélecteur Ai
254
Le type pointeur
• ptr(A)
– Retourne un terme de type A
– Quand on le déréférence
• Ex : ptr(int)
– En C : int *
255
Expressions de type
• En C
– struct list { char car ; struct list * suivant }
• Dénote
– list : struct(car:char, suivant:ptr(list))
• En ML
– (A->B)->(B->C)->(A->C)
• Dénote
– fun(fun(A,B), fun(fun(B,C), fun(A,C)))
256
Curryfication
• fun(A, fun(B,C)) peut se lire fun(A,B,C)
257
Système de typage
258
Jugement de type
• t:T
– Le terme t a le type T
259
Règles de déduction
• Forme générale d’une règle
• Axiome
260
Notion d’arbre de preuve
• Arbre de preuve
– Nœuds : instances de règles de déduction
– Racine : un jugement à prouver
261
Système de typage
Système de typage
=
Règles de déduction dont les jugements sont
des types
262
Axiomes
• Liés aux constantes
…
• Les variables
263
Règles de déduction
• Tableau
• Fonction
• Union
265
Règles de déduction
• Pointeurs
266
Quelques concepts
• Surcharge :
267
Quelque concepts
• Programme bien typé
268
Quelques concepts
• Vérification de type statique / dynamique
269
Quelques concepts
• Propriété du typage sain
– Un système de typage est dit sain (sound) si un
programme bien typé statiquement ne peut pas
causer d’erreur de type dynamique
270
Mise en œuvre
• En utilisant une grammaire attribuée
– Utiliser deux attributs
• Type : le type de l’expression
• Ok : vrai si pas d’erreur de typage, faux sinon
– Vérification en cours d’analyse syntaxique
271
Ex: mise en œuvre en GA (1/3)
• Attributs
– TS : table des symboles
• isFunction : permet de savoir si un symbole est une fonction
• type : retourne le type associé au symbole
– Ok : vrai si le typage est correct
– type : le type de l’expression
• Fonction utilisées
– to(FunctionType) => retourne ne type de retour de la
fonction
– from(FunctionType) => retourne le type du paramètre de
la fonction
272
Ex: mise en œuvre en GA (2/3)
• expr -> Symbol ‘(‘ expr1 ‘)’
{
expr1.TS = expr.TS;
expr.ok = expr1.ok && expr.TS.isFunction(Symbol) &&
from(expr.TS.type(symbol))==expr1.type;
expr.type = to(expr.TS.type(symbol));
}
273
Ex : mise en œuvre en GA (3/3)
• De manière schématique la partie droite de la
règle va apporter les hypothèses et la partie
gauche sera étiquetée avec la conclusion
Erreur de typage
274
Pour aller plus loin…
• Dans certains langages
– Déclaration de type absentes en partie…
• Ex : fonction génériques / lambda fonctions en C++,
Java…
– …ou totalement
• Ex : CAML
276
Retour sur l’exemple précédent
• expr -> Symbol ‘(‘ expr1 ‘)’
{
expr.ok = expr1.ok && expr.TS.isFunction(Symbol) ;
if (expr.ok
&& unifType(expr.TS.Type(Symbol),
makeFunction(Expr1.type, new varType)))
then {expr.type = varType}
else {expr.type=error; expr.ok=false}
}
277
Procédure d’unification
• unifType( fun( int, int ), fun( int, var(A) ) )
– OK, var(A) <- int
278
Procédure d’unification
• unifType( fun(var(X), var(X)), fun(var(V), var(A)))
– OK, var(X) <- var(V), var(A) <- var(V)
279
Conclusion
• Le type est vu comme une propriété
280
CODE INTERMÉDIAIRE
281
La sortie du front-end
• Le programme lu appartient bien au langage
– Lexicalement
– Syntaxiquement
– Sémantiquement
283
Intérêt du code intermédiaire
3 frontend 3 frontend
4*3 optimiseurs 1 optimiseurs
4*3 générateurs de code 3 générateurs de code
284
Différents types de code intermédiaire
• Le code intermédiaire doit être facile à produire, facile à
transformer en code machine
– Une sorte d’assembleur universel
– Ne doit pas contenir de paramètres spécifiques à une machine / un
processeur
285
Code intermédiaire
LE CODE 3 ADRESSES
286
Code 3 adresses
• Les instructions sont très simples
• Il y a une cible, au plus deux sources et un opérateur
• Les sources peuvent être des variables, des registres ou des
constantes
• Les cibles sont des registres ou des variables
- -
+ / + /
a d Graphe acyclique
a * d * orienté
b c b c *
Arbre de syntaxe abstraite b c
288
Code 3 adresses : instructions
• Instructions avec assignation
– a=b
• Copie b dans a
– a = unop b
• applique l’opérateur unaire unop sur b et stocke le résultat dans a
• Ex : -, !, ~
– a = b biop c
• Applique l’opération biop avec b et c pour opérandes et stocke le résultat dans
a
• Ex : +, -, *, /, &, |, <, >
• Instructions de saut
– goto L : saut inconditionnel au label L
– if t goto L : si t est vrai sauter à L
– Note : sur le if, il peut y avoir beaucoup de variantes
• Ex : ifnz, ifz…
289
Code 3 adresses : instructions
• Les fonctions
– func begin <name>
• Déclare le début de la fonction nommée <name>
– func end
• La fin de la fonction
– Return
• Retourne à la fonction appelante
– return a
• Retourne la valeur a à la fonction appelante
– param p
• Place la paramètre p sur la pile
– R = call <name> n
• Appelle la fonction <name> avec les n paramètres en sommet de pile
290
Code 3 adresses : instructions
• Les tableaux
– a = b[i]
• Stocke la valeur de la ieme case du tableau b dans a
– b[i] = a
• Stocke la valeur de a dans la ieme case du tableau b
• Les pointeurs
– a = &b
• Stocke l’adresse de la variable b dans a
– (*a)=b
• Stocke la valeur de b à l’adresse désignée par a
– a = (*b)
• Stocke dans a la valeur à stockée l’adresse mémoire b
291
Code 3 adresses : remarques
• Généralement les variables du code 3 adresses sont
– Les variables déclarées par l’utilisateur
– Eventuellement quelques variables ajoutées par une
transformation de code
Toutes présentes dans la table des symboles
292
Représentation intermédiaire
293
Production de code 3A
• Entrée : un arbre de syntaxe abstraite
• Sortie : un code 3 adresses
+
R0 = x
R1 = 2
x * R2 = y
R3 = R1 * R2
2 y R4 = R0 +R3
294
Stratégie générale
• Chaque nœud de l’arbre de syntaxe abstrait
– Produit des bribes de code selon un patron
prédéfini
• Sans avoir connaissance de ce que font les autres
nœuds
– Peut composer un nouveau code à partir des
codes des nœuds fils
x *
2 y
296
Retour sur l’exemple
+
x *
r0=x
2 y
297
Retour sur l’exemple
+
x *
r0=x
2 y
r1=2
298
Retour sur l’exemple
+
x *
r0=x
2 y
r1=2 r2=y
299
Retour sur l’exemple
+
x *
r0=x r1=2
r2=y
r3=r1*r2
2 y
r1=2 r2=y
300
Retour sur l’exemple
+
r0=x
r1=2
r2=y
r3=r1*r2
r4=r0+r3
x *
r0=x r1=2
r2=y
r3=r1*r2
2 y
r1=2 r2=y
301
Exemple de structure de contrôle
R0=a
if(a<b) R1=b
{ R2=R0<R1
ifz R2 goto false_label_0
b=3*a; R3=3
R4=a
} R5=R3*R4
b=R5
else goto end_if_label_1
{ false_label_0:
R6=3
a=3*b R7=b
R8=R6*R7
} a=R8
End_if_label_1:
302
Remarques
• Le code généré est naïf
– Contient beaucoup de variables intermédiaires
(registres)
– Certains traitements sont « peu » utiles
303
Code 3 adresses et runtime du langage
• Le langage source requiert des fonctionnalités
– Souvent codées directement dans le langage cible
– Lors de la production du code 3 adresses, utilisation de
fonctions au nom prédéfini
– Fonctions présentes dans la bibliothèque de runtime du langage
304
Transformation optimisantes
305
Le challenge de l’optimisation
• Un bon optimiseur
– Ne doit pas changer le comportement observable du
programme
– Devrait produire un code intermédiaire aussi efficace que
possible
– Ne devrait pas prendre trop de temps de calcul
• Cependant
– Les optimiseurs ratent souvent des optimisations
« faciles » de part les limitations des algorithmes
– Presque toutes les optimisations intéressantes sont NP-
complètes voire indécidables…
306
Que pouvons nous optimiser ?
• Temps d’exécution
– essayer de rendre le programme le plus rapide possible
– Souvent au détriment de la mémoire et de la consommation
énergétique
• L’occupation mémoire
– Essayer de minimiser l’occupation mémoire du code produit
– Souvent au détriment de la vitesse d’exécution et de la
consommation énergétique
• La consommation énergétique
– Essayer de réduire la consommation énergétique
– Souvent au détriment de la vitesse
307
Optimisation de code intermédiaire vs
optimisation de code
• La distinction n’est pas toujours claire…
• Typiquement:
– L’optimisation de code intermédiaire essaye de réaliser des
simplifications valables pour toutes les machines
– L’optimisation de code essaye d’améliorer les
performances en connaissant les spécificités de la machine
cible
308
Optimisations préservant la
sémantique
• Une optimisation conserve la sémantique si elle ne change
pas la sémantique du programme original
• Exemples
– Suppression de variables temporaires inutiles
– Calculer des valeurs qui sont connues au moment de la
compilation
• Ex : int a = 2*3 peut être remplacé par int a = 6
– Sortir des invariants de boucles
• Contre exemple
– Remplacer un tri à bulles par un quick sort…
• Ne préserve pas la sémantique du programme original
309
Notion de bloc de base
• Un bloc de base est une séquence d’instructions
de code intermédiaire telle que
– Il y a exactement un point d’entrée à la séquence et
s’il y a un contrôle entrant dans la séquence, il entre
au début de cette dernière
– Il y a exactement un endroit où le contrôle quitte la
séquence et cet endroit est la fin de la séquence
310
Graphe de flot de contrôle
• Un graphe de flot de contrôle est un graphe
contenant les blocs de base d’une fonction
312
Exemple : calcul de pgcd
Découpage en bloc de base
R0 = call _readInt
a = R0
R1 = call _readInt
b = R1
loop:
R2 = a
R3 = b
R4 = R2 % R3
r = R4
ifz r goto end_if
R5 = a
b = R5
R6 = r
a = R6
goto loop
end_if:
param b
call _printInt
313
Exemple : calcul de pgcd
Graphe de flot de contrôle
R0 = call _readInt Start
a = R0 loop:
R1 = call _readInt R2 = a
b = R1 R3 = b
loop: R0 = call _readInt
a = R0 R4 = R2 % R3
R2 = a r = R4
R3 = b R1 = call _readInt
b = R1 ifz r goto end_if
R4 = R2 % R3
r = R4
ifz r goto end_if
R5 = a R5 = a
b = R5 b = R5
end_if:
R6 = r R6 = r
param b
a = R6 a = R6
call _printInt
goto loop goto loop
end_if:
param b
call _printInt End
314
Les types d’optimisation
• Une optimisation est dite locale si elle travaille
sur un bloc de base
315
Transformation optimisantes
OPTIMISATIONS LOCALES
316
Un code exemple
o1 = i 4
b=a–d
a = t[o1] + d
o2 = i 4
c=a–d
t[o2] = c
317
Elimination de sous expressions
communes
o1 = i 4 o1 = i 4
b=a–d b=a–d
a = t[o1] + d a = t[o1] + d
o2 = i 4 o2 = o1
c=a–d c=b
t[o2] = c t[o2] = c
Conditions d’application ?
318
Elimination de sous expression
communes
• Remplacer la séquence suivante
– (1) t = a op b
– (2) …
– (3) … = a op b
• Par
– (1) t = a op b
– (2) …
– (3) … = t
• Si
– t, a et b ne sont pas modifiés dans la suite d’instructions (2)
– si (1) est toujours exécuté quand (3) l’est : toujours vrai dans un
bloc de base
319
Propagation de copies
o1 = i 4 o1 = i 4
b=a–d b=a–d
a = t[o1] + d a = t[o1] + d
o2 = o1 o2 = o1
c=b c=b
t[o2] = c t[o1] = b
Conditions d’application ?
320
Propagation de copies
• Remplacer la séquence suivante :
– (1) x = y
– (2) …
– (3) … x …
• Par
– (1) x = y
– (2) …
– (3) … y …
• Si
– x et y ne sont pas modifiés dans la suite d’instructions (2)
– si (1) est toujours exécuté quand (3) l’est : toujours vrai
dans un bloc de base
321
Elimination de code mort
o1 = i 4 o1 = i 4
b=a–d b=a–d
a = t[o1] + d
o2 = o1
c=b
t[o1] = b t[o1] = b
Conditions d’application ?
322
Elimination de code mort
• Remplacer
– (1) x = E
– (2) …
• Par
– (2) …
• Si x n’est pas utilisé dans (2)
• La valeur rangée dans x en un point (P) n’est pas lue après le point (P)
– On dit que x n’est pas vivante après ce point
323
Appliquer des optimisations locales
• Les optimisations précédentes prennent en
compte des petites optimisations
– La suppression des sous expressions communes
élimine des calculs inutiles
– La propagation de copies aide à identifier le code mort
– L’élimination de code mort supprime des affectations
inutiles
324
Exemple
b=a*a b=a*a
c=a*a c=b
d=b+c d=b+c
e=b+b e=b+b
325
Exemple
b=a*a b=a*a
c=b c=b
d=b+c d=b+b
e=b+b e=b+b
Propagation de copies
326
Exemple
b=a*a b=a*a
c=b c=b
d=b+b d=b+b
e=b+b e=d
327
Autres optimisations locales
• Simplification de sous expressions constantes
– Ex : x=4*5 peut se réécrire en x=20
• Simplification arithmétiques
–E 2 E+E shift-left1 E
–E 7 (shift-left3 E) – E
–E/4 shift-right2 E
328
Implémentation de l’optimisation
locale
• Expressions disponibles
– L’élimination des expressions communes (CSE) et la
propagation de copie (CP) reposent sur les expressions
disponibles à un endroit dans le code
– Une expression est disponible si une variable contient
la valeur de cette expression
– Dans l’élimination des expressions communes on
remplace une expression par la variable contenant sa
valeur
– Dans la propagation de copie, on remplace une
variable par l’expression qui lui est associée
329
Recherche des expressions disponibles
• Initialement, aucune expression de disponible
• Quand une instruction du type a = b op c est
exécutée
– Toute expression utilisant a est invalidée
– L’expression a = b op c devient disponible
– (de même pour a = op b ou encore a = b)
a=b
c=b
d=a+b
e=a+b
d=b
f=a+b
331
Expressions disponibles
{}
a=b
{a=b}
c=b
{a=b, c=b}
d=a+b
{a=b, c=b, d=a+b}
e=a+b
{a=b, c=b, d=a+b, e=a+b}
d=b
{a=b, c=b, d=b, e=a+b}
f=a+b
{a=b, c=b, d=b, e=a+b, f=a+b}
332
Elimination des sous expressions
communes
{}
a=b
{a=b}
c = b -> c=a
{a=b, c=b}
d=a+b
{a=b, c=b, d=a+b}
e = a + b -> e = d
{a=b, c=b, d=a+b, e=a+b}
d = b -> d = a
{a=b, c=b, d=b, e=a+b}
f = a + b -> f = e
{a=b, c=b, d=b, e=a+b, f=a+b}
333
Elimination des sous expressions
communes
a=b
c=a
d=a+b
e=d
d=a
f=e
334
Expressions disponibles
{}
a=b
{a=b}
c=a
{a=b, c=a}
d=a+b
{a=b, c=a}
e=d
{a=b, c=a, e=d}
d=a
{a=b, c=a, d=a}
f=e
{a=b, c=a, d=a, f=e}
335
Propagation de copie
{}
a=b
{a=b}
c = a -> c = b
{a=b, c=a}
d = a + b -> d = b + b
{a=b, c=a}
e=d
{a=b, c=a, e=d}
d = a -> d = b
{a=b, c=a, d=a}
f=e
{a=b, c=a, d=a, f=e}
336
Propagation de copie
a=b
c=b
d=b+b
e=d
d=b
f=e
337
Les variables vivantes
• L’analyse correspondant à l’élimination du code mort
repose sur la notion de variable vivante
338
Calcul des variables vivantes
• Initialement seul un sous ensemble des variables
sont connues comme étant vivantes
– Ex : valeur de retour pour une fonction…
339
Variables vivantes
{b}
a=b
{b}
c=b
{b}
d=b+b
{b,d}
e=d
{b,e}
d=b
{b,d,e}
f=e
{b, d}
340
Elimination de code mort
{b}
a=b
{b}
c=b
{b}
d=b+b
{b,d}
e=d
{b,e}
d=b
{b,d,e}
f=e
{b, d}
341
Elimination de code mort
d=b+b
e=d
d=b
342
Variables vivantes (2)
{b}
d=b+b
{b,d}
e=d
{b}
d=b
{b, d}
343
Elimination de code mort (2)
{b}
d=b+b
{b,d}
e=d
{b}
d=b
{b, d}
344
Elimination de code mort (2)
d=b+b
d=b
345
Variables vivantes (3)
{b}
d=b+b
{b}
d=b
{b, d}
346
Elimination de code mort (3)
{b}
d=b+b
{b}
d=b
{b, d}
347
Elimination de code mort (3)
d=b
348
Une autre vision de l’analyse locale
Vin
349
Formalisation de l’analyse locale
• Définition de l’analyse d’un bloc de base : quadruplet (D, V,
F, I)
350
Expressions disponibles
• D : avant
• I : l’ensemble vide
351
Variables vivantes
• D : arrière
• V : Ensemble de variables
352
Réaliser une analyse locale
• Etant donné une analyse (D,V,F,I) pour un bloc
basique
– Supposons que D est « avant » (si « arrière »,
parcourir les instructions en sens inverse)
• Initialement OUT[entrée] = I
• Pour chaque instruction s
– IN[s] = OUT[prev] avec prev l’instruction précédente
– OUT[s] = (IN[s]) avec la fonction de transfert
associée à s
353
Transformation optimisantes
OPTIMISATIONS GLOBALES
354
Analyse globale
• Fonctionne sur le graphe de flot de contrôle
complet
356
Suppression de code mort globale
• La suppression de code mort locale a besoin
de connaitre les variables vivantes en sortie de
bloc
Information uniquement calculable par analyse
globale
357
Graphe de flot de contrôle sans boucle
{a,c,d}
b=c+d
Entrée
e=c+d
{a,b,c,d}
{b,c,d} {a,b,c,d}
x=c+d y=a+b
a=b+c
{a,b,c,d} {a,b,c,d}
{a,b,c,d}
x=a+b {x, y}
y=c+d Sortie
{x, y}
358
Graphe de flot de contrôle sans boucle
{a,c,d}
b=c+d
Entrée
e=c+d
{a,b,c,d}
{b,c,d} {a,b,c,d}
x=c+d y=a+b
a=b+c
{a,b,c,d} {a,b,c,d}
{a,b,c,d}
x=a+b {x, y}
y=c+d Sortie
{x, y}
359
Graphe de flot de contrôle sans boucle
{a,c,d}
b=c+d
Entrée
{a,b,c,d}
{b,c,d} {a,b,c,d}
a=b+c
{a,b,c,d} {a,b,c,d}
{a,b,c,d}
x=a+b {x, y}
y=c+d Sortie
{x, y}
360
Graphe de flot de contrôle sans boucle
b=c+d
Entrée
a=b+c
x=a+b
y=c+d Sortie
361
Différences avec analyse locale
• Analyse locale : chaque instruction a
exactement 1 prédécesseur / successeur
363
Graphes de flot de contrôle avec
boucles
b=c+d
Entrée
c=c+d
a=b+c c=a+b
d=a+c
{a} a=a+b
Sortie d=b+c
364
Différences avec l’analyse locale
• Dans l’analyse locale, il y a toujours une première
instruction bien définie pour commencer le
traitement
{} {}
a=b+c c=a+b
d=a+c
{}
{a} a=a+b
Sortie d=b+c
366
Graphe de flot de contrôle avec
boucles
{}
b=c+d
Entrée
c=c+d
{} {}
a=b+c c=a+b
d=a+c
{}
{a} a=a+b
Sortie d=b+c
{a}
367
Graphe de flot de contrôle avec
boucles
{}
b=c+d
Entrée
c=c+d
{} {}
a=b+c c=a+b
d=a+c
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a}
368
Graphe de flot de contrôle avec
boucles
{}
b=c+d
Entrée
c=c+d
{} {}
a=b+c c=a+b
d=a+c
{a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a}
369
Graphe de flot de contrôle avec
boucles
{}
b=c+d
Entrée
c=c+d
{b,c} {}
a=b+c c=a+b
d=a+c
{a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a}
370
Graphe de flot de contrôle avec
boucles
{}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {}
a=b+c c=a+b
d=a+c
{a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a}
371
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {}
a=b+c c=a+b
d=a+c
{a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a}
372
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a}
373
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a}
374
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
375
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
376
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
377
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
378
Graphe de flot de contrôle avec
boucles
{c,d}
b=c+d
Entrée
c=c+d
{a,b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
379
Graphe de flot de contrôle avec
boucles
{a,c,d}
b=c+d
Entrée
c=c+d
{a,b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
380
Graphe de flot de contrôle avec
boucles
{a,c,d}
b=c+d
Entrée
c=c+d
{a,b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
381
Graphe de flot de contrôle avec
boucles
{a,c,d}
b=c+d
Entrée
c=c+d
{a,b,c}
{b,c} {a,b}
a=b+c c=a+b
d=a+c
{a,b,c} {a,b,c}
{a,b,c}
{a} a=a+b
Sortie d=b+c
{a,c,d}
382
Graphe de flot de contrôle avec
boucles
b=c+d
Entrée
c=c+d
a=b+c c=a+b
a=a+b
Sortie d=b+c
383
Autres optimisations
• Propagation de constante globale
• Déroulage de boucles
• Inlining
Supprimer l’appel d’une fonction en recopiant le corps de la fonction
dans l’appelant
• …
384
Conclusion
• Attention : optimisation optimalité
– Pas de transformations optimisantes à proprement parler
– Plutôt des transformations améliorantes
385
GESTION MÉMOIRE
386
Introduction
• Beaucoup de constructions dans les langages de
programmations ont besoin de mémoire
387
Gestion de la mémoire
1. De la mémoire est allouée à priori et est persistante durant
l’exécution
– Variables globales, table des fonction virtuelles, code exécutable…
388
Gestion mémoire
389
Gestion manuelle de la mémoire
• Le programmeur doit se charger de la gestion mémoire
• Avantages
– Le programmeur peut exercer un contrôle précis de
l’utilisation de la mémoire
• Inconvénients
– Le programmeur DOIT exercer un contrôle précis de
l’utilisation de la mémoire
390
Force de la gestion manuelle
• Relativement facile à implémenter
– Requiert un gestionnaire de mémoire
391
Problèmes de la gestion manuelle
• Conduit facilement à des bugs
392
Gestion mémoire
GARBAGE COLLECTION
393
Qu’est-ce qu’un objet « Garbage »
• A un point de l’exécution un objet est qualifié de « garbage » s’il ne sera
plus jamais utilisé par la suite
• Un objet est atteignable s’il peut encore être référencé par le programme
– Principe sur lequel beaucoup d’algorithmes de « garbage collection » reposent
394
Hypothèses de travail
• Supposons que durant l’exécution, nous puissions trouver
toutes les références dans un programme
– Impossible de fabriquer une référence vers un objet ex nihilo
– Impossible de transformer un pointeur en entier et
réciproquement
• Contre-exemples : C, C++…
395
Les types de « garbage collectors »
• Incrémental vs stop-the-world
– Un GC incrémental fonctionne en parallèle du programme
– Un GC stop-the-world met l’exécution du programme en
pause
• Compacting vs non-compacting
– Un compacting GC déplace les objets en mémoire pour les
tasser et éviter la fragmentation de la mémoire
– Un non-compacting GC laisse les objets à leur
emplacement initial (peut générer de la fragmentation
mémoire)
396
Garbage collection
COMPTAGE DE RÉFÉRENCES
397
Comptage de références
• Un framework simple
– Mais qui a de sérieux défauts !!!
398
LE PROBLEME
les références cycliques
• Un cycle de références est créé par des objets qui se
références les uns les autres et forment un cycle
399
Analyse
• Avantages
– Simple à implémenter
– Peut être implémenté dans une librarie au dessus de la
gestion manuelle de la mémoire
• Ex : C++ et std::shared_ptr<T>
• Désavantages
– Echoue dans la libération de la mémoire de tous les objets
non atteignables (problème des cycles)
– Peut être lent lorsque beaucoup d’objets sont libérés
– Ralenti les assignations de références (ou pointeurs) car
accède au compteur (pose des problèmes de cache)
400
Garbage collection
401
L’intuition
• Etant donné la connaissance de ce qui est
immédiatement accessible, trouver tous les objets
accessibles depuis le programme
403
Exemple
Liste de travail
1
Ensemble
racine 1 2
3
4
5 6
404
Exemple
Liste de travail
3
Ensemble
racine 1 2
3
4
5 6
405
Exemple
Liste de travail
56
Ensemble
racine 1 2
3
4
5 6
406
Exemple
Liste de travail
67
Ensemble
racine 1 2
3
4
5 6
407
Exemple
Liste de travail
77
Ensemble
racine 1 2
3
4
5 6
408
Exemple
Liste de travail
7
Ensemble
racine 1 2
3
4
5 6
409
Exemple
Liste de travail
Ensemble
racine 1 2
3
4
5 6
410
Exemple
Liste de travail
Ensemble
racine 1 2
3
4
5 6
411
Exemple
Liste de travail
Ensemble
racine 1
5 6
412
Implémentation du mark-and-sweep
• L’algorithme tel que présenté a deux problèmes
majeurs
414
L’algorithme de Baker
• Déplacer l’ensemble racine dans la liste d’exploration
• A ce point, tout ce qui est atteignable est dans la liste marqué et ce qui n’est pas
atteignable dans la liste inconnu
415
Analyse
• Avantages
– Trouve précisément les objets atteignables
– Avec l’algorithme de Baker, le temps d’exécution est
proportionnel au nombre d’objets atteignables
• Désavantages
– Approche de type stop-the-world qui peut imposer de
gros temps de pose
– Les informations de double chainage et d’état dans
chaque bloc alloué utilisent beaucoup de mémoire par
objet
416
Garbage collection
417
Amélioration de performances
• Il y a beaucoup de manière d’augmenter les performances d’un
programme
• Certaines peuvent être améliorées par un bon garbage collector
• Augmenter la localité
– Caches mémoires : accélération de l’accès à des données adjacentes
en mémoire
– Placer les objets de manière consécutive en mémoire peut réduire les
fautes de cache
418
Augmenter la localité
• Idée : durant le processus de « garbage collection »,
déplacer les objets pour qu’ils soient adjacents en mémoire
– Compactage de la mémoire
419
Augmenter la vitesse d’allocation
• Les implémentations de malloc / free utilisent des
listes de blocs libres en mémoire
421
Principe du stop-and-copy
Mémoire adressable
422
Principe du stop-and-copy
Mémoire adressable
423
Principe du stop-and-copy
424
Principe du stop-and-copy
425
Principe du stop-and-copy
426
Principe du stop-and-copy
427
Principe du stop-and-copy
428
Principe du stop-and-copy
429
Principe du stop-and-copy
430
Principe du stop-and-copy
Ensemble racine
431
Principe du stop-and-copy
Ensemble racine
• A partir de l’ensemble racine
• Recopier les objet accessibles
• Mettre à jour les références
432
Principe du stop-and-copy
433
Principe du stop-and-copy
434
Principe du stop-and-copy
435
Principe du stop-and-copy
436
Principe du stop-and-copy
437
Principe du stop-and-copy
438
Stop-and-copy : l’algorithme
• Partitionner la mémoire en deux régions : l’ancien espace et le nouvel
espace
• Conserver un pointeur sur l’espace libre (situé à la fin de la mémoire
allouée)
• Pour allouer n octets
– S’il y a un espace de n octets
• l’adresse allouée est dans le pointeur sur l’espace libre
• Incrémenter le pointeur de n octets
– Sinon, effectuer une étape de copie
• Etape de copie
– Pour chaque objet dans l’ensemble racine
• Copier l’objet dans l’ancien espace
• Copier récursivement les objets accessibles dans l’ancien espace
– Mettre à jours les références dans l’ancien espace pour qu’ils désignent les
nouveaux emplacements
– Echanger le rôle de l’ancien espace et du nouvel espace
439
Détail d’implémentation
• Le point important est de mettre correctement à jour les
références dans les objets copiés
• Idée : chaque objet contient un pointeur de réexpédition
sur sa copie
• Pour dupliquer un objet
1. Faire une copie bit à bit
• Toutes le références désignent encore les anciens emplacements
2. Mettre à jour le pointeur de réexpédition de l’objet original
pour qu’il désigne sa copie
• Après avoir dupliqué chaque objet :
– Suivre les références vers les objets désignés
– Remplacer les références par le pointeur de réexpédition de
l’objet désigné
440
Mise à jour des références
441
Mise à jour des références
442
Mise à jour des références
443
Mise à jour des références
444
Mise à jour des références
445
Mise à jour des références
446
Analyse
• Avantages
– Facile à implémenter
– Allocation mémoire très rapide
– Très bonne localité
• La copie en utilisant une exploration en profondeur d’abord
place les objets accessibles près les uns des autres
• Désavantages
– Seul 50% de la mémoire est utilisé
– Temps de collection proportionnel au nombre d’octets
utilisés par les objets accessibles
447
Garbage collection
APPROCHES HYBRIDES
448
Le meilleur des deux mondes
• Les meilleurs garbage collectors sont basés sur
la combinaison de plus petits garbage
collectors
• Quand la première génération est remplie exécuter le garbage collector sur cette
génération
– S’exécute rapidement, collecte de petites portions de mémoire
• Quand on ne trouve plus d’espace libre, lancer un garbage collector sur toute la
mémoire
• Le garbage collector lié à la jeune génération tourne fréquemment et ceux liés aux
plus vielles générations moins souvent
– Permet d’améliorer les performances
451
Conclusion
• C++
– Mémoire gérée à la main ou comptage de
référence
• Appel du destructeur avec la libération de la mémoire
• Exécution synchrone
• Java
– Pas de destructeur
– Mais une méthode finalize()
• Pourquoi ne l’avez-vous jamais utilisée ?
452
Conclusion
• Finalisation : action entreprise lors de la destruction
implicite d’un objet
453
Conclusion
• Vous aimez les garbage collectors
454
CONCLUSION GLOBALE
455
Ce qui a été abordé dans ce cours
• Théorie des langages
– Une approche très formalisée
– Permettant une automatisation
– Ex : ANTLR, Xtext…
• Grammaires attribuées
– Ajouter des traitements en cours d’analyse du langage
– Ex : construction d’un AST
• L’analyse sémantique
– Gestion des erreurs et avertissements
– Construction de la table des symboles
456
Ce qui a été abordé dans ce cours
• Le typage
– Une approche formelle
– Basée sur les preuves
– Typage explicite ? Typage implicite ?
– Certains langages marient les deux
• Ex : C++
457
Ce qui a été abordé dans ce cours
• Génération de code intermédiaire et
transformations optimisantes
– Une approche très systématique
– Génération d’un code naïf
– Puis transformations pour le rendre plus efficace
• La gestion mémoire
– Tous les langages ont un modèle mémoire
– Rq : la gestion du garbage collector est très liée au
compilateur
• Besoin de connaitre toutes les références…
458
Ce qui n’a pas été abordé
• Le backend : la génération de code
– Allocation de registres
– Ordonnancement d’instructions
• Joue avec la dépendance des données
• Possibilité d’entrelacer les instructions indépendantes
– La gestion des caches
• Réordonnancement des boucles
• Array of structures or structure of arrays ?
– La parallélisation
–…
459
Conclusion
• Les compilateurs sont des outils complexes
– Grand nombre de technologies mises en jeu
– Modularité extrême
460