• 汇编函数的调用约定


    汇编的调用约定

    函数调用过程的概述
    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
    
    • 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

    在汇编代码里面调用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
    
    • 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
  • 相关阅读:
    1230: 蜂巢
    【C++】图的存储 -- 邻接表
    酷开系统更多惊喜,尽享畅快观影之旅
    电子电气架构设计需要考虑哪些方面?
    Java面试题精选21到31
    fast lio 2 保存每一帧的点云PCD和里程计矩阵 Odom 在txt文件
    我的128创作纪念日
    股市资讯天宇优配|政策利好叠加竞争格局向好 机构做多建材板块
    B3627 立方根
    环印国际学校(GIIS)IB成绩38.6分,有什么秘密?
  • 原文地址:https://blog.csdn.net/m0_61567378/article/details/127610914