Your team's task for this week is to identify and implement a small optimization of your choice for the existing (Chapter 6) compiler (either your version or the reference version, which will be released on Thursday). Your implementation is due on Tuesday, Feb. 20 at noon, as usual. Your submission must include a short README reporting what optimizations you have attempted and any problems you encountered. It is essential to include tests that exercise your optimizations! I strongly suggest that you first write down a plan for what you want to do, and email it to me for review. I will give you prompt feedback, possibly suggest some alterations, and give some advice on implementation. Start by studying the compiler output for some sample programs to decide what opportunities exist. I suggest concentrating on optimizations that shorten the final code, rather than trying to find ones that have a big impact on execution time. The latter is very important, of course, but it may be difficult to achieve measurable improvements given the limited scope of our source language. (And smaller code almost always runs a bit faster anyway!) Remember that the essential feature of every program optimization is that it preserves the observable behavior of the program. For us, this means the final result value (if any: infinite loops must also be preserved!) and the sequence of reads and writes to the terminal. Here are some possibilities. This list is by no means exhaustive; feel free to invent something else! If in doubt about the suitability of a project, contact me. - Write a partial evaluator for LWhile, in the style used in Chapter 1 for LInt, e.g. to perform constant folding, algebraic simplifications, conditional simplification, etc.. It is probably best _not_ to attempt optimizations based on knowing the value of a variable, since this gets complicated in the presence of := operations. - Implement an X86If pass to order blocks so as to maximize useful fall-throughs and remove unreachable blocks. (Note: The current prelude_and_conclusion code converts the X86ProgramBlocksDict, in which the blocks are unordered and hence must each end with an explicit control transfer, into the X86ProgramBlocksList representation, in which blocks are ordered, and fall-throughs from one block to another is possible. If you tackle this optimization, you should convert to the List representation first, adjusting subsequent passes accordingly.) - Use liveness analysis to identify and remove dead assignments in CIf or X86If code. - Use data flow analysis to implement a constant propagation scheme across whole functions. (This is fairly ambitious.) - Improve the code generated for logical and comparison operations. This might involve a combination of changes at the Shrink, ExplicateControl, and InstructionSelection passes. - Perform peephole optimization on the X86If code to simplify or remove unnecessary operations. (Consider using Python pattern matching to identify rewriting opportunities.) Note: If you choose to do any work at the X86 level, by all means feel free to extend the subset of X86 instructions (and condition codes) beyond the minimal set currently supported. However, be aware that the X86 interpreter will likely fail in that case, so be prepared to use -x instead. Finally, don't go overboard with this assignment! It is better to do a small project well than a large one badly.