汇编的调用约定
函数调用过程的概述
stack frame
调用一个函数的时候,就会压入栈帧里面,在调用A函数后,再调用B函数,B函数的地址就会继续压栈,当B函数处理完之后,在栈帧里面的B地址就会出栈,入宫函数过多的话,有可能会出现爆栈的情况
函数调用约定
- caller就是一个函数调用者,callee就是被调用函数
- 在caller中调用call,调用了callee,callee执行完之后返回,返回到调用call的下一个地址
这里有一个问题,函数调用的时候,那些函数的调用参数,和函数的返回值应该放在哪里呢?
这就是我们下面要解决的问题
函数调用过程中的编程约定
有关寄存器的编程约定
x0-x31就是寄存器的最初始化的名字,最开始的32个寄存器
ABI就是在函数的时候吧这些寄存器普遍化修改的别名,主要使用的就是这些别名,我们之后都是使用这些别名,更好的进行理解
N/A:就是not available,没有人维护
ra:return address,就是jal函数返回后的下一个地址,由调用者来进行维护,因为函数返回之后要达到对应的位置
sp:stack pointer:就是用来存放栈指针,由被调用者来维护,因为出栈和入栈主要就是由被调用的子函数来执行
t:temporary:临时寄存器,用来函数调用者保存函数的一些临时变量
s :save :保存寄存器,就是和t是反过来的,对于调用者来说,要保证save里面的值在调用前和调用后值是不变的
a:argument参数寄存器,用于在函数时候保存函数的参数,以及传递返回值
我们在写risc-v的时候,一般使用a0,a1来进行返回
使用a0-a7来进行传递函数的参数
函数跳转和返回指令的编程约定
(1)
(2)
我们要将函数的ra寄存器保存在栈里面,避免之后调用的时候这个ra寄存器没了,寄存器没了的话,函数返回地址就没了,不知道返回到那里,所以我们要用s寄存器来保存ra的值,退栈的时候恢复ra的值
在c语言里面调用汇编代码
- 汇编
示例
# Calling Convention
# Demo how to write nested routines
#
# void _start()
# {
# // calling nested routine
# aa_bb(3, 4);
# }
#
# int aa_bb(int a, int b)
# {
# return square(a) + square(b);
# }
#
# int square(int num)
# {
# return num * num;
# }
.text # Define beginning of text section
.global _start # Define entry _start,这个_start是一个全局的标签地址
_start:
la sp, stack_end # prepare stack for calling functions ,同样进入主函数之后就开辟了栈内存空间
# aa_bb(3, 4);
li a0, 3
li a1, 4 # 给函数两个参数的值,传参
call aa_bb # 一个3,一个4
stop:
j stop # Infinite loop to stop execution
# int aa_bb(int a, int b)
# return a^2 + b^2
aa_bb:
# prologue
addi sp, sp, -16 # 压栈
sw s0, 0(sp) # 这里把s0,s2保存起来,函数的参数,以及栈指针,把sp+0存储到s0里面
sw s1, 4(sp)
sw s2, 8(sp)
sw ra, 12(sp) # 这里还要保存ra的值,sw 是存字,把 ra 的低位四字节存入地址 rs1+立即数中。
# cp and store the input params
mv s0, a0
mv s1, a1
# sum will be stored in s2 and is initialized as zero
li s2, 0
mv a0, s0
jal square # 这里是尾调用,所以就不会回来了,如果不保存ra的话,就会把ra的地址给改调了
add s2, s2, a0 # 上一层函数函数运行的结果的在a0里面,因为这就是用来处理函数参数和返回值的寄存器
mv a0, s1
jal square
add s2, s2, a0 # a0里面放的就是第二个参数调用函数处理的结果,放到s2里面
mv a0, s2 # 再把s2的值放到参数寄存器里面,返回
# epilogue
lw s0, 0(sp) # 恢复寄存器,退出栈帧
lw s1, 4(sp)
lw s2, 8(sp)
lw ra, 12(sp)
addi sp, sp, 16 # 把栈镇退出
ret # 返回
# int square(int num)
square:
# prologue
addi sp, sp, -8
sw s0, 0(sp)# 因为这里是最后一次调用函数所以不用存储函数的返回值ra
sw s1, 4(sp)
# `mul a0, a0, a0` should be fine,
# programing as below just to demo we can contine use the stack
mv s0, a0
mul s1, s0, s0
mv a0, s1
# epilogue
lw s0, 0(sp)
lw s1, 4(sp)
addi sp, sp, 8
ret
# add nop here just for demo in gdb
nop
# allocate stack space,开辟了栈空间
stack_start:
.rept 12
.word 0
.endr
stack_end:
.end # End of file
在汇编代码里面调用c代码
# C all ASM
.text # Define beginning of text section
.global _start # Define entry _start
.global foo # foo is a C function defined in test.c,c函数定义到这里
_start:
la sp, stack_end # prepare stack for calling functions
li a0, 1
li a1, 2
call foo # 调用c函数
stop:
j stop # Infinite loop to stop execution
nop # just for demo effect
stack_start:
.rept 12
.word 0
.endr
stack_end:
.end # End of file