Lab 6 AU21
Lab 6
Early Wed 24 Nov 2021
On time Wed 1 Dec 2021
No late labs accepted
Version V1
Contents
Lab 6 ………………………………………………………………………………………………………………………………………….. 1
Early Wed 24 Nov 2021 …………………………………………………………………………………………………………. 1
On time Wed 1 Dec 2021 ………………………………………………………………………………………………………. 1
No late labs accepted ……………………………………………………………………………………………………………. 1
Version V1 ………………………………………………………………………………………………………………………………….. 1
Topics ………………………………………………………………………………………………………………………………………… 2
Introduction ……………………………………………………………………………………………………………………………….. 2
Structs ……………………………………………………………………………………………………………………………………….. 2
Required files and functions …………………………………………………………………………………………………………. 3
stats.s …………………………………………………………………………………………………………………………………….. 3
team.s ……………………………………………………………………………………………………………………………………. 3
margin.s………………………………………………………………………………………………………………………………….. 4
Register Allocation ………………………………………………………………………………………………………………………. 5
Shims …………………………………………………………………………………………………………………………………………. 5
Required Stuff …………………………………………………………………………………………………………………………….. 6
Comments ………………………………………………………………………………………………………………………………….. 6
Readme ……………………………………………………………………………………………………………………………………… 6
Submission …………………………………………………………………………………………………………………………………. 6
Piazza ………………………………………………………………………………………………………………………………………… 7
Bonus ………………………………………………………………………………………………………………………………………… 7
Plan of Attack ……………………………………………………………………………………………………………………………… 7
stats: ………………………………………………………………………………………………………………………………………. 7
oneTeam: ……………………………………………………………………………………………………………………………….. 8
bestMargin: …………………………………………………………………………………………………………………………….. 8
Topics
This lab covers:
Arrays of basic types and arrays of structs
Structs
Caller and callee-saved register usage
Data sizes
Pointers
Functions
Introduction
This lab deals with sports scores. It computes wins – losses. It computes total net points over all
opponents (our score – their score). It updates wins, losses, and ties. It finds the team with the highest
margin of victory. It calls print far too many times.
Structs
Our code deals with the following structure
struct Record {
char name[15];
short scores[2][6];
int win, loss, tie;
};
This struct is 52 bytes in size (which you should verify by computing it by hand). There is one byte of
padding between name and scores. There is no other padding in the struct. It will be very helpful to
compute the offset of every member of the struct and of the beginning of the second row of the array.
Make full use of the Intel addressing mode capabilities to get into the struct. Use displacement to your
advantage.
Here is an example of data you might find in a struct Record:
{ “Ohio State”,
{
{52, 38, 49, 42, 52, 22 },
{17, 25, 27, 35, 12, 10 },
},
0, 0, 0
}
Required files and functions
stats.s
int stats(struct Record *rp);
The stats function will be passed a pointer to a struct Record. It will go through the array of scores.
There are 6 games recorded.
scores [0][i] is the points scored by the team given in the name field in game i
scores [1][i] is the points scored by the opposing team in game i
When it is done it will:
set the number of ties
set the number of losses
set the number of wins
return the total points of the team less the total points of the opposing teams.
The stats function makes no calls and should use registers appropriately. Stats may only touch memory
a single time at any location. Any single score should be read exactly once. The values of tie, loss, and
win should be written to memory exactly one time each in that order (tie, loss, win).
You can test stats by itself by building the stest build target in the supplied makefile
team.s
int oneTeam(struct Record *rp);
The oneTeam function will be passed a pointer to a struct Record. It will:
print the team name
print all of the scores
Call stats via shim
Print the 4 items that stats computed
Return the value it got from stats
Here is what that output looks like. There is a leading blank line.
oneTeam: Ohio State
52 to 17
38 to 25
49 to 27
42 to 35
52 to 12
22 to 10
Total margin of victory is 129
Record is 6 win, 0 lost, 0 tie (W-L = 6)
Note that the last two lines can be done in a single printf call, considerably shortening the code for this
function. When developing this function, do not write all of it in one go. At first you skip the loop that
prints the scores. This proves that your register handling is correct. Only then do you add in the loop.
Register selection is worth thinking about: “Does this value need to survive a function call?” and “Do I
still need this register to mean what it used to mean or can I repurpose it?”
Do this function after you prove in stats. You’ll need the ability to get into the struct that stats requires.
You can test oneTeam by building the otest target in the supplied makefile. It requires both stats and
oneTeam.
margin.s
struct Record *bestMargin(struct Record *rp, int count);
The bestMargin function will be passed a pointer to a struct Record and a count. The pointer points
to the first element of an array of structs that holds count many structs. It will:
call oneTeam via a shim for each struct Record in the array
It will keep track of the highest margin and the struct Record with that highest margin
When the loop is over, it prints the name of that team and the margin
return the address of the struct that has the highest margin of victory.
Here is sample output. There is a leading blank line.
bestMargin: Ohio State has highest margin at 129
This function faces register pressure. It should fit in the available 5 callee-saved registers. Consider
running the loop backwards (decreasing order) to cut down the number of registers needed. Do not
depend on any magic value to be lower than any possible margin returned from stats – “prime” the loop
with a call to oneTeam outside your loop. Unlike the other two functions, this one never actually
accesses the memory of any structs. It will need to compute addresses, but it won’t go to memory.
Test this with the lab6 target in the makefile. It requires all three functions.
Output
The above show the output for the first two targets. Lab 6 output is on piazza.
Register Allocation
Register allocation policy is graded.
You really do want a firm grasp on register allocation before you take the final exam, so work it out here.
Before you write any code, figure out how many “variables” you will need and assign them to registers
of the appropriate type.
Shims & Supplied Code
You must use the supplied shims in your code instead of calling the functions directly:
call oshim instead of oneTeam
call sshim instead of stats
call mshim instead of bestMargin
call print instead of printf
All printing is done via a call to print and not printf. The print function is a shim to make sure that all of
the caller saved registers are trashed.
Likewise there is a gift bag of shims to call instead of directly calling your code. See l6files.zip where
there is also a makefile and driver code. This is similar to lab 5.
Required Stuff
All of your functions must create a stack frame upon entry and tear it down upon exit.
The first usage of any register in a function must have a comment telling what that register means. For
example:
xorq %rax, %rax # rax is both a subscript and return value at the same time.
This is usually near the top of your function. It is your pocket guide to the registers. In your comments
you should refer to the registers not by name but by what they mean.
Comments
Comment your code. Comments should say things that are not obvious from the code. In assembler
you could easily have something to say for every line of code. You could easily have more comment lines
than code lines. Comment what each register holds. Comment about how the operation has a higher
level meaning. Try to avid comments that say the exact same thing as the code.
Put your name and the assignment number in your initial comments. Also add the statement that says
that you wrote all of the code in the file (see below). Or you can forget it and get a zero on the lab.
Readme
As always, create a text README file, and submit it with your code. All labs require a readme file.
Include:
Your name
Hours worked on the lab
Short description of any concerns, interesting problems, or discoveries encountered. General
comments about the lab are welcome.
Submission
No surprises here. Your zip file needs:
A readme file
All of your .s files
Makefile
The supplied .c files and shim .o files
Be sure to add this text to ALL of your .s files:
# BY SUBMITTING THIS FILE AS PART OF MY LAB ASSIGNMENT, I CERTIFY THAT
# ALL OF THE CODE FOUND WITHIN THIS FILE WAS CREATED BY ME WITH NO
# ASSISTANCE FROM ANY PERSON OTHER THAN THE INSTRUCTOR OF THIS COURSE
# OR ONE OF OUR UNDERGRADUATE GRADERS. I WROTE THIS CODE BY HAND,
# IT IS NOT MACHINE GENRATED OR TAKEN FROM MACHINE GENERATED CODE.
If you omit a required file, you get zero points.
If you fail to add the above comment you get zero points.
Your name goes in every file that you edit.
The make command must be able to build at least one of the three targets without warnings or errors or
you get a zero.
Piazza
Please don’t post “what’s wrong with my code” without giving more information, such as
A gcc error that you don’t understand
A description of what gdb showed you if it crashed
Output that you can’t explain
If it crashes, run it under gdb before seeking help. Use tui reg general at the gdb prompt.
Please don’t copy paste the template code off of a webpage, you’ll get odd characters. Do it by hand or
run it through strings or some other utility to sanitize it.
Bonus
+1 for implementing stats with no branching inside the loop. The loop itself needs a conditional jump,
but it is possible to do the rest without branching.
Plan of Attack
Prove in small chunks of code at a time and build from there. While you aren’t required to turn in
prototypes, you may want to act as if you were.
stats:
1. Write some throw away code to put four different values into caller saved registers, one of them
being rax.
Then write just enough code to write out three of those values to tie, loss, and win in that order.
Then build stest on that code alone to prove that you can write to memory in the struct in the
right place.
2. Remove the code that puts dummy values in rax and add code to loop through the array.
Put each of the two scores into two other registers.
Add one of those registers to rax and subtract the other from rax.
Then build stest and check the return value is corrct
3. Zero the registers that hold win, loss, and tie and use them to store the running values of win,
loss, and tie:
Add code to increment the tie register, build and test.
Add code to increment the loss register, build and test.
Add code to increment the win register, build and test – stats is now completed.
oneTeam:
Figure out your register allocations before you write the very first line of code.
1. Print the team name and return zero. Build otest and run it.
2. Add code to setup and make the sshim call, build and test. Make sure that the return value is
still good. Build and test.
3. Add the final print call for the total margin of victory. Make sure that the returned value is still
good. Build and test.
4. Take a glance at your loop code in stats and switch to different register for the loop control.
Inside the loop pull the scores out of memory into parameter registers to setup the print for the
scores. Note that these scores are not required to survive a function call. Build and test.
bestMargin:
You can use imul when trying to figure out addresses. You can also add or subtract 52. You’ll need the
count, something that points at structs, a best pointer and a best margin at a minimum. (When I wrote
this up, it looked like more registers would be needed.)
1. Set up all of your callee saved register values.
Set your best pointer to point to the first struct.
Setup the priming call to oshim and make the call
store that returned value in the best margin
Make the final print call.
Return the best pointer.
build lab6 and test
2. Add your loop code (which is considerable) after the priming code and before the print.
build and test