使用汇编可以直接控制 CPU 指令, 使用 c++ 可以调用到汇编代码, 这里记录一下调用汇编的流程
以一个汇编实现的swap 函数为例
首先在 c++ 层我们需要有一个函数声明
extern "C" { extern 返回类型 函数名(参数列表) asm("汇编符号名"); };
示例
extern "C" void asm_swap(void *a, void *b) asm("asm_swap");
这里要注意几点- extern "C" 要防止 c++ 编译器进行名称修饰,C++ 的名称修饰会导致链接失败
- 函数名与 asm() 内 汇编符号一致
- asm() 内代表汇编符号名, 外部汇编函数必须与声明保持一致
添加一个汇编文件
示例 :
swap.S.global asm_swap # 声明 swap 为全局符号,使得该符号可以被其他文件或模块引用。 asm_swap: # 表明后续的指令是该函数的实现。 movq (%rdi), %rax # 实现 swap 逻辑, movq 是移动 8 个字节 movq (%rsi), %rbx movq %rax, (%rsi) movq %rbx, (%rdi) ret # 它将控制权返回给调用者
同样有要注意的点
- 汇编代码要符合 调用约定 (Calling Convention):定义了函数如何传递参数、如何返回值、如何保存和恢复寄存器等细节
调用约定在不同的平台和操作系统中通常是不同的,
以下是一些常见平台的调用约定:(来自 ChatGPT, 注意辨别)
- x86-64 System V ABI (Linux/macOS) 参数传递:前 6 个整数/指针类型参数通过寄存器传递: %rdi:第 1 个参数 %rsi:第 2 个参数 %rdx:第 3 个参数 %rcx:第 4 个参数 %r8:第 5 个参数 %r9:第 6 个参数 额外参数通过栈传递。 返回值:通过 %rax 寄存器返回。 对齐:栈按 16 字节对齐。
- Windows x64 ABI 参数传递:前 4 个整数/指针类型参数通过寄存器传递: %rcx:第 1 个参数 %rdx:第 2 个参数 %r8:第 3 个参数 %r9:第 4 个参数 额外参数通过栈传递。 返回值:通过 %rax 返回(整数类型),浮点值通过 %xmm0 返回。 对齐:栈按 16 字节对齐。
- x86 (32-bit) cdecl (C 默认调用约定) 参数传递:所有参数通过栈传递。 返回值:通过 %eax 返回。 栈清理:调用者负责清理栈。
- x86 (32-bit) stdcall (Windows API 默认调用约定) 参数传递:所有参数通过栈传递。 返回值:通过 %eax 返回。 栈清理:被调用者负责清理栈。
- ARM 32-bit 参数传递:前 4 个整数参数通过寄存器 %r0 到 %r3 传递,额外的通过栈传递。 返回值:通过 %r0 返回。 栈对齐:通常按 8 字节对齐。
- ARM 64-bit (AArch64) 参数传递:前 8 个参数通过寄存器 %x0 到 %x7 传递,额外的通过栈传递。 返回值:通过 %x0 返回。 栈对齐:按 16 字节对齐。
- 编译链接
汇编文件编译为 .o 文件 与其他 .o 文件链接即可 示例:clang++ -c swap.S -o swap.o