C/C++函数调用过程的汇编代码分析(arm64指令集)

函数调用过程的汇编代码分析(arm64指令集),顺便关注一下栈空间的分配与回收。

本文引用自作者编写的下述图书; 本文允许以个人学习、教学等目的引用、讲授或转载,但需要注明原作者"海洋饼干叔
叔";本文不允许以纸质及电子出版为目的进行抄摘或改编。
1.《Python编程基础及应用》,陈波,刘慧君,高等教育出版社。免费授课视频 Python编程基础及应用
2.《Python编程基础及应用实验教程》, 陈波,熊心志,张全和,刘慧君,赵恒军,高等教育出版社Python编程基础及应用实验教程
3. 《简明C及C++语言教程》,陈波,待出版书稿。免费授课视频

下述C语言程序包含了一个简单的add()函数,该函数接受两个整型参数,计算并返回两者的和。

main()函数调用执行了add()函数来计算3+2的和,并将计算结果打印出来。

#include 

int add(int a, int b){
    int t =  a + b;
    return t;
}

int main(){
    int x = 3;
    x = add(x,2);
    printf("%d",x);
    return 1;
}

为了从机器指令层面理解上述函数调用过程中的传参、跳转、返回等行为,我们在浏览器中访问了下述网站:
C/C++函数调用过程的汇编代码分析(arm64指令集)_第1张图片
在粘贴相关代码并选择ARM64 gcc 9.3选项后,我们得到下述汇编语言指令序列:

add(int, int):
        sub     sp, sp, #32
        str     w0, [sp, 12]
        str     w1, [sp, 8]
        ldr     w1, [sp, 12]
        ldr     w0, [sp, 8]
        add     w0, w1, w0
        str     w0, [sp, 28]
        ldr     w0, [sp, 28]
        add     sp, sp, 32
        ret
.LC0:
        .string "%d"
main:
        stp     x29, x30, [sp, -32]!
        mov     x29, sp
        mov     w0, 3
        str     w0, [sp, 28]
        mov     w1, 2
        ldr     w0, [sp, 28]
        bl      add(int, int)
        str     w0, [sp, 28]
        ldr     w1, [sp, 28]
        adrp    x0, .LC0
        add     x0, x0, :lo12:.LC0
        bl      printf
        mov     w0, 1
        ldp     x29, x30, [sp], 32
        ret

第2 ~ 11行:函数add()的实体

第2行:将栈指针(stack pointer)寄存器sp的值减去32,结果仍存储到sp中。显然,该指令事实上为自动变量分配了栈空间。

第10行:在函数返回前,将栈指针(stack pointer)寄存器的值增加32,结果仍存储到sp中。显然,该指令事实上回收了栈空间,回收的数量与分配的数量一致。

第3行:将寄存器w0中的32位数值存储到内存地址sp+12处,结合后续代码可以知道,sp+12处存储的即是形参a。

第4行:将寄存器w1中的32位数值存储到内存地址sp+8处,结合后续代码可以知道,sp+8处存储的即是形参b。

第5行:将存储于sp+12处的形参a值装入寄存器w1。

第6行:将存储于sp+8处的形参b值装入寄存器w0。

第7行:将寄存器w1和w0的值相加,结果存入w0,此处事实上相加的即是形参a和b。

第8行:将寄存器w0中的32位数值存入地址sp+28处。可以推测,sp+28处存储的即为自动变量t。

第9行:将sp+28处的自动变量t装入寄存器w0, 结合后续代码可知,w0寄存器被用于向函数的调用者返回计算结果。

第11行:返回x30寄存器所标识的地址继续执行,此时,x30寄存器所标识的地址为上述代码的第22行。

第14 ~ 29行:函数main()的实体

第17行:将整数3存入w0寄存器。

第18行:将w0中的32位值存入sp+28处。合理推测,sp+28处为变量x的存放位置。

第19行:将整数2存入w1寄存器,结合add()代码可知,add()通过w1寄存器获得参数b。

第20行:将sp+28处的变量x的值存入寄存器w0。结合add()代码可知,add()通过w0寄存器获得参数a。

第 21行:在准备好函数调用的参数(a,b分别在w0和w1寄存器内)后,branch & link指令完成两件工作:

  • 将pc+4的值存入x30寄存器,其中,pc表示当前指令地址(第21行),pc+4即指向当前指令的下一条,也就是第22行。
  • 跳转到add()函数函数,即第2行。

当add()函数完成任务后,第11行ret指令将返回x30寄存器标识的地址继续执行,即返回代码的第22行。

第22行:将w0寄存器的23位数值装入内存地址sp+28处。结合add()函数代码可知,此时的w0寄存器事实上存储的是add()函数的计算结果。这行代码事实上取得了add()函数的返回值,并将其存入x变量(sp+28)。

在代码的后续部分,我们还看到main()函数对printf()函数的调用… 略。

总结

  • 函数执行过程中,确实通过修改sp(栈顶指针)的值来分配和回收栈空间,栈空间用于存储自动变量。
  • 在寄存器够用的情况下,程序会尽量通过寄存器传参,并获取返回值。因为寄存器的访问速度比内存快得多。

为了帮助更多的年轻朋友们学好编程,作者在B站上开了两门免费的网课,一门零基础讲Python,一门零基础C和C++一起学,拿走不谢!

简洁的C及C++
由编程界擅长教书,教书界特能编程的海洋饼干叔叔打造
Python编程基础及应用
由编程界擅长教书,教书界特能编程的海洋饼干叔叔打造

如果你觉得纸质书看起来更顺手,目前Python有两本,C和C++在出版过程中。

Python编程基础及应用

Python编程基础及应用实验教程
在这里插入图片描述

你可能感兴趣的:(C++,c++,c语言,算法)