This is a write-up about riscv rv64 and the assembly generated by GCC.
The example Link to heading
I am using a pre-built rv64 toolchain from the package gcc-riscv64-linux-gnu
apt install gcc-riscv64-linux-gnu
riscv64-linux-gnu-gcc -v
The example i am using is very simple. Basically setting a variable on the stack (will see later why we need this) then return that variable.
int main(){
int x;
x = 0;
return x;
}
riscv64-linux-gnu-gcc
is used with a twist. This toolchain is using the compressed instructions. To see the full 32 bits, i had to pass the following options to gcc
riscv64-linux-gnu-gcc main.c -march=rv64imafd -mabi=lp64d
vanilla riscv64-linux-gnu-objdump
is used to see the generated assembly
riscv64-linux-gnu-objdump -d a.out
Jumping to the disassembled main
:
00000000000005ec <main>:
5ec: fe010113 addi sp,sp,-32
5f0: 00813c23 sd s0,24(sp)
5f4: 02010413 addi s0,sp,32
5f8: fe042623 sw zero,-20(s0)
5fc: fec42783 lw a5,-20(s0)
600: 00078513 mv a0,a5
604: 01813403 ld s0,24(sp)
608: 02010113 addi sp,sp,32
60c: 00008067 ret
Note that the register names are not x0-x31 as mentioned in the specs because gcc is using ABI calling convention defined in Chapter 25 of RISCV spec
addi
is using the following opcode where sp
is mapped x2
. So, it’s easy to understand what fe010113
means here.
Assembly walk-through Link to heading
while we are here, we can see the walk-through the generated assembly annotated with explanation for each line.
First, gcc generates boiler-plate code for stack and frame pointers. This is done for all architectures to setup the function frames for successive function calls.
00000000000005ec <main>:
5ec: fe010113 addi sp,sp,-32 # move stack pointer up to init stack for the function
5f0: 00813c23 sd s0,24(sp) # store s0 (frame pointer callee saved) on the stack
5f4: 02010413 addi s0,sp,32 # move frame pointr for the current function
...
...
...
604: 01813403 ld s0,24(sp) # restore fp
608: 02010113 addi sp,sp,32 # restore sp
60c: 00008067 ret
And the actual code:
00000000000005ec <main>:
5f8: fe042623 sw zero,-20(s0) # store 0 to variable x on the stack. Note it's using fp to reference it.
5fc: fec42783 lw a5,-20(s0) # main return x, so it loads the variable to a5
600: 00078513 mv a0,a5 # move a5 to a0 (return value according to calling convention)