1、调用者保存寄存器与被调用者保存寄存器
- 假设:函数A调用了函数B,寄存器x在函数B中被修改了,对于A函数而言,逻辑上x内容在调用函数B的前后应该保持一致。现在需要解决前后不一致的问题,有两种思路:
- 第一种:调用者保存的寄存器
- 在函数A在调用函数B之前提前把寄存器x的值存入栈中,执行完函数B之后再恢复x的内容。
- 在函数跳转前保存的寄存器,叫调用者保存寄存器
- 第二种:被调用者保存的寄存器
- 函数B在使用寄存器x前,先保存寄存器x的值到栈中,在函数B返回之前,要恢复寄存器x原来存储
的内容
- 在函数中,使用前必须保存、返回前必须恢复的寄存器
- 至于怎么划分寄存器是调用者保存,还是被调用者保存,这是芯片架构的函数调用规范决定的,写C语言的程序员不用感知。想了解RISC-V架构的函数调用规范,可参考文章:《RISC-V架构的函数调用规范和栈布局》;
2、异常向量表的访问方式:direct mode、vector mode
3、attribute((interrupt))的作用
c
复制代码
__attribute__((interrupt)) void my_interrupt_function(void)
{
...... //中断处理
return;
}
- 在使用中断的vector模式时,需要将中断处理函数写到对应的异常向量表处,当产生中断时,硬件将自动跳转
- 中断处理就涉及到中断现场的保存和恢复,vector模式的中断处理函数一般C语言写的,那是否需要在中断处理函数里去写保存/恢复中断现场的代码?答案是不需要,只需要再中断处理函数前使用"attribute((interrupt))"修饰
- 作用:编译器将C语言写的中断处理函数翻译成汇编代码时,会自动插入保存/恢复现场的汇编代码。也就是把中断处理函数中会用到的寄存器都先存入栈中,在中断返回前,再从栈里面读出来恢复到寄存器中
4、汇编代码分析
- 同样的C语言函数,对比使用/不使用__attribute__((interrupt))修饰得到的反汇编文件
- 可以看到,使用__attribute__((interrupt))修饰后,汇编代码会多申请栈空间用于保存/恢复现场
5、总结
- 不使用__attribute__((interrupt))修饰,函数内只需要保存
被调用者保存的寄存器
- 使用__attribute__((interrupt))修饰后,只要在函数内使用到的寄存器都必须保存再恢复,会占用更多的栈空间,翻译得到的汇编代码也会增多
- 还可以指定中断处理函数的优化等级:
__attribute__((interrupt, optimize("O0")))
- 总结:只有中断处理函数才用__attribute__((interrupt))修饰,其余常规函数不要使用