710 likes | 847 Views
Code Generation. Compiler Baojian Hua bjhua@ustc.edu.cn. Middle and Back End. translation. AST. IR1. translation. IR2. other IR and translation. asm. Back-end Structure. instruction selector. IR. Assem. register allocator. TempMap. instruction scheduler. Assem. Recap.
E N D
Code Generation Compiler Baojian Hua bjhua@ustc.edu.cn
Middle and Back End translation AST IR1 translation IR2 other IR and translation asm
Back-end Structure instruction selector IR Assem register allocator TempMap instruction scheduler Assem
Recap • What about “CODE”? CODE DATA Procedures Global Static Variables Global Dynamic Data Control Flow Local Variables Temporaries Statements Parameter Passing Data Access Read-only Data
A Simpler Target ISA • To simplify the discussion, let’s start with a much simpler ISA---a stack machine • Stack machines once were very popular in the history • but not today, for its low speed • but we’d like to discuss it for: • generating code for stack machine is simpler • many (virtual) stack machines are in widely use today • Pascal P code • Java byte code • Postscript • …
Code Generation for Stack Machines
Stack Machine • Stack-based • no registers • ALU operates the stack and the memory • stack for expression calculation and function call (also called operand stack on JVM) Memory Stack ALU the stack: … Control
Stack Machine ISA // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret A subset of the Java virtual machine language (JVML)! stack operations memory access Memory arithmetic Stack ALU function call and return Control
Frame and Stack Each function comes with two storages: frame and stack • frame: holding arguments, locals and control • stack: computation // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … stack after: Control … 3
ISA Semantics: push push NUM: top++; stack[top] = NUM; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … after: … 3 Control
ISA Semantics: pop pop x: x = stack[top]; top--; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … 3 after: … 3 Control
ISA Semantics: unwind unwind n: top -= n; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … v v … v after: … Control
ISA Semantics: load load x: top++; stack[top] = x; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x x y … before: Stack ALU … after: … Control
ISA Semantics: store store x: x = stack[top]; top--; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … v after: … Control
ISA Semantics: add add: temp = stack[top-1] +stack[top]; top -= 2; push temp; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … 5 1 after: … 6 Control
ISA Semantics: sub sub: temp = stack[top-1] -stack[top]; top -= 2; push temp; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … 5 1 after: … 4 Control
ISA Semantics: mult sub: temp = stack[top-1] *stack[top]; top -= 2; push temp; // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret Memory frame: x y … before: Stack ALU … 5 2 after: … 10 Control
ISA Semantics: call call f: // create a new frame for f // pop all arguments to f’s // frame // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret frame: frame for f: x y … m n … before: before(empty): … 5 2 after: …
ISA Semantics: ret ret: // pop callee’s value and // push it onto the // caller’s stack top // ISA syntax s -> push NUM | pop x | unwind n | load x | store x | add | sub | mult | div | call f | ret frame: frame for f: x y … m n … before: before: … … … … v after: after(empty): …
Extended SLP // Extending SLP with functions: (* is the Kleen // closure) prog -> func* func -> id (x1, …, xn){ s } s -> s; s | x := e | print (es) | return e e -> n | x | e+e | e-e | e*e | e/e | f(es) es-> e, es | \eps
Sample Programs main (){ m := 10; n := 5; z := plus (m, n); print (z); } plus (x, y){ t = x+y; return t; }
Recursive Decedent Code Generation // Invariant: expression’s value is on stack top gen_s (s1; s2) = gen_s (s1); gen_s (s2); gen_s (x := e) = gen_e (e); “store x” gen_s (print (es)) = gen_es (es); “call print” gen_s (return e) = gen_e (e); “ret” gen_e (n) = “push n” gen_e (x) = “load x” gen_e (e1+e2) = gen_e (e1); gen_e (e2); “add” gen_e (…) // similar for -, *, / gen_e (f(es)) = gen_es(es); “call f” gen_es (e; es) = gen_e (e); gen_es (es)
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret main (){ m := 10; n := 5; z := plus (m, n); print (z); } plus (x, y){ t := x+y; return t; }
Example pc 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m n z operand stack(empty) :
Example pc 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m n z operand stack: 10
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret pc frame for main: m n z operand stack: 10
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret pc frame for main: m 10 n z operand stack:
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret pc frame for main: m 10 n z operand stack: 5
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret pc frame for main: m 10 n z operand stack: 5
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret pc frame for main: m 10 n 5 z operand stack:
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret pc frame for main: m 10 n 5 z operand stack: 10
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: pc m 10 n 5 z operand stack: 10 5
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z pc operand stack: 10 5 frame for plus: x y t operand stack:
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z pc operand stack: frame for plus: x 10 y 5 t operand stack: 10
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z operand stack: frame for plus: pc x 10 y 5 t operand stack: 10 5
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z operand stack: frame for plus: x 10 y 5 t pc operand stack: 10 15 5
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z operand stack: frame for plus: x 10 y 5 t operand stack: pc 15
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z operand stack: frame for plus: x 10 y 5 t 15 operand stack: 15 pc
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z operand stack: frame for plus: x 10 y 5 t 15 operand stack: 15 pc
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z operand stack: pc 15 frame for plus: x 10 y 5 t 15 operand stack:
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z 15 operand stack: 15 pc frame for plus: x 10 y 5 t 15 operand stack:
Example 0: push 10 // <- main 1: store m 2: push 5 3: store n 4: load m 5: load n 6: call plus 7: store z 8: load z 9: call print 10: load x // <- plus 11: load y 12: add 13: store t 14: load t 15: ret frame for main: m 10 n 5 z 15 operand stack: 15 pc frame for plus: x 10 y 5 t 15 operand stack:
Run the Stack machine code • Run the code on a real stack machine • if one is lucky to buy one… • Write an interpreter (virtual machine) • just like the JVM • Mimic a stack machine on non-stack machines: • E.g., use the call stack on x86 as the operand stack and the function frame • Or we may create a customized software stack
Mimic stack machine on x86 // gen_s as before gen_e (n) = “pushl $n” gen_e (x) = “pushl x” gen_e (e1+e2) = gen_e (e1) gen_e (e2) “addl 0(%esp), 4(%esp)” “addl $4, %esp” correct?
Mimic stack machine on x86 // gen_s as before gen_e (n) = “pushl $n” gen_e (x) = “pushl x” gen_e (e1+e2) = gen_e (e1) gen_e (e2) “popl %edx” “addl %edx, 0(%esp)”
Better code generation • Generating stack machine code for x86 reveals a serious defect: • the generated code may be too slow • this will be more severe on RISC • which does not operate memory directly, so there may be a lot of “load” and “store” • A better idea is to introduce some registers into the stack machine • and some more instructions
Stack Machine with one Register • Stack-based • but with one register: r Memory Stack ALU the stack: … r Control
Revised Stack Machine ISA // ISA semantics (sample) add: r = stack[top]+r; top--; // ISA syntax v -> NUM | x | r s -> push v | pop v | unwind n | load v | store v | add | sub | mult | div | call f | ret | mov v, v before: 2 … 1 after “add”: … 3
Recursive Decedent Code Generation (revised) // Invariant: expression value is in register “r” gen_s (s1; s2) = gen_s (s1); gen_s (s2); gen_s (x := e) = gen_e (e); “mov r, x” gen_s (print (es)) = gen_es (es); “call print” gen_s (return e) = gen_e(e); “ret” gen_e (n) = “mov n, r” gen_e (x) = “mov x, r” gen_e (e1+e2) = gen_e (e1) “push r” gen_e (e2) “add” gen_e (…) // similar for -, *, / gen_e (s, e) = gen_s (s); gen_e(e) gen_es (e; es) = gen_e (e); “push r”; gen_es (es)
Example 0: mov 10, r // <- main 1: mov r, m 2: mov 5, r 3: mov r, n 4: load m 5: load n 6: call plus 7: mov r, z 8: load z 9: call print 10: mov x, r // <- plus 11: push r 12: mov y, r 13: add 13: mov r, t 14: load t 15: ret main (){ m := 10; n := 5; z := plus (m, n); print (z); } plus (x, y){ t = x+y; return t; }