Lecture 4 - Procedures

Procedures

  • Calling procedures pushes more registers onto the stack and returning pops them off.
  • This is a stack and we clal $30 our stack pointer
  • Can use the stack for local storage if needed in procedures return.

Implementing Procedures in Assembly Language

  • Registers have global scope.
    • Need convention that ensures that calling a procedure does not overwrite data in registers that the caller might still need.
  • How to we transfer control to the callee and how do we return to the caller?
  • How will procedures calling other procedures work? What if a procedure calls itself recursively?
  • How will we pass arguments and return values from a procedure?

Protecting data in registers

The callee can store within RAM the original values of registers, use the registers and then just before returning to the caller, restore the values from RAM.

Need to use memory systematically by keeping track of which part of memory has already been used and which part is till free.

Procedures push registers they intend to use onto memory and when returning pop these registers from memory.

Free memory is used as a stack. And $30 points to the top. Stack is at the end of available memory, and grows down in terms of memory addresses.

  • Pushing a register value on the memory stack is done with a store instruction and an update to the stack pointer. Ex:

  • Popping a value from the memory stack into a register is done with a load instruction and an update to the stack pointer. Ex:

lis $1
.word 4
add $30, $30, $1 ; update stack pointer
lw $1, -4($30) ; load into $1

Calling and returning from procedures

Calling a procedure requires jumping to the label.

We can follow the .word directive with a label. The assembler will place the memory address tha tit had associated with the label.

Use instruction jalr jump and link register。

  • The instruction **jalr 31 the current value of PC and then sets PC to $s.
  • When we used jalr we overwrote the value currently in $31.
  • We must then first preserve the address currenlty stored in $31.
    • push $31 on the stack before using jalr and pop it off the stack once the call returns.
lis $21
.word foo ; load address of foo
jalr $21 ; $31 = PC, PC = $21
	foo:
	.......
	.......
	jr $31 ; jump to address stored in $31

Better version:

sw $31, -4($30)   ; push $31 to stack, since $30 points to the top of the stack, -4 
			      ; because $30 contain stuff, so we move 1 byte, so -4
lis $31           ; using $31 since it was just saved
.word 4           ; stored 4 into $31
sub $30, $30, $31 ;  -4 again ?? where is $31 now? is it being pushed too?
 
lis $21         
.word foo         ; load address of foo into $21
jalr $21          ; $31 = PC, PC = $21
	foo: 
	.....
	.....
	jr $31
 
lis $31           ; Using $31 since we will pop from stack
.word 4
add $30, $30, $31
lw $31, -4($30)   ; Pop $31 from stack
.....
jr $31            ; Return to loader

Passing and returning values from procedures

Convention is to use #3 to store the value being returned by a function although the top of the stack could also be used.

If number is small, use registers, but we can also use the stack.

Make sure to document where to look for the return value for a procedure.

Return register doesn’t need to be saved as the procedure intends to change it.