You are on page 1of 47

Slide Set 2 for Lecture Section 01

for ENCM 369 Winter 2017

Steve Norman, PhD, PEng

Electrical & Computer Engineering


Schulich School of Engineering
University of Calgary

January 2017
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 2/47

Contents

Introduction to Procedures

The $ra Register Conflict Problem and The Stack

The S-Register Conflict Problem

Argument Register Conflicts

Putting Local Variables on the Stack


ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 3/47

Outline of Slide Set 2 for Lecture Section 01

Introduction to Procedures

The $ra Register Conflict Problem and The Stack

The S-Register Conflict Problem

Argument Register Conflicts

Putting Local Variables on the Stack


ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 4/47

Introduction to Procedures

Procedure is the A.L. term for a thing like a C function


(sometimes called a subroutine or method in other high-level
languages).
This is a complex topic, and will require several lectures.
We’ll study rules that allow coding of A.L. programs with
hundreds or thousands of procedures and procedure calls!
(But lab programs will usually only have 2, 3, or 4 procedures.)
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 5/47

Procedures: The first few subtopics

Flow of instruction execution, MIPS jal and jr instructions.


Passing arguments in GPRs $a0–$a3, returning a value in
GPR $v0.
Definitions for the terms leaf and nonleaf.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 6/47

Procedures: Subtopics we’ll get to later

Problems with conflicts over GPRs. (How can many


procedures cooperate to share the same small set of GPRs?)
Managing the stack using A.L. instructions.
Using the stack to help with register conflicts.
Using the stack to allocate local variables in memory.
(Remember, some variables are in GPRs and others are in
memory.)
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 7/47

Flow of instruction execution, call and return


operations
memory

In machine language, a lower


procedure is just a addresses
"CALL"
sequence of instructions. instructions
of main
Suppose a program has
two procedures: main
and foo. Suppose main
instructions
calls foo. When foo is
of foo
done there is a return to
main. higher
"RETURN" addresses
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 8/47

Call and return operations in MIPS: jal and jr


instructions
CALL: A jal (jump and memory
link) instruction in main
lower
causes a jump to the addresses
first instruction of foo. "CALL"
instructions
RETURN: The last of main
instruction in foo is a jr
(jump to address in
register) instruction. instructions
This causes a jump back of foo
to the instruction just higher
after the jal instruction "RETURN" addresses
in main.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 9/47

The concept of a return address,


and the MIPS $ra register

For the return operation in the given example program to work


correctly, information had to be recorded about where to
resume execution in main when foo was done.
GPR 31 in MIPS, called $ra, is used for this purpose.

To see how $ra is used, let’s sketch out assembly language


code for main and foo.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 10/47

Differences between j, jal, and jr

j uses a label to identify the jump target. j does not


touch $ra.

jal uses a label, too. But jal also updates $ra to note where
to come back to when the called procedure has finished.

jr does not use a label—instead it looks in a GPR to find the


jump target address.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 11/47

Remarks about jal and jr

The word “link” in “jump and link” means “remember how to


come back”. (But jarhtcb would be too long as an
instruction mnemonic!)

jr is most often used with $ra for procedure return, but can
be used with other GPRs for other purposes. The “r” in jr
stands for “register”, not “return”.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 12/47

Useful terms: caller and callee

A procedure call involves two procedures: the caller, and the


callee.
The caller makes the call to the callee.
When the callee is done there is a return back to the caller.
In the earlier example, main was the caller and foo was the
callee.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 13/47

Arguments and return values

The caller sometimes needs to send argument values (“args”)


to the callee.
The callee sometimes needs to send a return value (“r.v.”)
back to the caller.
In MIPS certain GPRS are reserved for arguments and return
values. Which GPRs are they and what rules govern their use?
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 14/47

ENCM 339 terms: arguments and parameters


int addsub(int a, int b, int c) {
return a + b - c;
}

int main(void) {
int d = 42, e = 10, f;
f = addsub(d, d - e, e + 5);
return 0;
}

In ENCM 339 in Fall 2015 I used the term argument for


expressions such as d, d - e, and e + 5 in the above example
C program, and I used the term parameter for things like a, b,
and c.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 15/47

Terms for ENCM 369 will be different!


The data items passed from caller to callee are arguments.
int addsub(int a, int b, int c) {
return a + b - c;
}

int main(void) {
int d = 42, e = 10, f;
f = addsub(d, d - e, e + 5);
return 0;
}

In the example . . .
I d, d - e, and e + 5 are outgoing arguments from main;

I a, b, and c are incoming arguments of addsub.


ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 16/47

Example procedure with arguments and a return


value

What would be correct MIPS A.L. for the following


C function?

int quux(int a, int b, int c)


{
return a - b + c;
}
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 17/47

Limits to the uses of GPRs for arguments and


return values

The rules we’ve just learned work well, if the number of


arguments is ≤ 4 and all arguments and return values are ints
or pointers.
What if there are > 4 arguments?
What if an arg or r.v. is of type double? Or of a C struct
type? Those won’t fit in GPRs.
We’ll ignore all these issues for now, but more complicated
rules do exist to handle these cases.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 18/47

