Professor Jeremy Siek This exam has 12 questions, for a total of 100 points. 1. 10 points Draw a parse tree and an abstract syntax tree for the following program given the grammar below. In the case of ambiguities, use the precedences of the Python language. The abstract syntax tree should match what the Python compiler module would produce. Grammar: ::= integer identifier "(" L ")" "lambda" IL ":" "(" ")" L ::= "," L IL ::= identifier identifier "," IL Program: (lambda x, y: x)(lambda z: z, 2)(42) Parse tree: L L IL L IL IL ( lambda x, y : x ) ( lambda z : z, 2 ) ( 42 ) Abstract syntax tree: CallFunc node args CallFunc Const(42) node args args Lambda[x,y] code Name(x) Lambda[z] code Name(z) Const(2)
2. 5 points Translate the following P 1 program to GNU C. You can use the C functions declared in the Appendix. print input() and (input() + 5 or input()) int main() { print_any(({ pyobj tmp2 = input(); pyobj_to_bool(tmp2)? ({ pyobj tmp1 = ({ pyobj left0 = input(); add(left0, int_to_pyobj(5)); ); pyobj_to_bool(tmp1)? tmp1 : input(); ) : tmp2; )); return 0; Page 2 of 11
3. 10 points Perform type analysis and type specialization on the following program and translate it to GNU C. You may use the C functions declared in the Appendix. x = 0 n = 10 i = 0 if input() else 1 while i < n: if i == 0: y = x x = x * 0.5 i = i + 1 print y int main() { pyobj y; int i; int n; pyobj x; x = int_to_pyobj(0); n = 10; i = pyobj_to_bool(input())? 0 : 1; while (i < n) { if (i == 0) { y = x; x = mul(x, float_to_pyobj(0.5)); i = i + 1; print_any(y); return 0; Page 3 of 11
4. 10 points Translate the following P 4 program to GNU C. You may use the C functions declared in the Appendix. y = 1 f = lambda x: x + y y = 2 f(40) pyobj lambda0(pyobj fvs, pyobj x){ pyobj y; y = get(fvs, int_to_pyobj(0)); return add(x, get(y, int_to_pyobj(0))); int main() { pyobj f; pyobj y; y = make_list(1); put(y, int_to_pyobj(0), int_to_pyobj(1)); f = make_closure(lambda0, ({ pyobj fvs = make_list(1); put(fvs, int_to_pyobj(0), y); fvs; )); put(y_0, int_to_pyobj(0), int_to_pyobj(2)); print_pyobj(((pyobj(*)(pyobj, pyobj)) get_function_ptr(f))(get_free_vars(f), int_to_pyobj(40))); Page 4 of 11
5. 10 points Translate the following P 5 program to C. Perform partial evaluation to simplify the generated code for function calls. For example, in A() you know that A is a class, so you don t need to generate code that checks if it is a class. You may use the C functions declared in the Appendix. class A: def f(x): return x.y a = A() a.y = 21 print a.f() + A.f(a) pyobj lambda0(pyobj fvs, pyobj x) { return get_attribute(x, "y"); int main() { pyobj a; pyobj A; A = make_class_pyobj(make_list(0)); set_attribute(a, "f", make_closure(lambda0, make_list(0))); a = make_object(a); set_attribute(a, "y", int_to_pyobj(21)); pyobj af = get_attribute(a, "f"); pyobj caf = get_closure_from_bound_method(af); pyobj oaf = get_object_from_bound_method(f); pyobj Af = get_attribute(a, "f"); pyobj caf = get_closure_from_unbound_method(af); print_any(add(((pyobj(*)(pyobj, pyobj))get_function_ptr(caf))(get_free_vars(caf), oaf), ((pyobj(*)(pyobj, pyobj))get_function_ptr(caf))(get_free_vars(caf), a))); Page 5 of 11
6. 5 points Convert the following program to an equivalent program that does not contain any complex operators or operands nested inside of expressions or inside of returns. int factorial(int n) { if (n == 0) return 1; else return n * factorial(n - 1); int factorial(int n) { int t1, t2, t3; if (n == 0) return 1; else { t1 = n - 1; t2 = factorial(t1); t3 = n * t2; return t3; 7. 5 points Starting with your answer from the previous question, remove structured control flow statements, replacing them with labels and gotos or ifs whose bodies are only a goto. int factorial(int n) { int t1, t2, t3; if (n == 0) goto then_clause0; t1 = n - 1; t2 = factorial(t1); t3 = n * t2; return t3; then_clause0: return 1; Page 6 of 11
8. 15 points Starting with your answer from the previous question, convert to x86 assembly using the GNU Linux calling conventions. factorial: pushl subl cmpl je subl pushl call imull leave ret L2: leave ret %ebp %esp, %ebp $8, %esp $0, 8(%ebp) L2 8(%ebp), %eax $1, %eax %eax factorial $4, %esp %eax, -4(%ebp) 8(%ebp), %eax -4(%ebp), %eax %eax, -8(%ebp) -8(%ebp), %eax $1, %eax Page 7 of 11
9. 5 points Perform liveness analysis on the following intermediate representation and write down the live set for each statement. int f(int v) { int x, y, z; z = 1; y = v; push z; call g; z += y; x = eax; v = z; v += x; eax = v; int f(int v) { int x, y, z; z = 1; {v y = v; {v,z push z; {y,z call g; {y,z z += y; {eax,y,z x = eax; {eax,z v = z; {x,z v += x; {v,x eax = v; {v 10. 5 points Draw the interference graph for the program in question 9. x y v z eax 11. 5 points Draw the move graph for the program in question 9. x y v z eax Page 8 of 11
12. 15 points Perform register allocation on the program in question 9 using only two general purpose registers: eax and ebx (in addition to the stack pointer esp and the base pointer ebp). Recall that eax is a caller-save register and ebx is a callee-save register. If necessary, iterate the process until no more spill code is generated. For each iteration, show the assignment of variables to registers and stack locations, the new version of the code resulting from spill, the new results of liveness analysis, and the new interference and move graph. At the end, write down the final x86 code. First round: x eax, y ebx, v 8(%ebp), z 4(%ebp) (could have spilled y instead of z and put z in ebx). Add spill code and do liveness again: int f(int v) { int x, y, z, t0; z = 1; {v y = v; {v,z push z; {y,z call g; {y,z z += y; {eax,y,z x = eax; {eax,z t0 = z; {x,z v = t0; {t0,x v += x; {v,x eax = v; {v Interference graph: t0 x y v z eax Move graph: t0 x y v z eax Second round: y ebx, x eax, t 0 ebx Page 9 of 11
Final x86 code: f: $1, -4(%ebp) %ebx, 8(%ebp) pushl -4(%ebp) call g addl %ebx, -4(%ebp) -4(%ebp), %ebx %ebx, 8(%ebp) addl %eax, 8(%ebp) 8(%ebp), %eax leave ret Page 10 of 11
Appendix: Runtime C Functions /* input/output */ pyobj input(); void print_any(pyobj); /* conversions */ char pyobj_to_bool(pyobj); pyobj int_to_pyobj(int); pyobj float_to_pyobj(double); /* arithmetic */ pyobj add(pyobj, pyobj); pyobj mul(pyobj, pyobj); /* list operations */ pyobj make_list(int); pybobj get(pyobj list, pyobj key); void put(pyobj list, pyobj key, pyobj value); /* closure operations */ pyobj make_closure(void* function_ptr, pyobj free_vars); void* get_function_ptr(pyobj closure); pyobj get_free_vars(pyobj closure); /* operations on classes and objects */ pyobj make_class(pyobj bases); pyobj make_object(pyobj class); pyobj get_attribute(pyobj object, char* attr); pyobj set_attribute(pyobj object, char* attr, pyobj value); pyobj get_closure_from_unbound_method(pyobj); pyobj get_class_from_unbound_method(pyobj); pyobj get_class_from_object(pyobj); pyobj get_closure_from_bound_method(pyobj); pyobj get_object_from_bound_method(pyobj); Page 11 of 11