调用约定¶
本章描述了针对Linx程序的C/C++编译器标准的调用约定
数据类型与对齐¶
下表总结了Linx原生支持的数据类型。
| C类型 | 描述 | Linx字节数 |
|---|---|---|
| char | 字符/字节值 | 1 |
| short | 短整数 | 2 |
| int | 整数 | 4 |
| long | 长整数 | 8 |
| long long | 长长整数 | 8 |
| void* | 指针 | 8 |
| float | 单精度浮点数 | 4 |
| double | 双精度浮点数 | 8 |
| long double | 扩展精度浮点数 | 16 |
- char和unsigned char为8位无符号整数,存入Linx整数寄存器时进行零扩展。unsigned short为16位无符号整数,存入寄存器时同样零扩展
- signed char为8位有符号整数,存入寄存器时进行符号扩展(即高位63至7位全部填充符号位)
- short为16位有符号整数,存入寄存器时进行符号扩展。
- 32位类型(如int)存入整数寄存器时,会作为32位值的符号扩展存储,即第63至31位均与符号位相同。这一限制对无符号32位类型同样适用。编译器及合规软件在内存中存储上述数据类型时均保持自然对齐。
调用约定¶
Linx调用约定优先通过寄存器传递参数,最多使用8个整数寄存器(a0–a7)。若将函数参数视为具有指针对齐的C结构体字段,参数寄存器对应结构体的前5个指针字。但结构体中联合体或数组内的浮点字段需通过整数寄存器传递。此外,可变参数函数(显式声明的参数除外)的浮点参数也通过整数寄存器传递。
小于指针字长的参数存放在参数寄存器的低位。在小端内存系统中,栈上传递的此类参数在指针字的低位地址对齐。
双倍指针字长的基本类型参数在栈上传递时自然对齐;在整数寄存器中传递时,需占用对齐的奇偶寄存器对,偶寄存器存放低位。
超过双倍指针字长的参数通过引用传递。未通过寄存器传递的参数部分通过栈传递,栈指针sp指向第一个未通过寄存器传递的参数。
函数返回值通过整数寄存器a0和a1。其他不超过两个指针字的值通过a0和a1返回,更大的返回值通过内存传递:调用方分配内存并通过隐式首参传递指针。
标准Linx调用约定中,栈向下增长且栈指针始终保持16字节对齐。
除参数和返回值寄存器外,3个整数寄存器x0–x3,调用后可能失效,需调用方保存;8个整数寄存器s0–s7为保留寄存器,需被调用方保存。下表详述了寄存器的调用约定角色。
| 寄存器 | ABI名称 | 描述 |
|---|---|---|
| r0 | zero | 零寄存器 |
| r1 | sp | 栈指针寄存器 |
| r2 | a0 | 函数参数0 |
| r3 | a1 | 函数参数1 |
| r4 | a2 | 函数参数2 |
| r5 | a3 | 函数参数3 |
| r6 | a4 | 函数参数4 |
| r7 | a5 | 函数参数5 |
| r8 | a6 | 函数参数6 |
| r9 | a7 | 函数参数7 |
| r10 | ra | 返回地址寄存器 |
| r11 | fp(s0) | 帧寄存器/子函数寄存器0 |
| r12 | s1 | 子函数寄存器1 |
| r13 | s2 | 子函数寄存器2 |
| r14 | s3 | 子函数寄存器3 |
| r15 | s4 | 子函数寄存器4 |
| r16 | s5 | 子函数寄存器5 |
| r17 | s6 | 子函数寄存器6 |
| r18 | s7 | 子函数寄存器7 |
| r19 | s8 | 子函数寄存器8 |
| r20 | x0 | 父函数保存其他寄存器0 |
| r21 | x1 | 父函数保存其他寄存器1 |
| r22 | x2 | 父函数保存其他寄存器2 |
| r23 | x3 | 父函数保存其他寄存器3 |