Une fois cette archive décompressée (par exemple avec tar zxvf ertl-java.tar.gz), vous obtenez un répertoire mini_c/ contenant plusieurs fichiers Java :
ERTL.java | Langage ERTL (Explicit Register Transfer Language) |
ERTLinterp.java | Interprète de code ERTL (pour tester) |
Ces classes sont complètes et documentées ici. On prendra le temps de bien comprendre le code fourni.
On pourra récupérer ce nouveau Main.java qui ajoute au compilateur une option --interp-ertl pour exécuter le code ERTL et qui affiche le code ERTL quand on passe l'option --debug.
On suggère très fortement de procéder construction par construction, en testant systématiquement à chaque fois.
La seule exception est le cas d'une instruction Rmbinop(Mdiv, r1, r2, l) pour laquelle il faut placer r2 dans %rax avant de faire la division, puis récupérer le résultat dans %rax pour le mettre dans r2. Ne pas traiter le cas de l'instruction Rcall pour l'instant.
Traduire ensuite une fonction RTL vers une fonction ERTL, en se contentant pour l'instant de traduire chaque instruction. Comme pour la traduction vers RTL, on pourra stocker le graphe de flot de contrôle en cours de construction dans une variable globale. Ajouter dans ce graphe une instruction ERreturn au niveau de l'étiquette de sortie de la fonction.
Tester avec le programme :
int main() { return 42; }On doit obtenir quelque chose comme :
=== ERTL ================================================= main(0) entry : L2 locals: [] L2: mov $42 #1 --> L1 L1: returnTester avec d'autres programmes plus complexes (mais sans appel de fonction pour l'instant).
Tester avec des programmes comme
int fact(int n) { if (n <= 1) return 1; return n * fact(n-1); } int main() { return fact(42); }Au niveau de l'appel fact(42), par exemple, on doit obtenir quelque chose comme
L3: mov $42 #2 --> L2 L2: goto L16 L16: mov #2 %rdi --> L15 L15: call fact(1) --> L14 L14: mov %rax #1 --> L1(un seul argument, passé dans %rdi).
Tester avec des programmes comme
int fact(int n) { if (n <= 1) return 1; return n * fact(n-1); } int main() { return fact(42); }Pour la fonction fact, par exemple, on doit obtenir quelque chose comme
fact(1) entry : L31 locals: [#11, #12] L31: alloc_frame --> L30 L30: mov %rbx #11 --> L29 L29: mov %r12 #12 --> L28 L28: mov %rdi #3 --> L13 ... L4: goto L36 L36: mov #4 %rax --> L35 L35: mov #11 %rbx --> L34 L34: mov #12 %r12 --> L33 L33: delete_frame --> L32 L32: returnLes pseudo-registres #11 et #12 sont ici utilisés pour sauvegarder les registres callee-saved (limités ici à %rbx et %r12 pour simplifier).
On teste comme pour le code RTL :
> ./run -i "mon/chemin/vers/mini-c --interp-ertl" Test de mon/chemin/vers/mini-c --interp-ertl Interprétation Execution normale ----------------- ........................................................... Execution conduisant à un échec ------------------------------- ... Compilation: Compilation : 62/62 : 100% Comportement du code : 62/62 : 100%
class Liveness { Map<Label, LiveInfo> info; Liveness(ERTLgraph g) { ... } }qui prend en argument un graphe de flot de contrôle ERTL et construit un dictionnaire qui associe à chaque étiquette une information du type suivant :
class LiveInfo { ERTL instr; Label[] succ; // successeurs Set<Label> pred; // prédécesseurs Set<Register> defs; // définitions Set<Register> uses; // utilisations Set<Register> ins; // variables vivantes en entrée Set<Register> outs; // variables vivantes en sortie }On pourra procéder ainsi :
private void print(Set<Label> visited, Label l) { if (visited.contains(l)) return; visited.add(l); LiveInfo li = this.info.get(l); System.out.println(" " + String.format("%3s", l) + ": " + li); for (Label s: li.succ) print(visited, s); } void print(Label entry) { print(new HashSet<Label>(), entry); }et d'une méthode toString appropriée dans la classe LiveInfo.
Avec un programme comme
int main() { return 42; }on doit obtenir quelque chose comme
L5: alloc_frame --> L4 d={} u={} i={%r12, %rbx} o={%r12, %rbx} L4: mov %rbx #2 --> L3 d={#2} u={%rbx} i={%r12, %rbx} o={#2, %r12} L3: mov %r12 #3 --> L2 d={#3} u={%r12} i={#2, %r12} o={#2, #3} L2: mov $42 #1 --> L1 d={#1} u={} i={#2, #3} o={#1, #2, #3} L1: goto L10 d={} u={} i={#1, #2, #3} o={#1, #2, #3} L10: mov #1 %rax --> L9 d={%rax} u={#1} i={#1, #2, #3} o={#2, #3, %rax} L9: mov #2 %rbx --> L8 d={%rbx} u={#2} i={#2, #3, %rax} o={#3, %rax, %rbx} L8: mov #3 %r12 --> L7 d={%r12} u={#3} i={#3, %rax, %rbx} o={%r12, %rax, %rbx} L7: delete_frame --> L6 d={} u={} i={%r12, %rax, %rbx} o={%r12, %rax, %rbx} L6: return d={} u={%r12, %rax, %rbx} i={%r12, %rax, %rbx} o={}Tester ensuite avec des programmes plus complexes.