Question Details

No question body available.

Tags

assembly riscv calling-convention stack-frame riscv32

Answers (1)

Accepted Answer Available
Accepted Answer
March 21, 2026 Score: 3 Rep: 380,050 Quality: Expert Completeness: 80%

add sp, sp, -16 reserves 16 bytes. Storing to 12(sp) after that puts the return address at the very top of your stack frame, which seems like a good place for it. All your local vars can go below that, along with any outgoing stack args to callees where the args don't all fit in registers.
(If this is a leaf function, you don't need to save/restore the return address, it can just stay in the link register like in your fun2)

The stack grows downwards, so 12(sp) is the first 4 bytes of the stack. (The alignment requirement forces you to allocate more than you need if you don't need any space for locals.)

In a variadic function with some stack args, it might make sense to put the return address elsewhere so you can dump the register args contiguous with the stack args to form an array you can index.


The RISC-V calling-convention requirements on stack-frame layout that constrains where you should save the return address.

If you're also using a frame pointer, the ELF calling convention standardizes a specific stack-frame layout that puts the saved fp next to the saved return address if you're using an FP, so stack unwind / backtrace can follow it like a linked list without having to know other layout details of each function's stack frame.

(On x86 where call itself pushes the return address onto the stack instead of writing it to a register like on RISC ISAs, the usual convention is to save the caller's frame-pointer register right below that if using one. That's also where RISC-V's usual convention says to put it, below the return address. The RISC-V convention also says to put them at the very top of your stack frame, and to have fp = incoming sp. Except in variadic functions.)