过程调用的代码流程涉及多个关键步骤,确保程序能正确传递参数、执行被调用函数并安全返回。其典型流程如下:
-
参数传递与调用准备(Caller 侧):
- 计算并压入实际参数到运行时栈。
- 保存当前栈指针(SP),用于维护调用者栈帧。
- 记录参数个数(某些语言或调用约定中需要)。
- 将返回地址(当前指令的下一条)压栈或存入特定寄存器。
- 执行
JSR(Jump to Subroutine)指令跳转到被调用过程。
-
进入被调用过程(Callee 侧):
- 保存旧的栈基址指针(通常称为 FP 或 BP)。
- 建立新的栈帧:设置新的 SP 和 FP,构成活动记录(Activation Record)。
- 分配局部变量所需空间。
- 可选:保存某些寄存器内容(如临时变量使用的寄存器)。
-
执行过程体:
- 执行函数内部语句,可能包括表达式计算、控制流、递归调用等。
- 使用中间代码(如四元式)进行表示和优化。
-
过程返回(Return):
- 将返回值(如有)存入约定位置(寄存器或栈)。
- 恢复调用者的栈指针 SP 和基址指针 FP。
- 获取返回地址并跳转回调用点。
- 清理栈中参数(由调用者或被调用者完成,取决于调用约定)。
在编译过程中,中间代码优化 和 目标代码生成 是后端的关键阶段:
-
中间代码优化:
- 输入是中间表示(如四元式
(op, arg1, arg2, result))。 - 进行等价变换以提升效率,例如常量折叠、公共子表达式消除、循环不变代码外提、死代码删除等。
- 目标是在不改变程序语义的前提下减少运行时间和空间开销。
- 输入是中间表示(如四元式
-
目标代码生成:
- 由代码生成器将优化后的中间代码转换为具体机器的汇编或机器码。
- 需处理寄存器分配、指令选择、寻址模式匹配等问题。
- 四元式结构因其接近三地址码,便于映射到目标指令。
常见的过程调用约定(Calling Convention)定义了函数调用时参数如何传递、栈由谁清理、哪些寄存器需保存等规则。不同平台和编译器使用不同的调用约定,尤其在C/C++中较为显著。以下是几种典型的调用约定及其在参数清理和寄存器使用上的主要区别:
1. cdecl(C Declaration)
- 平台/语言:x86 架构下 GCC 和 MSVC 默认的 C 语言调用约定。
- 参数传递顺序:从右到左压入栈。
- 栈清理方:调用者(Caller) 负责清理栈空间。
- 寄存器使用:
EAX,ECX,EDX:调用者保存(caller-saved),被调用函数可自由使用。- 其他通用寄存器(如
EBX,ESI,EDI)为被调用者保存(callee-saved),若使用需先压栈。
- 用途:支持可变参数函数(如
printf),因为只有调用者知道传了多少参数。
2. stdcall(Standard Call)
- 平台:Windows API 常用。
- 参数传递顺序:从右到左压栈。
- 栈清理方:被调用者(Callee) 在
ret n指令中自动弹出参数。 - 寄存器使用:
- 类似 cdecl,
EAX,ECX,EDX为 caller-saved。 - 其余为 callee-saved。
- 类似 cdecl,
- 限制:不适用于变参函数(被调用者无法预知参数个数)。
3. fastcall
- 目的:提高性能,尽可能通过寄存器传参。
- 参数传递方式:
- 前两个双字(DWORD)大小的参数通过寄存器传递(通常是
ECX和EDX)。 - 剩余参数从右到左压栈。
- 前两个双字(DWORD)大小的参数通过寄存器传递(通常是
- 栈清理方:被调用者(Callee) 清理栈上参数部分。
- 寄存器使用:
ECX,EDX用于传参,属于 callee-saved 的一部分。EAX,EDX也常用于返回值(EAX返回整型,EDX:EAX返回64位值)。
- 优点:减少内存访问,提升速度。
4. thiscall(C++ 成员函数专用)
- 平台:Microsoft Visual C++ 使用。
- 隐含参数
this:通过寄存器(ECX)传递。 - 参数传递:其余参数从右到左压栈。
- 栈清理方:对于固定参数,由 被调用者 清理;变参则由调用者清理。
- 仅用于类成员函数调用。
5. System V AMD64 ABI(Unix-like 系统 x86-64)
- 主流标准:Linux、macOS 等 64 位系统默认。
- 参数传递:
- 前六个整型/指针参数依次使用寄存器:
RDI,RSI,RDX,RCX,R8,R9。 - 前八个浮点参数使用
XMM0–XMM7。 - 超出部分通过栈传递。
- 前六个整型/指针参数依次使用寄存器:
- 栈清理方:调用者 清理栈。
- 寄存器分类:
- caller-saved:
RAX,RCX,RDX,R8–R11 - callee-saved:
RBX,RBP,R12–R15,RSP
- caller-saved:
总结对比表:
| 调用约定 | 参数传递顺序 | 栈清理方 | 寄存器传参 | 支持变参 | 典型用途 |
|---|---|---|---|---|---|
| cdecl | 右→左 | Caller | 否 | 是 | C 函数(x86) |
| stdcall | 右→左 | Callee | 否 | 否 | Windows API |
| fastcall | 右→左 + 寄存器 | Callee | 是(前两个) | 否 | 性能敏感函数 |
| thiscall | 右→左 | Callee* | ECX (this) | 否/是 | C++ 非静态成员函数 |
| System V x64 | 寄存器优先 | Caller | 是(最多6个整型) | 是 | Linux/macOS 64位程序 |
*注:thiscall 的清理策略依实现而定。



被折叠的 条评论
为什么被折叠?



