La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

Génération de code intermédiaire Code intermédiaire Traduction des déclarations Instructions d'affectation Expressions booléennes Appels de procédures.

Présentations similaires


Présentation au sujet: "Génération de code intermédiaire Code intermédiaire Traduction des déclarations Instructions d'affectation Expressions booléennes Appels de procédures."— Transcription de la présentation:

1 Génération de code intermédiaire Code intermédiaire Traduction des déclarations Instructions d'affectation Expressions booléennes Appels de procédures

2 Introduction Constitue la fin de la partie frontale du compilateur Indépendant de la machine cible Avantages de séparer code intermédiaire et code final : possibilité de réutiliser le générateur de code intermédiaire pour des machines différentes possibilité d'améliorer le code intermédiaire indépendamment de la machine cible Peut être incorporé à l'analyseur (traducteur)... code final analyseur syntaxique contrôleur de types générateur de code intermédiaire générateur de code final

3 Code intermédiaire Les arbres syntaxiques sont déjà presque du code intermédiaire Rappel : arbre syntaxique de a := b-4+c - + id num4 id entrée pour b entrée pour c :=.. id entrée pour a. tmp1 := b - 4 tmp2 := tmp1 + c a := tmp2

4 Code intermédiaire Rappel : une grammaire attribuée qui construit l'arbre syntaxique d'une instruction d'affectation S --> id = ES.ptr := makeNode('=', makeLeaf(id, id.entry), E.ptr) E --> E + EE.ptr := makeNode('+', E 1.ptr, E 2.ptr) E --> E * EE.ptr := makeNode('*', E 1.ptr, E 2.ptr) E --> - EE.ptr := makeNode(unaryMinus, E 1.ptr) E --> ( E ) E.ptr := E 1.ptr E --> idE.ptr := makeLeaf(id, id.entry)

5 Code à trois adresses Proche d'un langage d'assemblage Utilise des noms symboliques (ex. add ou +) et des sauts Forme générale :x := y op z où op est un opérateur. Les trois adresses sont celles de x, y et z. Instructions d'affectation x := y op zx := op y Instructions de saut inconditionnelgoto L conditionnelif x relop y goto L Instructions indicées x := y [ i ]x [ i ] := y

6 Exemple Une grammaire attribuée qui engendre du code à trois adresses On utilise des noms temporaires pour l'évaluation des expressions Attributs des noeuds E E.place contient le nom lié à l'adresse qui contiendra la valeur de E à l'exécution E.code contient une séquence d'instructions en code intermédiaire Cette séquence est le résultat de la traduction de E newtemp() engendre un nouveau nom temporaire à chaque appel gen() engendre une instruction de code à trois adresses L'opérateur || représente la concaténation

7 Exemple : expressions S --> id = ES.code := E.code || gen(id.place, ':=', E.place) ; E --> E + EE.place := newtemp() ; E.code := E 1.code || E 2.code || gen(E.place, ':=', E 1.place, '+', E 2.place) ; E --> E * EE.place := newtemp() ; E.code := E 1.code || E 2.code || gen(E.place, ':=', E 1.place, '*', E 2.place) ; E --> - EE.place := newtemp() ; E.code := E 1.code || gen(E.place, ':=', unaryMinus, E 1.place) ; E --> ( E ) E.place := E 1.place ; E.code := E 1.code ; E --> idE.place := id.place ; E.code := ' ' ;

8 Exemple : contrôle S --> while ( E ) SS.begin := newlabel() ; S.after := newlabel() ; S.code := gen(S.begin, ':') || E.code || gen('if', E.place, '=', '0', 'goto', S.after) || S 1.code || gen('goto', S.begin) || gen(S.after, ':') || newlabel() engendre une nouvelle étiquette d'instruction à chaque appel Les étiquettes d'instructions servent de cible pour les sauts

