备注: 这里给出的代码是在Win10系统中VS2022开发环境下编译测试通过的。
c++代码:
#include
// 函数使用 C 风格命名规范、调用规则
extern "C" int CalcSum_(int a, int b, int c);
int main(int argc, unsigned short* argv[])
{
int a = 17, b = 11, c = 14;
int sum = CalcSum_(a, b, c);
std::cout << " a: " << a << std::endl;
std::cout << " b: " << b << std::endl;
std::cout << " c: " << c << std::endl;
std::cout << " (x86)sum: " << sum << std::endl;
return 0;
}
x86汇编代码:
.model flat,c
.code
; extern "C" int CalcSum_(int a, int b, int c)
;
; Description: This function demonstrates passing arguments between
; a C++ function and an assembly language function.
;
; Returns: a + b + c
CalcSum_ proc
; x86处理器环境下, 参数由堆栈传入(依旧是从右向左入栈)
; Initialize a stack frame pointer
push ebp
mov ebp,esp
; Load the argument values
mov eax,[ebp+8] ; eax = 'a'
mov ecx,[ebp+12] ; ecx = 'b'
mov edx,[ebp+16] ; edx = 'c'
; Calculate the sum
add eax,ecx ; eax = 'a' + 'b'
add eax,edx ; eax = 'a' + 'b' + 'c'
; Restore the caller's stack frame pointer
pop ebp
ret
CalcSum_ endp
end
运行结果:
c++代码:
#include
// 函数使用 C 风格命名规范、调用规则
extern "C" int CalcSum3_(int a, int b, int c);
extern "C" int CalcSum6_(int a, int b, int c, int d, int e, int f);
extern "C" int CalcSum7_(int a, int b, int c, int d, int e, int f, int g);
int main(int argc, unsigned short* argv[])
{
int a = 17, b = 11, c = 14;
int sum3 = CalcSum3_(a, b, c);
std::cout << " a: " << a << std::endl;
std::cout << " b: " << b << std::endl;
std::cout << " c: " << c << std::endl;
std::cout << " (x64)sum3(a+b+c) ,result: " << sum3 << std::endl;
std::cout << "" << std::endl;
a = 17, b = 11, c = 14;
int d = 3, e = 9, f = 5, g = 6;
int sum6 = CalcSum6_(a, b, c, d, e, f);
int sum7 = CalcSum7_(a, b, c, d, e, f, g);
std::cout << " a: " << a << std::endl;
std::cout << " b: " << b << std::endl;
std::cout << " c: " << c << std::endl;
std::cout << " d: " << d << std::endl;
std::cout << " e: " << e << std::endl;
std::cout << " f: " << f << std::endl;
std::cout << " g: " << g << std::endl;
std::cout << " (x64)sum6(a+b+c+d+e+f) ,result: " << sum6 << std::endl;
std::cout << " (x64)sum7(a+b+c+d+e+f+g),result: " << sum7 << std::endl;
return 0;
}
x64汇编代码:
; x64 新增 r8, r9, r10, r11, r12, r13, r14, r15 这8个通用寄存器
.code
; extern "C" int CalcSum3_(int a, int b, int c)
;
; Description: This function demonstrates passing arguments between
; a C++ function and an assembly language function.
;
; Returns: a + b + c
CalcSum3_ proc
; x64, 默认前四个参数由 rcx,rdx,r8,r9 四个寄存器传参
; Initialize a stack frame pointer
push rbp
mov rbp,rsp
; Load the argument values
mov rax,rcx ; eax = 'a'
; Calculate the sum
add rax,rdx ; eax = 'a' + 'b'
add rax,r8 ; eax = 'a' + 'b' + 'c'
; 返回值存放在rax中
; Restore the caller's stack frame pointer
pop rbp
ret
CalcSum3_ endp
; extern "C" int CalcSum6_(int a, int b, int c, int d, int e, int f)
;
; Description: This function demonstrates passing arguments between
; a C++ function and an assembly language function.
;
; Returns: a + b + c + d + e + f
CalcSum6_ proc
; x64处理器环境下, 默认前四个参数由 rcx,rdx,r8,r9 四个寄存器传参,更多参数由堆栈传入(依旧是从右向左入栈)(第五个参数e也存放在rax中)
; Initialize a stack frame pointer
push rbp
mov rbp,rsp
; Load the argument values
;mov r10d, dword ptr [rbp + 8 * (5 + 1)] ; 第5个参数, 即参数e
; 这里的5(即: 5 * 8 = 40个字节) 表示 4 * 8 = 32 字节的影子空间(home area 为rcx,rdx,r8,r9准备的预留空间)和8字节调用此函数的返回地址(push rbp)
mov r10d, [rbp + 8 * (5 + 1)] ; 第5个参数, 即参数e
mov r11d, [rbp + 8 * (5 + 2)] ; 第6个参数, 即参数f
mov rax,rcx ; eax = 'a'
; Calculate the sum
add rax,rdx ; eax = 'a' + 'b'
add rax,r8 ; eax = 'a' + 'b' + 'c'
add rax,r9 ; eax = 'a' + 'b' + 'c' + 'd'
add rax,r10 ; eax = 'a' + 'b' + 'c' + 'd' + 'e'
add rax,r11 ; eax = 'a' + 'b' + 'c' + 'd' + 'e' + 'f'
; 返回值存放在rax中
; Restore the caller's stack frame pointer
pop rbp
ret
CalcSum6_ endp
; extern "C" int CalcSum7_(int a, int b, int c, int d, int e, int f, int g)
;
; Description: This function demonstrates passing arguments between
; a C++ function and an assembly language function.
;
; Returns: a + b + c + d + e + f + g
CalcSum7_ proc
; x64处理器环境下, 默认前四个参数由 rcx,rdx,r8,r9 四个寄存器传参,更多参数由堆栈传入(依旧是从右向左入栈)(第五个参数e也存放在rax中)
; Load the argument values
;mov r10d, dword ptr [rsp + 8 * (4 + 1)] ; 第5个参数, 即参数e
; 这里的4, 即: 4 * 8 = 32 字节的影子空间(home area 为rcx,rdx,r8,r9准备的预留空间)。注意,这里直接使用了rsp
mov r10d, [rsp + 8 * (4 + 1)] ; 第5个参数, 即参数e
mov r11d, [rsp + 8 * (4 + 2)] ; 第6个参数, 即参数f
mov r12d, [rsp + 8 * (4 + 3)] ; 第7个参数, 即参数g
mov rax,rcx ; eax = 'a'
; Calculate the sum
add rax,rdx ; eax = 'a' + 'b'
add rax,r8 ; eax = 'a' + 'b' + 'c'
add rax,r9 ; eax = 'a' + 'b' + 'c' + 'd'
add rax,r10 ; eax = 'a' + 'b' + 'c' + 'd' + 'e'
add rax,r11 ; eax = 'a' + 'b' + 'c' + 'd' + 'e' + 'f'
add rax,r12 ; eax = 'a' + 'b' + 'c' + 'd' + 'e' + 'f' + 'g'
; 返回值存放在rax中
; Return
ret
CalcSum7_ endp
end
运行结果:
x64 环境下函数调用规则:
The x64 ABI considers the registers RAX, RCX, RDX, R8, R9, R10, R11, and XMM0-XMM5 volatile. When present, the upper portions of YMM0-YMM15 and ZMM0-ZMM15 are also volatile. On AVX512VL, the ZMM, YMM, and XMM registers 16-31 are also volatile. Consider volatile registers destroyed on function calls unless otherwise safety-provable by analysis such as whole program optimization.
The x64 ABI considers registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-XMM15 nonvolatile. They must be saved and restored by a function that uses them.
详见:
x64 calling convention | Microsoft Docs
可以看得出来,x64比x86环境下调用机制要复杂一些。所以x64环境下示例代码用了三个函数来演示说明。