Very important terms: leaf and nonleaf

It’s very important to know what these terms mean, because


they will be used a lot in upcoming discussion of how to make
procedures work correctly.
Let’s carefully write down the definitions.
Is quux (our most recent example) leaf or nonleaf?
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 19/47

Examples of nonleaf procedures in C

void f() int factorial(int n)


{ {
g(); if (n == 0)
} return 1;
else
int func1(int x) return n * factorial(n - 1);
{ }
if (x < 0)
x = func2(x);
return x + 17;
Let’s make some notes about each
}
of these procedures.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 20/47

Outline of Slide Set 2 for Lecture Section 01

Introduction to Procedures

The $ra Register Conflict Problem and The Stack

The S-Register Conflict Problem

Argument Register Conflicts

Putting Local Variables on the Stack


ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 21/47

Introduction to register conflict problems

Consider a program with hundreds of procedures.


In MIPS there are only 32 GPRs. (ARM and x86-64 have only
16, and x86 has only 8!)
It’s very likely that at many times during a run of the program,
two or more different procedures might simultaneously “want”
to use a single GPR for two or more different purposes. This
kind of situation is called a register conflict.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 22/47

First example of register conflict: Use of $ra

For the C code on the left, a programmer proposes the A.L. on


the right as a translation of f2 . . .

int f1(void); # This will NOT WORK!


int f2(void) f2:
{ jal f1
return 16 * f1(); sll $v0,$v0,4 # f2 rv = 16 * f1 rv
} jr $ra

Let’s assume that f1 is coded correctly, and that f2 is called


by main. What happens when f2 runs?
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 23/47

How many return addresses have to be maintained


at the same time?

Suppose main calls alpha, alpha calls beta, beta calls


gamma, and gamma calls delta.
When delta is running, how many different return addresses
must the program remember?
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 24/47

Solution to $ra conflict problem

Every nonleaf procedure needs to make a backup copy of the


$ra contents before it makes any procedure calls—that way,
return address information will not be lost.
The best place for these backup copies is a region of memory
called the stack.
It does not make sense to use a lot of different GPRs for
different return addresses, one GPR per address—that would
use too many GPRs, and put a limit on how long a chain of
procedure calls could be.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 25/47

The stack and other regions in address space


The stack is one of three main regions of memory address
space used by a program.
The other two are . . .
I The text segment: Where instructions are located. (The
word “text” is confusing—instructions are not sequences
of character codes!)
I The data segment: Used for statically allocated data.
May also be used for dynamically allocated chunks of
data obtained with malloc in C or new in C++.

(Real programming platforms, such as Linux, offer multiple


regions of memory for statically allocated data. We won’t dive into
the details of that in ENCM 369.)
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 26/47

How the stack works in MIPS

The stack is an array of words with addresses from


0x7fff_fffc down to whatever address is in the stack
pointer register—$sp, which is GPR 29.
How can the stack be made to grow by N words?
How can it be made to shrink by N words?
The bottom boundary of the stack, as shown on the “Stack
Handout”, moves up and down as $sp is updated.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 27/47

Fixing a defect

Here is C code and a proposed MIPS A.L. translation from a


few slides back . . .

int f1(void); # This will NOT WORK!


int f2(void) f2:
{ jal f1
return 16 * f1(); sll $v0,$v0,4 # f2 rv = 16 * f1 rv
} jr $ra

How can we use the stack to help in writing correct A.L. code
for f2?
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 28/47

Which Way Is Up?

In textbooks and manuals related to computer organization,


there are many diagrams (or “maps”) of memory.
Some diagrams show higher addresses nearer the top of a
page or screen, and others show higher addresses nearer the
bottom. Lectures in this course have already done both!
When reading diagrams, take time to figure out Which Way
Is Up.
When drawing diagrams, indicate clearly Which Way Is Up.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 29/47

Outline of Slide Set 2 for Lecture Section 01

Introduction to Procedures

The $ra Register Conflict Problem and The Stack

The S-Register Conflict Problem

Argument Register Conflicts

Putting Local Variables on the Stack


ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 30/47

The S-Register Conflict Problem

What if alpha’s caller is


int alpha(void) also using $s0 or $s1? Or
{ alpha’s caller’s caller, or
// Plan: use $s0 alpha’s caller’s caller’s
// for b, $s1 for c. caller? And so on . . .
int b, c;
b = beta(); In a program with hundreds
c = gamma(); of procedures, you do NOT
return c + b * 8; want to check them ALL for
} conflicting uses of $s0 and
$s1!
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 31/47

Sketch of solution to s-register conflict problem

Before alpha starts using $s0 or $s1, alpha should use the
stack to save copies of $s0 and $s1 in case alpha’s caller
(or caller’s caller, caller’s caller’s caller, etc.) is also using $s0
or $s1.
After alpha has finished using $s0 and $s1, and just before
alpha returns, alpha should read the old values of $s0
and $s1 back from the stack into $s0 and $s1.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 32/47

MIPS A.L. code for alpha

int alpha(void)
{
// Plan: use $s0
Let’s write a complete A.L.
// for b, $s1 for c.
implementation of alpha,
int b, c;
then study how it will
b = beta();
behave.
c = gamma();
return c + b * 8;
}
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 33/47

