Reputation: 58
my question is : how does mips use frame pointer fp on the stack? I don't quite understand how pointer fp work with pointer sp .
Upvotes: 3
Views: 2925
Reputation: 71536
In general and not specific to mips. the stack pointer generally wants/needs to point at the "top" (a relative term) of the stack, either the last thing on the stack or the first empty spot depends on the architecture.
When a program enters a function if you think of high level languages and their implementation there may be a need for some local storage, local variables certainly possibly copies of the parameters passed in, the return value, whatever it needs that it cant keep in registers. The easy way to do this is to have a frame pointer set at what the stack pointer was on the way into the function, then move the stack pointer forward to allocate the storage needed for this function so that an interrupt or a nested function call can use the stack pointer as intended to point at the end of the stack.
The code in the function uses the frame pointer (or the stack pointer) as a reference point to address by offset the items for that function. the first parameter passed in might be kept at frame pointer - 4 for example and the second at frame pointer - 8, the first local variable may be at frame pointer - 12 and so on. the compiler has done its job and knows not only how many things are on the stack for that function, but what offset they are so when it encodes the instructions it can easily reference either the sp or the fp.
You see this repeated on many/most architectures, MIPS, ARM, x86 and so on, with many/most compilers. Not something specific to mips.
EDIT
extern unsigned int more_fun ( unsigned int );
unsigned int fun ( unsigned int a, unsigned int b )
{
unsigned int c;
c = a + more_fun(b);
return(c);
}
Mips like ARM and others is one of those you really dont want/need a frame pointer, you can do it all with the stack pointer. It helps when the instruction set doesnt for example have stack relative addressing, doesnt have a lot of registers, etc.
Good or bad example the above code on mips could look like this:
00000000 <fun>:
0: 27bdffe8 addiu sp,sp,-24
4: afb00010 sw s0,16(sp)
8: 00808021 move s0,a0
c: afbf0014 sw ra,20(sp)
10: 0c000000 jal 0 <fun>
14: 00a02021 move a0,a1
18: 8fbf0014 lw ra,20(sp)
1c: 00501021 addu v0,v0,s0
20: 8fb00010 lw s0,16(sp)
24: 03e00008 jr ra
28: 27bd0018 addiu sp,sp,24
a stack frame is setup but it uses the stack pointer and the stack pointer is used to address into the stack to find local to the function information.
ARM doesnt even need to use the stack for those local items
00000000 <fun>:
0: e92d4010 push {r4, lr}
4: e1a04000 mov r4, r0
8: e1a00001 mov r0, r1
c: ebfffffe bl 0 <more_fun>
10: e0800004 add r0, r0, r4
14: e8bd4010 pop {r4, lr}
18: e12fff1e bx lr
lr has to be saved for the nested call, r4 is only saved because the calling convention says to keep the stack aligned on 64 bit boundaries, so this compiler chose to use r4, any register could have been used (other than lr, the pc or sp).
Now here is one that does use a frame pointer
00000000 <_fun>:
0: 1166 mov r5, -(sp)
2: 1185 mov sp, r5
4: 1d66 0006 mov 6(r5), -(sp)
8: 09f7 fff4 jsr pc, 0 <_fun>
c: 6d40 0004 add 4(r5), r0
10: 1585 mov (sp)+, r5
12: 0087 rts pc
r5 here is the frame pointer, the parameters are passed in on the stack which we dont see with mips or arm above, they have plenty of registers (would need a lot of parameters to see the stack used). r5 is pushed on the stack then r5 gets a copy of the stack after that push, typical of setting up a frame pointer. then because the code is taking an incoming parameter to pass to the nested function and this implementation uses the stack for parameter passing, that parameter is put on the stack for the nested call. using the frame pointer not the stack pointer to address it.
Then on the way out it adds the a variable (passed in to this function) referenced using the frame pointer again, and then cleans up. basically this is almost a classic stack frame with a frame pointer implementation. except no stack frame because of the code I wrote. If I use the same instruction set and dont optimize then we do see a frame pointer and a stack frame, sort of...
00000000 <_fun>:
0: 1166 mov r5, -(sp)
2: 1185 mov sp, r5
4: 65c6 fffe add $-2, sp
8: 1d66 0006 mov 6(r5), -(sp)
c: 09f7 fff0 jsr pc, 0 <_fun>
10: 65c6 0002 add $2, sp
14: 1d41 0004 mov 4(r5), r1
18: 6001 add r0, r1
1a: 1075 fffe mov r1, -2(r5)
1e: 1d40 fffe mov -2(r5), r0
22: 1146 mov r5, sp
24: 1585 mov (sp)+, r5
26: 0087 rts pc
So you dont need a frame pointer for mips, but if you were to use one then you do the normal thing, you first push that register on the stack to preserve it (dont want to mess up the frame pointer for function calling you). then adjust the stack pointer as needed for a stack frame. then instead of using the stack pointer to access things in the stack frame you use the frame pointer. at the end of the function you adjust the stack pointer back to unallocate the stack frame, and then pop the frame pointer off and return.
Upvotes: 2