environnements d'exécution notions de base spécificités des langages sources organisation de...
TRANSCRIPT
Environnements d'exécution
Notions de base
Spécificités des langages sources
Organisation de la mémoire
Stratégies d'allocation mémoire
Accès aux noms non locaux
Passage de paramètres
Objectifs
Décrire la relation entre le programme source et l'exécution
Exemple de notion statique : les noms des variables (font partie du programme source)
Exemple de notion dynamique : les adresses des variables (existent pendant l'exécution)
Décrire le moteur d'exécution
Le code que le système d'exploitation lance pour exécuter le programme compilé
Langages
C, Java, Pascal, Lisp, Fortran
Notions de base (1/2)
Procédures ou fonctions
Définies par l'association d'un identificateur, le nom, et d'une instruction, le corps
Parfois on dit fonction quand elle renvoie une valeur et procédure sinon ; nous ne ferons pas la différence
Paramètres formels
Dans la définition de la fonction
Paramètres effectifs
Dans un appel de la fonction
Activation d'une fonction
Période qui va de l'appel d'une fonction (avec éventuellement des paramètres réels) à son retour
Exempleprogram sort(input, output) ;
var a : array[0..10] of integer ;
procedure readarray ;
var i : integer ;
begin for i := 1 to 9 do read(a[i]) end ;
function partition(y, z : integer) : integer ;
var i : integer ;
begin ... end ;
procedure quicksort(m, n : integer) ;
var i : integer ;
begin
if (n>m) then begin
i := partition (m, n) ;
quicksort(m, i - 1) ; quicksort (i + 1, n)
Exempleend
end ;
begin
a[0] := - 9999 ;
a[10] := 9999 ;
readarray ;
quicksort(1, 9)
end.
Notions de base (2/2)
Fonctions récursives
Fonction dont une activation peut commencer alors qu'une autre est en cours
Arbre d'exécution
Les noeuds sont les activations des fonctions avec leurs paramètres
Chaque fils d'un noeud correspond à un appel
Pile d'exécution
La pile des activations en cours à un instant donné
Portée d'une déclaration
Noms locaux, noms globaux
Arbre d'exécution
readarray
partition(1, 9)quicksort(1, 3)
quicksort(1, 9)
quicksort(1, 0)
quicksort(2, 1)
quicksort(3, 3)
partition(2, 3)
quicksort(5, 9)
sort
quicksort(2, 3)
partition(1,3)
quicksort(5, 5)
quicksort(7, 7)
quicksort(9, 9)
partition(7, 9)
quicksort(7, 9)
partition(5, 9)
Pile d'exécution
readarray
partition(1, 9)quicksort(1, 3)
quicksort(1, 9)
quicksort(1, 0)
quicksort(5, 9)
sort
partition(1,3)
quicksort(5, 5)
partition(5, 9)
sort quicksort(1, 9) quicksort(1, 3)
......
fond de pile sommet de pile
Notions statiques et dynamiquesExemple : passage d'un nom de variable à une valeur
nom adresse valeur
liaison état
Si l'environnement associe une occurrence du nom x à l'adresse s, on dit que x est lié à s
Un nom peut être lié à plusieurs adresses
Une occurrence d'un nom peut être liée à plusieurs adresses (fonction récursive)
Une adresse peut contenir plusieurs valeurs successivement
Notions statiques et dynamiques
statique dynamique
connu à la compilation dépend de chaque exécution
définition d'une fonction activations d'une fonction
déclaration d'un nom liaisons d'un nom
portée d'une déclaration durée de vie d'une liaison
Spécificités des langages
L'organisation d'un compilateur dépend des réponses à des questions sur le langage source
Y a-t-il des fonctions récursives ?
Comment sont traités les noms locaux au retour des fonctions ?
Une fonction peut-elle utiliser des noms non locaux ?
Comment sont passés les paramètres ?
Les fonctions peuvent-elles être passées en paramètre ?
être renvoyées comme valeur ?
L'allocation de mémoire peut-elle être dynamique ?
La libération doit-elle être explicite ?
Organisation de la mémoire
Zone utilisée en mémoire pour l'exécution d'un programme
code
données statiques
pile
tas
Code exécutable (taille statique)
Données statiques : dont les adresses sont compilées dans le code
Pile d'exécution
Le tas est l'emplacement de toutes les autres informations : en C, la mémoire allouée dynamiquement
Les adresses dans la pile sont des adresses relatives (offset) par rapport au sommet de la pile
Enregistrements d'activation
Zone de mémoire empilée à l'appel d'une fonction et dépilée au retour
valeur renvoyée
paramètres effectifs
lien de contrôle
lien d'accès (en Pascal)
sauvegarde de l'état de la machine
variables locales
zone temporaire
Lien de contrôle : pointe sur l'enregistrement d'activation appelant
Lien d'accès : accès aux variables non locales
Etat machine : valeurs des registres au moment de l'appel
Zone temporaire : pour le calcul des expressions
fond de pile
sommet de pile
Disposition des données localesAdresses calculées à la compilation
Unité minimale d'adressage
L'octet (byte)
Pour certaines opérations : le mot machine, souvent 4 octets
Espace laissé vide : remplissage (padding)
Placement d'un objet
Octets consécutifs (la taille dépend du type)
L'adresse est celle du premier octet
Pendant l'analyse des déclarations
Adresses relatives
Par rapport à un point de référence dans l'enregistrement d'activation
ExempleLa taille de chaque type C dépend de la machine
type char short int long float
taille (bits) 8 16 32 32 32
alignement (bits) 8 16 32 32 32
taille 8 24 48 64 64
alignement 64 64 64 64 64
machine
1
2
Alignement : sur la machine 1, si un char est suivi d'un short, on perd 1 octet
Taille d'un pointeur : sur la machine 2, 24 bits pour le mot et 6 pour 1 bit parmi 64, donc 30 bits
Allocation statique
La stratégie d'allocation mémoire la plus simple
Toutes les liaisons sont permanentes pendant toute l'exécution
Les valeurs des noms locaux persistent d'un appel au suivant
Les adresses des données sont statiques
L'emplacement des enregistrements d'activation est statique
Limitations
La taille de toutes les données est statique
La récursivité est interdite
La création dynamique de structures de données est interdite
Exemple
Un programme en Fortran qui
- lit une ligne et la copie dans un tampon BUFFER
- copie BUFFER dans BUF jusqu'à rencontrer un espace, puis affiche BUF (programme principal)
Exemple entrée : demain matin sortie : demain
La fonction PRDUCE
- lit la ligne et la copie dans BUFFER (premier appel)
- puis lit dans BUFFER un caractère par appel
Le programme principal écrit dans BUF puis affiche BUF
Attention au choc culturel...
Exemple
PROGRAM CNSUME
CHARACTER * 50 BUF
INTEGER NEXT
CHARACTER C, PRDUCE
DATA NEXT /1/, BUF /' '/
6 C = PRDUCE()
BUF(NEXT:NEXT) = C
NEXT = NEXT + 1
IF ( C .NE. ' ' ) GOTO 6
WRITE (*, '(A)') BUF
END
CHARACTER FUNCTION PRDUCE()
CHARACTER * 80 BUFFER
INTEGER NEXT
SAVE BUFFER, NEXT
DATA NEXT /81/
IF ( NEXT .GT. 80 ) THEN
READ (*, '(A)') BUFFER
NEXT = 1
END IF
PRDUCE = BUFFER(NEXT:NEXT)
NEXT = NEXT+1
END
L'instruction SAVE permet à PRDUCE de retrouver les valeurs de BUFFER et NEXT d'un appel à l'autre
code de CNSUME
code de PRDUCE
enregistrement d'activation de CNSUME :
BUF
NEXT
C
enregistrement d'activation de PRDUCE :
BUFFER
NEXT
code
données
statiques
Allocation en pile
Les enregistrements d'activation sont empilés à l'appel de la fonction et dépilés au retour
Les valeurs locales sont perdues
Séquence d'appel
réserve et remplit un enregistrement d'activation
Séquence de retour
restaure l'état de la machine avant l'appel pour reprendre l'exécution où elle en était
valeur renvoyée
paramètres
lien d'accès
sauvegarde de l'état machine
données locales
temporaires
enregistrementd'activation del'appelant
à la chargede l'appelant
lien de contrôle
valeur renvoyée
paramètres
lien d'accès
sauvegarde de l'état machine
données locales
temporaires
lien de contrôleenregistrementd'activation del'appelé
sommetde pile
Séquences d'appel et de retourSéquence d'appelL'appelant évalue les paramètresIl enregistre l'adresse de retour et la valeur du sommet de pile dans
l'enregistrement d'activation de l'appeléIl incrémente le sommet de pileL'appelé sauvegarde les valeurs des registresIl initialise ses données locales
Séquence de retourL'appelé place la valeur à renvoyerIl restaure le sommet de pile et les autres registres et saute à l'adresse de
retourL'appelant copie la valeur renvoyée
Allocation en pileDonnées de taille variable
Les données de taille variable sont placées sur la pile au-dessus de l'enregistrement d'activation
Leur adresse relative n'est pas connue à la compilation
L'enregistrement d'activation contient des pointeurs vers ces données
Les adresses relatives de ces pointeurs sont connues à la compilation
On utilise un 2e pointeur de sommet de pile qui tient compte des données de taille variable (sommet)
lien d'accès
sauvegarde de l'état machine
enregistrementd'activation de p
tableaux de p
lien de contrôle
lien d'accès
sauvegarde de l'état machine
données locales
temporaires
lien de contrôleenregistrementd'activation de q
sommetde pile
pointeur sur Apointeur sur B
tableau A
tableau B
tableaux de qtableau A
sommet
Allocation en pile
Données de taille variable
Au retour de q,
la nouvelle valeur de Sommet est restaurée à partir de SommetPile en tenant compte de la taille des champs de sauvegarde, liens, paramètres et valeur de retour
la nouvelle valeur de SommetPile est la valeur du lien de contrôle
Accès aux noms non locauxDépend des règles de portée des variables dans le langage
source
Les règles qui relient les occurrences des variables à leurs déclarations
Portée statique ou lexicale
C, Java, Pascal, Ada
Portée déterminée par le texte source du programme
Portée dynamique
Lisp, APL, Snobol
Portée déterminée par les activations en cours
Structure de blocsBloc --> { Déclarations Instructions }
Les instructions peuvent contenir des blocs
Une fonction peut contenir plusieurs blocs
Portée statique
La portée d'une déclaration faite dans B est incluse dans B
Si un nom x n'est pas déclaré dans B (non local à B), une occurrence de x dans B est liée à la déclaration de x dans le plus petit bloc B' tel que
- x est déclaré dans B'
- B' contient B
Exemplemain()
{
int a = 0 ;
int b = 0 ;
{
int b = 1 ;
{
int a = 2 ;
printf("%d %d", a, b) ;
}
{
int b = 3 ;
printf("%d %d", a, b) ;
}
printf("%d %d", a, b) ;
}
printf("%d %d", a, b) ;
}
B0
B1
B2
B3
Portée statique
Réalisation : deux méthodes
Allocation en pile
On considère chaque bloc comme une fonction sans paramètres ni valeur de retour
Allocation globale
On regroupe toutes les variables déclarées dans les blocs de la fonction
On peut lier à une même adresse deux variables dont les portées sont disjointes (a de B2 et b de B3)
Portée statiquesans fonctions emboîtées
Les noms sont de deux sortes :
- locaux à une fonction
- globaux, déclarés hors des fonctions
Exemple : le langage C
Les noms globaux sont alloués de façon statique
Les noms locaux sont en pile, accessibles à partir du pointeur de sommet de pile
Pas besoin de lien d'accès
Fonctions passées en paramètre
Avec la portée statique sans fonctions emboîtées, on peut facilement passer une fonction en paramètre ou la renvoyer comme résultat
#include <stdio.h>
int m ;
int f(int n) { return m + n ; }
int g(int n) { return m * n ; }
int b(int (*h)(int)) { printf("%\n", h(2)) ; }
int main(void) {
m = 0 ;
b(f) ; b(g) ; }
Portée statiqueavec fonctions emboîtées (1/6)
En Pascal, on peut déclarer une fonction à l'intérieur d'une autre
Les emboîtements de fonctions forment un arbre statique
readarray exchange(i, j) quicksort(m, n)
sort
Profondeur d'emboîtement
1 pour le programme principal
On ajoute 1 pour chaque imbrication
Exempleprogram sort(input, output) ;
var a : array[0..10] of integer ; x : integer ;
procedure readarray ;
var i : integer ;
begin for i := 1 to 9 do read(a[i]) end ;
procedure exchange(i, j : integer) ;
begin x := a[i] ; a[i] := a[j] ; a[j] := x end ;
procedure quicksort(m, n : integer) ;
var k, v : integer ;
function partition(y, z : integer) : integer ;
var i, j : integer ;
begin ... a ... v ... exchange(i, j) ; ... end ;
begin ... end ;
begin ... end .
Portée statiqueavec fonctions emboîtées (2/6)
readarray
partition(y, z)
quicksort(m, n)
sort
Profondeur d'une variable
Profondeur d'emboîtement de la fonction où elle est définie
Une variable de profondeur v ne peut être utilisée que dans une fonction de profondeur f v
Une fonction de profondeur g ne peut être appelée que par une fonction de profondeur f g - 1 ; f = g - 1 seulement si g est locale à f
exchange(i, j)
Portée statiqueavec fonctions emboîtées (3/6)
Si une fonction g locale à f a un enregistrement d'activation dans la pile, alors f a un enregistrement d'activation au-dessous de celui de g
(c'est l'EA le plus récent qui soit de profondeur inférieure à celle de g)
L'enregistrement d'activation de f n'est pas forcément juste au-dessous de celui de g
Le lien de l'EA de g vers l'EA de f est le lien d'accès
readarray
partition(y, z)
quicksort(m, n)
sort
exchange(i, j)
Portée statiqueavec fonctions emboîtées (4/6)
Si une variable est utilisée dans une fonction g,
- elle est déclarée dans g ou dans une fonction h qui contient g
- on la lie à un enregistrement d'activation présent dans la pile : l'enregistrement d'activation de h le plus récent
- on accède à cet EA en remontant les liens d'accès
- le nombre de liens d'accès est la différence de profondeur entre g et h
readarray
partition(y, z)
quicksort(m, n)
sort
exchange(i, j)
Portée statiqueavec fonctions emboîtées (5/6)
Si une variable a de profondeur p(a) est utilisée dans une fonction f de profondeur p(f), on trouve son adresse :
- en remontant p(f) - p(a) liens d'accès
- en utilisant l'adresse relative de a dans l'enregistrement d'activation obtenu
Ces deux valeurs sont connues à la compilation
Elles représentent la liaison de cette occurrence de a
readarray
partition(y, z)
quicksort(m, n)
sort
exchange(i, j)
Portée statiqueavec fonctions emboîtées (6/6)
Calcul du lien d'accès dans la séquence d'appel
Une fonction f appelle une fonction g
Si p(f) = p(g) - 1
g est locale à f : le lien d'accès est égal au lien de contrôle
Si p(f) p(g)
on remonte p(f) - p(g) + 1 liens d'accès depuis l'enregistrement d'activation de f
Ces calculs sont faits à la compilation
sort
a, x
quicksort(1, 9)
k, v
quicksort(1, 3)
k, v
partition(1, 3)
i, j
exchange(1, 3)
accès
accès
accès
accès
sort
a, x
quicksort(1, 9)
k, v
quicksort(1, 3)
k, v
partition(1, 3)
i, j
accès
accès
accès
sort
a, x
quicksort(1, 9)
k, v
quicksort(1, 3)
k, v
accès
accès
sort
a, x
quicksort(1, 9)
k, v
accès
Fonctions passées en paramètre
program param(input, output) ;
procedure b(function h(n : integer) : integer ;
begin writeln(h(2)) end ;
procedure c ;
var m : integer ;
function f(n : integer) : integer ;
begin f := m + n end ;
begin m := 0 ; b(f) end ;
begin
c
end .
Fonctions passées en paramètre
Calcul du lien d'accès à l'appel de fparam
c
m
b <f ; >
k, v
accès
accès
accPour appeler f on a besoin de l'adresse
du code et du lien d'accès
Accès direct aux noms non locaux
Une méthode plus rapide pour accéder aux noms non locaux
Un tableau de pointeurs vers les enregistrements d'activation
Pour chaque profondeur j, display[j] pointe vers l'enregistrement d'activation où sont placés les noms non locaux de niveau j
A chaque appel on met à jour display
Quand on empile un enregistrement de profondeur i,
1. sauvegarder display[i] dans le nouvel enregistrement
2. faire pointer display[i] sur le nouvel enregistrement
Avant de dépiler, on restaure display[i]
sort
qs(1, 9)
qs(1, 3)
pt(1, 3)
ex(1, 3)
d[2]
d[2]
d[3]
d[2]
d[1]
d[2]
d[3]
sort
qs(1, 9)
qs(1, 3)
pt(1, 3)
d[2]
d[2]
d[3]
d[1]
d[2]
d[3]
sort
qs(1, 9)
qs(1, 3)
d[2]
d[2]
d[1]
d[2]
sort
qs(1, 9)
d[2]
d[1]
d[2]
Portée dynamique
Les liaisons des noms non locaux ne changent pas quand on appelle une fonction
Un nom non local dans la fonction appelée est lié au même emplacement que dans la fonction appelante
Réalisation
On recherche l'emplacement des noms non locaux en suivant les liens de contrôle
On n'a pas besoin de liens d'accès
Portée dynamiqueprogram dynamic(input, output)
var r : real ;
procedure show
begin write(r : 5:3) end ;
procedure small
var r : real ;
begin r := 0.125 ; show end ;
begin
r := 0.250 ;
show ; small ; writeln ;
show ; small ; writeln ;
end .
Résultats :
portée statique
0.250 0.250
0.250 0.250
portée dynamique
0.250 0.125
0.250 0.125
Passage des paramètres (1/5)Passage par valeur
Les valeurs des paramètres effectifs sont passés à la procédure appelée
Exemple : le langage C
Réalisation
Les paramètres formels sont traités comme des noms locaux
Les paramètres effectifs sont évalués par l'appelant
Ne modifie pas les valeurs dans l'enregistrement d'activation de l'appelant, sauf à travers des noms non locaux ou des pointeurs passés par valeur
Passage des paramètres (2/5)
Pascal
procedure echange(i, j : integer) ;
var x : integer ;
begin
x := a[i] ;
a[i] := a[j] ;
a[j] := x
end
C
swap(int * x, * y)
{
int temp ;
temp = * x ;
* x = * y ;
* y = temp ;
}
main()
{
int a = 1, b = 2 ;
swap(& a, &b) ;
}
Passage des paramètres (3/5)Passage par référence
Les adresses des paramètres effectifs sont passés à la procédure appelée
Exemple en Pascal program reference(input, output) ;
var a, b : integer ;
procedure swap(var x, y : integer) ;
var temp : integer ;
begin
temp := x ;
x := y ;
y := temp
end ;
begin
a := 1 ; b := 2 ; swap(a, b) ;
end .
Passage des paramètres (4/5)
Passage par copie et restauration
Les valeurs des paramètres effectifs sont passées à la procédure appelée
Au retour, les nouvelles valeurs des paramètres sont copiées à leur adresse
Exemple : Fortran
Passage des paramètres (5/5)
Passage par nom
Le corps de la fonction est substitué à l'appel
Les paramètres sont substitués littéralement
Exemples
Algol
Macros en C
#define swapint(a, b) {int x = a ; a = b ; b = x ; }
swapint(i, a[i]) affecte la valeur i à a[a[i]]