Précédent Index Suivant

Chapitre 10 :   Déclarations locales

On introduit donc ici les déclarations de variables locales aux procédures ; l'extension de PP5 ainsi construite se nomme PP6.

10.1   Grammaire et sémantique

Nous étendons la grammaire de PP5 par l'introduction d'une règle PROCS autorisant la définition de procédures pouvant inclure des déclarations de constantes et variables locales :
PROGRAM ::= program ID ; BLOCK .
BLOCK ::= CONSTS VARS PROCS INSTS
PROCS ::= { proc ID ; CONSTS VARS INSTS ; }

De même que dans PP5, on autorise l'appel de procédure dans les instructions :
INST ::= ... | APPEL | ...
APPEL ::= ID
l'identificateur ID de la règle APPEL doit être le nom d'une procédure précédemment définie.

Sémantiquement, nous définissons une notion de visibilité des variables. Une variable locale n'est visible que dans la procédure qui la déclare. Une variable globale est visible dans une procédure s'il n'existe pas de variable locale portant le même nom.

10.2   Visibilité des variables

Pour respecter cette règle de visibilité des variables locales, il est nécessaire que les déclarations locales apparaissent dans la table des symboles uniquement durant l'analyse de la procédure correspondante.

On va donc gérer une table des symboles à deux niveaux. Le premier niveau contient les déclarations globales (y compris les déclarations de procédures, qui sont globales) ; le deuxième niveau contient les déclarations locales à la procédure en cours d'analyse. La limite entre ces deux niveaux est indiquée par la variable LIMIT ; les entrées dans la table des symboles d'indice supérieure à LIMIT sont locales.

On obtient la procédure PROCS d'analyse de déclarations de procédures de la figure 10.1.

 procedure PROCS ; 
    var LIMIT : integer ; 
    begin
       while TOKEN = PROC_TOKEN do begin 
          NEXT_TOKEN ; 
          (* on entre le nom de la procedure en global *)
          TESTE_ET_ENTRE (ID_TOKEN, PROCEDURE) ; 
          TESTE (PT_VIRG_TOKEN) ; 
          (* on memorise le dernier global *)
          LIMIT := DERNIERSYM ; 
          if TOKEN = CONST_TOKEN then CONSTS ; 
          if TOKEN = VAR_TOKEN then VARS ;
          INSTS ; 
          TESTE (PT_VIRG_TOKEN) ; 
          (* on oublie les declarations locales *)
          DERNIERSYM := LIMIT ; 
          GENERER1 (RET)
       end 
    end ;
Figure 10.1 : Procédure PROCS d'analyse de déclarations de procédures.


10.3   Allocation des variables

Deux solutions sont possibles pour l'allocation des variables locales : l'allocation statique et l'allocation dynamique sur la pile.

10.3.1   Allocation statique

L'allocation statique associe un emplacement mémoire fixe dans la pile à chaque variable, que celle-ci soit globale ou locale. à chaque fois qu'une variable (globale ou locale) est déclarée, on incrémente l'OFFSET, l'adresse suivante est donc allouée à la variable. Pour le programme
 program X ; 
    var A, B ; 
    proc Y ; 
       var C, D ; 
          ...
    proc Z ; 
       var E, F ; 
          ...
    begin
       ...
    end .  
on obtient alors les adresses d'allocation dans la pile suivantes pour les différentes variables
Variable Adresse
A de X 0
B de X 1
C de Y 2
D de Y 3
E de Z 4
F de Z 5
Toute variable a donc une adresse différente et toutes les adresses sont connues à la compilation.

On obtient le schéma de P-Code suivant :
    INT N   réservation pour toutes les variables (globales et locales)
    BRN PP
    <proc>
    ...
    RET
    <proc>
    ...
    RET
PP   ...
    <program>
    ...
    HLT

Cette solution est celle choisie par le langage FORTRAN. Cette solution ne permet pas la récursivité, car à chaque appel d'une procédure ce sont toujours les mêmes adresses qui sont utilisées. Si l'on désire autoriser la récursivité (comme en Pascal ou en PP6 !), il faut des adresses différentes pour chaque appel récursif.

10.3.2   Allocation dynamique sur la pile

