Vous êtes sur la page 1sur 76

Paris-Sud Universite

` la compilation Introduction a
Sylvain Conchon Cours 1 / 5 septembre 2013

Presentation du cours
cours le jeudi, 10h4512h45 en petit amphi (PUIO)
mais transparents disponibles pas de polycopie,

TD
le lundi, 14h0016h30, avec Sylvain Conchon le mercredi, 8h3011h00, avec Kim Nguyen

evaluation
continu + examen partiel + controle

toutes les infos sur le site web du cours

http://www.lri.fr/~conchon/compilation/

Remerciements

` Jean-Christophe Filliatre ` a pour le materiel de son cours de compilation a lENS Ulm

Objectif du cours

` ma triser les mecanismes de la compilation, cest-a-dire de la transformation dun langage dans un autre comprendre les differents aspects des langages de programmation par le biais de la compilation

Les techniques vues dans ce cours sont aussi . . .


. . . utiles pour concevoir des outils qui manipulent les programmes de ` symbolique, comme les outils de maniere preuve de programmes (VCC, Dafny, Spec#, Frama-C, Spark, GNATProve, Why3, Boogie, etc.) verication par model checking (Slam, Spin, CBMC, Murphi, Cubicle, Blast, Uppaal, Java Pathnder, etc.) polyspace, etc.) analyse par interpretation abstraite (Astree, demonstration automatique (z3, cvc4, Alt-Ergo, etc.), test formel (Pex, PathCrowler, etc.) etc.

Programmation

ici on programme en cours en TD pour realiser le projet ` lexamen a on programme en Objective Caml

Un peu de lecture

Compilation

schematiquement, un compilateur est un programme qui traduit un programme dun langage source vers un langage cible, en signalant deventuelles erreurs

langage source

compilateur erreurs

langage cible

Compilation vers le langage machine


` la traduction dun quand on parle de compilation, on pense typiquement a langage de haut niveau (C, Java, Caml, ...) vers le langage machine dun processeur (Intel Pentium, PowerPC, ...)

% gcc -o sum sum.c source sum.c compilateur C (gcc) executable sum

int main(int argc, char **argv) { int i, s = 0; for (i = 0; i <= 100; i++) s += i*i; printf("0*0+...+100*100 = %d\n", s); }

00100111101111011111111111100000 10101111101111110000000000010100 10101111101001000000000000100000 10101111101001010000000000100100 10101111101000000000000000011000 10101111101000000000000000011100 10001111101011100000000000011100 ...

Langage cible
` la compilation dans ce cours, nous allons effectivement nous interesser a vers de lassembleur, mais ce nest quun aspect de la compilation un certain nombre de techniques mises en uvre dans la compilation ne a ` la production de code assembleur sont pas liees certains langages sont dailleurs es (Basic, COBOL, Ruby, etc.) interpret dans un langage intermediaire e compiles qui est ensuite interpret (Java, Caml, etc.) vers un autre langage de haut niveau compiles a ` la volee compiles

10

` Difference entre compilateur et interprete

un compilateur traduit un programme P en un programme Q tel que x, la sortie de Q(x) soit la meme pour toute entree que celle de P (x)

P Q x...

un programme P et une ` est un programme qui, etant un interprete donne x, calcule la sortie s de P (x) entree

P x s...

11

` Difference entre compilateur et interprete

dit autrement, le compilateur fait un travail complexe une seule fois, pour produire un code fonctionnant pour nimporte quelle entree ` effectue un travail plus simple, mais le refait sur chaque entree linterprete

est gen eralement autre difference : le code compile bien plus efcace que e le code interpret

12

Exemple de compilation et dinterpretation


source lilypond chier PostScript gs image

