Presentation is loading. Please wait.

Presentation is loading. Please wait.

More Functions and the Stack

Similar presentations


Presentation on theme: "More Functions and the Stack"— Presentation transcript:

1 More Functions and the Stack
CS/COE 0447 Jarrett Billingsley

2 Class announcements uh CS447

3 Passing arguments and returning values
CS447

4 How did you write syscalls?
a syscall is a weird kind of call, but when you pass an argument… what register(s) do you put them in? and when you get a value back (like the "read int" syscall in lab 2)… what register do you get the value back in? a0-a3 are the argument registers v0-v1 are the return value registers this is just a convention, there's no special features for them - arguments go in a0, a1 etc - return values come back in v0, v1 CS447

5 Well that's what we do v0 a0 a1 int gcd(int a, int b) {
if we have a function in a higher level language… v0 a0 a1 the two arguments will come in in a0 and a1. int gcd(int a, int b) { while(a != b) { if(a > b) a -= b; else b -= a; } return a; for this, just put the value you want to return in v0 before jr ra. CS447

6 int add_nums(int x, int y) {return x + y;}
Input, output let's write a function like this: int add_nums(int x, int y) {return x + y;} inside of our add_nums asm function… which register represents x? which register represents y? which register will hold the sum that we return? add_nums: add __, __, __ v0 a0 a1 jr ra - x = a0 - y = a1 - return value will go into v0 CS447

7 Calling that function li a0, 3 print(add_nums(3, 8)) li a1, 8
now let's make main do this: print(add_nums(3, 8)) how do we set 3 and 8 as the arguments? how do we call add_nums? afterwards, which register holds the sum? so how can we print that value? why do syscalls put the number of the syscall in v0? well what do you get when you cross an elephant and a rhino? hell if I know li a0, 3 li a1, 8 jal add_nums move a0, v0 li v0, 1 syscall - and what's worse, SOME SYSCALLS RETURN VALUES IN THE A REGISTERS, LIKE WHAT THE FUCK????? CS447

8 The Call Stack CS447

9 One busy desk there's a tiny desk that three people have to share person 1 is working at the desk. it's covered in their stuff. person 2 interrupts them and needs to do some important work what does person 2 do with the stuff? throw it in the trash? they put it somewhere else. P1 P1 what if person 2 didn't move person 1's stuff and started scribbling all over it? Trash P1 P1 - kind of a weird metaphor but just go with it, okay? - if person 2 did that, that would be a really dick move. - and it'd mess up person 1's work. - (HINT HINT) CS447

10 One busy desk P2 P2 P1 now person 2 is interrupted by person 3.
when person 3 is done, person 2 will come back. where do we put person 2's stuff? on top of the stack of stuff. the desk is the registers. the people are functions. the stack of stuff is… the stack. P2 P2 P2 P2 P1 P1 CS447

11 What's the stack? Stack Memory
it's an area of memory provided to your program by the OS when your program starts, it's already there the stack is holds information about function calls: the return address to the caller copies of registers that we want to change local variables/arguments that can't fit in registers how do we access the stack? through the stack pointer (sp) register Memory Stack - check out the sp register when your program first starts – it's already initialized to a value CS447

12 The stack pointer (animated)
let's say sp starts at the address 0xF000 we want to push something on the stack the first thing we'll do is move sp to the next available slot clearly, that's the previous address subtract 4 from sp then, we can store something in the memory that sp points to. ... 0xF008 0x 0xF004 0xF000 sp 0xEFFC 0x 0xC0DEBEEF - we say the stack "grows down" from higher addresses to lower ones - this is a common way for call stacks to work - cause now all the offsets to things on the stack are positive offsets from sp, rather than negative CS447

13 Doing that in MIPS (animated)
say ra = 0xC0DEBEEF first: move the stack pointer down (up?): sub sp, sp, 4 then, store ra at the address that sp holds. sw ra, (sp) now the value in ra is saved on the stack, and we can get it back later. and we can store as many return addresses as we want! ... 0xF008 0x 0xF004 0xF000 sp 0xEFFC 0x 0xC0DEBEEF - remember stores copy from left to right, and (sp) means "into memory at the address held in sp" CS447

14 Going the other direction (animated)
now we wanna pop the value off the stack and put it back in ra we do the same things, but in reverse what's the opposite of sw? lw ra, (sp) what's the opposite of sub? add sp, sp, 4 now we got back the old value of ra! and sp is back where it was before! ra 0xABAD1DEA 0xC0DEBEEF ... 0xF008 0x 0xF004 0xF000 sp 0xEFFC 0xC0DEBEEF making sure sp ends up where it started is called "balancing the stack." - if you don't balance the stack, that's like leaving junk on the stack of stuff before you leave the desk… - and the person who comes back to it won't be able to find their stuff. it's super important, OK? CS447

15 Shortening the pushes and pops
the push and pop operations always look and work the same since you'll be using them in most functions, I shortened em if you write push ra or pop ra, it'll do these things for you! sub sp, sp, 4 sw ra, (sp) push ra lw ra, (sp) add sp, sp, 4 pop ra these are pseudo-ops: fake instructions to shorten common tasks these can be used with ANY register, not just ra! - there are many pseudo-ops… many of the instructions you'll use are! - they make coding in assembly a little more bearable. CS447

16 Toes = protected ra PC 0x8000 jal fork fork: 0x8020 push ra 0x8028
jal spoon 0x802C pop ra 0x8034 jr ra spoon: 0x8040 sp 0x8004 stuff 0x8000 0x0000 After jal fork: 0x8020 0x8004 Then we push ra on the stack! After jal spoon: 0x8040 0x802C After spoon jr ra: 0x802C 0x802C Then we pop ra off the stack! Before fork jr ra: 0x8034 0x8004 After fork jr ra: 0x8004 0x8004 CS447

17 Writing a simple function
Writing a function is actually pretty easy: spoon: 1. Give it a name (label). push ra 2. Save ra to the stack. your code goes here 3. Do whatever. 4. Load ra from the stack. pop ra 5. Return! jr ra You only have to push/pop ra once in each function! ONLY PUSH/POP REGISTERS AT THE BEGINNINGS AND ENDS OF FUNCTIONS CS447

18 Doing a simple function call
On the caller side… 1. Put the arguments in the ax registers. li a0, 1 lw a1, some_var 2. Call the function. jal spoon 3. if it returns values, look at the vx registers. beq v0, 0, blahhhh CS447

19 Saved and Unsaved registers
CS447

20 Lah dee daa .data counter: .word 0 .text increment: la t0, counter
let's make a variable and a function to change it .data counter: .word 0 .text increment: la t0, counter lw t1, (t0) add t1, t1, 1 sw t1, (t0) jr ra then we can call it main: jal increment CS447

21 Everything's just fine, right?
let's write a loop that calls it ten times in a row so we need a loop counter ('i' in a for loop) li t0, 0 # our counter loop_begin: jal increment add t0, t0, 1 blt t0, 10, loop_begin loop_end: if we run this, it only increments the variable once. why? let's put a breakpoint on blt and see what it sees. (another way to write a for loop) CS447

22 Scribbling on someone else's notes
both functions are trying to use t0 for different purposes but there's only ONE t0! the increment function is in the clear the problem is actually the loop this is one of the contracts between the caller and the callee… a caller cannot depend on the t, a, or v registers to have the same values after a jal as before it. or to put it another way, callees are allowed to trash those registers. CS447

23 Another piece of the calling convention puzzle
when you call a function, it's allowed to change some registers but other registers must be left exactly as they were functions are required to put these registers back the way they were before they were called. anyone can change these. after you call a function, they might have totally different values from before you called it. Saved s0-s7 sp ra* Unsaved v0-v1 a0-a3 t0-t9 *ra is a little weird cause it's kinda "out of sync" with the other saved regs but you DO save and restore it like the others CS447

24 Whenever you call a function…
after a jal, you have no idea what's in these registers. ... jal increment Unsaved v0-v1 a0-a3 t0-t9 could be nonsense! garbage! bogus! CS447

25 Why it broke li t0, 0 loop_begin: jal increment add t0, t0, 1
if we look at this code again… t0 is our loop counter and everything's fiiiine. li t0, 0 loop_begin: jal increment add t0, t0, 1 blt t0, 10, loop_begin loop_end: uh oh. WHAT IS IN t0 NOW?? instead, this is a great place to use an s register. CS447

26 The fixed version li s0, 0 loop_begin: jal increment add s0, s0, 1
if we use an s register… s0 is our loop counter and everything's fiiiine. li s0, 0 loop_begin: jal increment add s0, s0, 1 blt s0, 10, loop_begin loop_end: uh oh. oh whew, we used an s register, it's fine. but s registers aren't magic. they don't do this automatically. CS447

27 The s register contract
if your function wants to use an s register… you must save and restore it, just like ra. my_func: push ra push s0 pop s0 pop ra jr ra moving the papers off the desk code that uses s0! it's fine! we saved it! the pops happen in reverse order! putting the papers back CS447

28 Oh, and… my_func: push ra push s0 ... bge ... b exit_func exit_func:
you must always pop the same number of registers that you push. to make this simpler for yourself… make a label before the pops. then you can leave the function by jumping/branching there. my_func: push ra push s0 ... bge ... b exit_func exit_func: pop s0 pop ra jr ra CS447


Download ppt "More Functions and the Stack"

Similar presentations


Ads by Google