Name | ABI Mnemonic | Meaning | Preserved across calls? |
---|---|---|---|
x0 |
zero |
Zero |
— (Immutable) |
x1 |
ra |
Return address |
No |
x2 |
sp |
Stack pointer |
Yes |
x3 |
gp |
Global pointer |
— (Unallocatable) |
x4 |
tp |
Thread pointer |
— (Unallocatable) |
x5 - x7 |
t0 - t2 |
Temporary registers |
No |
x8 - x9 |
s0 - s1 |
Callee-saved registers |
Yes |
x10 - x17 |
a0 - a7 |
Argument registers |
No |
x18 - x27 |
s2 - s11 |
Callee-saved registers |
Yes |
x28 - x31 |
t3 - t6 |
Temporary registers |
No |
In the standard ABI, procedures should not modify the integer registers tp and gp, because signal handlers may rely upon their values.
The presence of a frame pointer is optional. If a frame pointer exists, it must reside in x8 (s0); the register remains callee-saved.
If a platform requires use of a dedicated general-purpose register for a platform-specific purpose, it is recommended to use gp (x3). The platform ABI specification must document the use of this register. For such platforms, care must be taken to ensure all code (compiler generated or otherwise) avoids using gp in a way incompatible with the platform specific purpose, and that global pointer relaxation is disabled in the toolchain.
The presence of a frame pointer is optional. If a frame pointer exists, it must reside in x8 (s0); the register remains callee-saved.
Code that uses a frame pointer will construct a linked list of stack frames, where each frame links to its caller using a "frame record". A frame record consists of two XLEN values on the stack; the return address and the link to the next frame record. The frame pointer register will point to the innermost frame, thereby starting the linked list. By convention, the lowest XLEN value shall point to the previous frame, while the next XLEN value shall be the return address. The end of the frame record chain is indicated by the address zero appearing as the next link in the chain.
After the prologue, the frame pointer register will point to the Canonical
Frame Address or CFA, which is the stack pointer value on entry to the current
procedure. The previous frame pointer and return address pair will reside just
prior to the current stack address held in fp
. This puts the return address
at fp - XLEN/8
, and the previous frame pointer at fp - 2 * XLEN/8
.
It is left to the platform to determine the level of conformance with this convention. A platform may choose:
-
not to maintain a frame chain and use the frame pointer register as a general purpose callee-saved register.
-
to allow the frame pointer register be used as a general purpose callee-saved register, but provide a platform specific mechanism to reliably detect this condition.
-
to use a frame pointer to address a valid frame record at all times, but allow any procedure to choose to forgo creating a frame record.
-
to use the frame pointer to address a valid frame record at all times, except leaf functions, who may elect to forgo creating a frame record.
Name | ABI Mnemonic | Meaning | Preserved across calls? |
---|---|---|---|
f0 - f7 |
ft0 - ft7 |
Temporary registers |
No |
f8 - f9 |
fs0 - fs1 |
Callee-saved registers |
Yes* |
f10 - f17 |
fa0 - fa7 |
Argument registers |
No |
f18 - f27 |
fs2 - fs11 |
Callee-saved registers |
Yes* |
f28 - f31 |
ft8 - ft11 |
Temporary registers |
No |
*: Floating-point values in callee-saved registers are only preserved across calls if they are no larger than the width of a floating-point register in the targeted ABI. Therefore, these registers can always be considered temporaries if targeting the base integer calling convention.
The Floating-Point Control and Status Register (fcsr) must have thread storage duration in accordance with C11 section 7.6 "Floating-point environment <fenv.h>".
Name | ABI Mnemonic | Meaning | Preserved across calls? |
---|---|---|---|
v0-v31 |
Temporary registers |
No |
|
vl |
Vector length |
No |
|
vtype |
Vector data type register |
No |
|
vxrm |
Vector fixed-point rounding mode register |
No |
|
vxsat |
Vector fixed-point saturation flag register |
No |
Name | ABI Mnemonic | Meaning | Preserved across calls? |
---|---|---|---|
v0 |
Argument register |
No |
|
v1-v7 |
Callee-saved registers |
Yes |
|
v8-v23 |
Argument registers |
No |
|
v24-v31 |
Callee-saved registers |
Yes |
|
vl |
Vector length |
No |
|
vtype |
Vector data type register |
No |
|
vxrm |
Vector fixed-point rounding mode register |
No |
|
vxsat |
Vector fixed-point saturation flag register |
No |
*: Functions that use vector registers to pass arguments and return values must
follow this calling convention. Some programming languages can require extra
functions to follow this calling convention (e.g. C/C++ functions with
attribute riscv_vector_cc
).
Please refer to the Standard Vector Calling Convention Variant section for more details about standard vector calling convention variant.
The vxrm
and vxsat
fields of vcsr
are not preserved across calls and their
values are unspecified upon entry.
Procedures may assume that vstart
is zero upon entry. Procedures may assume
that vstart
is zero upon return from a procedure call.
Note
|
Application software should normally not write vstart explicitly.
Any procedure that does explicitly write vstart to a nonzero value must zero
vstart before either returning or calling another procedure.
|
This chapter defines standard calling conventions and standard calling convention variants, and describes how to pass arguments and return values.
Functions must follow the register convention defined in calling convention: the
contents of any register without specifying it as an argument register
in the calling convention are unspecified upon entry, and the content of any
register without specifying it as a return value register or callee-saved in
the calling convention are unspecified upon exit, the contents of all
callee-saved registers must be restored to what was set on entry, and the
contents of any fixed registers like gp
and tp
never change,
Note
|
Calling convention for big-endian is NOT included in this specification yet, we intend to define that in future version of this specification. |
The base integer calling convention provides eight argument registers, a0-a7, the first two of which are also used to return values.
Scalars that are at most XLEN bits wide are passed in a single argument register, or on the stack by value if none is available. When passed in registers or on the stack, integer scalars narrower than XLEN bits are widened according to the sign of their type up to 32 bits, then sign-extended to XLEN bits. When passed in registers or on the stack, floating-point types narrower than XLEN bits are widened to XLEN bits, with the upper bits undefined.
Scalars that are 2×XLEN bits wide are passed in a pair of argument registers, with the low-order XLEN bits in the lower-numbered register and the high-order XLEN bits in the higher-numbered register. If no argument registers are available, the scalar is passed on the stack by value. If exactly one register is available, the low-order XLEN bits are passed in the register and the high-order XLEN bits are passed on the stack.
Scalars wider than 2×XLEN bits are passed by reference and are replaced in the argument list with the address.
Aggregates whose total size is no more than XLEN bits are passed in a register, with the fields laid out as though they were passed in memory. If no register is available, the aggregate is passed on the stack. Aggregates whose total size is no more than 2×XLEN bits are passed in a pair of registers; if only one register is available, the first XLEN bits are passed in a register and the remaining bits are passed on the stack. If no registers are available, the aggregate is passed on the stack. Bits unused due to padding, and bits past the end of an aggregate whose size in bits is not divisible by XLEN, are undefined.
Aggregates or scalars passed on the stack are aligned to the greater of the type alignment and XLEN bits, but never more than the stack alignment.
Aggregates larger than 2×XLEN bits are passed by reference and are replaced in the argument list with the address, as are C++ aggregates with nontrivial copy constructors, destructors, or vtables.
Empty structs or union arguments or return values are ignored by C compilers which support them as a non-standard extension. This is not the case for C++, which requires them to be sized types.
Bitfields are packed in little-endian fashion. A bitfield that would span the
alignment boundary of its integer type is padded to begin at the next
alignment boundary. For example, struct { int x : 10; int y : 12; }
is
a 32-bit type with x
in bits 9-0, y
in bits 21-10, and bits 31-22
undefined. By contrast, struct { short x : 10; short y : 12; }
is a 32-bit
type with x
in bits 9-0, y
in bits 27-16, and bits 31-28 and bits 15-10
undefined.
Bitfields may larger than its integer type, bits excess than its integer
type will treat as padding bits, then padding to begin at the next alignment
boundary. For example struct { char x : 9; char y; }
is a 24 byte type with
x
in bits 7-0, y
in bit 23-16, and bits 15-8 undefined,
struct { char x : 9; char y : 2 }
is a 16-bit type with x
in bits 7-0, y
in bit 10-9, and bit 8, bits 15-11 is undefined.
Arguments passed by reference may be modified by the callee.
Floating-point reals are passed the same way as aggregates of the same size; complex floating-point numbers are passed the same way as a struct containing two floating-point reals. (This constraint changes when the integer calling convention is augmented by the hardware floating-point calling convention.)
In the base integer calling convention, variadic arguments are passed in the same manner as named arguments, with one exception. Variadic arguments with 2×XLEN-bit alignment and size at most 2×XLEN bits are passed in an aligned register pair (i.e., the first register in the pair is even-numbered), or on the stack by value if none is available. After a variadic argument has been passed on the stack, all future arguments will also be passed on the stack (i.e. the last argument register may be left unused due to the aligned register pair rule).
Values are returned in the same manner as a first named argument of the same type would be passed. If such an argument would have been passed by reference, the caller allocates memory for the return value, and passes the address as an implicit first parameter.
Note
|
There is no requirement that the address be returned from the function and so software should not assume that a0 will hold the address of the return value on return. |
The stack grows downwards (towards lower addresses) and the stack pointer shall be aligned to a 128-bit boundary upon procedure entry. The first argument passed on the stack is located at offset zero of the stack pointer on function entry; following arguments are stored at correspondingly higher addresses.
In the standard ABI, the stack pointer must remain aligned throughout procedure execution. Non-standard ABI code must realign the stack pointer prior to invoking standard ABI procedures. The operating system must realign the stack pointer prior to invoking a signal handler; hence, POSIX signal handlers need not realign the stack pointer. In systems that service interrupts using the interruptee’s stack, the interrupt service routine must realign the stack pointer if linked with any code that uses a non-standard stack-alignment discipline, but need not realign the stack pointer if all code adheres to the standard ABI.
Procedures must not rely upon the persistence of stack-allocated data whose addresses lie below the stack pointer.
Registers s0-s11 shall be preserved across procedure calls. No floating-point registers, if present, are preserved across calls. (This property changes when the integer calling convention is augmented by the hardware floating-point calling convention.)
The hardware floating-point calling convention adds eight floating-point argument registers, fa0-fa7, the first two of which are also used to return values. Values are passed in floating-point registers whenever possible, whether or not the integer registers have been exhausted.
The remainder of this section applies only to named arguments. Variadic arguments are passed according to the integer calling convention.
ABI_FLEN refers to the width of a floating-point register in the ABI. The ABI_FLEN must be no wider than the ISA’s FLEN. The ISA might have wider floating-point registers than the ABI.
For the purposes of this section, "struct" refers to a C struct with its
hierarchy flattened, including any array fields. That is, struct { struct
{ float f[1]; } a[2]; }
and struct { float f0; float f1; }
are
treated the same. Fields containing empty structs or unions are ignored while
flattening, even in C++, unless they have nontrivial copy constructors or
destructors. Fields containing zero-length bit-fields or zero-length arrays are
ignored while flattening. Attributes such as aligned
or packed
do not
interfere with a struct’s eligibility for being passed in registers according
to the rules below, i.e. struct { int i; double d; }
and struct
__attribute__((__packed__)) { int i; double d }
are treated the same, as are
struct { float f; float g; }
and struct { float f; float g __attribute__
((aligned (8))); }
.
Note
|
One exceptional case for the flattening rule is an array of empty
structs or unions; C treats it as an empty field, but C++
treats it as a non-empty field since C++ defines the size of an empty struct
or union as 1. i.e. for struct { struct {} e[1]; float f; } as the first
argument, C will treat it like struct { float f; } and pass f in fa0 as
described below, whereas C++ will pass the pass the entire aggregate in a0
(XLEN = 64) or a0 and a1 (XLEN = 32), as described in the integer calling
convention.
Zero-length arrays of empty structs or union will be
ignored for both C and C++. i.e. For struct { struct {} e[0]; float f; }; ,
as the first argument, C and C++ will treat it like struct { float f; }
and pass f in fa0 as described below.
|
A real floating-point argument is passed in a floating-point argument register if it is no more than ABI_FLEN bits wide and at least one floating-point argument register is available. Otherwise, it is passed according to the integer calling convention. When a floating-point argument narrower than FLEN bits is passed in a floating-point register, it is 1-extended (NaN-boxed) to FLEN bits.
A struct containing just one floating-point real is passed as though it were a standalone floating-point real.
A struct containing two floating-point reals is passed in two floating-point registers, if neither real is more than ABI_FLEN bits wide and at least two floating-point argument registers are available. (The registers need not be an aligned pair.) Otherwise, it is passed according to the integer calling convention.
A complex floating-point number, or a struct containing just one complex floating-point number, is passed as though it were a struct containing two floating-point reals.
A struct containing one floating-point real and one integer (or bitfield), in either order, is passed in a floating-point register and an integer register, provided the floating-point real is no more than ABI_FLEN bits wide and the integer is no more than XLEN bits wide, and at least one floating-point argument register and at least one integer argument register is available. If the struct is passed in this manner, and the integer is narrower than XLEN bits, the remaining bits are unspecified. If the struct is not passed in this manner, then it is passed according to the integer calling convention.
Unions are never flattened and are always passed according to the integer calling convention.
Values are returned in the same manner as a first named argument of the same type would be passed.
Floating-point registers fs0-fs11 shall be preserved across procedure calls, provided they hold values no more than ABI_FLEN bits wide.
The RISC-V V Vector Extension[riscv-v-extension] defines a set of thirty-two vector registers, v0-v31. The RISC-V Vector Extension Intrinsic Document[rvv-intrinsic-doc] defines vector types which include vector mask types, vector data types, and tuple vector data types. A value of vector type can be stored in vector register groups.
The remainder of this section applies only to named vector arguments, other named arguments and return values follow the standard calling convention. Variadic vector arguments are passed by reference.
v0 is used to pass the first vector mask argument to a function, and to return vector mask result from a function. v8-v23 are used to pass vector data arguments, tuple vector data arguments and the rest vector mask arguments to a function, and to return vector data and vector tuple results from a function.
It must ensure that the entire contents of v1-v7 and v24-v31 are preserved across the call.
Each vector data type and vector tuple type has an LMUL attribute that
indicates a vector register group. The value of LMUL indicates the number of
vector registers in the vector register group and requires the first vector
register number in the vector register group must be a multiple of it. For
example, the LMUL of vint64m8_t
is 8, so v8-v15 vector register group can be
allocated to this type, but v9-v16 can not because the v9 register number is
not a multiple of 8. If LMUL is less than 1, it is treated as 1. If it is a
vector mask type, its LMUL is 1.
Each vector tuple type also has an NFIELDS attribute that indicates how many vector register groups the type contains. Thus a vector tuple type needs to take up LMUL×NFIELDS registers.
The rules for passing vector arguments are as follows:
-
For the first vector mask argument, use v0 to pass it.
-
For vector data arguments or rest vector mask arguments, starting from the v8 register, if a vector register group between v8-v23 that has not been allocated can be found and the first register number is a multiple of LMUL, then allocate this vector register group to the argument and mark these registers as allocated. Otherwise, pass it by reference and are replaced in the argument list with the address.
-
For tuple vector data arguments, starting from the v8 register, if NFIELDS consecutive vector register groups between v8-v23 that have not been allocated can be found and the first register number is a multiple of LMUL, then allocate these vector register groups to the argument and mark these registers as allocated. Otherwise, pass it by reference and are replaced in the argument list with the address.
Note
|
The registers assigned to the tuple vector data argument must be
consecutive. For example, for the function
void foo(vint32m1_t a, vint32m2_t b, vint32m1x2_t c) , v8 will be allocated
to a , v10-v11 will be allocated to b , v12-v13 instead of v9 and v12 will
beallocated to c .
|
Note
|
It should be stressed that the search for the appropriate vector register
groups starts at v8 each time and does not start at the next register after the
registers are allocated for the previous vector argument. Therefore, it is
possible that the vector register number allocated to a vector argument can be
less than the vector register number allocated to previous vector arguments.
For example, for the function
void foo (vint32m1_t a, vint32m2_t b, vint32m1_t c) , according to the rules
of allocation, v8 will be allocated to a , v10-v11 will be allocated to b
and v9 will be allocated to c . This approach allows more vector registers to
be allocated to arguments in some cases.
|
Vector values are returned in the same manner as the first named argument of the same type would be passed.
Vector types are disallowed in struct or union.
Vector arguments and return values are disallowed to pass to an unprototyped function.
Note
|
Functions that use the standard vector calling convention variant must be
marked with STO_RISCV_VARIANT_CC , see [Dynamic Linking] for the meaning of
STO_RISCV_VARIANT_CC .
|
Note
|
setjmp /longjmp follow the standard calling convention, which clobbers
all vector registers. Hence, the standard vector calling convention variant
won’t disrupt the jmp_buf ABI.
|
Important
|
RV32E is not a ratified base ISA and so we cannot guarantee the stability of ILP32E, in contrast with the rest of this document. This documents the current implementation in GCC as of the time of writing, but may be subject to change. |
The ILP32E calling convention is designed to be usable with the RV32E ISA. This calling convention is the same as the integer calling convention, except for the following differences. The stack pointer need only be aligned to a 32-bit boundary. Registers x16-x31 do not participate in the calling convention, so there are only six argument registers, a0-a5, only two callee-saved registers, s0-s1, and only three temporaries, t0-t2.
If used with an ISA that has any of the registers x16-x31 and f0-f31, then these registers are considered temporaries.
The ILP32E calling convention is not compatible with ISAs that have registers that require load and store alignments of more than 32 bits. In particular, this calling convention must not be used with the D ISA extension.
This specification defines the following named ABIs:
- ILP32
-
Integer calling-convention only, hardware floating-point calling convention is not used (i.e. ELFCLASS32 and EF_RISCV_FLOAT_ABI_SOFT).
- ILP32F
-
ILP32 with hardware floating-point calling convention for ABI_FLEN=32 (i.e. ELFCLASS32 and EF_RISCV_FLOAT_ABI_SINGLE).
- ILP32D
-
ILP32 with hardware floating-point calling convention for ABI_FLEN=64 (i.e. ELFCLASS32 and EF_RISCV_FLOAT_ABI_DOUBLE).
- ILP32E
-
ILP32E calling-convention only, hardware floating-point calling convention is not used (i.e. ELFCLASS32, EF_RISCV_FLOAT_ABI_SOFT, and EF_RISCV_RVE).
- LP64
-
Integer calling-convention only, hardware floating-point calling convention is not used (i.e. ELFCLASS64 and EF_RISCV_FLOAT_ABI_SOFT).
- LP64F
-
LP64 with hardware floating-point calling convention for ABI_FLEN=32 (i.e. ELFCLASS64 and EF_RISCV_FLOAT_ABI_SINGLE).
- LP64D
-
LP64 with hardware floating-point calling convention for ABI_FLEN=64 (i.e. ELFCLASS64 and EF_RISCV_FLOAT_ABI_DOUBLE).
- LP64Q
-
LP64 with hardware floating-point calling convention for ABI_FLEN=128 (i.e. ELFCLASS64 and EF_RISCV_FLOAT_ABI_QUAD).
The ILP32* ABIs are only compatible with RV32* ISAs, and the LP64* ABIs are only compatible with RV64* ISAs. A future version of this specification may define an ILP32 ABI for the RV64 ISA, but currently this is not a supported operating mode.
The *F ABIs require the *F ISA extension, the *D ABIs require the *D ISA extension, and the LP64Q ABI requires the Q ISA extension.
Note
|
This means code targeting the Zfinx extension always uses the ILP32, ILP32E or LP64 integer calling-convention only ABIs as there is no dedicated hardware floating-point register file. |
While various different ABIs are technically possible, for software compatibility reasons it is strongly recommended to use the following default ABIs for specific architectures:
Note
|
Although RV64GQ systems can technically use LP64Q, it is strongly recommended to use LP64D on general-purpose RV64GQ systems for compatibility with standard RV64G software. |
The calling convention for system calls does not fall within the scope of this document. Please refer to the documentation of the RISC-V execution environment interface (e.g OS kernel ABI, SBI).
There are two conventions for C/C++ type sizes and alignments.
- ILP32, ILP32F, ILP32D, and ILP32E
-
Use the following type sizes and alignments (based on the ILP32 convention):
Table 5. C/C++ type sizes and alignments for RV32 Type Size (Bytes) Alignment (Bytes) bool/_Bool
1
1
char
1
1
short
2
2
int
4
4
long
4
4
long long
8
8
void *
4
4
__bf16
2
2
_Float16
2
2
float
4
4
double
8
8
long double
16
16
float _Complex
8
4
double _Complex
16
8
long double _Complex
32
16
- LP64, LP64F, LP64D, and LP64Q
-
Use the following type sizes and alignments (based on the LP64 convention):
Table 6. C/C++ type sizes and alignments for RV64 Type Size (Bytes) Alignment (Bytes) bool/_Bool
1
1
char
1
1
short
2
2
int
4
4
long
8
8
long long
8
8
__int128
16
16
void *
8
8
__bf16
2
2
_Float16
2
2
float
4
4
double
8
8
long double
16
16
float _Complex
8
4
double _Complex
16
8
long double _Complex
32
16
The alignment of max_align_t
is 16.
CHAR_BIT
is 8.
Structs and unions are aligned to the alignment of their most strictly aligned member. The size of any object is a multiple of its alignment.
char
is unsigned.
Booleans (bool
/_Bool
) stored in memory or when being passed as scalar
arguments are either 0
(false
) or 1
(true
).
A null pointer (for all types) has the value zero.
_Float16
is as defined in the C ISO/IEC TS 18661-3 extension.
__bf16
has the same parameter passing and return rules as for _Float16
.
_Complex
types have the same layout as a struct containing two fields of the
corresponding real type (float
, double
, or long double
), with the first
member holding the real part and the second member holding the imaginary part.
The type size_t
is defined as unsigned int
for RV32 and unsigned long
for RV64.
The type ptrdiff_t
is defined as int
for RV32 and long
for RV64.
The va_list
type is void*
. A callee with variadic arguments is responsible
for copying the contents of registers used to pass variadic arguments to the
vararg save area, which must be contiguous with arguments passed on the stack.
The va_start
macro initializes its va_list
argument to point to the start
of the vararg save area. The va_arg
macro will increment its va_list
argument according to the size of the given type, taking into account the
rules about 2×XLEN aligned arguments being passed in "aligned" register pairs.
Note
|
This section of the RISC-V calling convention specification only applies to Linux-based systems. |
In order to ensure compatibility between different implementations of the C library for Linux, we provide some extra definitions which only apply on those systems. These are noted in this section.
The following definitions apply for all ABIs defined in this document. Here there is no differentiation between ILP32 and LP64 ABIs.
Type | Size (Bytes) | Alignment (Bytes) |
---|---|---|
wchar_t |
4 |
4 |
wint_t |
4 |
4 |
-
[riscv-v-extension] "RISC-V V vector extension specification" https://github.com/riscv/riscv-v-spec
-
[rvv-intrinsic-doc] "RISC-V Vector Extension Intrinsic Document" https://github.com/riscv-non-isa/rvv-intrinsic-doc