- 一.C数据类型和对齐
- 二.RVG调用约定
- 三.Soft-Float调用约定
- 四.总结
- 使用编译器生成汇编代码分析调用过程
- 五.参考
一.C数据类型和对齐
所有数据保持自然对齐。
ILP32,LP64
C type | Description | Bytes in RV32 | Bytes in RV64 |
---|---|---|---|
char/unsigned char | 8-bit unsigned integer,zero-extended | 1 | 1 |
signed char | 8-bit signed integer,sign-extended | ||
short | 16-bit signed integer,sign-extended | 2 | 2 |
unsigned short | 16-bit unsigned integer,zeroextended | ||
int | int都是32位 | 4 | 4 |
long | 指针和long和整数寄存器一样宽 | 4 | 8 |
long long | long long都是64位 | 8 | 8 |
void * | 指针和long和整数寄存器一样宽 | 4 | 8 |
float | 32-bit IEEE 754-2008 | 4 | 4 |
double | 64-bit IEEE 754-2008 | 8 | 8 |
long double | 128-bit IEEE floating-point | ||
IEEE floating-point | 16 | 16 |
在RV64中,32位类型不管是int还是unsigned都是符号扩展到64位。
二.RVG调用约定
a0-a7
,fa0-fa7
:用于函数传递参数,其中0-1
用于返回值,a
表示arguments
。
都是调用者负责保存,因为是传参肯定是在函数调用前就要准备好,所部不可能是被调用者去负责保存。
- 如果函数参数为结构体的字段,每一个都是指针对齐的,则参数寄存器是结构体前面8个指针字
pointer-words
的影子shadow
。
如果是小于8个的浮点值,则使用fai
传递;小于8个的整数则使用ai
传递。
如果浮点参数是联合体unions
的字段,或者结构体的数组字段,则使用整数寄存器传递。
另外可变参数函数中除了显示指定的参数外的参数,如果是浮点数也是使用整数寄存器传递。
- 小于指针字
pointer-word
的参数使用低位传递,子指针字sub-pointer-word
的参数通过栈传递时,使用指针字pointer-word
的低地址,因为RISC-V
是小端的存储系统。 - 当原始参数两倍于指针字
pointer-word
时通过栈传递,使用自然对齐。当它们使用整数寄存器传递时,使用对齐的偶-奇
寄存器对,偶寄存器存低位。比如RV32
的void foo(int, long long)
使用a0
传递第一个参数,a2-a3
传递第二个参数,因为由偶寄存器对齐,且a2
存低位,返回值通过a0
传递。 - 两倍于指针字
pointer-word
的参数通过引用传递。 - 结构体中部分参数未使用整数寄存器传递的使用栈传递,栈指针
sp
指向第一个未使用整数寄存器传递的参数。 a0,a1
,fa0,fa1
用于函数返回值。只有结构体成员只有一个或者两个浮点成员,或者primitives
时才使用浮点寄存器返回;其他的由a0-a1
组成的两倍指针字的two pointer-words
大小返回;更大的返回值通过内存传递;调用者负责分配这个内存,并传递指向该内存的指针,隐含的作为第一个参数传递给被调用者。- 标准
RISC-V
调用中,栈向下生长,并且保持16字节对齐。 - 7个临时整数寄存器
t0-t6
,12个临时浮点寄存器ft0-ft11
在调用过程是可变的,如果后面需要使用则必须由调用者负责保存。其中t
表示Temporaries
。
- 这里有点疑惑,临时寄存器是被调用者使用的,只有被调用者才知道自己要用哪些寄存器,为什么不是被调用者负责保存?
这样理解,因为这些寄存器是可变的,对于被调用者来说既然是可变的则可以随便使用,也就是可能被被调用者修改,所以对于调用者来说,如果这些寄存器的值不能被破坏则自己需要负责保存。
- 12个整数寄存器
s0-s11
,12个浮点寄存器fs0-fs11
在调用过程是必须保持的,所以如果被调用者需要使用则必须由被调用者保存。
实际上上面的8
和9.
,t
和s
寄存器的可变volatile
和保持preserved
是对被调用者来说的,也就是对被调用者申明,告诉被调用者,
t
这些寄存器是可变的,那么被调用者可以随便使用,此时调用者则必须考虑被被调用者随便使用而修改,需要调用者保存;
s
这些寄存器是保持的,那么被调用者不能随便使用,如果要用就要负责保存。
所以对于a
寄存器也可以这样理解,因为a
寄存器用于传递参数,所以是被调用者随便使用的,即不保持的,所以需要调用者负责保存,并赋参数值。
Register | ABI Name | Description | Saver |
---|---|---|---|
x0 | zero | 硬件固定为0 | / |
x1 | ra | 返回地址 | Caller调用者 |
x2 | sp | 栈指针 | Callee被调用者 |
x3 | gp | 全局指针 | / |
x4 | tp | 线程指针 | / |
x5-x7 | t0-t2 | 临时使用 | Caller调用者 |
x8 | s0/fp | 保存寄存器/帧指针 | Callee被调用者 |
x9 | s1 | 保存寄存器 | Callee被调用者 |
x10-x11 | a0-a1 | 函数参数/返回值 | Caller调用者 |
x12-x17 | a2-a7 | 函数参数 | Caller调用者 |
x18-x27 | s2-s11 | 保存寄存器 | Callee被调用者 |
x28-x31 | t3-t6 | 临时使用 | Caller调用者 |
f0-f7 | ft0-ft7 | FP临时使用 | Caller调用者 |
f8-f9 | fs0-fs1 | FP保存寄存器 | Callee被调用者 |
f10-f11 | fa0-fa1 | FP函数参数/返回值 | Caller调用者 |
f12-f17 | fa2-fa7 | FP参数 | Caller调用者 |
f18-f27 | fs2-fs11 | FP保存寄存器 | Callee被调用者 |
f28-f31 | ft8-ft11 | FP临时使用 | Caller调用者 |
三.Soft-Float调用约定
在没有浮点硬件,或者不使用F
,D
,Q
扩展的硬件浮点,不使用浮点寄存器,完全由软件实现浮点。
整数参数的传入和返回值和RVG一样。
浮点参数和返回值,通过整数寄存器传递,原则是使用大小相同的整数寄存器传递。
比如RV32
的
double foo(int, double, long double)
则第一个参数通过a0
传递;
第二个参数通过a2
和a3
传递;
第三个参数通过a4
传引用传递;
结果通过a0
和a1
传递。
如果是RV64
则
则第一个参数通过a0
传递;
第二个参数通过a1
传递;
第三个参数通过a2-a3
传递;
结果通过a0
传递。
动态舍入模式和产生的异常标志通过C99
的fenv.h
提供的接口访问。
四.总结
从以下几个部分去理解
- 寄存器
理解函数参数的传递与返回值,a0-a1,a2-a7
,fa0-fa1,fa2-fa7
,0-1
用于返回值。
理解ra
寄存器,函数的返回地址
理解SP
栈指针,理解栈的向下生长,理解进入子函数时减少sp
分配空间,分配的空间用于存储s
寄存器和局部变量使用,和退出子函数时增加sp
恢复sp
。也就是调用完子函数返回后sp
要保持不变。
理解t0-t6
,ft0-ft11
;s0-s11
.fs0-fs11
,这里重点站在被调用者角度去理解可变和保持,进而理解谁负责保存寄存器。
- 函数调用
jal ra label
或者jal ra rd imm
简化为伪指令jal label
或者jalr rd
(立即数为0)。jal
跳转即将PC + 4
存储到ra
寄存器,即函数返回后的下一条执行的指令。jalr
类似只是设置PC
为rd + imm
。
注意与无条件跳转jal x0 label
和jalr x0 rd imm
,伪指令j label
,jr rd
(立即数为0)的区别,无条件跳转是不返回了的所以不保存返回地址到ra
,而是保存到了x0
寄存器,而x0
寄存器是硬件固定为0的,所以相当于不保存,
两者指令是统一的,这也体现了RISC-V指令设计的简洁统一的美学。
其中jal
的l
可以理解为link
,类似于ARM
的LR
寄存器的L
。
- 进入和退出函数
除非使用栈传递参数,否则子函数返回后sp
必须保持不变。
所有的s
寄存器在子函数返回后必须保持,这也是其保持的含义,也是为什么被调用者需要负责保存。
子函数退出时返回ra
处执行
函数进入时的处理:减少sp
,s
寄存器个数和局部变量大小的空间,存储使用到的s
寄存器到栈中。如果还有子函数调用则存储ra
到栈中(因为子函数的子函数的返回值要存到ra
会覆盖ra
)。
函数退出时的处理:恢复栈中保存的s
寄存器,更新sp
值。如果有需要恢复ra
值,恢复sp
值到函数进入之前的值,返回到ra
处执行。
最好通过编写c
代码,使用编译工具生成汇编代码,对照c
和汇编代码的方式去理解。
五.参考
-
代码
+关注
关注
30文章
4780浏览量
68539 -
编译器
+关注
关注
1文章
1624浏览量
49111 -
数据类型
+关注
关注
0文章
236浏览量
13618 -
RISC-V
+关注
关注
45文章
2271浏览量
46134
发布评论请先 登录
相关推荐
评论