Conception du compilateur - Environnement d'exécution
Un programme en tant que code source est simplement une collection de texte (code, instructions, etc.) et pour le rendre vivant, il nécessite des actions à effectuer sur la machine cible. Un programme a besoin de ressources mémoire pour exécuter des instructions. Un programme contient des noms de procédures, d'identificateurs, etc., qui nécessitent un mappage avec l'emplacement de mémoire réel au moment de l'exécution.
Par runtime, nous entendons un programme en cours d'exécution. L'environnement d'exécution est un état de la machine cible, qui peut inclure des bibliothèques de logiciels, des variables d'environnement, etc., pour fournir des services aux processus exécutés dans le système.
Le système de support d'exécution est un package, principalement généré avec le programme exécutable lui-même et facilite la communication de processus entre le processus et l'environnement d'exécution. Il s'occupe de l'allocation et de la désallocation de la mémoire pendant l'exécution du programme.
Arbres d'activation
Un programme est une séquence d'instructions combinées en un certain nombre de procédures. Les instructions d'une procédure sont exécutées séquentiellement. Une procédure a un délimiteur de début et de fin et tout ce qu'il contient est appelé le corps de la procédure. L'identifiant de procédure et la séquence d'instructions finies qu'il contient constituent le corps de la procédure.
L'exécution d'une procédure s'appelle son activation. Un enregistrement d'activation contient toutes les informations nécessaires pour appeler une procédure. Un enregistrement d'activation peut contenir les unités suivantes (selon la langue source utilisée).
Temporaires | Stocke les valeurs temporaires et intermédiaires d'une expression. |
Données locales | Stocke les données locales de la procédure appelée. |
État de la machine | Stocke l'état de la machine, comme les registres, le compteur de programmes, etc., avant que la procédure ne soit appelée. |
Lien de contrôle | Stocke l'adresse d'enregistrement d'activation de la procédure de l'appelant. |
Lien d'accès | Stocke les informations des données qui sont en dehors de la portée locale. |
Paramètres réels | Stocke les paramètres réels, c'est-à-dire les paramètres utilisés pour envoyer une entrée à la procédure appelée. |
Valeur de retour | Stocke les valeurs de retour. |
Chaque fois qu'une procédure est exécutée, son enregistrement d'activation est stocké sur la pile, également appelée pile de contrôle. Lorsqu'une procédure appelle une autre procédure, l'exécution de l'appelant est suspendue jusqu'à la fin de l'exécution de la procédure appelée. A ce moment, l'enregistrement d'activation de la procédure appelée est stocké sur la pile.
On suppose que le contrôle du programme se déroule de manière séquentielle et lorsqu'une procédure est appelée, son contrôle est transféré à la procédure appelée. Lorsqu'une procédure appelée est exécutée, elle renvoie le contrôle à l'appelant. Ce type de flux de contrôle permet de représenter plus facilement une série d'activations sous la forme d'un arbre, appelé leactivation tree.
Pour comprendre ce concept, nous prenons un morceau de code comme exemple:
. . .
printf(“Enter Your Name: “);
scanf(“%s”, username);
show_data(username);
printf(“Press any key to continue…”);
. . .
int show_data(char *user)
{
printf(“Your name is %s”, username);
return 0;
}
. . .
Voici l'arborescence d'activation du code donné.
Nous comprenons maintenant que les procédures sont exécutées en profondeur d'abord, donc l'allocation de pile est la meilleure forme de stockage appropriée pour les activations de procédure.
Allocation de stockage
L'environnement d'exécution gère les exigences de mémoire d'exécution pour les entités suivantes:
Code: Il s'agit de la partie texte d'un programme qui ne change pas à l'exécution. Ses besoins en mémoire sont connus au moment de la compilation.
Procedures: Leur partie texte est statique mais ils sont appelés de manière aléatoire. C'est pourquoi, le stockage de pile est utilisé pour gérer les appels de procédure et les activations.
Variables: Les variables ne sont connues qu'au moment de l'exécution, sauf si elles sont globales ou constantes. Le schéma d'allocation de mémoire de tas est utilisé pour gérer l'allocation et la désallocation de mémoire pour les variables au moment de l'exécution.
Allocation statique
Dans ce schéma d'allocation, les données de compilation sont liées à un emplacement fixe dans la mémoire et elles ne changent pas lorsque le programme s'exécute. Comme les besoins en mémoire et les emplacements de stockage sont connus à l'avance, le package de support d'exécution pour l'allocation et la désallocation de mémoire n'est pas nécessaire.
Allocation de pile
Les appels de procédure et leurs activations sont gérés au moyen de l'allocation de mémoire de pile. Il fonctionne dans la méthode du dernier entré, premier sorti (LIFO) et cette stratégie d'allocation est très utile pour les appels de procédure récursifs.
Allocation de tas
Les variables locales à une procédure sont allouées et désallouées uniquement lors de l'exécution. L'allocation de tas est utilisée pour allouer dynamiquement de la mémoire aux variables et la réclamer lorsque les variables ne sont plus nécessaires.
À l'exception de la zone de mémoire allouée statiquement, la mémoire de pile et de tas peut augmenter et diminuer de manière dynamique et inattendue. Par conséquent, ils ne peuvent pas être fournis avec une quantité fixe de mémoire dans le système.
Comme le montre l'image ci-dessus, la partie texte du code se voit allouer une quantité fixe de mémoire. La pile et la mémoire de tas sont disposées aux extrêmes de la mémoire totale allouée au programme. Les deux rétrécissent et grandissent l'un contre l'autre.
Passage de paramètres
Le moyen de communication entre les procédures est appelé passage de paramètres. Les valeurs des variables d'une procédure appelante sont transférées à la procédure appelée par un mécanisme. Avant d'aller de l'avant, parcourez d'abord quelques terminologies de base relatives aux valeurs d'un programme.
valeur r
La valeur d'une expression est appelée sa valeur r. La valeur contenue dans une seule variable devient également une valeur r si elle apparaît sur le côté droit de l'opérateur d'affectation. Les valeurs r peuvent toujours être affectées à une autre variable.
valeur l
L'emplacement de la mémoire (adresse) où une expression est stockée est appelé valeur l de cette expression. Il apparaît toujours à gauche d'un opérateur d'affectation.
Par exemple:
day = 1;
week = day * 7;
month = 1;
year = month * 12;
À partir de cet exemple, nous comprenons que les valeurs constantes telles que 1, 7, 12 et des variables telles que jour, semaine, mois et année ont toutes des valeurs r. Seules les variables ont des valeurs l car elles représentent également l'emplacement mémoire qui leur est attribué.
Par exemple:
7 = x + y;
est une erreur de valeur l, car la constante 7 ne représente aucun emplacement mémoire.
Paramètres formels
Les variables qui prennent les informations transmises par la procédure de l'appelant sont appelées paramètres formels. Ces variables sont déclarées dans la définition de la fonction appelée.
Paramètres réels
Les variables dont les valeurs ou adresses sont transmises à la procédure appelée sont appelées paramètres réels. Ces variables sont spécifiées dans l'appel de fonction en tant qu'arguments.
Example:
fun_one()
{
int actual_parameter = 10;
call fun_two(int actual_parameter);
}
fun_two(int formal_parameter)
{
print formal_parameter;
}
Les paramètres formels contiennent les informations du paramètre réel, en fonction de la technique de passage de paramètre utilisée. Cela peut être une valeur ou une adresse.
Passer par valeur
Dans le mécanisme de passage par valeur, la procédure appelante transmet la valeur r des paramètres réels et le compilateur la place dans l'enregistrement d'activation de la procédure appelée. Les paramètres formels contiennent alors les valeurs transmises par la procédure appelante. Si les valeurs détenues par les paramètres formels sont modifiées, cela ne devrait avoir aucun impact sur les paramètres réels.
Passer par référence
Dans le mécanisme de passage par référence, la valeur l du paramètre réel est copiée dans l'enregistrement d'activation de la procédure appelée. De cette façon, la procédure appelée a maintenant l'adresse (emplacement mémoire) du paramètre réel et le paramètre formel fait référence au même emplacement mémoire. Par conséquent, si la valeur pointée par le paramètre formel est modifiée, l'impact doit être vu sur le paramètre réel car ils doivent également pointer vers la même valeur.
Passer par copie-restauration
Ce mécanisme de passage de paramètres fonctionne de manière similaire au «passage par référence», sauf que les modifications apportées aux paramètres réels sont effectuées à la fin de la procédure appelée. Lors de l'appel de fonction, les valeurs des paramètres réels sont copiées dans l'enregistrement d'activation de la procédure appelée. Les paramètres formels s'ils sont manipulés n'ont aucun effet en temps réel sur les paramètres réels (lorsque les valeurs l sont transmises), mais lorsque la procédure appelée se termine, les valeurs l des paramètres formels sont copiées dans les valeurs l des paramètres réels.
Example:
int y;
calling_procedure()
{
y = 10;
copy_restore(y); //l-value of y is passed
printf y; //prints 99
}
copy_restore(int x)
{
x = 99; // y still has value 10 (unaffected)
y = 0; // y is now 0
}
Lorsque cette fonction se termine, la valeur l du paramètre formel x est copiée dans le paramètre réel y. Même si la valeur de y est modifiée avant la fin de la procédure, la valeur l de x est copiée dans la valeur l de y, ce qui lui permet de se comporter comme un appel par référence.
Passer par les noms
Des langages comme Algol fournissent un nouveau type de mécanisme de passage de paramètres qui fonctionne comme un préprocesseur en langage C. Dans le mécanisme de passage par nom, le nom de la procédure appelée est remplacé par son corps réel. Pass-by-name substitue textuellement les expressions d'argument dans un appel de procédure aux paramètres correspondants dans le corps de la procédure afin qu'elle puisse maintenant travailler sur des paramètres réels, un peu comme le passage par référence.