<< \chords { c2 c f2 c } \new Staff \relative c { \time 2/4 c4 c g4 g a4 a g2 } \new Lyrics \lyricmode { twin4 kle twin kle lit tle star2 >>

2 4
twin kle twin kle lit tle star
13

dun compilateur Qualite

` quoi juge-t-on la qualite dun compilateur ? A

` sa correction a ` lefcacite du code quil produit a ` sa propre efcacite a

14

Phases dun compilateur

typiquement, le travail dun compilateur se compose dune phase danalyse


` traduire et sa signication reconna t le programme a signale les erreurs et peut donc echouer de typage, etc.) (erreurs de syntaxe, de portee,

` puis dune phase de synthese


production du langage cible utilise de nombreux langages intermediaires nechoue pas

15

Phase danalyse

source

analyse lexicale

` suite de lexemes (tokens)

analyse syntaxique

arbre de syntaxe abstraite (AST )

analyse semantique

syntaxe abstraite + table des symboles

16

` Phase de synthese

syntaxe abstraite

production de code (nombreuses phases)

langage assembleur

assembleur (as)

langage machine

editeur de liens (ld)

code executable

17

lassembleur MIPS

(voir la documentation sur la page du cours)

18

Un peu darchitecture

` schematiquement, tres un ordinateur est compose de calcul (CPU), contenant dune unite
un petit nombre de registres entiers ou ottants de calcul des capacites

dune memoire vive (RAM)


dun tres ` grand nombre doctets (8 bits) composee 33 par exemple, 1 Go = 230 octets = 233 bits, soit 22 etats possibles et des instructions contient des donnees

19

Un peu darchitecture
RAM

CPU

$pc 0000052

$a0 0000012 $a1 0000040 $a2 0000022 $a3 0000000 $v0 0000000 ...

` a ` la memoire ` un milliard dinstructions par seconde, lacces coute cher (a ` ne parcourt que 30 centimetres ` la lumiere entre 2 instructions !)
20

Un peu darchitecture

e est bien plus complexe la realit es aux ottants plusieurs (co)processeurs, dont certains dedi un ou plusieurs caches une virtualisation de la memoire (MMU) etc.

21

Principe dexecution

schematiquement, lexecution dun programme se deroule ainsi ` executer un registre ($pc) contient ladresse de linstruction a ` cette adresse (fetch) on lit les 4 (ou 8) octets a ` ces bits comme une instruction (decode) on interprete on execute linstruction (execute) ` linstruction suivante on modie le registre $pc pour passer a ` sauf en cas de saut) (typiquement celle se trouvant juste apres,

22

Principe dexecution
RAM

CPU

$pc 0000052

$a0 0000012 $a1 0000040 $a2 0000022 $a3 0000000 $v0 0000000 ...

instruction : 000000 00001 00010 0000000000001010 decodage : add $a1 $a2 10 i.e. ajouter 10 au registre $a1 et stocker le resultat dans le registre $a2
23

Principe dexecution

` encore la realit e est bien plus complexe la pipelines


ees en parallele ` plusieurs instructions sont execut

prediction de branchement
pour optimiser le pipeline, on tente de predire les sauts conditionnels

24

Quelle architecture pour ce cours ?


deux grandes familles de microprocesseurs CISC (Complex Instruction Set )
beaucoup dinstructions beaucoup de modes dadressage beaucoup dinstructions lisent / ecrivent en memoire peu de registres exemples : VAX, PDP-11, Motorola 68xxx, Intel x86

RISC (Reduced Instruction Set )


` peu dinstructions, reguli eres ` peu dinstructions lisent / ecrivent tres en memoire beaucoup de registres, uniformes exemples : Alpha, Sparc, MIPS, ARM

on choisit MIPS pour ce cours (les TD et le projet)

25

Larchitecture MIPS

` r31 32 registres, r0 a r0 contient toujours 0


` des conventions utilisables sous dautres noms, correspondant a (zero, at, v0v1, a0a3, t0t9, s0s7, k0k1, gp, sp, fp, ra)

trois types dinstructions


instructions de transfert, entre registres et memoire instructions de calcul instructions de saut

documentation : sur le site du cours

26

Simulateurs MIPS
en pratique, on utilisera un simulateur MIPS, MARS (ou SPIM) en ligne de commande

java -jar Mars 4 2.jar file.s


en mode graphique et interactif

java -jar Mars 4 2.jar


charger le chier et lassembler ` pas, visualisation des registres, de la memoire, mode pas a etc. documentation : sur le site du cours

27

Jeu dinstructions : constantes, adresses et copies

dans un registre chargement dune constante (16 bits signee)

li lui

$a0, 42 $a0, 42

# a0 <- 42 # a0 <- 42 * 2^16

copie dun registre dans un autre

move $a0, $a1

# copie a1 dans a0 !

28

Jeu dinstructions : arithmetique


addition de deux registres

add add

$a0, $a1, $a2 $a2, $a2, $t5

# a0 <- a1 + a2 # a2 <- a2 + t5

de meme, sub, mul, div addition dun registre et dune constante

addi $a0, $a1, 42


(mais pas subi, muli ou divi !) negation

# a0 <- a1 + 42

neg
valeur absolue

$a0, $a1

# a0 <- -a1

abs

$a0, $a1

# a0 <- |a1|
29

Jeu dinstructions : operations sur les bits


NON logique (not(1001112 ) = 0110002 )

not

$a0, $a1

# a0 <- not(a1)

ET logique (and(1001112 , 1010012 ) = 1000012 )

and $a0, $a1, $a2 # a0 <- and(a1, a2) andi $a0, $a1, 0x3f # a0 <- and(a1, 0...0111111)

OU logique (or(1001112 , 1010012 ) = 1011112 )

or ori

$a0, $a1, $a2 $a0, $a1, 42

# a0 <- or(a1, a2) # a0 <- or(a1, 0...0101010)

30

Jeu dinstructions : decalages


` gauche (insertion de zeros) decalage a

sll $a0, $a1, 2 sllv $a1, $a2, $a3

# a0 <- a1 * 4 # a1 <- a2 * 2^a3

` droite arithmetique decalage a (copie du bit de signe)

sra

$a0, $a1, 2

# a0 <- a1 / 4

` droite logique (insertion de zeros) decalage a

srl

$a0, $a1, 2

rotation

rol ror

$a0, $a1, 2 $a0, $a1, 3


31

Jeu dinstructions : tests

comparaison de deux registres

slt

$a0, $a1, $a2

# a0 <- 1 si a1 < a2 # 0 sinon

ou dun registre et dune constante

slti $a0, $a1, 42


variantes : sltu (comparaison non signee), sltiu de meme : sle, sleu / sgt, sgtu / sge, sgeu : seq, sne egalit e

32

Jeu dinstructions : transfert (lecture)

lire un mot (32 bits) en memoire

lw

$a0, 42($a1)

# a0 <- mem[a1 + 42]

par un registre et un decalage ladresse est donnee sur 16 bits signes ou non (lb, lh, lbu, lhu) variantes pour lire 8 ou 16 bits, signes

33

Jeu dinstructions : transfert (ecriture)

ecrire un mot (32 bits) en memoire

sw

$a0, 42($a1)

# mem[a1 + 42] <- a0 # attention au sens !

par un registre et un decalage ladresse est donnee sur 16 bits signes variantes pour ecrire 8 ou 16 bits (sb, sh)

34

Jeu dinstructions : branchements et sauts

on distingue branchement : typiquement un saut conditionnel, dont le sur 16 bits signes (-32768 a ` 32767 deplacement est stocke instructions) saut : saut inconditionnel, dont ladresse de destination est stockee sur 26 bits

35

Jeu dinstructions : branchements

branchement conditionnel

beq

$a0, $a1, label # si a0 = a1 saute ` a label # ne fait rien sinon

variantes : bne, blt, ble, bgt, bge (et comparaisons non signees) variantes : beqz, bnez, bgez, bgtz, bltz, blez

36

Jeu dinstructions : sauts


saut inconditionnel ` une adresse (jump) a

label

avec sauvegarde de ladresse de linstruction suivante dans $ra

jal

label

# jump and link

` une adresse contenue dans un registre a

jr

$a0

avec ladresse contenue dans $a0 et sauvegarde dans $a1

jalr $a0, $a1


37

` Jeu dinstructions : appel systeme


` quelques appels systeme fournis par une instruction speciale

syscall

le code de linstruction doit etre dans $v0, les arguments dans $a0$a3 ; dans $v0 le resultat eventuel sera place ` exemple : appel systeme print int pour afcher un entier

li $v0, 1 li $a0, 42 syscall

# code de print_int # valeur ` a afficher

de meme read int, print string, etc. (voir la documentation)


38

Assembleur MIPS

on ne programme pas en langage machine mais en assembleur : lassembleur fourni un certain nombre de facilites etiquettes symboliques globales allocation de donnees pseudo-instructions

39

Assembleur MIPS
la directive

.text
indique que des instructions suivent, et la directive

7FFFFFFC16

.data
suivent indique que des donnees a ` partir de ladresse 0x400000 le code sera charge a ` partir de ladresse 0x10000000 et les donnees une etiquette symbolique est introduite par
1000000016

. . . donn ees code


40000016

(r eserv e)

label:
dans un registre et ladresse quelle represente peut etre chargee

la

$a0, label
40

Exemple : hello world

main:

hw:

.text li la syscall li syscall .data .asciiz

$v0, 4 $a0, hw $v0, 10

# # # #

code de print_string adresse de la cha^ ne appel syst` eme exit

"hello world\n"

pour .byte 104, 101, ... 0) (.asciiz est une facilite

41

de la compilation Le de

cest de traduire un programme dun langage de haut niveau vers ce jeu dinstructions en particulier, il faut (tests, boucles, exceptions, etc.) traduire les structures de controle traduire les appels de fonctions complexes (tableaux, traduire les structures de donnees enregistrements, objets, clotures, etc.) allouer de la memoire dynamiquement

42

Appels de fonctions

constat : les appels de fonctions peuvent etre arbitrairement imbriques ` les registres ne peuvent sufre pour les parametres / variables locales il faut allouer de la memoire pour cela ` ` les fonctions procedent selon un mode last-in rst-out, cest-a-dire de pile

43

La pile

pile donn ees dynamiques (tas) donn ees statiques code

tout en haut, et cro la pile est stockee t dans le sens des adresses decroissantes ; $sp pointe sur le sommet de la pile dynamiques (survivant aux appels de les donnees sur le tas (eventuellement fonctions) sont allouees par un GC), en bas de la zone de donnees, juste au dessus des donnees statiques ainsi, on ne se marche pas sur les pieds

44

Appel de fonction
lorsquune fonction f (lappelant ou caller ) souhaite appeler une fonction g ou callee), elle execute (lappele

jal

en a termine, il lui rend le controle avec et lorsque lappele

jr

$ra

` probleme : si g appelle elle-meme une fonction, $ra sera ecras e par g sera perdu pour f de meme, tout registre utilise ` il existe de multiples manieres de sen sortir, eral on saccorde sur des conventions dappel mais en gen
45

Conventions dappel
utilisation des registres a ` lassembleur et lOS $at, $k0 et $k1 sont reserv es pour passer les quatre premiers arguments (les $a0$a3 sont utilises sur la pile) et $v0$v1 pour renvoyer le resultat autres sont passes

$t0$t9 sont des registres caller-saved i.e. lappelant doit les


qui sauvegarder si besoin ; on y met donc typiquement des donnees nont pas besoin de survivre aux appels doit les $s0$s7 sont des registres callee-saved i.e. lappele de duree de vie longue, sauvegarder ; on y met donc des donnees ayant besoin de survivre aux appels

$sp est le pointeur de pile, $fp le pointeur de frame $ra contient ladresse de retour
statiques (1000800016 ) $gp pointe au milieu de la zone de donnees
46

Lappel, en quatre temps

il y a quatre temps dans un appel de fonction


1 2 3 4

pour lappelant, juste avant lappel au debut pour lappele, de lappel a ` la n de lappel pour lappele, ` lappel pour lappelant, juste apres

au sommet de la pile appele le sorganisent autour dun segment situe entre $fp et $sp tableau dactivation, en anglais stack frame, situe

47

Lappelant, juste avant lappel

passe les arguments dans $a0$a3, les autres sur la pile sil y en a plus de 4 ` lappel sauvegarde les registres $t0$t9 quil compte utiliser apres (dans son propre tableau dactivation) execute

jal

appel e

48

au debut Lappele, de lappel


... argument 5 argument 6 registres sauv es variables locales $sp
3

alloue son tableau dactivation, par exemple

addi
2

$sp, $sp, -28

$fp

sauvegarde $fp puis le positionne, par exemple

sw addi

$fp, 24($sp) $fp, $sp, 24

sauvegarde $s0$s7 et $ra si besoin

$fp permet datteindre facilement les arguments et variables locales, avec


un decalage xe quel que soit letat de la pile

49

a ` la n de lappel Lappele,

1 2 3

place le resultat dans $v0 (voire $v1) restaure les registres sauvegardes depile son tableau dactivation, par exemple

addi $sp, $sp, 28


4

execute

jr

$ra

50

` lappel Lappelant, juste apres

1 2

depile les eventuels arguments 5, 6, ... restaure les registres caller-saved

51

Exercice

exercice : programmer la fonction factorielle

52

Recapitulation

une machine fournit


dinstructions, tres ` primitives un jeu limite ` couteux ` la memoire des registres efcaces, un acces a

en la memoire est decoup ee


statiques / tas (donnees dynamiques) / pile code / donnees

les appels de fonctions sarticulent autour


dune notion de tableau dactivation de conventions dappel

53

Un exemple de compilation

t(a,b,c){int d=0,e=a&~b&~c,f=1;if(a) for(f=0;d=(e-=d)&-e;f+=t(a-d,(b+d)*2, (c+d)/2));return f;}main(q){scanf("%d", &q);printf("%d\n",t(~(~0<<q),0,0));}

54

Clarication

int t(int a, int b, int c) { int d=0, e=a&~b&~c, f=1; if (a) for (f=0; d=(e-=d)&-e; f+=t(a-d, (b+d)*2, (c+d)/2)); return f; } int main() { int q; scanf("%d", &q); printf("%d\n", t(~(~0<<q), 0, 0)); }

55

Clarication (suite)
int t(int a, int b, int c) { int f=1; if (a) { int d, e=a&~b&~c; f = 0; while (d=e&-e) { f += t(a-d, (b+d)*2, (c+d)/2); e -= d; } } return f; } int main() { int q; scanf("%d", &q); printf("%d\n", t(~(~0<<q), 0, 0)); }
ce programme calcule le nombre de solutions ` du probleme dit des n reines

q q q q q

q q q
56

Comment c a marche ?
recherche par force brute (backtracking ) comme des ensembles : entiers utilises par ex. 13 = 0 011012 = {0, 2, 3} entiers ensembles

0 a&b a+b a-b ~a a&-a ~(~0<<n) a*2 a/2

ab a b, quand a b = a\ b, quand b a a min (a), quand a = {0, 1, . . . , n 1} S (a) {i + 1 | i a}, note P (a) {i 1 | i a i = 0}, note
57

Clarication : version ensembliste


int t(a, b, c) f 1 if a =

e (a\b)\c f 0 while e = d min (e) f f + t(a\{d}, S (b {d}), P (c {d})) e e\{d} return f


int queens(n) return t({0, 1, . . . , n 1}, , )

58

Signication de a, b et c

q q

? ? ? ? ? ? ? ?

59

Signication de a, b et c

q qqq q q

q q

` remplir = 111001012 a = colonnes a

59

Signication de a, b et c

q qq q q

` cause de diagonales vers la gauche = 011010002 b = positions interdites a

59

Signication de a, b et c

q q q

q q

` cause de diagonales vers la droite = 000010012 c = positions interdites a

59

Signication de a, b et c

q q q q

` essayer = 100001002 a&~b&~c = positions a

59

et de ce programme pour la compilation Inter


int t(int a, int b, int c) { int f=1; if (a) { int d, e=a&~b&~c; f = 0; while (d=e&-e) { f += t(a-d,(b+d)*2,(c+d)/2); e -= d; } } return f; } int main() { int q; scanf("%d", &q); printf("%d\n", t(~(~0<<q), 0, 0)); }

court, mais contient un test (if) une boucle (while) une fonction recursive quelques calculs cest aussi une excellente solution ` au probleme des n reines

60

Compilation

commenc ons par la fonction recursive t ; il faut allouer les registres compiler
le test la boucle lappel recursif les differents calculs

61

Allocation de registres
dans $a0, $a1 et $a2 a, b et c sont passes dans $v0 le resultat est renvoye ` les parametres a, b, c, tout comme les variables locales d, e, f, ont car ils sont utilises apres ` lappel recursif besoin detre sauvegardes, on va utiliser des registres callee-save

a b c d e f

$s0 $s1 $s2 $s3 $s4 $s5

il faut donc allouer 7 mots sur la pile pour sauvegarder ladresse de retour $ra et ces 6 registres
62

Sauvegarde / restauration des registres


t: addi sw sw sw sw sw sw sw ... lw lw lw lw lw lw lw addi jr $ra $sp, $ra, $s0, $s1, $s2, $s3, $s4, $s5, $ra, $s0, $s1, $s2, $s3, $s4, $s5, $sp, $sp, -28 24($sp) 20($sp) 16($sp) 12($sp) 8($sp) 4($sp) 0($sp) 24($sp) 20($sp) 16($sp) 12($sp) 8($sp) 4($sp) 0($sp) $sp, 28 # allocation de 7 mots # sauvegarde des registres

# restauration des registres

# d esallocation

63

Compilation du test

int t(int a, int b, int c) { int f=1; if (a) { ... } return f; }

li beqz ... t return: move

$s5, 1 $a0, t return

$v0, $s5

64

eral (a = 0) Cas gen

if (a) { int d, e=a&~b&~c; f = 0; while ... }

move move move move not and not and li

$s0, $s1, $s2, $s4, $t0, $s4, $t0, $s4, $s5,

$a0 # sauvegarde $a1 $a2 $s0 # e=a&~b&~c $s1 $s4, $t0 $s2 $s4, $t0 0 # f = 0

noter lutilisation dun registre temporaire $t0 non sauvegarde

65

Compilation de la boucle

L1:

while (test) { body }

L2:

... ... calcul de test dans $t0 ... beqz $t0, L2 ... body ... j L1 ...

66

Compilation de la boucle
il existe cependant une meilleure solution

L1: while (test) { body }

L2:

... j L2 ... body ... ... test ... bnez $t0, L1

ainsi on fait seulement un seul branchement par tour de boucle ` part la toute premiere ` fois) (mis a
67

Compilation de la boucle
j body: sub add sll add srl jal add sub test: neg $t0, $s4 # d=e&-e and $s3, $s4, $t0 bnez $s3, body t return: ...
68

test $a0, $a1, $a1, $a2, $a2, t $s5, $s4, $s0, $s1, $a1, $s2, $a2, $s3 # a-d $s3 # (b+d)*2 1 $s3 # (c+d)/2 1

while (d=e&-e) { f += t(a-d, (b+d)*2, (c+d)/2); e -= d; }

$s5, $v0 $s4, $s3 # e -= d

Programme principal
main: li $v0, syscall li $a0, not $a0, sllv $a0, not $a0, int main() { li $a1, int q; li $a2, scanf("%d", &q); jal t printf("%d\n", move $a0, t(~(~0<<q), 0, 0)); li $v0, } syscall li $v0, la $a0, syscall li $v0, syscall 5 # read_int

0 # t(...) $a0 $a0, $v0 $a0 0 0 $v0 # printf 1 4 newline 10 # exit

69

Optimisation

ce code nest pas optimal par exemple, dans le cas a = 0, on sauve/restaure inutilement les registres (y compris $ra), alors quon pourrait se contenter de

li jr

$v0, 1 $ra

70

Lec on

produire du code assembleur efcace nest pas chose aisee (observer le code produit avec gcc -S -fverbose-asm ou encore ocamlopt -S) maintenant il va falloir automatiser tout ce processus

71

La semaine prochaine

TD
eration gen de code pour un mini-langage dexpressions arithmetiques

Cours
analyse lexicale

72

Vous aimerez peut-être aussi