9 Code à trois adresses L'implémentation du code à trois adresses peut se faire par quadruplets op, arg1, arg2, résultat oparg1arg2résultat (0)unaryMinusct1 (1)*bt1t2 (2)unaryMinusct3 (3)*bt3t4 (4)+t2t4t5 (5):=t5a

10 Code à trois adresses ou par triplets op, arg1, arg2 en utilisant les noms des triplets comme nom du résultat oparg1arg2 (0)unaryMinusc (1)*b(0) (2)unaryMinusc (3)*b(2) (4)+(1)(3) (5):=a(4)

11 Traduction des déclarations On suppose que les déclarations sont groupées au début des fonctions Adresses relatives liées aux noms locaux : variable deplct Attributs des noeuds T (type) T.type représente le type simple ou complexe (expression de type) T.taille représente la place mémoire occupée par les objets du type Schéma de traduction

12 Traduction des déclarations P --> { sommet := new Env() ; deplct := 0 ; } D D --> D --> T id ; { sommet.mettre(id.name, T.type, deplct) ; deplct += T.taille ; } D T --> B{ t := B.type ; w := B.taille ; C{ T.type := C.type ; T.taille := C.taille ; } B --> int{ T.type := entier ; T.taille := 4 ; } B --> float{ T.type := flottant ; T.taille := 8 ; } C --> { C.type := t ; C.taille := w ; } C --> [ num ] C {C.type := tableau(num.val, C 1.type) ; C.taille := num.val * C 1.taille ; }

13 Table des symboles Mémorise les informations sur les identificateurs contenus dans le programme À un instant donné de la compilation, et donc à un point donné du code source, la table des symboles indique ce qui est connu sur chaque identificateur du programme dans cet environnement Le contenu de la table des symboles évolue pendant la compilation Quand on entre dans une boucle, on ajoute de nouvelles variables locales, et on les enlève quand on sort

14 Table des symboles Entrées de la table des symboles Correspondent à des déclarations Si un même nom est déclaré plusieurs fois, il a plusieurs entrées Opérations sur la table des symboles - consultation de l'entrée correspondant à un identificateur - insertion d'une nouvelle entrée - empiler/dépiler un bloc d'entrées correspondant à un bloc du code source On peut voir chaque bloc d'entrées comme une table de symboles séparée

15 Traduction des déclarations Une grammaire plus complète avec les fonctions emboîtées (comme en Pascal) ou les blocs (comme en C) On construit une table des symboles séparée pour chaque fonction en-tête0 a x readarray exchange quicksort sort en-têtereadarray en-têteexchange en-tête k v partition quicksort en-tête i j partition

16 Traduction des déclarations new Env() crée une nouvelle table des symboles et renvoie un pointeur sur elle Env.empiler(table) empile une table terminée table.mettre(nom, type, deplct) crée une entrée dans table On utilise deux piles parallèles : Env pour les tables des blocs Depl pour les déplacements correspondants

17 Traduction des déclarations P --> M Bloc M --> εsommet = null ; deplct = 0 ; Bloc --> { N D ; S }sommet = Env.depiler() ; deplct= Depl.depiler() ; N --> εEnv.empiler(sommet) ; sommet = new Env ; Depl.empiler(deplct) ; deplct = 0 ; D --> T id ;sommet.mettre(id.name, T.type, deplct) ; deplct +=T.type ; D D --> ε S --> S I S --> ε I --> Bloc

18 Traduction des déclarations P --> M D ; SaddWidth(top(tables), top(offsets) ; pop(tables) ; pop(offsets) ; M --> εt := makeTable(0) ; push(t, tables) ; push(0, offsets) ; D --> D ; D D --> id ( ) { N D ; S } t := top(tables) ; addWidth(t, top(offsets)); pop(tables) ; pop(offsets) ; enterFunc(top(tables, id.name, t) ; D --> T identer(top(tables), id.name, T.type, top(offsets)) ; top(offsets) +=T.type ; N --> εt := makeTable(top(tables)) ; push(t, tables) ; push(0, offsets) ;

19 Traduction des déclarations On peut ajouter les types structures ou classes T --> struct { L D }T.type := structure(sommet) ; T.taille := deplct ; sommet = Env.depiler() ; deplct= Depl.depiler() ; L --> εEnv.empiler(sommet) ; sommet = new Env() ; Depl.empiler(deplct) ; deplct = 0 ;

20 Traduction des déclarations On peut ajouter les types enregistrements, dont la traduction est très semblable à celle des déclarations de fonctions T --> struct { L D } T.type := struct(top(tables)) ; T.width := top(offsets); pop(tables) ; pop(offsets) ; L --> εt := makeTable(0) ; push(t, tables) ; push(0, offsets) ;

21 Affectations Un schéma de traduction pour traduire les affectations Le schéma crée et utilise des noms temporaires lookup(name) consulte la table des symboles emit() écrit une instruction de code à trois adresses L'ordre des instructions correspond à l'ordre dans lequel elles ont été écrites

22 S --> id = Ep := lookup(id.name) ; if (p) emit(p, ':=', E.place) else error() ; E --> E + EE.place := newtemp() ; emit(E.place, ':=', E 1.place, '+', E 2.place) ; E --> E * EE.place := newtemp() ; emit(E.place, ':=', E 1.place, '*', E 2.place) ; E --> - EE.place := newtemp() ; emit(E.place, ':=', unaryMinus, E 1.place) ; E --> ( E ) E.place := E 1.place ; E --> idp := lookup(id.name) ; if (p) E.place := p else error() ;

23 Affectations Adaptation au cas où les fonctions emboîtées sont autorisées On peut utiliser la grammaire précédente P --> D ; S D --> D ; D D --> id ( ) { D ; S } D --> T id Il faut raffiner la fonction lookup() On consulte d'abord la table de la fonction en cours en utilisant la pile de tables (nom local) Si le nom n'est pas local, on consulte la table de la fonction englobante, etc.

24 Adressage des éléments de tableaux L'adresse de a[i] est base + (i - low) w où low est la borne inférieure des indices, base l'adresse relative du début du tableau et w la taille des éléments En séparant la partie qui peut être calculée à la compilation i w + base - low w Dans une instruction de code à trois adresses x := y [ z ], base - low w correspond à y et i w à z En langage C, a[i] est équivalent à *(a+i) et low = 0 L'expression se réduit à i w + base

25 Tableaux à plusieurs dimensions Les éléments peuvent être rangés - ligne par ligne comme en C et en Pascal (exemple ci-dessous) - colonne par colonne comme en Fortran a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] a[i][j]j = 0j = 1j = 2 i = 0basebase+wbase+2w i = 1base+3wbase+4wbase+5w C'est le dernier indice qui varie le plus vite

26 Tableaux à plusieurs dimensions Dans un tableau à deux dimensions rangé ligne par ligne, les i 1 premières lignes contiennent (i 1 - low 1 ) n 2 éléments, où n 2 est le nombre des valeurs possibles de i 2 Adresse de a[i 1 ][i 2 ] : base + ((i 1 - low 1 ) n 2 + i 2 - low 2 ) w En séparant la partie qui peut être calculée à la compilation (i 1 n 2 + i 2 ) w + base - (low 1 n 2 + low 2 ) w On généralise à k dimensions ((...((i 1 n 2 + i 2 ) n 3 + i 3 )...) n k + i k ) w + base - ((...(low 1 n 2 + low 2 ) n 3 + low 3 )...) n k + low k ) w La deuxième ligne peut être calculée à la compilation

27 Tableaux à plusieurs dimensions En C, la deuxième ligne de la formule se réduit à + base Pour traduire les références de tableau, on engendre le code qui calcule la première ligne e k w avec e k = (...((i 1 n 2 + i 2 ) n 3 + i 3 )...) n k + i k On utilise la récurrence : e 1 = i 1 e k = e k-1 n k + i k

28 Tableaux à plusieurs dimensions Une grammaire simple : L --> id [ Elist ] | id Elist --> Elist ] [ E | E mais l'attribut donnant n k sera hérité : il est obtenu à partir du nom du tableau et transmis de haut en bas par les noeuds Elist Une version où l'attribut est synthétisé : L --> Elist ] | id Elist --> Elist ] [ E | id [ E L'attribut donnant n k obtenu à partir du nom du tableau peut être transmis de bas en haut par les noeuds Elist

29 Tableaux S --> L = E E --> E + E E --> ( E ) E --> L L --> Elist ] L --> id Elist --> Elist ] [ E Elist --> id [ E Attributs L.place est l'entrée dans la table des symboles (contient l'adresse du tableau) L.offset contient l'adresse relative de l'élément dans le tableau

30 S--> L = Eif (L.offset) emit(L.place, '[', L.offset, ']', ':=', E.place) ; else emit(L.place, ':=', E.place) ; E--> E + E... E--> ( E )... E--> Lif (L.offset) { E.place := newtemp() ; emit(E.place, ':=', L.place, '[', L.offset, ']', ) ; } else E.place = L.place ;

31 L--> Elist ]L.place := newtemp() ; L.offset := newtemp() ; emit(L.place, ':=', const(Elist.array)) ; emit(L.offset, ':=', width(Elist.array), '*', Elist.place) ; L--> idL.place := id.place ; L.offset := 0 ; Elist--> Elist ] [ Et := newtemp() ; m := Elist 1.ndim + 1 ; emit(t, ':=', Elist 1.place, '*', limit(Elist 1.array, m)) ; emit(t, ':=', t, '+', E.place) ; Elist.ndim := m ; Elist.array := Elist 1.array ; Elist.place := t ; Elist--> id [ EElist.ndim := 1 ; Elist.array := id.place ; Elist.place := E.place ; const() donne l'adresse du tableau connue à la compilation width() donne wlimit() donne n m

32 Expressions booléennes Les expressions booléennes sont souvent employées dans des conditions d'instructions de contrôle : if, while, for Grammaire E --> E or E | E and E | not E | ( E ) | id relop id | true | false Méthodes de traduction 1. Comme les expressions arithmétiques 2. Par sauts (évaluation paresseuse)

33 Évaluation arithmétique 0 représente faux, 1 représente vrai Code source b or x < y Code intermédiaire 100 :t := b 101 :if x < y goto :u := :goto :u := :v:= t or u Si b vaut vrai, on exécute quand même les instructions 101 et 105

34 Évaluation paresseuse On évite de calculer toutes les sous-expressions quand ce n'est pas nécessaire Dès qu'on peut déduire la valeur de l'expression à partir de la valeur d'une sous-expression, on fait un saut pour court-circuiter la fin Première version On suppose qu'on sait vers quelles étiquettes il faut sauter quand on a trouvé la valeur de E : E.vrai si la valeur est vraie, E.faux si elle est fausse

35 Évaluation paresseuse Dans le code intermédiaire qui traduit E, certaines instructions peuvent contenir un saut inconditionnel vers E.vrai ou vers E.faux E E.vraiE.faux E E E.vrai E.faux E E E.vrai E.faux E --> E and EE --> E or E E.vraiE.fauxE.vraiE.faux

36 Exemple Code sourcea < b or c < d and e < f Code intermédiaire if a < b goto Evrai goto E1 E1:if c < d goto E2 goto Efaux E2: if e < f goto Evrai goto Efaux Difficulté si on ne connaît pas la valeur de Evrai et Efaux

37 Réalisation Problème En général, les instructions cibles E.vrai et E.faux sont engendrées après la traduction de E Solutions 1. Opérer en deux passes Première passe : les étiquettes sont des noms temporaires Deuxième passe : on les remplace par les adresses des instructions 2. Reprise arrière Dans E.vrai, on ne met plus le nom de l'instruction cible, mais la liste des sauts vers l'instruction cible Quand on engendre ces sauts, on les laisse incomplets Dès qu'on connaît le numéro de l'instruction cible, on les complète

38 Reprise arrière : exemple 100 :if a < b goto 101 :goto :if c < d goto :goto 104 :if e < f goto 105 :goto E.v={100,104} E.f={103,105} E.v={100} E.f={101} or and a

39 Reprise arrière makeList(i) crée une nouvelle liste chaînée d'instructions de saut contenant seulement i merge(p, q) concatène deux listes chaînées d'instructions backpatch(p, i) complète les instructions de saut contenues dans la liste p en leur donnant pour cible l'instruction i E.vrai contient la liste des sauts incomplets qui doivent être exécutés dès qu'on sait que E est vraie De même pour E.faux E.vrai et E.faux seront des attributs synthétisés

40 Grammaire On ajoute à la grammaire précédente un marqueur M qui sert à sauvegarder le numéro de la prochaine instruction dans un attribut M.next Ce numéro est conservé dans next, mis à jour par emit() E --> E or M E E --> E and M E E --> not E E --> ( E ) E --> id relop id E --> true E --> false M --> ε

41 Schéma de traduction E--> E or M Ebackpatch(E 1.f, M.next) ; E.f := E 2.f ; E.v := merge(E 1.v, E 2.v) ; E--> E and M Ebackpatch(E 1.v, M.next) ; E.v := E 2.v ; E.f := merge(E 1.f, E 2.f) ; E--> not EE.v := E 1.f ; E.f := E 1.v ; E--> ( E )E.v := E 1.v ; E.f := E 1.f ; E--> id relop idE.v := makeList(next) ; emit('if', id 1.place, relop, id 2.place, 'goto') ; E.f := makeList(next) ; emit('goto') ; E--> trueE.v := makeList(next) ; emit('goto') ; E--> falseE.f := makeList(next) ; emit('goto') ; M--> εM.next = next ;

42 Instructions de contrôle S--> if ( E ) S | if ( E ) S else S | while ( E ) S E.code S 1.code si-alors... E.vE.f si-alors-sinon... E.vE.f E.code S 1.code goto S 2.code... E.code S 1.code goto tant que... E.v E.f...

43 Instructions de contrôle S--> if ( E ) S | if ( E ) S else S | while ( E ) S | { L } | A L--> L ; S | S A représente une affectation Une instruction S peut contenir des sauts vers la première instruction à exécuter après S Ces sauts sont engendrés incomplets et listés dans S.suivant De même pour L et L.suivant On ajoute des marqueurs M et N

44 Schéma de traduction S --> if ( E ) M Sbackpatch(E.v, M.next) ; S.suivant := merge(E.f, S 1.suivant) ; M --> εM.next := next ; S --> while ( M E ) M Sbackpatch(E.v, M 2.next) ; backpatch(S 1.suivant, M 1.next) ; S.suivant := E.f ; emit('goto', M 1.next) ; S --> if ( E ) M S else N Sbackpatch(E.v, M.next) ; backpatch(E.f, N.next) ; S.suivant := merge(S 1.suivant, N.suivant, S 2.suivant) ; N --> εN.suivant := makeList(next) ; emit('goto') ; N.next := next ;

45 Schéma de traduction S --> { L }S.suivant := L.suivant ; S --> AS.suivant := makelist() ; L --> L ; M Sbackpatch(L.suivant, M.next) ; L.suivant := S.suivant ; L --> SL.suivant := S.suivant ;

46 Exemple Code sourcewhile (a < b) if (c < d) x = y + z ; Code intermédiaire 100 :if a < b goto :goto :if c < d goto :goto :t := y + z 105 :x := t 106 :goto :


Télécharger ppt "Génération de code intermédiaire Code intermédiaire Traduction des déclarations Instructions d'affectation Expressions booléennes Appels de procédures."

Présentations similaires


Annonces Google