Vous êtes sur la page 1sur 45

Introduction à la Compilation

Cours Compilation & Interprétation

Institut Supérieur d’Informatique et Multimédia de Sfax

ITWM 1
Plan
Introduction
Introduction(1/2)

Dans les années 50 : Apparition du besoin de langages de


plus haut niveau que l’assembleur
Abstraire certaines particularités propres à la machine
Registres en nombre fini
Instructions de branchement basiques, …
Se concentrer mieux sur les aspects algorithmiques
Passer à l’échelle sur des projets plus complexes
Pouvoir écrire un seul programme pour des
architectures différentes
Introduction(2/2)
Programmeurs de l’époque:
Habitude de production des codes assembleurs très efficaces
Production automatique des exécutables aussi efficace
impossible à partir de langages de haut niveau
Les langages haut niveau se sont heurtés aux doutes
Pour se contenter de traduire les langages de haut niveau, un
compilateur doit les optimiser
Optimisations associées au gain d’échelle
Optimisations offertes par l’abstraction
Généralisation de l’utilisation de langages de haut niveau
Chaine de Compilation
Chaine de compilation (1/2)
Un langage haut niveau fait abstraction de la structure et des
détails du calculateur sur lequel les programmes sont destinés à
être exécutés
Tout programme écrit dans un langage haut niveau ne peut être
exécuté par un ordinateur que s'il est traduit en instructions
exécutables par l'ordinateur
instructions élémentaires directement exécutables par le
processeur (langage machine)
L’exécutable est un autre programme, écrit en langage
assembleur ou en bytecode ou un autre langage de
programmation.
Chaine de compilation (2/2)
Pour pouvoir programmer en langages haut niveau, il faut:
Soit disposer d’un interpréteur: un exécutable qui va lire le
programme et évaluer son contenu
Soit traduire le programme en code machine exécutable, on parle
alors de compilation.
Une autre phase importante qui intervient après la compilation pour
obtenir un programme exécutable est la phase d'éditions de liens
Résoudre les références à des appels de routines dont le code
est conservé dans des bibliothèques
Lier les fichiers objets avec les fichiers précompilés d'une ou
plusieurs bibliothèques
Processus de compilation
Terminologie
Langages bas niveau (1/2)

langage natif d'un processeur : le seul qu'il puisse traiter.


Considéré comme le langage du plus bas niveau
Suite de bits qui est interprétée par le processeur d'un
ordinateur exécutant un programme informatique
Composé d'instructions et de données à traiter codées en
binaire
Propre à la machine (processeur/architecture)
Très difficile à comprendre pour un programmeur
Débogage très fastidieux

Apparition d’autres langages de programmation: Assembleur


Langages bas niveau (2/2)

Se rapproche le plus du langage machine


Langage symbolique donnant des noms aux instructions
Remplace la plupart des instructions binaires du processeur
par des mots clés Plus lisible que des suites de bits
Facilite énormément la programmation
Chaque processeur possède un langage machine qui lui est
propre
Le langage assembleur est aussi propre au processeur
L’assembleur reste un langage difficile à comprendre et à
déboguer pour un programmeur
Langages haut niveau (1/2)

Se rapproche beaucoup plus d’un langage dit naturel que


du langage machine
Il n’est pas propre au processeur (jeu d’instructions)
Programmes fonctionnels sur plusieurs machines et plus
faciles à écrire
Certains programmes peuvent être uniques à un système
d’exploitation (Windows, Linux, …)
Pour être exécuté, un programme utilisant un langage de haut
niveau doit être compilé ou interprété.
Langages haut niveau (2/2)

Indépendant du processeur, polyvalent, proche de l’anglais


Langage procédural: description des opérations à faire pour
résoudre un problème
Exemples: C, Pascal, COBOL, …

Langage descriptif spécifique fortement lié à un domaine


Description de ce que l’on désire faire mais pas de la manière
Exemples:
Base de données: Oracle, SQL, PostgreSQL …
Mathématiques: Matlab, Maple…
Langages haut niveau (2/2)

Langage descriptif pour la programmation des systèmes


experts
Basé sur le concept de résolution de problèmes ou la
programmation par contraintes
Exemple: Prolog

Orienté Objet
Toutes les informations nécessaires sont encapsulées dans un
seul objet
Exemples: Ada, Java, ObjectPascal, …
Interpréte (Interpréteur)
Exécution au fur et à mesure des opérations spécifiées par le
programme source
Analyse des instructions l’une après l'autre puis exécution
immédiate
Présence sur le système à chaque fois que le programme est
exécuté
Traitement simultané du programme et des données

! "
Inconvénient: code source accessible aux utilisateurs
Exemples: HTML, LISP, Prolog, Shell…
Compilateur (1/2)
Programme qui traduit automatiquement un programme écrit dans
un langage source en un programme écrit dans un autre langage
appelé cible
Fichier résultant (exécutable) directement exécutable une fois
pour toute indépendamment des données

