Exos 2009 4
Exos 2009 4
Exos 2009 4
Génération de code
Master d'Informatique M1 20092010
29 octobre 2009
1 Passage de paramètres
Décrire le comportement des programmes suivants lorsque les paramètres des procédures sont
passés par valeur, par référence, par copie/restauration, ou par nom.
1. program param;
var i : integer; a:array [1..2] of integer;
procedure p ([MODE] x,y:integer);
begin x:=x+y; i:=i+1; y:=y+1 end
begin
i:=1; a[1]:=1; a[2]:=1; p(a[i],a[i]); writeln(i,a[1],a[2]);
end
2. program param;
var i : integer; a:array [0..2] of integer;
procedure swap ([MODE] x,y:integer);
begin x:= x+a[i]; y:= x-a[i]; x:=x-y end
begin i:=0; a[0]:=1 ; a[1]:=2 ; a[2]:=3;
swap (i,a[i]); writeln(i,a[0],a[1],a[2]);
swap (a[i],a[i]); writeln(a[0],a[1],a[2]);
end
3. program part; var limit:integer;
function summation ([MODE] lim:integer); var s : integer;
begin s:=0; while lim>0 do begin s:=s+limit; limit:=limit-2 end
summation:=s
end;
begin limit:=6; writeln ('Sum=',summation(limit)) end.
2 Passage de paramètres
(Examen janvier 2001)
1. Soit le programme:
program param;
var x : int;
procedure proc(y:int);
begin x:=x+y; y:=y+1 end;
begin x:=2; proc(x); print(x) end;
Quel est le résultat de ce programme lorsque le paramètre y de proc est passé :
October 28, 2009 2
2. Quels sont les avantages et les inconvénients de ces diérentes modes de passage de para-
mètres ?
class A {
int x;
A (int v) { x=v;}
static void m (A a) {a = new A(2);}
public static void main (String argv[]) {
A b = new A(0); m (b);
System.out.println(b.x); }
}
1. Quelle est le résultat de l'évaluation de ce programme ?
2. Justier votre réponse en donnant les étapes de l'évaluation du programme (indiquer les
variables allouées dans la pile et leur valeur en terme d'adresses des objets alloués dans le
tas).
class B {
A a;
B (int v) { a=new A(0); }
static void m (B b) {b.a = new A(2);}
public static void main (String argv[]) {
B c = new B(0); m (c);
System.out.println(c.a.x); }
}
Donner le résultat de ce programme et expliquer les étapes de son évaluation.
program test;
var i,sum,facteur : integer;
procedure q (x:integer, y:integer);
begin y:=facteur * x + y end;
begin
i:=1; sum:=0; facteur:=2;
while i <= 10 do begin q(i+1,sum); i:=i+1 end;
writeln sum;
end
1. Indiquer comment seront allouées les variables globales et locales dans la pile.
October 28, 2009 3
3. Lorsqu'un paramètre est passé par référence, il faut pouvoir transmettre son adresse. Pour
cela on utilisera deux paramètres, l'un indiquant l'adresse du bloc où est dénie la variable
et l'autre le décalage de la variable dans le bloc. Reprendre les questions précédentes lorsque
le paramètre y est passé par référence.
5 Récursion terminale
On se donne un mini-langage fonctionnel pour la manipulation d'expressions arithmétiques.
Un programme de ce langage est composé de la déclaration de fonctions ou de variables suivi
d'une expression à évaluer. On remarquera que les variables sont soient locales (paramètres des
fonctions) soit globales (déclarées par val)
La grammaire est la suivante :
P :=B E E :=num
B := E :=id
B :=B D E :=id(F)
D :=val id = E E :=if E relop E then E else E
D :=fun id (L) = E E :=E op E
L :=id F :=E
L :=L, id F :=F, E
op (resp. relop) est un symbole terminal dont la valeur est l'un des opérateurs arithmétiques
{+, −, ∗, /} (resp. {<, ≤, =}). E représente une expression, D une déclaration de variable globale
ou de fonction, L représente une suite d'identicateurs, F une suite d'expressions, B une suite
de déclarations.
La valeur d'un programme formé d'une suite de déclarations B et d'une expression E est
égale à la valeur de l'expression E évaluée dans l'environnement correspondant à B. Ce langage
est compilé vers la machine à pile décrite en cours.
On appelle P le programme suivant :
val a = 4
fun square(z) = z*z
fun f(x,y)=square(x)+square(y)
val b = 2
f(a,b)+a
1. Donner pour le programme P une manière de stocker les variables globales et les paramètres
des fonctions dans la pile. Indiquer pour chaque variable les codes d'accès en lecture ou en
écriture.
La traduction est eectuée à l'aide de fonctions de codage code B , code E , code F . . . pour les
principaux non-terminaux de la grammaire. Ces fonctions sont spéciées par des équations.
October 28, 2009 4
4. On suppose dans un premier temps que les fonctions dénies ne sont pas récursives. Décrire
comment calculer pour chaque programme la taille maximale de la pile utilisée lors de
l'exécution de ce programme.
5. On suppose maintenant que les fonctions peuvent être récursives. Que peut-on dire sur la
taille de la pile ?
10. En utilisant cette stratégie, calculer la taille de la pile nécessaire à l'évaluation de fact(10).
6 Tableaux d'activation
1. Construire l'arbre des déclarations pour le programme suivant:
October 28, 2009 5
program sort;
var a : array [1..10] of integer; x:integer;
procedure reada; var i:integer; {...};
procedure exch (i,j:integer); {x:=a[i];a[i]:=a[j]; a[j]:=x}
procedure quicks (m,n:integer);
var k,v : integer;
function part(y,z:integer) : integer; var i,j:integer;
{v:=a[y]; ...; exch(i,j)...};
{if n > m then {k:=part(m,n); quicks(m,k-1); quicks(k+1,n)}}
{reada;quicks(1,10)}
2. Indiquer pour chaque corps de procédure quelles sont les symboles visibles.
4. Donner l'arbre d'activation des appels du programme en supposant que part(n,m) renvoie
la partie entière de (n + m)/2 et en ignorant les appels à la procédure exch.
5. Indiquer les diérents états de la pile lors de l'exécution des premières instructions du
programme.
6. On suppose que chaque tableau d'activation de procédure possède un lien vers le tableau
d'activation de la procédure père dans laquelle elle est déclarée. Soit une procédure q
appelée dans le corps d'une procédure p. Combien de liens père faut-il suivre à partir de p
pour trouver le tableau d'activation du père de q en fonction des niveaux de p et de q?
7. Montrer que la procédure ancêtre r est forcément la dernière activée de son niveau.
8. En déduire une méthode plus ecace que les liens vers le père pour accéder au tableau
d'activation déclarant une variable non locale.
Soit le programme:
procedure tableau;
var a : array [1..10] of int;
procedure writea; var k : int; { for k:=1 to 10 do write(a[k]) };
function id (i:int) : int; { id:=i };
function carre (i:int) : int; { carre:=i*i }
procedure init (f:int->int);
var k : int; { for k:=1 to 100 do a[k]:=f(k); writea };
procedure apply (f:int->int,i:int,j:int);
var k : int; { for k:=i to j do a[k]:=f(a[k]); writea };
{ init(id); apply(carre,2,8) }.
1. Donner pour chaque procédure et fonction la forme du tableau d'activation.
L := id
I := L:= E
P := C V J L := L.id
I := L.id(F)
C := E := num
I := if E then J else J fi
C := C class Cid extends Cid V M E := new Cid
J :=
V := E := L
J := J I
V := V var id := E E := E op E
X :=
M := F :=
X := id : Cid
M := M method id(X) = J F := E
X := X, id : Cid
F := F, E
v.move(30)
c.move(10)
v.rejoint(c)
Table des symboles On suppose que l'analyse de visibilité a été faite, c'est-à-dire que chaque
identicateur (de classe, variable, paramètre ou méthode) a une entrée dans une table des sym-
boles. Cette table est accessible à partir de la déclaration de l'identicateur. Chaque utilisation
d'un identicateur de classe est reliée à cette entrée de la table de symbole. Une valeur gauche
l est formée d'une suite de noms de variables x1 .x2 . . . xn , les conditions de visibilité demandent
que x1 soit une variable ou un paramètre de méthode préalablement déclaré. L'utilisation de x1
peut donc être reliée à sa déclaration via la table des symboles. Par contre, l'interprétation des
identicateurs x2 , . . . , x n dépend de la classe des objets manipulés, la liaison entre l'utilisation
de ces identicateurs et leur déclaration sera donc faite plus tard au moment du typage ou de la
compilation. Il en est de même pour les identicateurs de méthode.
C'est à vous de préciser quelles informations doivent être stockées dans la table
des symboles pour réaliser les fonctions demandées.
Exemple Pour ajouter une information sur la nature de l'identicateur id, on décide que la
table des symboles comportera un champ genre dont la valeur peut-être paramètre, variable,
méthode ou classe. Vous pouvez utiliser la notation id.genre :=method pour enregistrer que
l'identicateur id est une méthode et tester directement la valeur de id.genre =méthode pour
traiter le cas où id est une méthode.
Valeur d'un objet Un objet dans une classe Cid est représenté en mémoire en juxtaposant
les valeurs des variables de la classe de cet objet dans des cases contiguës de la mémoire. A
sa création par la fonction new Cid, un objet de la classe Cid est créé avec les valeurs initiales
données aux variables dans la dénition de la classe.
Exemple un nouvel objet dans la classe Vehicule est représenté par la variable pos qui est
initialisée à0; un nouvel objet de la classe Voiture est représenté par deux variables : pos héritée
de la classe Véhicule initialisée à 0 et places initialisée à 1; un nouvel objet de la classe Camion
est également composé de deux variables : pos initialisée à 0 ainsi que charge initialisée à 10.
Valeur d'un programme Un programme P est formé d'une suite de déclarations de classes
C, de variables V et d'instructions J. La valeur de P est l'ensemble des valeurs des variables V
à l'issue de l'exécution des instructions J.
Compilation La compilation des méthodes est analogue à celle des procédures. Une méthode
m de paramètres X déclarée dans une classe Cid se compile comme une procédure admettant
un paramètre supplémentaire self : Cid que l'on peut choisir de mettre en premier. Ainsi
l'invocation L.m(F ) est traduite comme l'invocation de la procédure m sur l'objet représenté
par la valeur gauche L et les paramètres F. Le passage des paramètres se fait par référence.
October 28, 2009 8
Questions
1. Donner un type pour représenter les arbres de syntaxe abstraite de ce langage.
2. Typage Les classes permettent d'associer un type à chaque objet. Ainsi new Cid est un
nouvel objet dont le type est la classe Cid.
Dans les langages objet, les types sont ordonnés par une relation d'héritage. Si C1 et C2 sont
deux identicateurs de classe, alors on dira que C1 ≤ C2 si l'une des conditions suivantes
est vériée :
C1 = C2 ;
C1 est une classe déclarée avec la mention extends C2 ;
il existe C3 un identicateur de classe tel que C1 ≤ C3 et C3 ≤ C2 .
Si une expression E a pour type la classe C1 et que C1 ≤ C2 alors E a également pour
type C2 .
(b) Une variable est soit un paramètre de méthode qui est déclaré avec son type, soit une
variable x déclarée par var x := E . Le type de x est alors le type de E . Dans le corps
d'une méthode m de la classe Cid, self est un paramètre implicite, déclaré avec le
type Cid.
Une valeur gauche L représente un objet. Si L est de la forme id alors c'est une variable
ou un paramètre déclaré, relié à sa déclaration dans la table des symboles. Si L est
de la forme L' .id alorsL0 est une valeur gauche qui représente un objet, soit C1 la
classe de cet objet. L' .id est correctement typé si id est une variable de la classe C1 ,
auquel cas le type de L'.id est égal au type de cette variable.
E1 op E2 est une expression bien typée de type Int si E1 et E2 sont bien typés de
type Int.
Question : Donner une méthode pour vérier qu'une expression est correctement
typée et calculer le type de chaque variable déclarée et de chaque expression.
(c) Indiquer une manière de calculer, pour chaque classe, la taille occupée en mémoire
par un objet dans cette classe.
(e) Une déclaration de classe est bien formée, si les déclarations de variables dans la
classe sont bien formées et si pour chaque méthode, les déclarations de variables et
les instructions de la méthode sont bien formées.
De plus si C1 ≤ C2 , une méthode déclarée dans C2 peut être redénie dans C1 auquel
cas les types des paramètres doivent être les mêmes.
Question : Donner une méthode pour vérier qu'une déclaration de classe est bien
formée.
3. Méthodes statiques/ méthodes dynamiques Une méthode m dénie dans une classe
C1 peut être redénie dans une classe C2 telle que C2 ≤ C1 . Si on invoque c.m il faut
October 28, 2009 9
déterminer quel code appliquer, pour cela on s'appuie sur la classe de c. On distingue, la
classe statique de c qui est la classe déterminée statiquement par le programme (celle que
vous avez calculée à la question 2b) de la classe dynamique qui est la classe de l'objet
correspondant à la valeur de la variable. Par exemple si x est une variable de type C1
et v un objet de type C2 alors on peut exécuter l'instruction x := v . À la suite de cette
instruction, le type dynamique de x est devenu C2 .
Dans le cas statique, l'invocation de x.m x soit C1 , dans
correspondant au type déclaré de
le cas dynamique, il s'agit du type à l'exécution dex soit C2 .
Question Donner le résultat (valeurs des variables v et c) de l'évaluation du programme P
dans les cas où les méthodes sont compilées de manière statique puis de manière dynamique.
4. Compilation statique
(a) Réécrire le code du programme P dans un langage à la Pascal ne comportant pas de
fonctionnalités objet (transformer les classes en record, les méthodes en procédures,
introduire des fonctions correspondant à new).
(b) Donner le code compilé correspondant au programme P dans le cas où les méthodes
sont toutes statiques. On utilisera le code intermédiaire de la machine à pile vue en
cours.
(b) Quelles instructions faut-il ajouter à la machine pour compiler le code correspondant ?
class Vehicule
{ int pos;
Vehicule () {pos = 0;}
void move (int x) { pos = pos+x; }
}
class Main {
October 28, 2009 10
3. Expliquer la représentation des objets pour chacune des classes non statiques. Proposer
un code qui traduise l'eet des instructions new sur chacune de ces classes. La commande
DUP n permet de dupliquer les n valeurs en sommet de pile.
4. Proposer un code pour la fonction instanceof entre un objet et une classe. Pour cela on
précisera l'algorithme utilisé ainsi que le tableau d'activation de la fonction instanceof.
5. Comment traduit-on les instructions de cast lorsque ceux-ci mettent en jeu des types
numériques.
6. Proposer un format de tableau d'activation et donner le code pour les diérentes méthodes
dynamiques du programme Java.
Pour un appel de méthode o.m() ou o est de classe C, l'analyse de la hierarchie des classes
(donnée par la relation d'héritage) peut etre utilisée pour déterminer les sous-classes de C
redénissant C_m. S'il n'existe aucune redénition de C_m alors l'instance de la méthode
est nécessairement C_m. Donner le code de la compilation de l'appel de méthode dans ce
cas.
2. Expliquer les grandes lignes du schéma de compilation des appels de méthodes dynamique
et statique. Quel type d'appel de méthode est le plus ecace ?
Déclarations Une déclaration de variable s'écrit : let id = exp avec id le nom de la variable
et exp sa valeur.
Une déclaration de fonction s'écrit : let id (id1 , . . . , idn ) = exp avec id le nom de la procédure,
idi le nom du i-ème paramètre, exp une expression représentant le corps de la fonction et pouvant
faire référence à id.
11.2 Typage
Les fonctions peuvent prendre des tableaux en arguments et renvoyer des tableaux. Les don-
nées manipulées sont des entiers ou bien des tableaux formés d'objets quelconques. Les fonctions
peuvent être polymorphes, c'est-à-dire s'appliquer à des tableaux contenant des objets arbitraires.
Les types du langage sont soit le type de base des entiers int, soit le type des booléens bool,
soit un type tableau contenant des objets de type τ, noté τ array.
Pour traiter le polymorphisme, on introduit les schémas de type qui peuvent contenir des
variables de type qui représentent des types arbitraires. Un schéma de type est donc soit int,
soitbool, soit une variable de type (notée α, β, . . .), soit un schéma de type tableau noté σ array
avec σ un schéma de type.
Un schéma de type représente un ensemble de types obtenus en remplaçant les variables de
type par des types quelconques. Ainsi le schéma de type α représente tous les types, le schéma
de type α array représente tous les types de la forme τ array avec τ un type (ie int array,
bool array, (int array) array, ((int array) array) array . . .)
À toute fonction est associée un prol de la forme σ1 × . . . × σn → σ où σi sont des schémas
de type.
On peut associer aux opérations primitives les prols suivants :
October 28, 2009 12
let f(x,y)=x
let get_matrix(m,i,j) = get(get(m,i),j)
let set_matrix(m,i,j,v) = set(get(m,i),j,v)
2. Écrire une fonction add_array qui prend en argument deux tableaux d'entiers x et y de
même taille n et renvoie un nouveau tableau z de taille n tel que pour tout i compris entre
1 et n on ait z[i] = x[i] + y[i].
Le langage est compilé vers le code d'une machine à pile dont les instructions sont rappelées
à la n de ce document.
Les tableaux sont alloués dans la zone du tas. La valeur d'un tableau sera l'adresse du bloc
dans lequel le tableau est stocké. Un tableau n'est jamais recopié lors d'un appel de fonction,
seule l'adresse est passée en argument. La seule fonction qui alloue de la place pour un tableau
dans le tas est la fonction create.
Par rapport à la machine vue en cours, on suppose que l'on dispose de la commande suivante :
ALLOC dépile un entier n, réserve un bloc de n+1 mots dans le tas et renvoie l'adresse
addr du bas du bloc au sommet de la pile (les élements du bloc seront donc placés
aux adresses addr à addr + n)
1. Peut-on connaître statiquement (à la compilation) la taille du tableau correspondant à la
valeur d'une expression quelconque du langage, en particulier pour l'expression correspon-
dant au corps d'une fonction ?
2. Proposer une manière de représenter les tableaux qui permette que tous les objets manipulés
dans la pile (entiers ou tableaux) aient la même taille et d'implanter la fonction length.
3. On veut coder les opérations primitives (length, get, set et create). Expliquer l'orga-
nisation du tableau d'activation créé par l'appel de ces fonctions et donner le code de la
machine à pile correspondant au corps de chacune de ces procédures
4. Soit le programme :
let x = create(2,0)
let y = x
let x' = set(x,1,2)
let v = get(y,1)
Indiquer l'état de la pile et du tas après l'exécution de chaque déclaration.
6. L'étudiant Arthur doit implanter une fonction d'addition de trois tableaux de même taille,
il décide de réutiliser la fonction add_array dénie précédemment et procède de la manière
suivante :
7. On veut étendre le langage pour pouvoir spécier dans une déclaration de fonction qu'un
paramètre (de type tableau) est passé par valeur, c'est-à-dire qu'un nouveau tableau est
créé au moment de chaque appel. On introduit pour cela la déclaration let f (val x) = exp.
October 28, 2009 13
Lors de l'appel de f (a), les modications apportées aux éléments de x dans l'expression
exp n'auront pas d'incidence sur les éléments de a.
Montrer que l'on peut eectuer cette extension du langage sans modier le compilateur vers
la machine à pile, en transformant simplement une déclaration de fonction avec appel par
valeur en une déclaration de fonction avec appel par référence ayant le même comportement.
Machine à pile
Les instructions de cette machine à pile sont analogues à celles décrites dans le cours. Le
registre gp indique le bas de la pile, le registre fp indique le pointeur de référence des variables
locales, le registre sp indique la première case libre de la pile. Des instructions LABEL peuvent
être introduites dans le code, elles ne modient pas l'état de la pile. Empiler x signie ajouter
x au sommet de la pile ( sp 1). Dépiler x signie retirer la valeur au sommet
est augmenté de
de la pile qui sera notée x ( sp 1).
est diminué de
PUSHI n empile la constante entière n.
ADD (resp. SUB) dépile y puis x et empile x + y (resp. x − y )
EQ (resp. INF,INFEQ) dépile y puis x et empile 1 si x = y (resp. x < y , x ≤ y ) et 0 sinon)
PUSHL n empile la valeur située n cases au dessus de fp .
STOREL n dépile x et le stocke à l'emplacement situé n cases au dessus de fp .
LOAD n dépile une adresse a et empile la valeur située n cases au dessus de a.
STORE n dépile une valeur x, une adresse a et stocke x à l'adresse située n cases au dessus
de a.
LOADN dépile un entier n et une adresse a, empile la valeur située n cases au dessus de a.
STOREN dépile une valeur x, un entier n et une adresse a, et stocke x à l'adresse située n
cases au dessus de a.
PUSHN n empile n valeurs nulles sur la pile.
POP n dépile n valeurs de la pile.
JZ l dépile x, si x = 0 le pointeur de code saute à l'instruction LABEL l.
JUMP l saute à l'instruction LABEL l.
CALL f sauve la valeur courante de fp et le pointeur d'instructions, aecte à fp la valeur
de la première case libre de la pile, saute à l'instruction LABEL f .
RETURN dépile toutes les valeurs au-dessus de fp , restaure l'ancienne valeur de fp, reprend
l'exécution à l'instruction suivante la dernière instruction CALL.
Les instructions sont formées d'aectations, d'appels de procédures et peuvent être mises en
séquence.
i::= g = e | i; i | id(e, . . . , e)
Un programme est donné comme une suite de déclarations de procédures de la forme :
procedure id(params){vars | i}
Les paramètres peuvent être déclarés par valeur : id : τ ou bien par référence var id : τ . Les
variables locales sont déclarées avec leur type.
Arbres de syntaxe abstraite Les arbres de syntaxe abstraite sont déclarés ainsi :
3. On suppose que la table des symboles est à jour et que les programmes sont bien formés.
Écrire une fonction typeof qui calcule (sans vérier) le type d'une expression.
4. On choisit de calculer les expressions sur la pile et de représenter les données dans les types
produits par des éléments consécutifs sur la pile. Donner le code pour l'expression (e1 , e2 )
et celle pour l'expression e.1 ainsi que le code pour une variable (on distinguera le cas où
la variable est passée par valeur ou par référence).
October 28, 2009 15
{let, type, id, num, true, false, op, :=, if, then, else, ,, (, )}
id représente un identicateur correspondant à une variable ou une fonction, num représente une
constante entière, op est un des opérateurs arithmétiques {+, −, ∗, /, =, <, >}, type représente
le type d'une expression qui peut être l'un des deux mots clés int ou bool.
Les expressions représentent des entiers ou des booléens. Elles sont engendrées par la gram-
maire suivante dans laquelle F représente une suite d'expressions (éventuellement vide) séparées
par des virgules :
Un programme dans ce langage est composé d'une suite de déclarations qui peuvent être soit
des déclarations de fonctions soit l'association d'un nom à une expression. Par exemple :
La particularité de ce langage est de permettre de donner des valeurs par défaut à certains para-
mètres des fonctions. Une valeur par défaut peut être n'importe quelle expression. Au moment
de l'appel, il n'est pas nécessaire de fournir une valeur pour un tel paramètre, à défaut, la valeur
associée au paramètre au moment de la déclaration de la fonction sera utilisée. Ainsi on peut
écrire :
October 28, 2009 16
L'identicateur t aura pour valeur 1 ∗ square(3) + square(4) c'est-à-dire 25 tandis que u aura
pour valeur 2 ∗ square(3) + square(4) c'est-à-dire 50 .
On choisit dans la déclaration des paramètres de regrouper tous les paramètres sans valeur
par défaut d'abord puis de mettre les paramètres avec valeurs par défaut ensuite.
1. Compléter la grammaire des expressions pour donner la grammaire complète des pro-
grammes de ce langage.
2. Compléter les déclarations de type suivantes (expr et prog) pour représenter les arbres de
syntaxe abstraite de ce langage.
3. Proposer une structure pour la table des symboles des fonctions et des variables qui per-
mette de vérier qu'un programme est bien formé, c'est-à-dire qu'il respecte les règles
usuelles de portée et de typage :
toute variable utilisée dans une expression doit au préalable avoir été déclarée.
Les paramètres d'une fonction ne sont visibles que dans le corps de la fonction, les
fonctions peuvent être récursives
Les opérateurs s'appliquent à des entiers, sauf = qui peut s'appliquer à deux booléens.
Dans une expression conditionnelle, la condition a pour type un booléen et les deux
branches ont le même type.
5. On souhaite engendrer du code assembleur pour calculer les valeurs de chaque variable
introduite dans le programme. Expliquer de quelle manière on peut traiter les arguments
donnés par défaut : détailler la déclaration d'une fonction avec paramètres ayant une valeur
par défaut ainsi que l'appel d'une telle fonction.
6. Que faut-il changer au mode de compilation si on autorise les valeurs par défaut à faire
référence aux paramètres de la fonction comme dans l'exemple :
Les expressions du langage BC sont soit des constantes entières, soit des variables (globales)
soit une opération binaire (arithmétique ou test d'égalité) appliquée à deux expressions, on
convient que les booléens true et false sont représentés respectivement par les entiers 1 et 0.
Une instruction de BC peut être soit une aectation x := e, soit une conditionnelle if e
then s1 else s2 end ou bien if e then s end, soit un bloc formé d'une liste d'instructions
{s1 ; . . . ; sn } soit une boucle while e do s end, soit une instruction d'échappement break ou
continue.
Les types CAML suivants permettent de représenter les arbres de syntaxe abstraite de ce
langage (les variables sont directement représentées par leur décalage par rapport à la base de la
pile):
{ p:=2;
while true do
if p=n then { r:=1; break } end;
if n mod p = 0 then { r:=0; break }
else p:=p+1 end
end }
Donner des instructions de la machines à pile correspondant au code compilé de cette
fonction.
3. Écrire une fonction de compilation dans le cas général où les corps de boucle peuvent
mentionner les instructions break et continue.
4. Dans un langage comme Java les instructions break ou continue peuvent mentionner
un label qui a été introduit en tête d'une instruction while. Une instruction break ou
continue sans label sort de la première boucle while englobante tandis qu'une instruction
break (resp. continue) avec un label l sort de (resp. réitère) la boucle while portant le
label l.
Les constructeurs While, Break et Continue du type stat sont modiés de la manière
suivante :
Machine à pile
Les instructions de cette machine à pile sont analogues à celles décrites dans le cours, on a
juste ajouté une opération MOD qui calcule directement le reste de la division entière de deux
nombres : On a x mod y = x − (x/y) ∗ y.
October 28, 2009 18
# dene EOF -1
main()
{int c, nwhite, nother;
nwhite = nother = 0;
while ((c=getchar()) != EOF)
switch (c) {
case ' ':
case '\n':
case '\t':
nwhite++; break;
default:
nother++; break;
}
printf("white space = %d, other = %d\n",nwhite,nother);
}
L'instruction switch doit vérier que toutes les constantes apparaissant dans les labels case
sont distinctes et qu'il y a au plus un label default. Dans la suite on supposera que ces conditions
sont toujours vériées.
La sémantique de l'instruction switch (e) {li} est d'évaluer l'expression e en une valeur v,
de chercher dans la suite d'instructions avec labels li s'il y a une étiquette case c telle que la
valeur de la constante c est aussi v. Trois cas peuvent se produire :
Si un label case c de valeur v existe, les instructions de li sont exécutées à partir de
l'instruction comportant ce label,
s'il n'existe pas de label de valeur v mais qu'il y a un label default alors les instructions
de li sont exécutées à partir de l'instruction comportant le label default,
s'il n'y a pas de label de valeur v ni de label default alors aucune instruction n'est exécutée.
Dans tous les cas l'instruction break apparaissant dans li provoque la sortie de l'instruction
switch.
1. On suppose que la grammaire du langage est partiellement écrite avec des non-terminaux
cste, expr et instr qui reconnaissent respectivement les expressions constantes, les expres-
sions du langage, et les instructions et qui comportent au moins les règles :
switch (c) {
case ' ':
case '\n':
case '\t':
nwhite++; break;
default:
nother++; break;
}
3. On suppose dénis des type de données asa_cste, asa_expr et asa_instr pour représenter
respectivement les arbres de syntaxe abstraite des constantes, expressions et instructions.
La gure 2 donne un sous-ensemble des constructeurs des arbres de syntaxe abstraite avec
leurs types.
4. On suppose donné une fonction val_cste de type asa_cste → int qui calcule la valeur
(entière) d'une expression constante. Pour un caractère, il s'agit de son code ASCII. On
rappelle que les codes ASCII de
0 \t0 , 0 \n0 et 0 0 sont respectivement 9, 10 et 32.
Donner le schéma de compilation général pour une instruction switch en supposant que
les fonctions compil_expr et compil_instr sont déjà dénies pour les autres cas.
6. On ajoute maintenant à notre machine une instruction de saut indéxé JUMPI qui prend en
argument un label l et eectue un saut du pointeur de code pc à l'adresse l + i si i est la
valeur du sommet de la pile (qui est dépilé).
L'idée est de construire pour chaque instruction switch un tableau correspondant à l'éven-
tail des cas possibles du switch.
Dans le cas de l'exemple on a trois cas à tester : les caractères ' ', '\t' et '\n' correspondant
aux valeurs numériques de 32, 9 et 10. Il sut donc de garder une table de choix pour les
valeurs numériques entre 9 et 32. Dans cette table de taille 32 − 9 + 1 = 24, les cases 0, 1 et
23 correspondant aux valeurs 9, 10 et 32 contiendront l'instruction JUMP l si l est le label
de l'instruction qui suit ces cas; les autres cases contiendront l'instruction JUMP def si def
est le label de l'instruction par défaut.
October 28, 2009 20
On suppose que chaque instruction switch est prétraitée, et qu'on lui associe un label
choix qui indique la place du tableau de choix, deux entiers min, max qui donnent la
valeur minimum et la valeur maximum apparaissant dans les case ainsi qu'une étiquette
n pour marquer la n du switch et une étiquette def pour marquer les instructions de
la clause par défaut (cette étiquette est la même que l'étiquette n s'il n'y a pas de clause
par défaut) et nalement une liste d'étiquettes lcases pour chaque instruction préxée d'un
case.
Dans le cas de notre exemple, le programme compilé comporterait une table d'initialisation
décrite dans la gure 3.
choix :
JUMP l indice 0 correspondant au caractère '\t' de valeur 9
JUMP l indice 1 correspondant au caractère '\n' de valeur 10
JUMP def indice 2 correspondant au caractère de valeur 11
JUMP def indice 3 correspondant au caractère de valeur 12
JUMP def indice 4 correspondant au caractère de valeur 13
JUMP def indice 5 . . .
JUMP def indice 6 . . .
JUMP def indice 7 . . .
JUMP def indice 8 . . .
JUMP def indice 9 . . .
JUMP def indice 10 . . .
JUMP def indice 11 . . .
JUMP def indice 12 . . .
JUMP def indice 13 . . .
JUMP def indice 14 . . .
JUMP def indice 15 . . .
JUMP def indice 16 . . .
JUMP def indice 17 . . .
JUMP def indice 18 . . .
JUMP def indice 19 . . .
JUMP def indice 20 . . .
JUMP def indice 21 . . .
JUMP def indice 22 . . .
JUMP l indice 23 correspondant au caractère ' ' de valeur 32
À l'endroit d'une instruction switch (e) { li }, on insère le code pour evaluer l'expression
e, le code pour eectuer le branchement indexé aproprié en fonction de la valeur de e puis
enn les instructions du corps du switch avec les étiquettes correspondantes. Dans le cas
de notre exemple, on obtient :
(b) Donner le schéma général pour cette partie du code en supposant données les valeurs
min et max ainsi que les étiquettes choix et def.
(c) Quels sont les avantages et les inconvénients de cette seconde méthode de compilation
par rapport à la méthode qui n'utilise que les instructions de branchement JZ et JUMP ?
7. L'étudiant Gus doit écrire un programme interactif qui comprend en particulier les instruc-
tions Find, Load, Write et Quit. Il choisit d'associer à chaque instruction un caractère (la
première lettre) et écrit son programme sous la forme :
switch (c) {
case 'f ':. . .
case 'l':. . .
case 'w':. . .
case 'q':. . .
default:. . .
}
Son collègue, qui préfère utiliser CAML, commence par dénir un type de données command
puis utilise du ltrage :
Sachant que CAML associe à chaque constructeur un numéro et compile le ltrage à l'aide
d'une instruction analogue à switch, quels avantages le compilateur peut-il tirer de cette
deuxième approche ?
16 Allocation de registres
Soit le programme écrit en langage intermédiaire :
y:=1
m:=x
e:=n
loop:
if e=0 goto fin:
t:=y mod 2
if t=0 goto pair:
y:= m*y
pair:
m:=m * m
e:=e div 2
goto loop:
fin:
print y
1. Donner en chaque point de programme, l'ensemble des variables vivantes (variables qui
seront lues avant d'être aectées).
2. Construire le graphe d'interférence (graphe dont les sommets sont les variables et où il y a
une arête entre x et y si elles sont simultanément vivantes).
October 28, 2009 22
Un type CAML possible pour les arbres de syntaxe abstraite de ce langage est
1. On suppose donnée une fonction index qui à chaque variable locale x de E, associe un
entier n qui représente le décalage par rapport à la base de la pile à laquelle la variable
x est stockée. On suppose que le nombre de cases mémoire diérentes à utiliser pour
stocker les variables locales de l'expression E est N . On suppose également que pour
chaque fonction prédénie f , il y a un label label(f ) auquel sont associées les instructions
machines correspondant au calcul de f . Ces fonctions prennent en argument deux entiers
et renvoient un entier, cet entier est stocké à la place du premier argument.
Proposer une fonction code qui produit pour chaque expression E, les instructions de la
machine à pile nécéssaires à l'évaluation de cette expression.
2. Soit l'expression
let x = 1 in
let t = let u = 2 in plus(u,3) in
f(let y = h(2,t) in g(y,x), let z=4 in g(z,z))
October 28, 2009 23
4. Proposer une fonction maxvar qui calcule pour chaque expression E le nombre de cases
mémoire à réserver pour les variables locales en utilisant la stratégie précédente.
Combien de cases mémoire doit-on utiliser pour l'expression de la question 2 ?.
5. Proposer un décalage pour les variables de l'expression de la question 2 qui n'utilise que
deux cases mémoire en supposant que dans l'expression f (e1 , e2 ), l'expression e1 est évaluée
avant e2 .
6. On se propose de mettre en ÷uvre une analyse plus ne pour le calcul de index qui prenne
en compte l'utilisation eective des variables. Pour réaliser cette analyse, on transforme
l'expression en une suite d'aectations élémentaires de la forme x := t avec x une variable, et
t un terme simple qui peut être une constante c, une variable y ou bien f (y, z) l'application
d'une fonction f à deux variables y et z . La variable x dans laquelle le résultat de t est
stocké peut être soit une variable du programme, soit désigné le sommet de pile.
type sexpr =
Scte of int
| Svar of v
| Sfun of string * v * v
Pour compiler une expression e on se donne une variable r dans laquelle on souhaite stocker
la valeur de e. Si e est une constante ou une variable, on utilise l'aectation correspondante.
Si e est de la forme let x = e1 in e2 on engendre les aectations élémentaires pour calculer
la valeur de l'expression e1 dans la variable x puis les aectations pour calculer e2 dans
la variable r . Si e est de la forme f (e1 , e2 ), on crée des nouvelles variables r1 et r2 , on
engendre les aectations pour calculer e1 dans r1 puis e2 dans r2 et on ajoute l'aectation
r := f (r1 , r2 ), si e1 (resp. e2 ) est déjà une variable, on peut l'utiliser directement.
(a) Donner la suite d'aectations correspondant à l'expression de la question 2.
(b) Fournir pour chaque point du programme ainsi obtenu l'ensemble des variables vi-
vantes. On ne prendra pas en compte les variables stockés dans la pile. Construire le
graphe d'interférence et en déduire une manière d'allouer chacune des variables.
(c) Proposer une fonction transforme qui étant données une variable r et une expression
e, calcule la suite d'aectations équivalente pour le calcul de la valeur de e dans la
variable r.
7. On se donne une suite d'aectations p, proposer une fonction vivantes qui calcule l'en-
semble des variables vivantes au début de p (qui seront donc utilisées dans p avant d'être
redénies). On pourra utiliser les notations ensemblistes suivante : l'ensemble vide empty, le
singleton (singleton x), l'union de deux ensembles (union e1 e2) et la diérence entre
deux ensembles (diff e1 e2).
October 28, 2009 24
18 Filtrage
(Examen janvier 04)
On étudie un langage permettant la dénition d'expressions par ltrage.
Exemple Dans un tel langage, on peut par exemple introduire un nouveau type tree pour
représenter les arbres, avec deux constructeurs Leaf et Node.
type tree = Leaf of int | Node of int ∗ (tree ∗ tree)
On peut également former des motifs plus complexes comme dans l'expression suivante notée E0
dans la suite de l'exercice :
Grammaire Ce langage est décrit par la grammaire suivante dans laquelle cte est un token
correspondant à une valeur entière, ident est un token correspondant à un identicateur repré-
sentant soit une variable, soit une fonction prédénie, soit un constructeur et nalement let, in,
match, with sont des mots clés et =, (, ,, ->, |, _ et ) sont des symboles. E, P et B sont des
non-terminaux respectivement pour les expressions, les motifs et les branches de ltrage.
Types Les expressions de ce langage sont typées. Les types sont soit le type primitif int, soit
un type produit τ1 ∗ τ2 soit un identicateur correspondant à un type déni par l'utilisateur. Un
tel type est introduit par une déclaration de type de la forme :
type t = C1 of τ1 | . . . | Cn of τn
Évaluation du ltrage Une valeur v est soit une constante c, soit une valeur construite C(v0 )
avec C un constructeur, soit la paire (v1 , v2 ) de deux valeurs v1 et v2 .
Une valeur v ltre un motif p dont les variables sont {x1 , . . . , xn } s'il existe des valeurs v1 , . . . , vn
telles que l'expression let x1 = v1 in . . . let xn = vn in p s'évalue en la valeur v . La liste des
couples [(x1 , v1 ); . . . ; (xn , vn )] est la solution du ltrage. On peut réaliser une fonction de ltrage
filtre d'une valeur v par un motif p de la manière suivante :
filtre(v, x) = [(x, v)] si x est une variable
filtre(v, _) = []
filtre(c, c) = [] si c est une constante
filtre(C(v), C(p)) = filtre(v, p) C est un constructeur
filtre((v1 , v2 ), (p1 , p2 )) = filtre(v1 , p1 )@filtre(v2 , p2 )
filtre(v, p) = erreur dans tous les autres cas
L'objectif de cet exercice est d'étudier diérents aspects de la compilation d'un langage avec
ltrage (analyse syntaxique, typage et génération de code).
P ::= atom | P, P | (P )
(a) La grammaire précédente est ambiguë, expliquer pourquoi.
(b) Construire l'automate SLR(1) de cette grammaire, dans quel état a lieu le conit,
quelle est la nature de ce conit ?
(c) On souhaite que l'expression p1 , p2 , p3 soit interprétée comme p1 , (p2 , p3 ), donner les
transitions à eectuer à partir de l'état conictuel pour obtenir ce comportement.
2. Typage des motifs Un motif p est bien formé si on peut lui associer un type τ et un
environnement l = [x1 : τ1 ; . . . ; xn : τn ] tel que
Var(l) = {x1 , . . . , xn } sont les variables introduites par le motif p;
chaque variable xi apparaît exactement une fois dans le motif p;
dans l'environnement l le motif p est bien formé de type τ .
On note cette relation l ` p : τ . On la dénit formellement par les règles suivantes :
[] ` c : int [] ` _ : τ [x : τ ] ` x : τ
l`p: τ0 C 0
constructeur de prol (τ , τ ) l1 ` p1 : τ1 l2 ` p2 : τ2 Var(l1 ) ∩ Var(l2 ) = ∅
l ` C(p) : τ l1 ; l2 ` p1 , p2 : τ1 ∗ τ2
(a) On appelle p1 , p2 , p3 , p4 E0 introduite au début de l'exercice.
les motifs de l'expression
Donner les environnements l1 , l2 , l3 , l4 tels que li ` pi : tree
(b) Proposer une fonction qui étant donnés un motif p et un type t calcule l tel que l ` p : t
si le motif p est bien formé de type t et échoue sinon.
3. Typage des expressions Les règles de typage du langage dénissent sous quelles condi-
tions une expression e est bien typée de type τ dans un environnement Γ. On note cette
relation Γ ` e : τ.
c est une constante entière Γ(x) = τ Γ ` e : τ0 profil(F ) = (τ 0 , τ )
Γ ` c : int Γ`x:τ Γ ` F (e) : τ
Γ ` e1 : τ1 Γ; x : τ1 ` e2 : τ2 Γ ` e1 : τ1 Γ ` e2 : τ2
Γ ` let x = e1 in e2 : τ2 Γ ` e1 , e2 : τ1 ∗ τ2
Γ`e:t l1 ` p1 : t Γ; l1 ` e1 : τ . . . ln ` pn : t Γ; ln ` en : τ
Γ ` match e with p1 -> e1 | . . . | pn -> en : τ
Proposer une fonction typeof qui étant donnés un environnement Γ et une expression e
calcule le type τ tel que Γ ` e : τ si un tel type existe et échoue sinon.
October 28, 2009 26
que projC (E) s'évalue en la valeur v si l'expression E s'évalue en une valeur C(v) et
échoue sinon.
(a) Proposer une fonction transforme qui prend en argument une expression e, un motif
p, une expression succès pouvant mentionner les variables du motif p et une expression
échec, et qui calcule une expression ne comportant pas d'expression match mais pou-
vant contenir les fonctions et constructions données ci-dessus, dont le comportement
est le même que celui de l'expression : match e with p -> succès | _ -> échec.
C'est-à-dire que si e s'évalue en une valeur v , si filtre(v, p) renvoie la séquence
[(x1 , v1 ), . . . , (xn , vn )] alors le résultat attendu s'évalue comme let x1 = v1 in . . . let xn =
vn in succès sinon le résultat attendu est échec.
On construira la fonction transforme par cas suivant le motif p. Les cas du caractère
_ et d'une constante c peuvent se dénir de la manière suivante :
transforme(e, _, succès, échec) = succès
transforme(e, c, succès, échec) = if eq(e, c) then succès else échec
(b) En déduire une fonction élimine qui étant donnée une expression e remplace les
constructions match par des conditionnelles simples et des fonctions de test et de
projection. On supposera que dans les constructions match de e, les ltrages sont
exhaustifs c'est-à-dire qu'il y a toujours un motif pour lequel le ltrage réussit.
Soit code la fonction qui étant donnée une expression sans ltrage produit le code pour la
machine à pile permettant de calculer la valeur de cette expression. Expliciter la dénition
de code dans les cas suivants :
Application C(e) et fonction de test test(C, e) avec C un constructeur et e une expression;
paire e1 , e2 de deux expressions e1 et e2 ; projection fst(e) de l'expression e sur la première
composante.
October 28, 2009 27
p ::= x | C(p1 , . . . , pn )
v ::= C(v1 . . . , vn )
On dit qu'un motif p ltre une valeur v s'il existe une substitution σ (de variables par des valeurs)
telle que σ(p) = v . Pour un ltrage a plusieurs cas de la forme
1. Montrer que l'ensemble des valeurs ltrées par C(p1 , . . . , pn ) est l'ensemble des C(v1 , . . . , vn )
où chaque pi ltre vi pour i = 1, . . . , n.
2. Proposer une compilation simple du ltrage à une ligne sous la forme d'une fonction
F (p, v, e) où p est le motif, v la valeur ltrée et e l'action à eectuer en cas de succès
(une expression).
4. Pour faire mieux, il faut considérer le ltrage dans sa globalité (et non pas ligne à ligne)
et considérer également le ltrage simultané de plusieurs valeurs. On représente donc le
problème de ltrage par une matrice de la forme
v1 v2 . . . vm
p1,1 p1,2 . . . p1,m → e1
. . .. . .
. . . . .
. . . .
pn,1 pn,2 . . . pn,m → en
où les vi sont les m valeurs à ltrer, les pi,j les motifs, et les ei les actions. Une telle matrice
correspond donc au ltrage suivant :
v1 v2 . . . vm
x1,1 p1,2 . . . p1,m → e1
M= . . .. . .
. . . . .
. . . .
xn,1 pn,2 . . . pn,m → en
alors on peut supprimer cette première colonne :
v2 . . . v m
p1,2 . . . p1,m → let x1,1 =v1 in e1
F (M ) = F .
.. . .
.. . .
. . .
pn,2 . . . pn,m → let xn,1 =v1 in en
enn, si la colonne de gauche contient au moins un motif n'étant pas une variable, on
construit pour chaque constructeur C apparaissant en tête d'un motif de la première co-
lonne une sous-matrice MC regroupant les lignes commençant par le constructeur C ainsi
que les lignes commençant par une variable. Dans cette sous-matrice M C, la première
colonne est remplacée par k colonnes correspondant aux k arguments du constructeur
C. Les actions sont inchangées, sauf dans le cas où le motif était une variable x et où
l'action e devient alors let x=v1 in e.
On construit également la sous-matrice MR regroupant les lignes commençant par une
variable, où la première colonne est supprimée et les actions modiées comme ci-dessus.
Si C1 , . . . , C p sont les p constructeurs apparaissant en tête dans la première colonne de
M, on dénit alors
match v with [] → 1 | 1 :: y → 2 | z :: y → z
match x with
| (true, true) → true
| (y, false) → false
| (false, y) → false
match v with
| C(q) → e1
| D → e2
| x → e3
| E(r, s) → e4
| y → e5
| C(t) → e6
| E(u, v) → e7
(b) Que peut-on dire du nombre de tests eectués ? de la taille du code produit ?
L'environnement contient la liste des valeurs de ses variables libres. Dans l'exemple ci-dessus, la
fonction fun y -> x + y + 2 fait référence à la variable libre x et la fonction add ne contient
aucune variable libre.
L'objectif de ce TD est de proposer une méthode de compilation d'un (petit) langage fonc-
tionnel. Cette compilation sera réalisée en deux étapes principales. La première étape est une
étape d'identication des fermetures et de désimbrication. Elle permet d'obtenir un programme
ne contenant plus de fonctions imbriquées. La seconde étape consiste à traduire le code intermé-
diaire vers du bytecode.
Avec l'exemple précédent, la première étape traduira le programme dans un programme de
la forme:
(* syntaxe abstraite *)
and expr =
Eint of int
| Ebool of bool
| Elocal of string
| Eglobal of string
| Eop of op * expr * expr
| Eapp of expr * expr
| Eifthenelse of expr * expr * expr
| Efun of string * expr
| Elet of string * expr * expr
| Erec of string * string * expr
and op =
Eegal (* = *)
| Enegal (* != *)
| Einf (* < *)
| Esup (* > *)
| Einf_egal (* <= *)
| Esup_egal (* >= *)
| Eplus (* + *)
| Emoins (* - *)
| Emult (* * *)
| Ediv (* / *)
1. Ecrire une fonction vars: expr -> string list telle que [vars e] calcule la liste des
variables libres de [e].
On suppose que la portée des variables a été résolue précédemment (autrement dit, toute
variable est dénie une seule fois)
type def =
Mfundef of string * env * string * expr
| Mdef of string * expr
and expr =
Mint of int
| Mbool of bool
| Mlocal of string
| Mglobal of string
| Mop of Syntaxe_abstraite.op * expr * expr
| Mcall of expr * expr
| Mifthenelse of expr * expr * expr
| Mlet of string * expr * expr
| Mclos of string * env
(* on ajoute les fonctions d'acces pour recuperer *)
(* le code et l'environnement d'une fermeture *)
| Mget_code of expr
| Mget_env of expr
3. Ecrire une fonction clos: Syntaxe_abstraite.def list -> Cmm.def list qui traduit
une liste de déclarations.
Une fermeture est habituellement une paire composée d'une fonction et d'un environnement.
La fonction prend donc un argument supplémentaire permettant d'accéder à ses variables libres.
Pour obtenir un code ecace, une fermeture est représentée ici par un tableau alloué dans le
tas avec les caractéristiques suivantes. Si l'environnement contient les variables libres x1 , ..., xn
alors ce tableau est de taille n + 1 et contient les valeurs [@f, v1 , ..., vn ]. @f désigne l'adresse de
la fonction et se trouve à l'indice 0 du tableau, suivi des n valeurs des variables libres.
On dénit un environnement de liaison de la manière suivante:
Le tableau d'activation sera tel que le résultat de l'application est rangé à l'adresse f p − 3,
la valeur de e2 à l'adresse fp − 2 et la valeur de e1 a l'adresse f p − 1.
2. Donner le principe de la compilation d'une variable locale suivant qu'elle est dénie loca-
lement ou qu'elle apparaît dans l'environnement de fermeture.
October 28, 2009 32
En déduire la fonction compile_var: string -> Instr.t list de compilation d'une va-
riable locale.
5. Dénir une fonction compile_decl: Cmm.def list -> Instr.t list de compilation d'une
suite de déclarations.
Le code intermédiaire est exécuté par une machine à pile. La pile d'exécution contient des
entiers, des réels et des adresses. Les chaînes de caractères sont stockées dans une mémoire séparée
et repérées par leurs adresses. Trois registres permettent d'accéder à diérentes parties de la pile.
Le registre sp (stack pointer) repère le sommet courant de la pile, il pointe sur la première
case libre de la pile.
Le registre fp (frame pointer) repère l'adresse de base des variables locales.
Le registre gp contient l'adresse de base des variables globales.
La machine comporte un registre pc qui pointe sur l'instruction courante du code à exécuter.
Une pile annexe permet de sauvegarder les appels, elle contient des couples de pointeurs, l'un
sauvegardant le registre d'instructions pc et l'autre le registre fp des variables locales.
21.2 Instructions
Les instructions sont désignées par un nom et peuvent prendre un ou deux arguments. Ceux-ci
peuvent être:
des constantes entières,
des constantes réelles,
des chaînes de caractères entre double quote avec les mêmes conventions que le langage C
pour ce qui concerne les caractères spéciaux \", \n, \\,
des adresses référençant des emplacements dans la zone des chaînes, dans la pile principale
ou dans la pile d'appels,
une étiquette désignant un emplacement dans les instructions soit sous forme d'un entier
(on référence alors la n-ème instruction à partir du début du programme) soit une étiquette
symbolique.
21.2.1 Terminologie
On utilisera les conventions suivantes:
Empiler une valeur x signie stocker x à l'emplacement P[sp ] et incrémenter sp de 1.
Empiler n fois une valeur x signie itérer n fois l'opération précédente.
Dépiler n valeurs revient à décrémenter de n la valeur de sp .
Le sommet de la pile représente la dernière valeur stockée dans la pile soit P[sp − 1], le
sous-somment de la pile représente l'avant dernière valeur stockée dans la pile soit P[sp−2].
opérations de comparaison est un entier qui vaut 0 ou 1. L'entier 0 représente la valeur booléenne
"faux" tandis que 1 représente la valeur booléenne "vrai".
Égalité
Le test d'égalité teste que les deux objets (entiers, réels ou adresses) sur la pile sont égaux, Une
erreur d'exécution a lieu si les deux objets ne sont pas de même type. Deux chaînes égales sont
stockées à la même adresse, cette instruction permet donc de tester de l'égalité de deux chaînes.
Instruction Description
EQUAL dépile n puis m qui doivent être de même type et empile le résultat de
n=m
Conversions
Diérentes instructions permettent de convertir une chaîne de caractères en entier ou réel et
réciproquement.
Instruction Description
ATOI dépile l'adresse d'une chaîne et empile sa conversion en nombre entier,
échoue si la chaîne ne représente pas un entier.
ATOF dépile l'adresse d'une chaîne et empile sa conversion en nombre réel,
échoue si la chaîne ne représente pas un réel.
STRI dépile un entier et empile l'adresse d'une chaîne représentant cet entier
STRF dépile un réel et empile l'adresse d'une chaîne représentant ce réel
October 28, 2009 34
Empiler
Instruction Argument Description
PUSHI n entier empile n
PUSHN n entier empile n fois la valeur entière 0
PUSHF n réel empile n
PUSHS n chaîne stocke n dans la zone des chaînes et empile l'adresse
PUSHG n entier empile la valeur située en gp[n]
PUSHL n entier empile la valeur située en fp[n]
PUSHSP empile la valeur du registre sp
PUSHFP empile la valeur du registre fp
PUSHGP empile la valeur du registre gp
LOAD n entier dépile une adresse a et empile la valeur dans la pile située en a[n]
LOADN dépile un entier n, une adresse a et empile la valeur dans la pile
située en a[n]
DUPN n entier duplique et empile les n valeurs en sommet de pile
Dépiler
Instruction Argument Description
POP n entier dépile n valeurs dans la pile
POPN dépile un valeur entière n sur la pile puis dépile n valeurs
Stocker
Instruction Argument Description
STOREL n entier dépile une valeur et la stocke dans la pile en fp [n]
STOREG n entier dépile une valeur et la stocke dans la pile en gp [n]
STORE n entier dépile une adresse a et une valeur v , stocke v à l'adresse a[n]
dans la pile
STOREN dépile un entier n, une adresse a et une valeur v, stocke v à
l'adresse a[n] dans la pile
Divers
Instruction Argument(s) Description
CHECK n, p entiers vérie que le sommet de la pile est un entier i tel que n≤
i ≤ p, sinon déclenche une erreur
SWAP dépile n puis m et rempile n puis m
21.2.4 Entrées-Sorties
Les instructions suivantes permettent de gérer les entrées-sorties.
Instruction Description
WRITEI dépile un entier et l'imprime sur la sortie standard
WRITEF dépile un réel et l'imprime sur la sortie standard
WRITES dépile l'adresse d'une chaîne et imprime la chaîne correspondante sur la
sortie standard
READ lit une chaîne de caractères au clavier, terminée par un retour-chariot,
stocke la chaîne (sans le retour-chariot) et empile l'adresse.
October 28, 2009 35
Modication du registre pc
Instruction Argument Description
JUMP label étiquette aecte au registre pc l'adresse dans le programme correspondant à
label qui peut être un entier ou une valeur symbolique.
JZ label étiquette dépile une valeur, si elle est nulle aecte au registre pc l'adresse
dans le programme correspondant à label sinon incrémente pc de
1.
PUSHA label étiquette empile l'adresse dans le code correspondant à l'étiquette label
Procédure
Lors de l'appel de procédure il est nécessaire de sauvegarder les registre d'instructions et de
variables locales qui seront restaurées au moment du retour.
Instruction Description
CALL dépile une adresse de code a, sauvegarde pc et fp dans la pile des appels,
aecte à fp la valeur courante de sp et à pc la valeur a.
RETURN aecte à sp la valeur courante de fp , restaure de la pile des appels les
valeurs de fp et pc et incrémente pc de 1 pour se retrouver à l'instruction
suivant celle d'appel.
21.2.6 Initialisation et n
Dans l'état initial, le registre pc pointe sur la première instruction du programme. La pile
des appels et la pile d'exécution sont vides. Les registre gp et sp pointent sur la base de la
pile d'exécution tandis que fp n'est pas déni. Le registre fp doit être initialisé par l'instruction
START qui ne peut être utilisée qu'une seule fois. Les instructions suivantes servent à stopper
la machine à la n du programme ou en cas d'erreur.
Instruction Argument Description
START Aecte la valeur de sp à fp
NOP ne fait rien.
ERR x chaîne déclenche une erreur d'instruction avec le message x.
STOP arrête l'exécution du programme