lemar hadad
lemar hadad

Reputation: 58

Mips_how does mips use frame pointer fp on the stack?

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

Answers (1)

old_timer
old_timer

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

Related Questions