! "

Vérification du programme (même par rapport au sens) et


détection des erreurs
Compilateur (2/2)
Pour qu’un compilateur soit correct, il faut que le code produit ait
le même comportement que celui attendu pour le programme
source.
Il est nécessaire de connaître la sémantique des langages
source et cible (règles d’inférences ou instructions machines)
Un compilateur ne traduit pas forcément un langage en code
machine: il peut produire du code dans un langage intermédiaire
qui sera ensuite lui-même compilé ou interprété.
Exemples:
Java est compilé vers le bytecode qui sera par la suite interprété
Ocaml dispose de deux compilateurs: ocamlc produisant du
bytecode et ocamlopt produisant du code machine
ByteCode
Un code intermédiaire entre les instructions machines et le code
source, il n'est pas directement exécutable.
Le bytecode peut être créé à la volée et résider en mémoire
(compilation à la volée, JIT Just In Time) ou bien résider dans un
fichier.
Les instructions sont bien représentées par une suite binaire, lues
et interprétées à travers un programme (bas niveau) par le
processeur.
Avantage: Portabilité Pour exécuter le programme sur une
autre machine, on n’a pas besoin de modifier le compilateur. Il suffit
de porter le programme qui implémente la machine virtuelle.
Langage P-Code
Un langage P-code (dit encore intermédiaire) est un langage à mi-
chemin de l'interprétation et de la compilation
Le code source est traduit (compilé) dans une forme binaire
compacte (du pseudo-code ou p-code) qui n'est pas encore du code
machine. Lorsque l'on exécute le programme, ce P-code est
interprété.

Exemple:
Le bytecode est un P-code généré après la compilation de Java
Traducteur
La compilation n'est pas limitée à la traduction d'un programme
informatique écrit dans un langage de haut niveau en un
programme directement exécutable par une machine
On peut aussi traduire un langage de programmation de haut
niveau vers un autre langage de programmation de haut niveau
On parle de traducteur
A l’inverse d’un compilateur, le traducteur n’engendre pas une
perte d'informations (on garde les commentaires, par exemple).
Exemples:
Traduction de Pascal vers C
Historique
Historique (1/2)
Le premier compilateur, écrit en 1957, traduisait du Fortran
(FORmulas TRANslated) en code machine pour l’IBM 704
Le compilateur Fortran a été développé de 1954-57: Une année
pour la construction du compilateur et trois années pour sa mise à
jour.
En 1958, 50% des programmes ont été écrit en Fortran.
Amélioration de la productivité et meilleure utilisation de la
machine.
FORTRAN I a eu un succès énorme, et son compilateur a gardé
durant 20 ans le record d'optimisation du code objet produit
Historique (2/2)
En 1958: LISP, spécialisé dans le traitement des listes
En 1959: COBOL (Common Business Oriented Language),
premier langage pouvant être compilé sur plusieurs architectures
pour développer les applications de gestion
En 1969: langage C
En 1970: langage Pascal, pour servir l’enseignement des
langages
En 1970: YACC (Yet A Compiler of Compiler), un outil de
génération d’analyseurs syntaxiques en langage C
En 1975: Lex (Lexical Analysers) un outil de génération
d’analyseurs lexicaux en langage C.
Structure et fonctionnement d’un compilateur
Phases de compilation (1/2)
La compilation se décompose en deux phases :
Phase d'analyse: permet de séparer les différents
constituants du programme source et produit une
représentation intermédiaire. Il s’agit de :
Déterminer les variables, les instructions et les opérateurs
Elaborer la structure syntaxique du programme
Elaborer certaines propriétés sémantiques
Phase de synthèse et de production qui devra produire le
code cible. Elle génère le programme cible à partir de la
représentation intermédiaire.
Phases de compilation (2/2)

#
$

# #

#
#
Analyse lexicale (1/2)
Les êtres humains examinent le sens de la phrase à travers sa
décomposition en mots.
Les espaces, la ponctuation et la capitalisation aident le lecteur à
décomposer le groupe de lettres en une séquence de mots afin de
reconnaitre les mots.
Exemple:
«Ceci est une phrase» Phrase composée de 4 mots :
(ceci, est, une et phrase).
« es tce ciu ne phr ase »?? La première étape pour
comprendre un langage c’est la compréhension des mots lus.
Analyse lexicale (2/2)
Appelée aussi analyse linéaire
Il s'agit de reconnaitre les types des mots
L'analyse lexicale se charge de:
Eliminer les caractères superflus (commentaires, espaces, …)
Identifier et traiter les parties du texte qui ne font pas partie du
programme mais sont des directives pour le compilateur
Identifier les symboles qui représentent des identificateurs,
des constantes réelles ou entières, chaines de caractères, des
opérateurs, des séparateurs, les mots clefs du langage
Les caractères sont regroupés en unités lexicales appelées
lexèmes
Exercice
Décomposer l’instruction suivante en lexèmes.
If ( x==y ) then z=1 ; else z=2 ;
La décomposition est comme suit:
Mots clés : if, then et else
Constantes: 1 et 2
Opérateurs: ==, = et =
Signes de ponctuation: (, ), ; et ;
Question: comment connaitre le == s’il s’agit d’un double
symbole = ou c’est un unique symbole == ?
Analyse syntaxique (1/2)
Une fois les mots sont reconnus, l’étape suivante consiste à
connaitre la structure de la phrase :
Identifier le rôle de chacun des mots de la phrase
Grouper les mots dans des structures de plus haut niveau
« La jeune fille repasse la chemise»