Saving s-registers on the stack: General remarks

The rule is: All s-registers used by a procedure should have


their values saved on the stack near the beginning of that
procedure, and restored from the stack near the end.
The rule is really good for big programs. In a program with
hundreds of procedures, every procedure can use up to eight
s-registers without any worry about overwriting variables of
other procedures.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 34/47

Saving s-registers on the stack: General remarks

A possible objection: Sometimes a procedure could save and


restore, for example, $s4, even though none of the caller,
caller’s caller, etc., were using $s4. Wouldn’t this be a waste
of time and stack space?
Answer: Yes, that would be very slightly wasteful, but the
waste is acceptable as a tradeoff for the simplicity of being
able to use s-registers in any one procedure without checking
s-register use in all the other procedures.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 35/47

What about t-registers?

# This might or
// a, b, c are ints # might not work!
// in $s0, $s1, $s2. add $t0, $s0, $s1 # $t0 = a + b
// f returns an int. addi $a0, $zero, 7 # $a0 = 7
c = a + b + f(7); jal f
add $s2, $t0, $v0 # c = $t0 + f rv

I What is dangerous about the A.L. code?


I How could the A.L. code be reorganized to avoid this
risk?
I What if the C code were c = f(a) + f(b); ?
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 36/47

Outline of Slide Set 2 for Lecture Section 01

Introduction to Procedures

The $ra Register Conflict Problem and The Stack

The S-Register Conflict Problem

Argument Register Conflicts

Putting Local Variables on the Stack


ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 37/47

Argument Register Conflicts

A nonleaf procedure gets argument values from its caller in


some or all of $a0-$a3, but may need some or all of $a0-$a3
to send argument values to its callees.
Solution: Near the beginning of a procedure, incoming
arguments should be copied from a-registers to other
locations. After that is done, the a-registers are available for
outgoing arguments.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 38/47

More useful procedure-related terms: prologue,


body, epilogue

(We’ve already seen a few pieces of terminology: caller, callee,


leaf, nonleaf.)
The prologue, body, and epilogue are all parts of procedures.
Let’s write down what each of these parts do.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 39/47

What are the “other locations” to which incoming


args should be copied?

Two strategies can be used:


(1) Copy the incoming args to s-registers in the prologue.
(2) Copy the incoming args to the stack in the prologue.
Pages 2 and 3 of the “Stack Handout” provide examples of
both strategies.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 40/47

Let’s look at the A.L. code for fred


for strategy (1)—page 2 of the “Stack Handout”

What is fred using $s0, $s1, and $s2 for?


What does the stack frame of fred look like?
Is the order of the instructions in the prologue of fred
important? (Could the add instructions be done before the
sw instructions?)
bob only takes one argument, in $a0. Why is it necessary for
the prologue of fred to make copies of both $a0 and $a1?
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 41/47

Let’s look at the A.L. code for fred


for strategy (2)—page 3 of the “Stack Handout”

In strategy (2) the “other locations” for the arguments are


“stack slots”—words reserved within a procedure’s stack
frame.
In the procedure body, access to the incoming arguments is
done with lw and sw instructions.
Let’s draw a diagram for the stack frame of fred, and make a
few remarks about how this implementation of fred will work.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 42/47

Which strategy is better for managing a-register


conflicts?

copying incoming args to s-registers


I faster access to incoming args in body of procedure

copying incoming args to stack slots


I prologue and epilogue are shorter

I reduced danger of “running out of s-registers” when there


are a lot of incoming args and local variables
So each strategy has an advantage relative to the other.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 43/47

Outline of Slide Set 2 for Lecture Section 01

Introduction to Procedures

The $ra Register Conflict Problem and The Stack

The S-Register Conflict Problem

Argument Register Conflicts

Putting Local Variables on the Stack


ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 44/47

Putting Local Variables on the Stack

Usually, local variables of nonleaf procedures can go in


s-registers.
Usually, local variables of leaf procedures can go in t-registers.
But what if a local variable is an array? Or what if a local
variable occupies a single word, but needs to have an
address? In both cases, memory allocation is needed.
Remember, registers do not have addresses!
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 45/47

Example of local variables on the stack—page 4 of


the “Stack Handout”
The C function test_negatives has 3 local variables:
count, sum, and x.
count and sum need to have addresses, because &count and
&sum are used in the call to negatives. So count and sum
must be in memory.
x is an array, so it too must be in memory.
Solution: make space within the stack frame of
test_negatives for count, sum, and x.
Let’s sketch a stack frame for test_negatives and write
down a few remarks about it.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 46/47

A note about the test_negatives example

The A.L. code for test_negatives did not use any


s-registers, because all of the local variables of
test_negatives had to be in memory.

A more typical procedure might use s-registers for some local


variables, and stack slots for other local variables.
ENCM 369 Winter 2017 Slide Set 2 for Lecture Section 01 slide 47/47

Moving on . . .

Lectures material on procedures and procedure calling


conventions is now mostly finished.

Pay attention to Lab 3 and Lab 4 exercises designed to help


you learn this material.

You might also like