Compilation Compilateur Compilateur / interpréteur Exemple

publicité
Compilateur
Compilation
Un compilateur est un programme
Laurent Braud
1
Département d’Informatique
Laboratoire d’Informatique Fondamentale, Luminy
2
[email protected]
pageperso.lif.univ-mrs.fr/~laurent.braud/compilation/
Groupe Cours, TD
TP
1
Laurent Braud Laurent Braud
2
Janusz Malinowski
Compilateur / interpréteur
compilateurinterpréteur
programme cible
Le compilateur signale de plus toute erreur contenue dans le
programme source
Lorsque le programme cible est un programme exécutable, en
langage machine, l’utilisateur peut ensuite le faire exécuter afin
de traiter des données et de produire des résultats.
Exemple
programme source
données
qui lit un autre programme rédigé dans un langage de
programmation, appelé langage source
et qui le traduit dans un autre langage, le langage cible.
résultat
Un interpréteur est un programme qui effectue lui-même les
opérations spécifiées par le programme source directement sur les
données fournies par l’utilisateur.
programme source
programme cible
program p;
var
d:integer;
f:
pop
pop
push
add
function f(a, b : integer)
sw
: integer
sw
var
pop
c, k : integer
push
begin
jra
k := a + b;
main: li
f := k;
sw
end;
push
li
begin
push
d := 7;
jla
write(f(d, 2) + 1);
pop
end.
...
$t0
$t1
$ra
$t2, $t0, $t1
$t2, k
$t2, f
$ra
$t2
$t0, 7
$t0, d
$t0
$t1, 2
$t2
f
$t2
Nécessité d’une analyse syntaxique
A l’exception de quelques cas (rares), la traduction ne peut être
faite “mot à mot”
Le programme source doit être décomposé en composants
pertinents ou constructions du langage source.
La traduction d’une construction dépend de la position qu’elle
occupe au sein du programme.
Deux parties d’un compilateur
Décomposition d’une définition de fonction
function f
(a, b : integer)
: integer
var
c, k : integer
begin
k :=
a + b;
f := k;
end;
(*
(*
(*
(*
(*
(*
(*
(*
(*
(*
nom de la fonction *)
parametres *)
type de retour *)
debut de la decl. des variables *)
decl. des variables locales *)
debut du corps de la fonction *)
affectation *)
expression arithmetique *)
instruction de retour *)
fin du corps de la fonction *)
Deux parties d’un compilateur
programme source
La traduction du programme source en programme cible se
décompose en deux étapes :
L’analyse, réalisée par la partie frontale du compilateur, qui
découpe le programme source en ses constituants ;
détecte des erreurs de syntaxe ou de sémantique ;
produit une représentation intermédiaire du programme source ;
conserve dans une table des symboles diverses informations sur les
procédures et variables du programme source.
syntaxe
représentation intermédiaire
La synthèse, réalisée par la partie finale du compilateur, quit
construit le programme cible à partir de la représentation
intermédiaire et de la table des symboles
sémantique
programme cible
Nature de la représentation intermédiaire
Portabilité
La conception d’une bonne RI est un compromis :
Elle doit être raisonnablement facile à produire à partir du
programme source.
Un compilateur est portable s’il permet de générer du code pour
plusieurs processeurs.
Elle doit être raisonnablement facile à traduire vers le langage
cible.
Le recours à une RI permet de produire un compilateur portable
en ne multipliant que le générateur de code à partir de la RI.
Elle doit donc être raisonnablement éloignée (ou raisonnablement
proche) du langage source et du langage cible.
Portabilité
Syntaxe du langage source
programme source
Le programme source vérifie un certain nombre de contraintes
syntaxiques.
syntaxe
représentation intermédiaire
L’ensemble de ces contraintes est appelé grammaire du langage
source.
Si le programme ne respecte pas la grammaire du langage, il est
considéré incorrect et le processus de compilation échoue.
sémantique
sémantique
sémantique
assembleur x86 assembleur SPARC assembleur 68K
Description de la grammaire du langage source
Grammaires formelles
Littéraire
Un programme est une suite de définitions de fonction
Une définition de fonction est composée
du nom de la fonction suivie de ses arguments
suivie de la declaration de ses variables internes
suivie d’un bloc d’instructions
Une instruction est . . .
Formelle
programme
listeDecFonc
listeDecFonc
decFonc
→
→
→
→
...
listeDecFonc ’.’
decFonc listeDecFonc
Les contraintes syntaxiques sont représentées sous la forme de
règles de réécriture.
La règle A → BC nous dit que le symbole A peut se réécrire
comme la suite des deux symboles B et C.
L’ensemble des règles de réécriture constitue la grammaire du
langage.
La grammaire d’un langage L permet de générer tous les
programmes corrects écrits en L et seulement ceux-ci
ID FCT listeParam listeDecVar ’ ;’ instrBloc
Notations et Terminologie
Dans la règle A → α
A est appelé partie gauche de la règle.
α est appelé partie droite de la règle.
Lorsque plusieurs règles partagent la même partie gauche :
A → α1 , A → α2 , . . . , A → α n
On les note :
A → α1 | α2 | . . . | α n
Grammaire partielle des expressions arithmétiques
EXPRESSION
OP2
EXPRESSION
EXPRESSION
NOMBRE
CHIFFRE
→
→
→
→
→
→
EXPRESSION OP2 EXPRESSION
+|-|*|/
NOMBRE
( EXPRESSION )
CHIFFRE | CHIFFRE NOMBRE
0|1|2|3|4|5|6|7|8|9
Les symboles EXPRESSION, OP2, NOMBRE, CHIFFRE sont
appelés symboles non terminaux de la grammaire
Les symboles +, -, *, /, (, ), 0, 1 , ..., 9 sont appelés symboles
terminaux de la grammaire
Avantages des grammaires formelles
Dérivation d’une expression arithmétique
Une grammaire formelle :
Pousse le concepteur d’un langage à en décrire la syntaxe de
manière exhaustive.
Permet de répondre automatiquement à la question
mon programme est-il correct ? à l’aide d’un analyseur syntaxique.
Fournit, à l’issue de l’analyse, une représentation explicite de
l’organisation du programme en constructions (structure
syntaxique du programme).
Cette représentation est utile pour la suite du processus de
compilation.
Arbre de dérivation
L’expression arithmétique 2 ∗ (3 + 1) est-elle correcte ?
EXPRESSION ⇒ EXPRESSION OP2 EXPRESSION
⇒ NOMBRE OP2 EXPRESSION
⇒ CHIFFRE OP2 EXPRESSION
⇒ 2 OP2 EXPRESSION
⇒ 2 * EXPRESSION
⇒ 2 * ( EXPRESSION )
⇒ 2 * ( EXPRESSION OP2 EXPRESSION)
⇒ 2 * ( NOMBRE OP2 EXPRESSION)
⇒ 2 * ( CHIFFRE OP2 EXPRESSION)
⇒ 2 * ( 3 OP2 EXPRESSION)
⇒ 2 * ( 3 + EXPRESSION)
⇒ 2 * ( 3 + NOMBRE)
⇒ 2 * ( 3 + CHIFFRE)
⇒ 2 * ( 3 + 1)
Analyse lexicale
EXPRESSION
EXPRESSION
OP2
NOMBRE
*
EXPRESSION
(
CHIFFRE
2
EXPRESSION
)
EXPRESSION
Afin de simplifier la grammaire décrivant un langage, on omet
de cette dernière la génération de certaines parties simples du
langage.
Ces dernières sont prises en charge par un analyseur lexical
EXPRESSION
OP2
EXPRESSION
NOMBRE
+
NOMBRE
CHIFFRE
CHIFFRE
3
4
L’analyseur lexical traite le programme source et fournit le
résultat de son traitement à l’analyseur syntaxique.
Nouvelle grammaire des expressions arithmétiques
Analyseur lexical
Lit le programme source
Syntaxe
Lexique
EXPRESSION
EXPRESSION
EXPRESSION
OP2
NOMBRE
CHIFFRE
→
→
→
→
→
→
EXPRESSION OP2 EXPRESSION
NOMBRE
( EXPRESSION )
+|-|*|/
CHIFFRE | CHIFFRE NOMBRE
0|1|2|3|4|5|6|7|8|9
Reconnaı̂t des séquences de caractères significatives appelées
lexèmes
Pour chaque lexème, l’analyseur lexical émet un couple
(type du lexème, valeur du lexème)
Exemple
La nouvelle grammaire omet les détails de la génération d’un
NOMBRE et d’un opérateur binaire. Cette partie est à la charge
de l’analyseur lexical.
La frontière entre analyse lexicale et analyse syntaxique est en
partie arbitraire.
Analyseur syntaxique plus simple
NOMBRE, 2
OP2,*
EXPRESSION
PF, )
EXPRESSION
EXPRESSION
NOMBRE, 3
OP2,+
Les symboles terminaux de la grammaire (ou types de lexèmes)
constituent l’interface entre l’analyseur lexical et l’analyseur
syntaxique. Ils doivent être connus des deux.
La traduction dirigée par la syntaxe est réalisée en attachant des
actions sémantiques aux règles de la grammaire.
Elle repose sur deux concepts :
EXPRESSION
PO, (
Les types de lexèmes sont des symboles, ils constituent les
symboles terminaux de la grammaire du langage.
Traduction dirigée par la syntaxe
EXPRESSION
EXPRESSION
(NOMBRE, 123)
EXPRESSION
NOMBRE, 4
Les attributs. Un attribut est une quantité quelconque associée à
une construction du langage de programmation.
Exemples :
le type d’une expression
la valeur d’une expression
le nombre d’instructions dans le code généré
Les constructions étant représentées par les symboles de la
grammaire, on associe les attributs à ces derniers.
Notations : A.t est l’attribut t associé au symbole A.
Traduction dirigée par la syntaxe
Traduction — exemple
Les schémas de traduction sont une notation permettant
d’attacher des fragments de programme aux règles de la
grammaire.
Les fragments sont exécutés quand la production est utilisée lors
de l’analyse syntaxique.
Le résultat combiné des exécutions de tous ces fragments, dans
l’ordre induit par l’analyse syntaxique, produit la traduction du
programme auquel ce processus est appliqué.
E
E
E
O
O
N
N
C
C
C
C
Traduction — Exemple
O (*)
N (2)
*
action sémantique
=
E1 .t || E2 .t || O.t
=
E1 .t
=
N.t
=
+
=
−
=
C.t || N2 .t
=
C.t
= 0
= 1
= 2
...
C.t
= 9
E.t
E.t
E.t
O.t
O.t
N.t
N.t
C.t
C.t
C.t
Attributs synthétisés
E (2 3 1 + *)
E (2)
règle
→ EOE
→ (E)
→ N
→ +
→ −
→ CN
→ C
→ 0
→ 1
→ 2
...
→ 9
Un attribut est dit synthétisé si sa valeur au niveau d’un nœud A
d’un arbre d’analyse est déterminée par les valeurs de cet
attribut au niveau des fils de A et de A lui même.
E (3 1 +)
(
E (3 1 +)
A
)
A.t = f ( B.t, C.t, D.t)
B
C (2)
E (3)
O (+)
E (1)
2
N (3)
+
N (1)
C (3)
C (1)
3
1
C
D
Les attributs synthétisés peuvent être évalués au cours d’un
parcours ascendant de l’arbre de dérivation.
Un tel parcours peut être effectué simultanément à la
construction de l’arbre (lors de l’analyse syntaxique).
Dans l’exemple, B.t, C.t et D.t doivent être calculés avant de
calculer A.t
Table des symboles
Table de symboles — exemple
programme source
Elle rassemble toutes les informations utiles concernant les
variables et les fonctions ou procédures du programme.
Pour toute variable, elle garde l’information de :
son nom
son type
sa portée
son adresse en mémoire
Pour toute fonction ou procédure, elle garde l’information de :
son nom
sa portée
le nom et le type de ses arguments, ainsi que leur mode de passage
éventuellement le type du résultat qu’elle fournit
La table des symboles est construite lors de l’analyse syntaxique.
Arbre de dérivation v/s arbre abstrait
L’arbre de dérivation produit par l’analyse syntaxique possède
de nombreux nœuds superflus, qui ne véhiculent pas
d’information.
De plus, la mise au point d’une grammaire nécessite souvent
l’introduction de règles dont le seul but est de simplifier
l’analyse syntaxique.
Un arbre abstrait constitue une interface plus naturelle entre
l’analyse syntaxique et l’analyse sémantique, elle ne garde de la
structure syntaxique que les parties nécessaires à l’analyse
sémantique et à la production de code.
L’arbre abstrait est construit lors de l’analyse syntaxique, en
associant à toute règle de grammaire une action sémantique.
program p;
var
d:integer;
table des symboles
function f(a, b : integer)
: integer
var
c, k : integer
begin
k := a + b;
f := k;
end;
id
f
a
b
c
k
main
d
classe
fonct
param
param
locale
locale
fonct
locale
type
entier
entier
entier
entier
entier
entier
entier
adr
1
args
2
11
0
begin
d := 7;
write(f(d, 2) + 1);
end.
Arbre de dérivation v/s arbre abstrait
*
EXPRESSION
EXPRESSION
OP2,*
EXPRESSION
NOMBRE, 2
PO, (
EXPRESSION
PF, )
EXPRESSION
EXPRESSION
NOMBRE, 3
OP2,+
+
2
EXPRESSION
NOMBRE, 4
3
1
Analyse sémantique
Représentation intermédiaire
L’analyse sémantique utilise l’arbre abstrait, ainsi que la table de
symboles afin d’effectuer un certain nombre de contrôles
sémantiques, parmi lesquels :
vérifier que les variables utilisées ont bien été déclarées.
le contrôle de type : le compilateur vérifie que les opérandes
d’un opérateurs possèdent bien le bon type.
conversions automatiques de types.
Exemple
La production de code est effectuée lors du parcours de la
Représentation intermédiaire (RI) construit à l’issue le l’étape
d’analyse.
Plusieurs choix sont possibles pour la RI :
l’arbre abstrait
une séquence d’instructions élémentaires
...
Code à 3 adresses
Production de code lors du parcours d’un nœud associé à une
instruction si expr alors instr.
si
arbre
représentant
expr
La production de code consiste à produire une séquence
d’instructions sémantiquement équivalente au programme
source qui pourra être interprétée par une machine donnée.
⇒
arbre
représentant
instr
calculer expr
le ranger dans x
et le rangeant dans x
SIFAUX x ALLER A suite
exécuter instr
suite :
Avant d’écrire tout-à-fait le code, on peut passer par une version
simplifiée, où l’on ne se préoccupe pas des problèmes de la table de
symboles.
boucle: t1 = n - 1
while i < n-1 do
SI i > t1 ALLER A suite
begin
t2 = i * 2
i := i * 2 + 1;
i = t1 + 1
end
ALLER A boucle
suite:
Sources
A.Aho, M.Lam, R.Sethi et J.Ullman,
Compilateurs : principes, techniques et outils.
2ème édition. Pearson Education, 2007
A.Appel,
Modern compiler implementation in C.
Cambridge University Press, 1998
Henri Garreta,
Polycopié du cours de compilation.
Projet
Construction en langage C d’un compilateur.
Le langage source est une version simplifiée d’un langage
impératif bien connu, le Pascal.
Le langage cible est l’assembleur MIPS. Il est interprété par une
machine virtuelle appelée spim.
http://henri.garreta.perso.luminy.univmed.fr/Polys/PolyCompil.pdf
Pascal
Pascal — instructions
Langage classique, connu pour sa simplicité et sa rigueur.
Types : notre Pascal connaı̂t trois types :
deux types simples : le type entier et les booléens.
un type dérivé : les tableaux d’entiers.
Opérateurs : le Pascal reconnaı̂t les opérateurs suivants :
arithmétiques : + , −, ∗, /, mod
comparaison : =, <, <=, <>
logique : not, and, or
Le langage reconnaı̂t les instructions suivantes :
Instruction vide
Bloc d’instructions, délimité par un begin et end
Affectation a := b + 1 (contrairement à C, une affectation n’est
pas une expression, elle ne correspond pas à une valeur ; on ne
peut écrire a := (b := 4))
Instruction if ... then begin ... end
et sa variante ... else begin ... end
Instruction while ... do begin ... end
Pascal — sous-programmes
Sous-programmes : il y a deux types de sous-programmes : les
fonctions et les procédures.
Leurs arguments sont simples (des entiers).
Le passage se fait par valeur.
Elles possèdent des variables locales.
Un sous-programme ne peut pas être déclaré à
l’interieur d’un autre.
Les procédures ne renvoient rien.
Les fonctions renvoient toujours un résultat entier.
Elles disposent d’une variable locale
supplémentaire qui a le même nom que la
fonction, dans laquelle on stocke le résultat.
Procédures prédéfinies : Les entrées-sorties de valeurs entières se
font à l’aide de la fonction read et de la procédure
write, toutes deux prédéfinies :
a := read();
write(a);
Assembleur MIPS
Langage utilisé par le processeur MIPS. On utilisera la machine
virtuelle spim.
Mémoire séparée en trois parties :
l’espace global (variables)
la zone de code,
la pile.
En plus, registres utilisés dans la plupart des instructions.
registres courants : $t0-$t9,
pointeur de sommet de pile : $sp,
adresse de retour de saut : $ra ...
Pascal — exemple
program p;
var
d:integer;
(* debut du programme *)
(* decl. des variables globales *)
function f(a, b : integer) (* decl. d’une fonction a 2 args *)
: integer
(* type de retour *)
var
(* decl. des variables locales *)
c, k : integer
begin
(* debut du corps de la fonction *)
k := a + b;
(* affect. et expression arithmetique *)
f := k;
(* affectation de la valeur de retour *)
end;
(* fin du corps *)
begin
d := f(d, 2);
write(d + 1);
end.
(* debut du corps du programme *)
(* appel de fonction, affectation *)
(* appel de la procedure predefinie *)
(* fin du programme *)
MIPS — exemples
lw $t0, k
li $t1, 8
add $t2, $t0, $t1
jump label
charge la variable à l’adresse k dans $t0
charge la valeur 8 dans $t1
addition : $t2 :=$t0 + $t1
saute à l’adresse label dans le code
Structure du compilateur
Etapes
programme source
analyse lexicale
syntaxe
analyse syntaxique
arbre abstrait
table de symboles
analyse sémantique
code à 3 adresses
données
assembleur
résultats
TP
1
2
3
4
5
6
durée
1
2
1
1
2
2
intitulé
analyseur lexical
analyseur syntaxique
production de l’arbre abstrait
code à 3 adresses
table des symboles, production de code
utilisation des outils lex et yacc
Téléchargement