RISC-V ISA 学习笔记(4)函数调用约定+RV32G列表及对应的汇编
本帖最后由 皋陶 于 2020-8-28 10:53 编辑RISC-V ISA 学习笔记(1) 指令集介绍及基本指令集RV32I v2.0
RISC-V ISA 学习笔记(2) 乘除法标准扩展“M”和原子扩展“A” v2.01. 整数乘除法的标准扩展“M”
RISC-V ISA 学习笔记(3) 单精度浮点标准扩展 “F” v2.0
RISC-V ISA 学习笔记(4) 函数调用约定+RV32G列表及对应的汇编
RISC-V ISA 学习笔记(4)函数调用约定+RV32G列表及对应的汇编伪指令表1. 调用约定
首先函数调用约定是给C编译器用的,用于约定怎么处理函数及其参数的传递,对想用汇编写完整C函数的开发者来说是很有用的。包括RV32G和缺少浮点单元的RV32I上的软浮点约定。
不过最新版本的指令集中已经移除调用约定这一部分,而是被移动到了GitHub文档RISCV-elf-psabi-doc中。
(1)数据类型及对齐
RISC-V C天然支持的数据类型包括 (u)char、(u)short、(u)int、(u)long、(u)long long、void、float、double和long double,长度从1~8字节不等。RV32G,任何数据类型的指针宽度都是32位。
还有就是RSIC-V上暂没有原生的低字长处理指令char/uchar short/ushort等低精度类型的数据会被当作32位的数据来处理,即在从内存载入数据到寄存器时char/short通过带符号扩展实现,而 uchar/ushort 则通过0扩展来实现。
编译器和兼容软件在保存上述数据类型时一般会保证自然对齐(即其起始内存地址必须是其类型本身长度的整数倍)。
(2)RV32G调用约定
1.RISC-V的函数调用约定,尽可能优先使用寄存器来传递参数。默认有8个整数寄存器a0-a7和8个浮点寄存器fa0-fa7可以用,其中前两个(a0,a1)、(fa0,fa1)也用来返回值传递。
函数参数是结构体时,则参数每一个字段按指针长度对齐,参数寄存器中保存的就是结构体最前面8个指针字长的参数数据。
浮点类型的参数将通过浮点寄存器fai传递,否则通过整数寄存器ai传递。
union类型的一部分或者struct数组的浮点参数将通过整数寄存器传递。
变长函数(variadic fuction)的所有参数(包括浮点)都通过整数寄存器传递。
比一个指针字长小的参数,通过参数寄存器的LSB传递。对应的通过栈传递的小于指针字的参数将出现在指针字的较低地址上(RISC-V默认为小端存储器系统)。
指针字的两倍长的参数通过栈传递的时候数据是自然对齐的,通过整数寄存器传递时,它们被放置在对齐的偶-奇( even-odd)寄存器对中, 其中的偶数编号寄存器保存了LSB。
例如在RV32中,函数void foo(int, long long)通过a0传递它 的第一个参数,通过a2、a3传递第二个参数。寄存器a1中没有东西。 大小大于两倍指针字的参数,通过引用(reference)传递。
结构体中不能通过寄存器传递的部分参数将通过栈传递,栈指针sp指向第一个没有在寄存器中的参数。
函数的返回值存放在整数寄存器a0、a1或者浮点寄存器fa0、fa1中。不过只有当它们是原始参数或者是只包含1个或者2个浮点值的struct的时候,浮点值才通过浮点寄存器返回,
其他两个指针字长类型的返回值放入a0和a1,更大的则全部通过存储器返回(此时这些存储器由调用者分配,并将它作为第一个隐藏参数传递给被调用者)。
在RISC-V调用约定标准中,栈是向下增长并且栈指针总是16字节对齐。
除了参数寄存器和返回值寄存器之外,7个整数寄存器t0-t6和12个浮点寄存器ft0-ft11是临时寄存器,它们在可以在调用过程中被修改,如果后面还有使用的话,则主调者必须先保存起来。
12个整数寄存器s0-s11和12个浮点寄存器fs0-fs11在调用过程后被保持不变,如果被调者需要用的话,使用前需要先保存,使用后恢复。下表指明了在调用约定中每个整数寄存器和浮点寄存器扮演的角色。
(3)软浮点调用约定
软浮点调用约定用于缺少硬件浮点单元的RV32和RV64实现中。它避免使用所有的F、D、Q 标准扩展中的指令,以及f寄存器。整型参数如同RVG那样被传递和返回,并且栈的规则也相同,除了栈只需要对齐到XLEN/8字节边界(例如,对RV32I是4字节对齐) 。
因为在栈上的浮点数据是通过整数 load和store访问的 ,所以在软浮点调用约定中不需要在更粗的粒度上维护栈对齐。减小栈对齐可以节约储存器空间需求,通常在存储器紧张的系统中使用。
浮点参数通过整数寄存器传递和返回,使用相同大小整数参数相同的规则。例如在RV32 中,函数double foo(int, double, long double)通过a0传递第一个参数,通过a2和a3传递第二个参数,通过a4传递第三个参数的引用;它的结果在a0和a1中返回。在RV64中,参数通过a0、a1、a2-a3对,来传递,结果从a0返回。
由于硬件缺少fcsr寄存器,动态舍入模式和异常标志的设置/查询失效,只能通过C99头文件 “fenv.h” 提供的例程来代替。
2. RV32G 指令集列表
在笔记(1)中有讲过,RV32G 是由基本指令子集“I”,及标准扩展指令集整数乘除指令子集“M”+原子操作子集“A”+单精度浮点子集“F“+双精度浮点“D”组成。所以本节权当成前面几节的总结了。
再复习一下,几乎所有类型的操作指令最后面都是7bit的操作码,但是没有单独讲,这里单独列出操作码的映射规则。
具有3个或者更多最低位被置为1的主要操作码,保留给长度超过32位的指令。
标记为reserved的操作码应当避免在定制的指令集扩展中使用,因为它们可能被未来的标准扩展使用。 在标准32位指令格式中,标记为custom-0和 custom-1的主要操作码会避免被未来的标准扩展使用,因此推荐给定制指令集扩展使用。 标记为custom-2/rv128和custom-3/rv128的操作码,保留给未来的RV128使用,但不会给标准扩展使用,因此也可以用于RV32和RV64的定制指令集扩展。
除了G和压缩指令子集C外,以后新增的指令趋向于更加领域专用,并仅对某些类型的应用程序有益,例如对多媒体或者安全。与绝大多数商业ISA不同,RISC-V ISA 设计清晰地分离了基本ISA、适用面很广的标准扩展和专用的增强子集。接下来就是列表了
指令格式
暂时就这么多了,不要问为啥没有64位的,我用不到→_→…。
3. RISC-V指令对应的汇编伪指令列表
搞软件的程序员还是最经常和汇编伪指令打交道,所以伪指令列表特别重要。
暂时就没有后续了。。。
本篇完,感谢关注:RISC-V单片机中文网
页:
[1]