Register Convention
Name | Alias | Meaning | Preserved across calls |
---|---|---|---|
|
|
Constant zero |
(Constant) |
|
|
Return address |
No |
|
|
Thread pointer |
(Non-allocatable) |
|
|
Stack pointer |
Yes |
|
|
Argument registers / return value registers |
No |
|
|
Argument registers |
No |
|
|
Temporary registers |
No |
|
Reserved |
(Non-allocatable) |
|
|
|
Frame pointer / Static register |
Yes |
|
|
Static registers |
Yes |
Name | Alias | Meaning | Preserved across calls |
---|---|---|---|
|
|
Argument registers / return value registers |
No |
|
|
Argument registers |
No |
|
|
Temporary registers |
No |
|
|
Static registers |
Yes |
Temporary registers are also known as caller-saved registers. Static registers are also known as callee-saved registers.
Aliases for return value registers
You may see the names $v0
$v1
$fv0
$fv1
in some very early LoongArch
assembly code; they simply alias to $a0
$a1
$fa0
$fa1
respectively.
The aliases are initially meant to match MIPS convention with separate
argument / return value registers. However, because LoongArch actually has no
dedicated return value registers, such usage may lead to confusion.
Hence, it is not recommended to use these aliases.
Due to implementation details, it may not be easy to give a register multiple ABI names for a given downstream project. New programs processing LoongArch assembly should not support these aliases. Portable LoongArch assembly should avoid these aliases.
Note
|
For toolchain components provided by the Loongson Corporation, the migration procedure is: Let the version of the component at this spec’s effect date be N,
For the respective upstream projects of the components, the procedure above shall be followed if support for such usage is already upstream; "version N" shall be interpreted as the first release version containing LoongArch support in that case. For the components not yet upstream, and not interacting with other components that may expect such usage, support for such usage will never be implemented. |
Type Size and Alignment
Scalar type | Size (Bytes) | Alignment (Bytes) |
---|---|---|
|
1 |
1 |
|
1 |
1 |
|
2 |
2 |
|
4 |
4 |
|
8 |
8 |
|
8 |
8 |
pointer types |
8 |
8 |
|
4 |
4 |
|
8 |
8 |
|
16 |
16 |
Scalar type | Size (Bytes) | Alignment (Bytes) |
---|---|---|
|
1 |
1 |
|
1 |
1 |
|
2 |
2 |
|
4 |
4 |
|
4 |
4 |
|
8 |
8 |
pointer types |
4 |
4 |
|
4 |
4 |
|
8 |
8 |
|
16 |
16 |
For all base ABI types of LoongArch,
the char
datatype is signed by default.
ELF Object Files
All common ELF definitions referenced in this section can be found in the latest SysV gABI specification.
EI_CLASS: File class
EI_CLASS | Value | Description |
---|---|---|
|
|
ELF32 object file |
|
|
ELF64 object file |
e_machine: Identifies the machine
LoongArch (258)
e_flags: Identifies ABI type and version
Bit 31 - 8 | Bit 7 - 6 | Bit 5 - 3 | Bit 2 - 0 |
---|---|---|---|
(reserved) |
ABI version |
ABI extension |
Base ABI Modifier |
The ABI type of an ELF object is uniquely identified by EI_CLASS
and e_flags[7:0]
in its header.
Within this combination, EI_CLASS
and e_flags[2:0]
correspond to the base ABI type,
where the expression of C integral and pointer types (data model)
is uniquely determined by EI_CLASS
value, and e_flags[2:0]
represents additional properties
of the base ABI type, including the FP calling convention. We refer to e_flags[2:0]
as
the base ABI modifier.
As a result, programs in lp64*
/ ilp32*
ABI should only be encoded with ELF64 / ELF32
object files, respectively.
0x0
0x4
0x5
0x6
0x7
are reserved values for e_flags[2:0]
.
Name | EI_CLASS | Base ABI Modifier (e_flags[2:0] ) |
Description |
---|---|---|---|
|
|
|
Uses 64-bit GPRs and the stack for parameter passing.
Data model is LP64, where |
|
|
|
Uses 64-bit GPRs, 32-bit FPRs and the stack for parameter passing.
Data model is LP64, where |
|
|
|
Uses 64-bit GPRs, 64-bit FPRs and the stack for parameter passing.
Data model is LP64, where |
|
|
|
Uses 32-bit GPRs and the stack for parameter passing.
Data model is ILP32, where |
|
|
|
Uses 32-bit GPRs, 32-bit FPRs and the stack for parameter passing.
Data model is ILP32, where |
|
|
|
Uses 32-bit GPRs, 64-bit FPRs and the stack for parameter passing.
Data model is ILP32, where |
e_flags[5:3]
correspond to the ABI extension type.
Name | e_flags[5:3] | Description |
---|---|---|
|
|
No extra ABI features. |
|
(reserved) |
e_flags[7:6]
marks the ABI version of an ELF object.
ABI version | Value | Description |
---|---|---|
|
|
Stack operands base relocation type. |
|
|
Supporting relocation types directly writing to immediate slots. Can be implemented separately without compatibility with v0. |
|
Reserved. |
Relocations
Enum | ELF reloc type | Usage | Detail |
---|---|---|---|
0 |
|
||
1 |
|
Runtime address resolving |
|
2 |
|
Runtime address resolving |
|
3 |
|
Runtime fixup for load-address |
|
4 |
|
Runtime memory copy in executable |
|
5 |
|
Runtime PLT supporting |
implementation-defined |
6 |
|
Runtime relocation for TLS-GD |
|
7 |
|
Runtime relocation for TLS-GD |
|
8 |
|
Runtime relocation for TLS-GD |
|
9 |
|
Runtime relocation for TLS-GD |
|
10 |
|
Runtime relocation for TLE-IE |
|
11 |
|
Runtime relocation for TLE-IE |
|
12 |
|
Runtime local indirect function resolving |
|
… Reserved for dynamic linker. |
|||
20 |
|
Mark la.abs |
Load absolute address for static link. |
21 |
|
Mark external label branch |
Access PC relative address for static link. |
22 |
|
Push PC-relative offset |
|
23 |
|
Push constant or absolute address |
|
24 |
|
Duplicate stack top |
|
25 |
|
Push for access GOT entry |
|
26 |
|
Push for TLS-LE |
|
27 |
|
Push for TLS-IE |
|
28 |
|
Push for TLS-GD |
|
29 |
|
Push for external function calling |
|
30 |
|
Assert stack top |
|
31 |
|
Stack top operation |
|
32 |
|
Stack top operation |
|
33 |
|
Stack top operation |
|
34 |
|
Stack top operation |
|
35 |
|
Stack top operation |
|
36 |
|
Stack top operation |
|
37 |
|
Stack top operation |
|
38 |
|
Instruction imm-field relocation |
with check 5-bit signed overflow |
39 |
|
Instruction imm-field relocation |
with check 12-bit unsigned overflow |
40 |
|
Instruction imm-field relocation |
with check 12-bit signed overflow |
41 |
|
Instruction imm-field relocation |
with check 16-bit signed overflow |
42 |
|
Instruction imm-field relocation |
with check 18-bit signed overflow and 4-bit aligned |
43 |
|
Instruction imm-field relocation |
with check 20-bit signed overflow |
44 |
|
Instruction imm-field relocation |
with check 23-bit signed overflow and 4-bit aligned |
45 |
|
Instruction imm-field relocation |
with check 28-bit signed overflow and 4-bit aligned |
46 |
|
Instruction fixup |
with check 32-bit unsigned overflow |
47 |
|
8-bit in-place addition |
|
48 |
|
16-bit in-place addition |
|
49 |
|
24-bit in-place addition |
|
50 |
|
32-bit in-place addition |
|
51 |
|
64-bit in-place addition |
|
52 |
|
8-bit in-place subtraction |
|
53 |
|
16-bit in-place subtraction |
|
54 |
|
24-bit in-place subtraction |
|
55 |
|
32-bit in-place subtraction |
|
56 |
|
64-bit in-place subtraction |
|
57 |
|
GNU C++ vtable hierarchy |
|
58 |
|
GNU C++ vtable member usage |
|
… Reserved |
|||
64 |
|
18-bit PC-relative jump |
with check 18-bit signed overflow and 4-bit aligned |
65 |
|
23-bit PC-relative jump |
with check 23-bit signed overflow and 4-bit aligned |
66 |
|
28-bit PC-relative jump |
with check 28-bit signed overflow and 4-bit aligned |
67 |
|
[31 … 12] bits of 32/64-bit absolute address |
|
68 |
|
[11 … 0] bits of 32/64-bit absolute address |
|
69 |
|
[51 … 32] bits of 64-bit absolute address |
|
70 |
|
[63 … 52] bits of 64-bit absolute address |
|
71 |
|
[31 … 12] bits of 32/64-bit PC-relative offset |
|
72 |
|
[11 … 0] bits of 32/64-bit address |
|
73 |
|
[51 … 32] bits of 64-bit PC-relative offset |
|
74 |
|
[63 … 52] bits of 64-bit PC-relative offset |
|
75 |
|
[31 … 12] bits of 32/64-bit PC-relative offset to GOT entry |
|
76 |
|
[11 … 0] bits of 32/64-bit GOT entry address |
|
77 |
|
[51 … 32] bits of 64-bit PC-relative offset to GOT entry |
|
78 |
|
[63 … 52] bits of 64-bit PC-relative offset to GOT entry |
|
79 |
|
[31 … 12] bits of 32/64-bit GOT entry absolute address |
|
80 |
|
[11 … 0] bits of 32/64-bit GOT entry absolute address |
|
81 |
|
[51 … 32] bits of 64-bit GOT entry absolute address |
|
82 |
|
[63 … 52] bits of 64-bit GOT entry absolute address |
|
83 |
|
[31 … 12] bits of TLS LE 32/64-bit offset from TP register |
|
84 |
|
[11 … 0] bits of TLS LE 32/64-bit offset from TP register |
|
85 |
|
[51 … 32] bits of TLS LE 64-bit offset from TP register |
|
86 |
|
[63 … 52] bits of TLS LE 64-bit offset from TP register |
|
87 |
|
[31 … 12] bits of 32/64-bit PC-relative offset to TLS IE GOT entry |
|
88 |
|
[11 … 0] bits of 32/64-bit TLS IE GOT entry address |
|
89 |
|
[51 … 32] bits of 64-bit PC-relative offset to TLS IE GOT entry |
|
90 |
|
[63 … 52] bits of 64-bit PC-relative offset to TLS IE GOT entry |
|
91 |
|
[31 … 12] bits of 32/64-bit TLS IE GOT entry absolute address |
|
92 |
|
[11 … 0] bits of 32/64-bit TLS IE GOT entry absolute address |
|
93 |
|
[51 … 32] bits of 64-bit TLS IE GOT entry absolute address |
|
94 |
|
[63 … 52] bits of 64-bit TLS IE GOT entry absolute address |
|
95 |
|
[31 … 12] bits of 32/64-bit PC-relative offset to TLS LD GOT entry |
|
96 |
|
[31 … 12] bits of 32/64-bit TLS LD GOT entry absolute address |
|
97 |
|
[31 … 12] bits of 32/64-bit PC-relative offset to TLS GD GOT entry |
|
98 |
|
[31 … 12] bits of 32/64-bit TLS GD GOT entry absolute address |
|
99 |
|
32-bit PC relative |
|
100 |
|
Instruction can be relaxed, paired with a normal relocation at the same address |
Program Interpreter Path
Base ABI type | ABI extension type | Operating system / C library | Program interpreter path |
---|---|---|---|
|
|
Linux, Glibc |
|
|
|
Linux, Glibc |
|
|
|
Linux, Glibc |
|
|
|
Linux, Glibc |
|
|
|
Linux, Glibc |
|
|
|
Linux, Glibc |
|
Procedure Calling Convention
Abbreviations
In this document, GRLEN is the bit width of general-purpose register, FRLEN is the bit width of floating-point register and WOA is the bit width of the argument. The general-purpose argument register is denoted as GAR and the floating-point argument register is denoted as FAR.
Argument Registers
The basic principle of the LoongArch procedure calling convention is to pass arguments in registers as much as possible (i.e. floating-point arguments are passed in floating-point registers and non floating-point arguments are passed in general-purpose registers, as much as possible); arguments are passed on the stack only when no appropriate register is available.
The argument registers are:
-
Eight floating-point registers
fa0-fa7
used for passing pass floating-point arguments, andfa0-fa1
are also used to return values. -
Eight general-purpose registers
a0-a7
used for passing pass integer arguments, witha0-a1
reused to return values.
Generally, the GARs are used to pass fixed-point arguments, and floating-point arguments when no FAR is available.
Bit fields are stored in little endian.
In addition, subroutines should ensure that the values of general-purpose registers s0-s9
and floating-point registers fs0-fs7
are preserved across procedure calls.
ABI LP64D
That is, GRLEN = 64, FRLEN = 64.
C Data Types and Alignment
The C data types and alignment in the LP64D ABI are defined in the table 3.
In most cases, the unsigned integer data types are zero-extended when stored in general-purpose register, and the signed integer data types are sign-extended.
However, in the LP64D ABI, unsigned 32-bit types, such as unsigned int
, are stored in general-purpose registers as proper sign extensions of their 32-bit values.
Argument passing
Generally speaking, FARs are only used to pass floating-point arguments, GARs are used to pass non floating-point arguments and floating-point arguments when no FAR is available(long double
type is also passed in a pair of GARs) and the reference.
Arguments passed by reference may be modified by the callee.
Scalar
There are two cases:
-
0 < WOA ≤ GRLEN
-
Argument is passed in a single argument register, or on the stack by value if none is available.
-
If the argument is floating-point type, the argument is passed in FAR. if no FAR is available, it’s passed in GAR. If no GAR is available, it’s passed on the stack. When passed in registers or on the stack, floating-point types narrower than GRLEN bits are widened to GRLEN bits, with the upper bits undefined.
-
If the argument is integer or pointer type, the argument is passed in GAR. If no GAR is available, it’s passed on the stack. When passed in registers or on the stack, the unsigned integer scalars narrower than GRLEN bits are zero-extended to GRLEN bits, and the signed integer scalars are sign-extended.
-
-
-
GRLEN < WOA ≤ 2 × GRLEN
-
The argument is passed in a pair of GAR, with the low-order GRLEN bits in the lower-numbered register and the high-order GRLEN bits in the higher-numbered register. If exactly one register is available, the low-order GRLEN bits are passed in the register and the high-order GRLEN bits are passed on the stack. If no GAR is available, it’s passed on the stack.
-
Structure
Empty structures are ignored by C compilers which support them as a non-standard extension(same as union arguments and return values). Bits unused due to padding, and bits past the end of a structure whose size in bits is not divisible by GRLEN, are undefined. And the layout of the structure on the stack is consistent with that in memory.
-
0 < WOA ≤ GRLEN
-
The structure has only fixed-point members. If there is an available GAR, the structure is passed through the GAR by value passing; If no GAR is available, it’s passed on the stack.
-
The structure has only floating-point members:
-
One floating-point member. The argument is passed in a FAR; If no FAR is available, the value is passed in a GAR; if no GAR is available, the value is passed on the stack.
-
Two floating-point members. The argument is passed in a pair of available FAR, with the low-order
float
member bits in the lower-numbered FAR and the high-orderfloat
member bits in the higher-numbered FAR. If the number of available FAR is less than 2, it’s passed in a GAR, and passed on the stack if no GAR is available.
-
-
The structure has both fixed-point and floating-point members, i.e. the structure has one
float
member and…-
Multiple fixed-point members. If there are available GAR, the structure is passed in a GAR, and passed on the stack if no GAR is available.
-
Only one fixed-point member. If one FAR and one GAR are available, the floating-point member of the structure is passed in the FAR, and the integer member of the structure is passed in the GAR; If no floating-point register but one GAR is available, it’s passed in GAR; If no GAR is available, it’s passed on the stack.
-
-
-
GRLEN < WOA ≤ 2 × GRLEN
-
Only fixed-point members.
-
The argument is passed in a pair of available GAR, with the low-order bits in the lower-numbered GAR and the high-order bits in the higher-numbered GAR. If only one GAR is available, the low-order bits are in the GAR and the high-order bits are on the stack, and passed on the stack if no GAR is available.
-
-
Only floating-point members.
-
The structure has one
long double
member or onedouble
member and two adjacentfloat
members or 3-4float
members. The argument is passed in a pair of available GAR, with the low-order bits in the lower-numbered GAR and the high-order bits in the higher-numbered GAR. If only one GAR is available, the low-order bits are in the GAR and the high-order bits are on the stack, and passed on the stack if no GAR is available. -
The structure with two
double
members is passed in a pair of available FARs. If no a pair of available FARs, it’s passed in GARs. A structure with onedouble
member and onefloat
member is same.
-
-
Both fixed-point and floating-point members.
-
The structure has one
double
member and only one fixed-point member.-
If one FAR and one GAR are available, the floating-point member of the structure is passed in the FAR, and the integer member of the structure is passed in the GAR; If no floating-point registers but two GARs are available, it’s passed in the two GARs; If only one GAR is available, the low-order bits are in the GAR and the high-order bits are on the stack; And it’s passed on the stack if no GAR is available.
-
-
Others
-
The argument is passed in a pair of available GAR, with the low-order bits in the lower-numbered GAR and the high-order bits in the higher-numbered GAR. If only one GAR is available, the low-order bits are in the GAR and the high-order bits are on the stack, and passed on the stack if no GAR is available.
-
-
-
-
WOA > 2 × GRLEN
-
It’s passed by reference and are replaced in the argument list with the address. If there is an available GAR, the reference is passed in the GAR, and passed on the stack if no GAR is available.
-
Structure and scalars passed on the stack are aligned to the greater of the type alignment and GRLEN bits, but never more than the stack alignment.
Union
Union is passed in GAR or stack.
-
0 < WOA ≤ GRLEN
-
The argument is passed in a GAR, or on the stack by value if no GAR is available.
-
-
GRLEN < WOA ≤ 2 × GRLEN
-
The argument is passed in a pair of available GAR, with the low-order bits in the lower-numbered GAR and the high-order bits in the higher-numbered GAR. If only one GAR is available, the low-order bits are in the GAR and the high-order bits are on the stack. The arguments are passed on the stack when no GAR is available.
-
-
WOA > 2 × GRLEN
-
It’s passed by reference and are replaced in the argument list with the address. If there is an available GAR, the reference is passed in the GAR, and passed on the stack if no GAR is available.
-
Complex
A complex floating-point number, or a structure containing just one complex floating-point number, is passed as though it were a structure containing two floating-point reals.
Variadic arguments
Variadic arguments are passed in GARs in the same manner as named arguments. And 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.
-
0 < WOA ≤ GRLEN
-
The variadic arguments are passed in a GAR, or on the stack by value if no GAR is available.
-
-
GRLEN < WOA ≤ 2 × GRLEN
-
The variadic arguments are passed in a pair of GARs. If only one GAR is available, the low-order bits are in the GAR and the high-order bits are on the stack, and passed on the stack if no GAR is available. or on the stack by value if none is available. It should be noted that
long double
data tpye is passed in an aligned GAR pair(the first register in the pair is even-numbered).
-
-
WOA > 2 × GRLEN
-
It’s passed by reference and are replaced in the argument list with the address. If there is an available GAR, the reference is passed in the GAR, and passed on the stack if no GAR is available.
-
Return values
-
Generally speaking,
a0
anda1
are used to return non floating-point values, andfa0
andfa1
are used to return floating-point values. -
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 argument.
-
The reference of the return value is returned that is stored in GAR
a0
if the size of the return value is larger than 2×GRLEN bits.
Stack
-
In general, the stack frame for a subroutine may contain space to contain the following:
-
Space to store arguments passed to subroutines that this subroutine calls.
-
A place to store the subroutine’s return address.
-
A place to store the values of saved registers.
-
A place for local data storage.
-
-
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.
-
Procedures must not rely upon the persistence of stack-allocated data whose addresses lies below the stack pointer.
Appendix: Revision History
-
v1.00
-
Add register usage convention, data type conventions and the list of ELF relocation types.
-
-
v2.00
-
Add description of ILP32 data model.
-
Add description of return value register aliases.
-
Add relocation types with direct immediate-filling semantics.
-
Add ABI version porting guidelines for toolchain implementations.
-
Add link to SysV gABI documentation.
-
Adjust asciidoc code style.
-
-
v2.01
-
Adjust description of ABI type encoding scheme.
-
Add header for all tables.
-