environnements d'exécution notions de base spécificités des langages sources organisation de...

Post on 04-Apr-2015

104 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

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]]

top related