Programmer
Contents |
ABI
Assembler mnemonics for RISC-V integer and floating-point registers.
Register | ABI Name | Description | Caller | Calee |
---|---|---|---|---|
x0 | zero | Hard-wired zero | ||
x1 | ra | Return address | * | |
x2 | sp | Stack pointer | * | |
x3 | gp | Global pointer | ||
x4 | tp | Thread pointer | ||
x5 | t0 | Temporary/alternate link register | * | |
x6–x7 | t1-t2 | Temporaries | * | |
x8 | s0/fp | Saved register/frame pointer | * | |
x9 | s1 | Saved register | * | |
x10–x11 | a0-a1 | Function arguments/return values | * | |
x12–x17 | a2-a7 | Function arguments | * | |
x18–x27 | s2-s11 | Saved registers | * | |
x28–x31 | t3-t6 | Temporaries | * | |
f0–f7 | ft0-ft7 | FP temporaries | * | |
f8–f9 | fs0-fs1 | FP saved registers | * | |
f10–f11 | fa0-fa1 | FP arguments/return values | * | |
f12–f17 | fa2-fa7 | FP arguments | * | |
f18–f27 | fs2-fs11 | FP saved registers | * | |
f28–f31 | ft8-ft11 | FP temporaries | * |
Preprocessor macros
32 bits
$ ./riscv-none-embed-gcc -march=rv32i -mabi=ilp32 -E -dM - < /dev/null | egrep -i 'risc|fp[^-]|version|abi|lp' | sort
#define __GXX_ABI_VERSION 1011
#define __STDC_VERSION__ 201112L
#define __VERSION__ "7.1.1 20170509"
#define __riscv 1
#define __riscv_cmodel_medlow 1
#define __riscv_float_abi_soft 1
#define __riscv_xlen 32
$ ./riscv-none-embed-gcc -march=rv32imac -mabi=ilp32 -E -dM - < /dev/null | egrep -i 'risc|fp[^-]|version|abi|lp' | sort
#define __GXX_ABI_VERSION 1011
#define __STDC_VERSION__ 201112L
#define __VERSION__ "7.1.1 20170509"
#define __riscv 1
#define __riscv_atomic 1
#define __riscv_cmodel_medlow 1
#define __riscv_compressed 1
#define __riscv_div 1
#define __riscv_float_abi_soft 1
#define __riscv_mul 1
#define __riscv_muldiv 1
#define __riscv_xlen 32
$ ./riscv-none-embed-gcc -march=rv32imafdc -mabi=ilp32d -E -dM - < /dev/null | egrep -i 'risc|fp[^-]|version|abi|lp' | sort
#define __FP_FAST_FMA 1
#define __FP_FAST_FMAF 1
#define __GXX_ABI_VERSION 1011
#define __STDC_VERSION__ 201112L
#define __VERSION__ "7.1.1 20170509"
#define __riscv 1
#define __riscv_atomic 1
#define __riscv_cmodel_medlow 1
#define __riscv_compressed 1
#define __riscv_div 1
#define __riscv_fdiv 1
#define __riscv_flen 64
#define __riscv_float_abi_double 1
#define __riscv_fsqrt 1
#define __riscv_mul 1
#define __riscv_muldiv 1
#define __riscv_xlen 32
64 bits
$ ./riscv-none-embed-gcc -march=rv64i -mabi=lp64 -E -dM - < /dev/null | egrep -i 'risc|fp[^-]|version|abi|lp' | sort
#define _LP64 1
#define __GXX_ABI_VERSION 1011
#define __LP64__ 1
#define __STDC_VERSION__ 201112L
#define __VERSION__ "7.1.1 20170509"
#define __riscv 1
#define __riscv_cmodel_medlow 1
#define __riscv_float_abi_soft 1
#define __riscv_xlen 64
$ ./riscv-none-embed-gcc -march=rv64imac -mabi=lp64 -E -dM - < /dev/null | egrep -i 'risc|fp[^-]|version|abi|lp' | sort
#define _LP64 1
#define __GXX_ABI_VERSION 1011
#define __LP64__ 1
#define __STDC_VERSION__ 201112L
#define __VERSION__ "7.1.1 20170509"
#define __riscv 1
#define __riscv_atomic 1
#define __riscv_cmodel_medlow 1
#define __riscv_compressed 1
#define __riscv_div 1
#define __riscv_float_abi_soft 1
#define __riscv_mul 1
#define __riscv_muldiv 1
#define __riscv_xlen 64
$ ./riscv-none-embed-gcc -march=rv64imafdc -mabi=lp64d -E -dM - < /dev/null | egrep -i 'risc|fp[^-]|version|abi|lp' | sort
#define _LP64 1
#define __FP_FAST_FMA 1
#define __FP_FAST_FMAF 1
#define __GXX_ABI_VERSION 1011
#define __LP64__ 1
#define __STDC_VERSION__ 201112L
#define __VERSION__ "7.1.1 20170509"
#define __riscv 1
#define __riscv_atomic 1
#define __riscv_cmodel_medlow 1
#define __riscv_compressed 1
#define __riscv_div 1
#define __riscv_fdiv 1
#define __riscv_flen 64
#define __riscv_float_abi_double 1
#define __riscv_fsqrt 1
#define __riscv_mul 1
#define __riscv_muldiv 1
#define __riscv_xlen 64
Misc
#define __riscv_xlen 32
#define __riscv_xlen 64
#define __riscv_cmodel_medlow 1
#define __riscv_cmodel_medany 1
#define __riscv_float_abi_soft 1
#define __riscv_float_abi_single 1
#define __riscv_float_abi_double 1
The gp
(Global Pointer) register
The gp
(Global Pointer) register is a solution to further optimise memory accesses within a single 4KB region.
The linker uses the __global_pointer$
symbol definition to compare the memory addresses and, if within range, it replaces absolute/pc-relative addressing with gp-relative addressing, which makes the code more efficient. This process is also called relaxing, and can be disabled by -Wl,--no-relax
.
$ cat main.c
int i;
int main()
{
return i;
}
$ riscv-none-embed-gcc main.c --save-temps
$ cat main.s
...
lui a5,%hi(i)
lw a5,%lo(i)(a5)
...
$ riscv-none-embed-objdump -d a.out
...
101b4: 8341a783 lw a5,-1996(gp) # 11fdc <i>
...
The gp
register should be loaded during startup with the address of the __global_pointer$
symbol and should not be changed later.
.section .reset_entry,"ax",@progbits
.align 1
.globl _reset_entry
.type _reset_entry, @function
_reset_entry:
.option push
.option norelax
la gp, __global_pointer$
.option pop
la sp, __stack
j _start
The 4K region can be anywhere in the addressed memory, but, for the optimisation to be effective, it should preferably cover the most intensely used RAM area. For standard newlib applications, this is the area where the .sdata
section is allocated, since it includes variables like _impure_ptr
, __malloc_sbrk_base
, etc. Thus, the definition should be placed right before the .sdata
section. For example:
PROVIDE( __global_pointer$ = . + (4K / 2) );
*(.sdata .sdata.*)
The region size is 4K because RISC-V immediate values are 12-bit signed values, which are +/- 2048 in decimal or +/- 0x800 in hex; since the values are signed, the __global_pointer$
must point to the middle of the region.