Code Generation II (codegen2) Project
Overview
In this project you implement Slang compiler support for pointer operations.
Setup
Run the following commands to download the starter code for this
project, and to set up your git repository for project
submission:
mkdir -p ~/cop3402fal26/
wget https://www.cs.ucf.edu/~pappas/teaching/cop3402/fal2026/files/codegen2-starter-code.tar
tar -xvf codegen2-starter-code.tar -C ~/cop3402fal26/
cd ~/cop3402fal26/codegen2
git init
git add .
git commit
# Enter a commit message in the editor that pops up
git remote add submission gitolite3@eustis3.eecs.ucf.edu:cop3402/$USER/codegen2
git push --set-upstream submission masterStarter code structure
The starter code for this project has the same structure as that of codegen1.
All starter code files must be added, committed, and pushed with
git. Do not add to source control any
generated or binary files (e.g., .o files, or the
bin/slang binary itself).
You do not need to read all these files in order to complete this
project. The only file you will be modifying is
src/codegen.c. The project
requirements section explains what changes to make to this file.
Building your project
Run the following command to build your compiler (tested on eustis):
cd ~/cop3402fal2026/codegen2
makeThe result of the above command will be the bin/slang
executable file. This is the executable for the Slang compiler.
Project requirements
For this and all other codegen assignments, you will complete the
assignment by implementing a few specific functions in the file
src/codegen.c that are labeled with comments that say
TODO. Other functions will either be given to you, or
hard-coded to work for this specific assignment’s test cases. You should
only modify functions labeled with TODO
comments, and should not modify any given or hard-coded
functions.
Here is a listing of all the functions in this assignment, and whether they are given to you, hard-coded, or are a function you must implement yourself:
| Function | Category |
|---|---|
visit_reference() |
Implement yourself |
visit_assignment_dereference() |
Implement yourself |
visit_dereference_assignment() |
Implement yourself |
visit_arithmetic() |
Hard-coded (leave blank) |
visit_label() |
Hard-coded (leave blank) |
visit_goto() |
Hard-coded (leave blank) |
visit_if() |
Hard-coded (leave blank) |
visit_parameters() |
Hard-coded (leave blank) |
visit_locals() |
Hard-coded |
visit_return() |
Hard-coded |
visit_call() |
Hard-coded |
codegen_err() |
Given |
value_to_string() |
Given |
visit_assignment() |
Given |
visit_statement() |
Given |
visit_body() |
Given |
visit_function() |
Given |
visit_program() |
Given |
visit() |
Given |
The next subsections describe the functions you must implement yourself.
visit_reference()
This function should emit assembly code that retrieves the address of a given source variable, and then stores that address in a destination variable.
Here are some examples (assume a has an offset of
-16 and b has an offset of -24):
| Slang code | Assembly code |
|---|---|
|
|
|
|
Here is the relevant snippet from the grammar.ebnf file
defining the grammar rule for a Slang reference statement:
reference = identifier '=' '&' identifierA Slang reference statement consists of an identifier, followed by an
equals sign (=), followed by an ampersand
(&), followed by an identifier.
Here is the definition of the Reference struct you
defined in include/parse.h while completing the slang-format exercise; this
struct has all the fields you need to implement
visit_reference():
typedef struct reference {
Identifier left_hand_side;
Identifier right_hand_side;
} Reference;To compile Slang reference statements to assembly code, modify the
function visit_reference() to emit assembly code that does
the following steps:
- Loads the address of the
%rbpregister into the%raxregister. - Adds the source variable’s stack frame offset to the
%raxregister. - Stores the value in
%raxinto the destination variable.
By adding the source variable’s offset to address of the stack frame
(i.e., the value stored in %rbp), we can obtain the address
of the source variable, even though this specific address may not known
be known to us at run-time.
Loading address of
%rbp into %rax
To load the value of %rbp into %rax, simply
use a mov instruction. When emitting the mov
instruction, remember that the left operand is the source, and the right
operand is the destination.
Adding the source
variable’s offset to %rax
To add the source variable’s offset to %rax, first
obtain the offset using the provided offset_for_name()
function (defined in src/symbol_table.c). For instance, to
get the offset for a variable with name name, you would
use:
int offset = offset_for_name(name);In the case of compiling reference statements, the source variable’s
name is stored in reference.right_hand_side.text.
After obtaining the source variable’s offset, next emit an
add instruction to add this offset to the %rax
register. For instance, to add the offset -16 to
%rax, you would emit:
add $-16, %raxStoring the result in the destination variable
Finally, store the value in %rax into the destination
variable. Pass the destinations variable’s name to the given function
offset_for_name() to obtain the offset of the destination
variable, then emit assembly code that use a mov
instruction to store the value in %rax into the memory
address at the destination variable’s offset from the base pointer
(%rbp). In the case of reference statements, the name of
the destination variable is in
reference.left_hand_side.text.
For instance, assuming the variable x has a stack frame
offset of -16, you would emit the following assembly code to store the
contents of the %rax register into x:
mov %rax, -16(%rbp)For an example of some code that uses the
offset_for_name() function, see the definition of the given
function visit_assignment().
visit_assignment_dereference()
This function should emit assembly code that retrieves the address stored in a given variable, dereferences it to obtain the value at the stored address, and then stores the resulting value in a destination variable.
Here are some examples (assume a has an offset of
-16 and b has an offset of -24):
| Slang code | Assembly code |
|---|---|
|
|
|
|
Here is the relevant snippet from the grammar.ebnf file
defining the grammar rule for a Slang assignment dereference
statement:
assignment_dereference = identifier '=' '*' identifierA Slang assignment dereference statement consists of an identifier,
followed by an equals sign (=), followed by an asterisk
(*), followed by an identifier.
Here is the definition of the Assignment_Dereference
struct you defined in include/parse.h while completing the
slang-format exercise; this
struct has all the fields you need to implement
visit_assignment_dereference():
typedef struct assignment_dereference {
Identifier left_hand_side;
Identifier right_hand_side;
} Assignment_Dereference;To compile Slang assignment dereference statements to assembly code,
modify the function visit_assignment_dereference() to emit
assembly code that does the following steps:
- Loads the address stored in the source variable into
%rax. - Dereferences that address, and stores the result into
%rbx. - Stores the value in
%rbxinto the destination variable.
Loading
the source variable’s stored address into %rax
To load the address stored in the source variable into the
%rax register, first pass the source variable’s name to the
given function offset_for_name() to obtain the source
variable’s stack frame offset. Then emit assembly code that uses a
mov instruction to move the value at this offset from
%rbp into the %rax register. In the case of an
assignment dereference statement, the name of the source variable is in
assignment_dereference.right_hand_side.text.
For instance, assuming the variable x has a stack frame
offset of -16, you would emit the following assembly code to store the
value stored in x into the %rax register:
mov -16(%rbp), %raxDereferencing the address in
%rax
To dereference the value in %rax, wrap the operand in
parentheses, i.e., use (%rax). Use this syntax to emit a
mov instruction that dereferences (%rax), and
stores the result into the %rbx register. The assembly code
you emit here should only be a single line of code. Remember that the
first operand to mov is the source operand, and the second
operand is the destination.
Storing the result into the destination variable
See the description of
visit_reference() for instructions on how to copy the
value in %rbx into the destination variable. Note that in
this case, the source operand for the move instruction is
%rbx, not %rax.
visit_dereference_assignment()
This function should emit assembly code that retrieves the address stored in a destination variable, and then stores a given value at the memory location stored in the destination variable (not in the destination variable itself).
Here are some examples (assume a has an offset of
-16 and b has an offset of -24):
| Slang code | Assembly code |
|---|---|
|
|
|
|
Here is the relevant snippet from the grammar.ebnf file
defining the grammar rule for a Slang dereference assignment
statement:
dereference_assignment = '*' identifier '=' valueA Slang dereference assignment statement consists of an asterisk
(*), followed by an equals sign (=), followed
by value (which is either an integer or a variable).
Here is the definition of the Dereference_Assignment
struct you defined in include/parse.h while completing the
slang-format exercise; this
struct has all the fields you need to implement
visit_dereference_assignment():
typedef struct dereference_assignment {
Identifier left_hand_side;
Value right_hand_side;
} Dereference_Assignment;To compile Slang dereference assignment statements to assembly code,
modify the function visit_dereference_assignment() to emit
assembly code that does the following steps:
- Loads the address stored in the destination variable into
%rax. - Loads the value to be stored at the address into
%rbx. - Stores the value in
%rbxat the memory address in%rax.
Loading
the destination variable’s stored address into %rax
See the description
of visit_assignment_dereference() for instructions on
how to and load address stored in a variable into the %rax
register. Note that in this case, the name of the variable you are
obtaining the stored address of is in
dereference_statement.left_hand_side.text.
Loading the source value into
%rbx
To load the source value into the %rbx register, emit
(i.e., printf()) assembly code that uses a mov
instruction to load the source value into the %rax
register. To get the assembly code for the source value, pass
dereference_assignment.right_hand_side to the given
function value_to_string(). For instance, you can use the
following code snippet to get the assembly code for the source
value.
char *value = value_to_string(dereference_assignment.right_hand_side);Given a Slang value, value_to_string() returns a
null-terminated string containing its assembly representation. For
instance, the value 2 would result in "$2",
and a variable a with offset -16 from the stack frame would
result in "-16(%rbp)". The result of
value_to_string() must be freed after you are done using
it, e.g.:
free(value);For more examples of how to use value_to_string(), check
the definition of the given function
visit_assignment().
After getting the assembly code for the source value, emit a
mov instruction that stores the value into the
%rbx register.
Storing the
value in %rbx at the destination address
Emit a mov instruction that stores the value in
%rbx into the memory address stored in
%rax. To move a value into a memory address stored in a
register, and not into the register itself, wrap the target register’s
name in parentheses, e.g., (%rax). For instance, the
following assembly code stores the value 1 at the memory
address stored in %rax:
mov $1, (%rax)Note that in the case of visit_dereference_assignment(),
the source operand should not be $1, but rather the
register %rbx.
Running your compiler
Follow the instructions in codegen1 to run your compiler on the given template file, and to modify the template file to ensure your project fulfills the project requirements.
Running your compiled code
Follow the instructions in codegen1 to assemble
and link your compiled Slang code with the provided iolib.c
file, and to run the resulting executable.
Self-check
The makefile included in the starter code provides a
make test rule you can use to quickly test that your
compiler correctly implements all required functionality for this
assignment. You can run this rule as follows:
make testIf your code works correctly, then make test should
print the name of all this project’s odd-numbered
test cases, each followed by the word “success”. Note that the
grading server will test your code against the even-numbered test cases
as well, which are not given to you, but are described below. You should test that your compiler passes
all even-numbered test cases by either writing your own tests, or by running your compiler on sample
programs made by modifying the given template file,
template.sg.
After using make test to verify that your compiler
passes all odd-numbered test cases (and hopefully using your own tests
to verify that it passes all even-numbered test cases as well), you
should absolutely also check that you submitted your code correctly via
git by running make test from within a fresh
clone of your repo. You can do this with the following
command:
rm -fr ~/tmp/codegen2_selfcheck
git clone gitolite3@eustis3.eecs.ucf.edu:cop3402/$USER/codegen2 ~/tmp/codegen2_selfcheck
cd ~/tmp/codegen2_selfcheck
make testIf your code was pushed successfully, then your fresh clone should pass all tests as well.
Test cases
The test cases used to grade the project are listed below. You can
run make test to test your code against the odd-numbered
test cases, which are provided publicly. The even-numbered test cases
are private, and you should write your own tests for them.
All test cases test your compiler on code based on the provided
template file, template.sg. To ensure that your code works
with this template, be sure to follow the project requirements, and
only modify functions labeled with TODO
comments.
| # | Short Name | Description |
|---|---|---|
| 1 | ref-correct-address | Use gdb to check that address of a is 16
bytes away from %rbp |
| 2 | ref-not-zero | Check that the reference address is not 0 |
| 3 | ref-deref | Take a reference and print via dereference |
| 4 | ref-deref-change | Take a reference and change the referenced variable |
| 5 | ref-deref-copy | Take a reference and copy its value via the ref |
| 6 | ref-assignderef | Take a reference and do a dereference assignment |
| 7 | ref-deref-assignderef | Do all three pointer operations |
Grading schema
| Criterion | Points |
|---|---|
| The git repo exists | 1 |
| The program is written, builds, and runs as described here | 1 |
| Tests pass, prorated by the number of test cases passing | 6 |
| TOTAL | 8 |