1 EE 109 Unit 11 – Subroutines and Stack
2 Program Counter and GPRs (especially $sp, $ra, and $fp) REVIEW OF RELEVANT CONCEPTS
3 Review of Program Counter • PC is used to fetch an instruction – PC contains the address of the next instruction – The value in the PC is placed on the address bus and the memory is told to read – The PC is incremented, and the process is repeated for the next instruction Processor Memory PC = Addr = 0 0 PC Addr 0 inst. 1 op. 1 inst. 2 Data = inst.1 machine code 2 inst. 3 ALU out in1 Data 3 inst. 4 ADD, SUB, AND, OR 4 inst. 5 in2 Control = Read … $0-$31 Control FF
4 GPR's Used for Subroutine Support Assembler Name Reg. Number Description $zero $0 Constant 0 value $at $1 Assembler temporary $v0-$v1 $2-$3 Procedure return values or expression evaluation $a0-$a3 $4-$7 Arguments/parameters $t0-$t7 $8-$15 Temporaries $s0-$s7 $16-$23 Saved Temporaries $t8-$t9 $24-$25 Temporaries $k0-$k1 $26-$27 Reserved for OS kernel $gp $28 Global Pointer (Global and static variables/data) $sp $29 Stack Pointer $fp $30 Frame Pointer $ra $31 Return address for current procedure
5 Subroutines (Functions) • Subroutines are portions of code that we can call from anywhere in our code, execute that subroutine, and then return to where we left off C code: void main() { We call the ... subroutine to x = 8; calculate the average and return to where res = avg(x,4); we called it ... } A subroutine to calculate the average int avg(int a, int b){ of 2 numbers return (a+b)/2; }
6 Subroutines • Subroutines are similar to branches where we jump to a new location in the code C code: void main() { ... x = 8; res = avg(x,4); ... Call “avg” sub -routine 1 will require us to branch } to that code int avg(int a, int b){ return (a+b)/2; }
7 Normal Branches vs. Subroutines • Difference between normal branches and subroutines branches is that with subroutines we have to return to where we left off • We need to leave a link to the return location before we jump to the subroutine…once in the function its too late void main() { C code: ... x = 8; res = avg(x,4); ... Call “avg” sub -routine to 1 After subroutine } calculate the average completes, 2 int avg(int a, int b){ return to the statement in the return (a+b)/2; main code } where we left off
8 Implementing Subroutines • To implement subroutines in assembly we need to be able to: – Branch to the subroutine code – Know where to return to when we finish the subroutine C code: Assembly: ... .text ... res = avg(x,4); Call jal AVG ... ... Definition int avg(int a, int b) AVG: ... { ... } jr $ra
9 Jumping to a Subroutine • JAL instruction (Jump And Link) – Format: jal Address/Label – Similar to jump where we load an address into the PC [e.g. PC = addr] • Same limitations (26-bit address) as jump instruction • Addr is usually specified by a label • JALR instruction (Jump And Link Register) – Format: jalr $rs – Jumps to address specified by $rs (so we can jump a full 32-bits) • In addition to jumping, JAL/JALR stores the PC into R[31] ($ra = return address) to be used as a link to return to after the subroutine completes
10 Jumping to a Subroutine • Use the JAL instruction to jump execution to the subroutine and leave a link to the following instruction PC before exec. of jal: 0040 0000 Assembly: $ra before exec. of jal: 0x400000 jal AVG jal will cause the program to 0x400004 add 0000 0000 jump to the label AVG and ... store the return address in $ra/$31. PC after exec. of jal: 1 0040 0810 AVG: = 0x400810 add $ra after exec. of jal: ... 0040 0004 jr $ra
11 Returning from a Subroutine • Use a JR with the $ra register to return to the instruction after the JAL that called this subroutine PC before exec. of jr: 0040 08ec $ra before exec. of jr: 0x400000 jal AVG jal will cause the program 0x400004 add 0040 0004 to jump to the label AVG ... and store the return address in $ra/$31. PC after exec. of jr: 1 0040 0004 AVG: = 0x400810 add 2 ... Go back to where we left 0x4008ec jr $ra off using the return address stored by JAL
12 Return Addresses • No single return address for a subroutine since AVG may be called many times from many places in the code • JAL always stores the address of the instruction after it (i.e. PC of ‘jal’ + 4) Assembly: 0x400000 jal AVG PC 0040 0000 0x400004 is the return address for this JAL 0x400004 add ... 0x400028 is the return address for this JAL 0x400024 jal AVG PC 0040 0024 0x400028 sub ... 0x400810 AVG ... jr $ra
13 Return Addresses • A further complication Assembly: ... is nested subroutines (a jal SUB1 subroutine calling 0x40001A ... 1 another subroutine) 4 • Example: Main routine SUB1 jal SUB2 calls SUB1 which calls 0x400208 jr $ra SUB2 2 3 • Must store both return SUB2 ... jr $ra addresses but only one $ra register
14 Dealing with Return Addresses Assembly: • Multiple return addresses ... can be spilled to memory jal SUB1 – “Always” have enough 0x40001A ... 1 memory 4 • Note: Return addresses will be accessed in reverse SUB1 jal SUB2 order as they are stored 0x400208 jr $ra – 0x400208 is the second RA to 2 be stored but should be the 3 SUB2 ... first one used to return jr $ra – A stack is appropriate!
15 Stacks • Stack is a data structure where data is accessed in reverse order as it is stored (a.k.a. LIFO = Last-in First-out) • Stack Pointer Use a stack to store the return addresses Always points to and other data top occupied • 0x7fffeffc is the base of System stack defined as growing towards element of the the system stack for smaller addresses stack the MARS simulator – MARS starts stack at 0x7fffeffc $sp = – Normal MIPS starts stack at 0x80000000 0000 0000 7fffeffc 7fffeffc • Top of stack is accessed and maintained 0040 0208 7fffeff8 using $sp=R[29] (stack pointer) 0000 0000 7fffeff4 – $sp points at top occupied location of the 0000 0000 7fffeff0 stack 0000 0000 7fffefec 0000 0000 7fffefe8 Stack grows towards lower addresses
16 Stacks • 2 Operations on stack 0000 0000 7fffeffc 7fffeffc – Push: Put new data on top of $sp = 0000 0000 7fffeff8 stack 0000 0000 7fffeff4 • Decrement $sp Empty stack • Write value to where $sp points 0000 0000 7fffeffc 7fffeff8 – Pop: Retrieves and “removes” $sp = 0040 0208 7fffeff8 0000 0000 7fffeff4 data from top of stack Push Push will add a value to the top of • Read value from where $sp the stack points 7fffeffc 0000 0000 7fffeffc • Increment $sp to effectively $sp = 0040 0208 7fffeff8 “delete” top value 0000 0000 7fffeff4 Pop Pop will remove the top value from the stack
17 Push Operation • Recall we assume $sp points Push return address (e.g. 0x00400208) at top occupied location $sp = • Push: Put new data on top of 0000 0000 7fffeffc 7fffeffc stack 0040 0208 7fffeff8 7fffeff8 0000 0000 7fffeff4 – Decrement SP • addi $sp,$sp,-4 Decrement SP by 4 (since pushing • Always decrement by 4 since a word), then write value to where $sp is now pointing addresses are always stored as words (32-bits) – Write return address ($ra) to where SP points • sw $ra, 0($sp)
18 Pop Operation • Pop: Retrieves and Pop return address "removes" data from top of stack 7fffeffc 0000 0000 7fffeffc – Read value from where SP 7fffeff8 0040 0208 7fffeff8 points $sp = 0000 0000 7fffeff4 • lw $ra, 0($sp) Read value that SP points at then – Increment SP to effectively increment SP (this effectively "deletes" top value deletes the value because the next push will overwrite it) • addi $sp,$sp,4 • Always increment by 4 when Warning : Because the stack grows towards popping addresses lower addresses, when you push something on the stack you subtract 4 from the SP and when you pop, you add 4 to the SP.
19 Subroutines and the Stack • When writing native assembly, programmer must add code to manage return addresses and the stack • At the beginning of a routine (PREAMBLE) – Push $ra (produced by 'jal') onto the stack addi $sp,$sp,-4 sw $ra,0($sp) • Execute subroutine which can now freely call other routines • At the end of a routine (POSTAMBLE) – Pop/restore $ra from the stack lw $ra,0($sp) addi $sp,$sp,4 jr $ra
20 Subroutines and the Stack ... 0 0000 0000 7fffeffc 7fffeffc $sp = jal SUB1 0000 0000 7fffeff8 0000 0000 7fffeff4 0x40001A ... 0040001a $ra = 0 1 0000 0000 7fffeff8 7fffeffc $sp = SUB1 addi $sp,$sp,-4 sw $ra,0($sp) 0040 001a 7fffeff8 1 jal SUB2 0000 0000 7fffeff4 0040001a $ra = 0x400208 lw $ra,0($sp) addi $sp,$sp,4 3 jr $ra 0000 0000 $sp = 7fffeff4 7fffeffc 2 0040 001a 7fffeff8 0040 0208 7fffeff4 SUB2 addi $sp,$sp,-4 00400208 $ra = sw $ra,0($sp) 2 ... 0000 0000 $sp = 7fffeffc 7fffeffc lw $ra,0($sp) 3 addi $sp,$sp,4 0040 001a 7fffeff8 jr $ra 0040 0208 7fffeff4 0040001a $ra =
21 Optimizations for Subroutines • Definition: – Leaf procedure: A procedure that does not call another procedure • Optimization – A leaf procedure need not save $ra onto the stack since it will not call another routine (and thus not overwrite $ra)
Recommend
More recommend