内存和寄存器的分配
函数的底层实现
在C语言中,子程序被称为函数
C程序本质上是函数的集合
把C程序翻译到RISC-V机器上,需要理解C函数
的翻译过程
ABI
操作系统为运行在该系统下的应用程序提供了
应用程序二进制接口(Application Binary
Interface,ABI)
ABI包含了应用程序在这个系统下运行时必须
遵守的编程约定
包括系统调用的规定,以及关于程序可以使用的
内存和寄存器的规定
Linux ABI
C程序,编译到RISC-V计算机时,必须遵守操
作系统ABI规定
GNU为RISC-V计算机定义了Linux操作系统下的
ABI
RV32I内存分配
内存分配
代码区:程序,程序计数器PC
为C的变量分配存储空间:静态数据区和运行
时栈
静态数据区:静态存储类变量,寄存器x3
运行时栈:局部变量,x2指向栈顶
堆:动态分配数据
系统空间:操作系统,如自陷处理例程,自陷
向量表,I/O设备寄存器的内存映射地址等。
寄存器分配
寄存器的访问比存储器快得多
RISC-V算术/逻辑运算指令,对寄存器进行运算
应尽量多的使用寄存器
RV32I寄存器分配
寄存器 用途 ABI助记符 保存/恢复
x0 0 zero 无
x1 返回地址 ra (Return Address)
x2 栈指针 sp (Stack Pointer) callee-save
x3 全局指针 gp (Global Pointer) 无
x4 线程指针 tp (Thread Pointer) 无
x5~x7 临时值 t0~t2 (Temporary)
x8 保存寄存器/帧指针 s0/fp (Frame Pointer) callee-save
x9 保存寄存器 s1 (Saved) callee-save
x10~x11 参数/返回值 a0~a1 (Argument)
x12~x17 参数 a2~a7 (Argument)
x18~x27 保存寄存器 s2~s11 (Saved) callee-save
x28~x31 临时值 t3~t6 (Temporary)
保存寄存器
x8~x9,x18~x27:需要保存的寄存器,例如局
部变量,其中,x8也可用于帧指针
这里的局部变量指的是基本数据类型的变量
x5~x7和x28~x31:存储临时产生的值
助记符
ABI定义
在RISC-V汇编指令中,可以使用助记符
寄存器保存/恢复
局部变量必须由被调用者保存/恢复,即采用
callee-save策略
被调用者知道使用了哪些寄存器存储局部变量
栈指针如果被修改,采用callee-save策略保存/
恢复
x0,x3和x4中的值是不变的,不需要保存/恢复
其他寄存器,视情况可采用caller-save策略保
存/恢复
为变量分配内存
静态存储类变量,分配到内存中
数组、结构体等类型的变量,分配到内存中
当寄存器数量不足时
例如,当局部变量(基本数据类型)个数多于12个,
或者参数个数多于8个
需要使用内存
寄存器的保存/恢复,需要使用内存
静态数据区
静态存储类变量
全局变量
关键字static声明的静态变量
静态数据区示例
……
int a;
int b;
int main()
{
……
}
变量距离gp/x3的偏移量
a: 0
b: 1
t0/x5b
lw t0, 4(gp)
运行时栈
局部变量/自动存储类变量(基本数据类型)
数组、结构体等
寄存器保存/恢复
x2/sp,栈指针
运行时栈示例
……
int main()
{
int x[10];
/*对数组x进行初始化*/
……
/*输出累加和*/
printf("sum = %d \n", sum);
}
数组x是局部变量,被分配到栈中
把变量x[9]加载到临时寄存器x5/t0中
lw t0, 36(sp)
帧指针
更方便的访问运行时栈中的变量
帧指针
作为基址
x8/fp
t0 x[9]
假设x[9]距离fp的偏移量为-2,在fp和x[9]之
间存储的都是整数类型的值
lw t0, -8(fp)
函数的栈帧
函数的栈帧/活动记录
从帧指针所指的单元到栈指针所指的单元之间的
这段存储空间
问题:
在函数的栈帧中,除了局部变量,还存储了哪些
内容?
答案:与函数的调用过程有关