汇编函数的调用约定

文章目录

汇编的调用约定

函数调用过程的概述
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语言里面调用汇编代码
在这里插入图片描述

  • 汇编

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# 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代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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

汇编函数的调用约定
http://example.com/2022/11/05/汇编函数的调用约定/
作者
Zevin
发布于
2022年11月5日
许可协议