% & ' ( '

%
Analyse syntaxique (2/2)
Appelée aussi analyse hiérarchique ou grammaticale
(Parsing)
Il s'agit de vérifier que les unités lexicales sont dans le bon
ordre défini par le langage
Regrouper les unités lexicales en structures grammaticales
Découvrir la structure du programme
L'analyseur syntaxique sait comment doivent être construits les
expressions, les instructions, les déclarations de variables, les
appels de fonctions, …
Exemple: If ( x==y then z=1 ; else z=2 ; Détection d’erreur
L’analyseur syntaxique produit un arbre syntaxique abstrait
Exercice
Analyser syntaxiquement cette phrase:
If ( x==y ) then z=1 ; else z=2 ;
une instruction if-then-else est composée de 3 parties:
Condition: une relation de comparaison entre deux
variables x==y
instruction-THEN: C’est une relation d’affectation z=1
instruction-else: C’est une relation d’affectation z=2
Ainsi la décomposition de la phrase en blocs de mots fait
reconnaitre sa structure.
Il reste à comprendre le sens de la phrase.
Analyse sémantique
Appelée aussi analyse contextuelle
Dans cette phase, on opère certains contrôles (contrôles de type
par exemple) afin de vérifier que l'assemblage des constituants du
programme a un sens.
Cette phase produit un arbre abstrait décoré
Exemples:
Additionner un réel avec une chaine de caractères.
Affecter une variable à un nombre, …
Exemple:
«La jaune fille repasse l’escalier» !!!
Génération de code
Il s'agit de produire les instructions en langage cible.
Le langage cible est dans ce cas défini par le type de processeur
utilisé Il n'est pas alors évident de porter ce compilateur sur une
autre machine cible.
On a introduit des machines dites abstraites qui font
abstraction des architectures réelles existantes.
Le code intermédiaire sera par la suite optimisé et généré en
programme cible
Optimisation
Cette phase vise à améliorer le code produit afin que le
programme résultant soit plus rapide et utilise le minimum de
ressources.
Optimisation qui dépend de la machine cible
Remplacement des instructions générales par d’autres plus
adaptées
Utilisation optimale des registres
Optimisation qui ne dépend pas de la machine cible
Élimination de calculs inutiles (faits en double)
Propagation des constantes
Extraction des boucles des invariants de boucle…
Table des symboles
C’est une structure de données contenant un enregistrement pour
chaque identificateur. Il sert à stocker les informations qui
concernent les identificateurs du programme source.
Pour les attributs, la table de symboles garde:
Son emplacement mémoire, son type, sa portée…
Pour les fonctions, la table de symboles garde:
le nombre et les types de ses arguments
le mode de passage de chacun d'eux et la valeur de retour
Le remplissage de cette table aura lieu lors des phases d’analyse.
Ces informations seront utilisées lors des analyses syntaxique et
sémantique, ainsi que lors de la génération de code.
Gestion parallèle: Table de symbole

#
$

# #

#
#
Gestion parallèle: Erreurs

#
$

# #

#
#
Gestion des erreurs
Chaque phase peut rencontrer des erreurs. Il s’agit de:
Détecter les erreurs et informer le programmeur avec le
maximum de précision (erreur de syntaxe, de sémantique ou
erreur système)
Traiter les erreurs de telle manière que la compilation puisse
continuer et que d’autres erreurs puissent être aussi détectées
Un compilateur qui s’arrête à la première erreur n’est pas trop
performant
Un très grand nombre d’erreurs peuvent entrainer l’arrêt de
l’exécution du compilateur
Regroupement des phases de compilation

#
$

# #

#
#
Partie Frontale
Appelée aussi partie analyse (frontend)
C’est une partie constituée des phases qui dépendent
principalement du langage source.
Elle inclut:
l'analyse lexicale,
l'analyse syntaxique,
l'analyse sémantique,
la création de la table des symboles,
le traitement des erreurs associées à chacune de ces
phases
Partie Dorsale

Appelée aussi partie finale, synthèse (backend)


Elle est constituée des phases qui dépendent de la machine
cible.
Elle inclut:
Production du code intermédiaire,
Optimisation du code intermédiaire,
Production du code final,
Création de la table des symboles,
Traitement des erreurs.
Conclusion
Conclusion
# !
! ) &

# # # !
! )
#
#

#
#
$

! #
!