P4: Code Generation
Objective
The goal of our semester-long project is to gain experience in compiler implementation by constructing a simple compiler. In this phase you will add code generation: translate a valid Decaf AST into linear ILOC code.
Introduction
Add a code generator to your compiler. Drop the test-p4/ folder
into your project and extend your compiler so that, after analysis, it emits ILOC
for the program. Read the "Operational Semantics" and "Application Binary
Interface" (ABI) sections of the language
reference carefully first. The rules in the Project Overview apply as always.
Your compiler emits code; it does not run it. When run with
--fdump-iloc --stop-after-codegen, your compiler prints the ILOC it
generated; that ILOC is executed by the standalone, trusted simulator
isim (bundled in test-p4/). isim reads an
ILOC program from a file or from standard input, so you can run the compiler and
the simulator together in one step:
decaf --fdump-iloc --stop-after-codegen prog.decaf | isim
(The automated harness does the same thing in two steps — it saves the
ILOC to a file and then runs isim on that file — which is
convenient for inspecting the generated code.)
ILOC provides a large bank of virtual registers here — 2048, far more than any test program needs (physical register allocation comes in P5). How you traverse the AST and track intermediate results is your design.
Assignment
Generate correct ILOC for Decaf programs, following the calling conventions,
stack-frame layout, and static-storage rules in the ABI. You are responsible for
emitting function prologues/epilogues, laying out parameters and locals, and
handling the built-in output functions (print_int, etc.) with the
PRINT ILOC instruction as described in the reference.
This phase is more open-ended than the earlier ones: many
different ILOC programs are equivalent, so your output need not match the sample
below. Grading is almost entirely behavioral — your emitted code
is run through isim and checked for the correct program output and
return value — so a debug view of the raw ILOC is essential while
developing. Keep any developer trace output behind your own switch, and make sure
--fdump-iloc prints only the ILOC (the tests assume this).
When behavior is underspecified, match decaf-ref.
Notes:
- Every assignment should actually write the value to the correct location in memory. Do not attempt to optimize or skip assignments.
- You can ignore the
PHIinstruction (it is for static-single-assignment data-flow analysis, which we are not doing). - Emiting a correct prologue is very important! Local variables will not work unless the base pointer is established from the stack pointer on function entry, and the stack pointer is adjusted by the frame's local size.
- You should emit the stack allocator even if the local size is zero. You may need to adjust the frame size in P5 to hold spilled registers.
- Recursive (and mutually recursive) functions will not yet work correctly, because virtual registers are reused across calls. This is expected; P5 fixes it by spilling live registers at call sites.
- Use
--fdump-ilocto inspect your output; pipe it toisimto run it. (decaf-refalso offers--fdump-iloc-tree, a debugging visualization, but you are not required to produce it.)
Sample Input
def int add(int x, int y)
{
return x + y;
}
def int main()
{
int a;
a = 3;
return add(a, 2);
}
Sample Output
One valid --fdump-iloc rendering (yours may differ and still be
correct):
add: push BP i2i SP => BP addI SP, 0 => SP loadAI [BP+16] => r0 loadAI [BP+24] => r1 add r0, r1 => r2 i2i r2 => RET jump l0 l0: i2i BP => SP pop BP return main: push BP i2i SP => BP addI SP, -8 => SP loadI 3 => r3 storeAI r3 => [BP-8] loadAI [BP-8] => r4 loadI 2 => r5 push r5 push r4 call add addI SP, 16 => SP i2i RET => r6 i2i r6 => RET jump l1 l1: i2i BP => SP pop BP return
Piping this to isim prints RETURN VALUE = 5.
Submission
Submit your entire project directory: run
/cs/students/cs432/f26/submit.sh p4 from your project root and
confirm the P4 assignment on Canvas.
Code Reviews
After this project you will review two other students' submissions and offer constructive feedback per the given rubric, graded separately for effort (see the syllabus). Submit your review on Canvas by the date given in the corresponding assignment.
Grading
This checkpoint is autograded on the tier rubric below (behaviorally, via
isim); your grade is the highest tier whose requirements all pass.
Tests are cumulative and mostly not provided to you in advance, so write your
own.
| Grade | Description | Requirements |
|---|---|---|
| A | Exceptional |
|
| B | Good |
|
| C | Satisfactory |
|
| D | Deficient |
|
| F | Unacceptable |
|
Array code generation is an A-level requirement here. Array support is deliberately tiered differently in each phase: array parsing is C-level (P2), the array semantic checks are B-level and A-level (P3), and array code generation is A-level (P4). The rubric shows the base grade possible if your submission meets the criteria listed; most items are assessed by automated testing using cases mostly NOT provided in advance.
I will also examine your submission manually for acceptable style and documentation and for the use of any unsafe functions. Deficiencies may earn a numerical deduction, and egregious ones a half- or full-letter deduction. If you are unsure of my standards, review the style guide and list of unsafe functions from CS 261.