Afin d'associer des adresses différentes aux variables à chaque appel récursif, il devient impossible de connaître les adresses lors de la compilation (le nombre d'appels récursifs ne pouvant être connu). On va donc allouer dynamiquement sur la pile les variables locales. On obtient le schéma de P-Code suivant :
    INT N   réservation pour les variables globales
    BRN PP
    INT N1   réservation dynamique pour les variables de P1
    <proc>
    ...
    RET
    INT N2   réservation dynamique pour les variables de P2
    <proc>
    ...
    RET
PP   ...
    <program>
    HLT

Les adresses associées aux variables locales sont alors des adresses relatives au début de la zone allouée dynamiquement dans la pile. Pour le même programme
 program X ; 
    var A, B ; 
    proc Y ; 
       var C, D ; 
          ...
    proc Z ; 
       var E, F ; 
          ...
    begin
       ...
    end .  
on obtient alors les adresses d'allocation suivantes pour les différentes variables
Variable Adresse
A de X globale 0
B de X globale 1
C de Y relative 0
D de Y relative 1
E de Z relative 0
F de Z relative 1

Modifier la procédure PROCS pour prendre en compte cette allocation des variables.

10.4   Accès aux variables locales

L'accès à une variable globale se fait par un LDA i avec i un déplacement par rapport à la base de la mémoire, c'est-à-dire par rapport à la base de la pile dans la zone réservée par le INT n du programme principal.

L'accès à une variable locale se fera par LDL i avec i un déplacement par rapport à la base de la zone mémoire réservée par le INT n exécuté à l'appel de la procédure.

Il se pose alors le problème suivant : comment connaître cette adresse de base lors de l'exécution ?
  1. Lorsque l'on appelle une procédure, c'est simple : la base pour la procédure appelée est le pointeur de pile au moment de l'appel ;
  2. Au retour de la procédure, il faut retrouver la base de la procédure appelante.
L'idée est de chaîner les zones de la pile. Par exemple, lorsque le programme principal a appelé la procédure P1 qui a elle-même appelé la procédure P2, l'état de la pile est celui de la figure 10.2.


                         |            |
                         |------------|
                         | variables  |
                         | locales de |
                         |    P2      |
                         |------------|
                         |    ------------+
                         |------------|   |
                         |    AR      |<--|-------- BASE
                         |------------|   | 
                         | variables  |   |
                         | locales de |   |
                         |    P1      |   |
                         |------------|   |
                         |    ------------|----+
                         |------------|   |    |
                         |    AR      |<--+    |
                         |------------|        |
                         | variables  |        |
                         | globales   |        |
                         +------------+<-------+

Figure 10.2 : État de la pile aprés l'appel de la procédure P2, appelée par la procédure P1, elle-même appelée par le programme principal.


BASE est une variable de l'interpréteur de P-Code qui pointe sur la base de la zone de la procédure en cours d'exécution. BASE vaut zéro lorsque l'exécution est dans le programme principal.

On modifie en conséquence les instructions CAL, RET de l'interpréteur et on ajoute l'instruction LDL.

10.5   Travaux dirigés et travaux pratiques

  1. Quels sont les avantages et inconvénients d'une allocation statique des variables locales telle qu'elle est réalisée en Fortran ?
  2. Donner le P-Code à générer et l'évolution, lors de la compilation, de la table des symboles utilisée pour l'exemple de programme PP6 de la figure 10.3.

     program X ; 
        var A ; 
        proc Y ; 
           var B ; 
           begin
              read (B) ; 
              A : = A + B 
           end ; 
        proc Z ; 
           var B ; 
           begin
              read (B) ; 
              A : = A * B ; 
              Y  
           end ; 
        begin
           read (A) ; 
           Z ; 
           write (A) 
        end .
    
    Figure 10.3 : Exemple de programme PP6.


  3. Même chose pour le programme récursif de la figure 10.4.

     program INVERT ; 
        var OK ; 
        proc INV ; 
           var N ; 
           begin
              read (N) ; 
              if N <> OK then INV ; 
              write (N)
           end ; 
        begin
           OK := 0 ; 
           INV 
        end . 
            
    Figure 10.4 : Exemple de programme PP6 récursif.


  4. On modifiera le code de la procédure PROCS donnée à la figure 10.1 pour prendre en compte l'allocation des variables locales relativement à la base de la zone locale de mémoire.

  5. Donner le nouveau code des instructions P-Code CAL, RET, et LDL.

  6. Implémenter les références aux variables en fonction de leur classe : variable locale ou variable globale.

Précédent Index Suivant