7

I converted a very simple C program to an assembly file (Here, RISC-V ISA), and there were some operations done on the stack pointer that I did not understand.

The C program :

int main()
{
        int a = 10;
        return 0;
}

The associated assembly code :

        .file   "alternating_branches_2.c"
        .option nopic
        .text
        .align  1
        .globl  main
        .type   main, @function
main:
        addi    sp,sp,-32
        sd      s0,24(sp)
        addi    s0,sp,32
        li      a5,10
        sw      a5,-20(s0)
        li      a5,0
        mv      a0,a5
        ld      s0,24(sp)
        addi    sp,sp,32
        jr      ra
        .size   main, .-main
        .ident  "GCC: (GNU) 8.3.0"

Here is my understanding.

sp contains the last address of the stack memory. Pushing to stack would, therefore, decrease the value of sp. s0 is the frame pointer, pointing to the previous value of sp.

In the first line, an offset of 32 is decreased from the stack pointer. Is this to create a stack frame? And usually, in a stack, a push operation would decrease the stack pointer. However, since a stack frame is already created, and sp now points to lower memory of stack, will pushing increase the value of sp?

-------------| <--sp
-------------|
-------------|
-------------|
-------------|
-------------|
-------------|

After the creation of the stack frame :

-------------| <--s0
-------------|
-------------|
-------------|
-------------|
-------------|
-------------| <--sp

And now, pushing to the stack must result in an increase in sp, correct? Or can pushing also be done using the frame pointer, s0? I apologize if this is a very basic question. Thank you.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
ElPsyKongroo
  • 135
  • 1
  • 7
  • 1
    risc-v doesn't have a push instruction, and compilers fail to use `push` for initializing variables anyway. It just creates space for a stack frame by moving `sp`, then does regular stores into the stack frame, not push or pop. – Peter Cordes Dec 12 '19 at 10:41
  • @Peter Cordes I see, thank you. And does the first instruction: addi sp, sp, -32 create a stack frame? Also I apologize for not asking this in the question, but does OFFSET(SP) store data from the offset towards the higher memory, or towards the lower memory? – ElPsyKongroo Dec 12 '19 at 10:55
  • 1
    The addressing mode is `offset + register` same as always. It's added, not subtracted. And yes, moving `sp` allocates or deallocates space on the stack. In this case, allocating 32 bytes for a stack frame. – Peter Cordes Dec 12 '19 at 11:02
  • @Peter Cordes Thank you. Also, since stack frame is already created, does creating local variables (inside stack frame) increase sp as opposed to "pushing" data normally to stack which decreases sp? – ElPsyKongroo Dec 12 '19 at 11:10
  • 1
    No, of course not. How would that make any sense? If SP increased, it would shrink the stack frame. Instead, locals are placed wherever the compiler wants *inside* the stack frame. Compiler with `gcc -fverbose-asm` to have it put comments on each instruction. [How to remove "noise" from GCC/clang assembly output?](//stackoverflow.com/a/38552509) (For your case you probably *do* want un-optimized `-O0` code, or use `volatile` on your vars so they're not optimized out.) – Peter Cordes Dec 12 '19 at 11:15
  • I though the frame pointer handled the boundaries of the stack frame, sorry. And thank you for the verbose GCC option. – ElPsyKongroo Dec 12 '19 at 11:23
  • Creating a pointer to the top of the stack frame is optional, but yes `-O0` defaults to `-fno-omit-frame-pointer` so GCC does point `s0` at the top of the frame in case it wants to increase SP some more later. – Peter Cordes Dec 12 '19 at 11:48

1 Answers1

5

Unlike the x86 ISA – which has dedicated instructions for pushing onto and popping from the stack, the RISC-V ISA can only access memory through load and store instructions. It has no push and pop instructions that access the stack – which is the memory – and modify the stack pointer.


Since the stack grows downwards, decreasing the stack pointer, sp, allocates space on the stack, whereas increasing it deallocates space. That is, addi sp,sp,-32 allocates 32 bytes on the stack and addi sp,sp,32 deallocates 32 bytes from the stack. The former creates the new stack frame, and the latter destroys it.

The s0 register – which is the same as fp – is the frame pointer and points to the beginning of the stack frame. Its current value is saved at the very beginning of the newly created stack frame (sd s0,24(sp)). Then, it is set to the address pointing at the beginning of the stack frame (addi s0,sp,32). Finally, the frame pointer is restored with its previous value (ld s0,24(sp)) before leaving the function.

The representation of the stack after the stack frame creation and the frame pointer setup is:

|     ...     |
|-------------|<- s0 | beginning of stack frame
| previous s0 |
|-------------|<- sp-24
|-------------|
|-------------|
|-------------|
|-------------|
|-------------|
|-------------|
|-------------|<- sp | end of stack frame
JFMR
  • 23,265
  • 4
  • 52
  • 76