汇编语言函数调用约定

准确的说,函数调用约定是C/C++到汇编语言的翻译阶段要考虑的时候。在32位的系统上,这种调用约定有很多种方式,本文不考虑所谓的fastcall stdcall cdcel三种调用方式的区别,而以现代编译器在64位体系架构下默认流行的方式进行描述和总结。调用约定描述了函数之间变量的传递规则。如果使用汇编语言编程,你需要知道在哪些寄存器中放置该函数需要使用的值。另外,在构建高性能底层算法库的时候,需要将核心的代码做汇编级的加速,此时需要严格遵循这些约定,需要确定哪些寄存器用于哪些函数的参数。所幸的是,早就有一套统一的标准来规范这些事情,但在不同的平台或系统上还是有很大的不同。例如对于32位的ARM架构和32位的x86架构则完全不同.其中32位arm架构的参数传递规则,可以参考笔者早年之前的一篇文章总结,具体见参考文献1. 本文主要总结64位x64架构和64位 arm-cortex架构下的参数传递规则。

Linux平台 AMD64 ABI调用约定

  • 非浮点参数传递规则
第一个参数RDI
第二个参数RSI
第三个参数RDX
第四个参数RCX
第五个参数R8
第六个参数R9

更多的参数通过栈以相反的顺序传递,这样就可以按照正确的顺序弹出。例如,一个函数有10个参数,则依次压入第10个参数,第9个参数,第8个参数,第7个参数。同时,要记住每压入一个参数,栈指针RSP将减少8个字节。压入第7个参数之后,栈指针较小了8x4等于32个字节。之后的指令是调用函数,此时RIP指令被压入栈,保存了函数的返回地址,同时栈指针又减少了8个字节。此时栈指针总共较少了40个字节,但还必须符合栈按照16字节的边界对其的要求,因此还可能需要将RSP较小8个字节,总共达到48个字节的位置。

  • 浮点参数传递规则

浮点型参数通过xmm寄存器来传递,如果参数多于8个,则仍然通过栈来传递。

第一个参数XMM0
第二个参数XMM1
第三个参数XMM2
第四个参数XMM3
第五个参数XMM4
第六个参数XMM5
第七个参数XMM6
第八个参数XMM7
  • 关于函数返回值

无论是linux平台还是windows平台,函数使用xmm0返回浮点运算的结果,使用RAX保存整数或地址的返回值。

Windows平台Microsoft x64调用约定

windows平台只使用4个寄存器来保存参数,更多的参数要保存到栈里面以此完成参数的传递。

参数位置int/pointer/obj /arrayfloat/double
1stRCXXMM0
2ndRDXXMM1
3rdR8XMM2
4thR9XMM3
morestackstack

实际上,window系统会为每一个参数预留对应的栈空间。例如,假设函数有6个参数,实际情况如下:

            void  func(a,b,c,d,e,f)
//  ASM code:
             SUB RSP,48
             MOV RCX,a
             MOV RDX,b
             MOV R8, c
             MOV R9, d
             MOV [RSP+20h],e
             MOV [RSP+28h],f
             CALL func
             ADD RSP,48

在函数func内部会再次把a,b,c,d 保存到对应的栈的位置,即实际代码为:

            MOV [RSP+0x8] ,a
            MOV [RSP+0x10],b
            MOV [RSP+0x18],c
            MOV [RSP+0x20],d
// now      e is [RSP+0x28], f is [RSP+0x30]
// when the last ret instruction will correct the RSP plus 8 bytes.

寄存器的保护规则

在写汇编优化的过程中,有些寄存器是可以直接拿来使用的,而有些寄存器要拿来用就必须要先入栈保存起来,用完之后,在从栈里面弹出以恢复之前的值。可以直接被拿来用的寄存器叫scratch register, 需要保护才能使用的寄存器叫volatile register.相对而言scrach register也叫non-volatile register,volatile register也叫preserved register。具体总结如下:

寄存器名称寄存器属性
RAXscratch/non-volatile
RBXpreserved/volatile
RCXscratch/non-volatile
RDXscratch/non-volatile
RSIscratch/non-volatile
RDIscratch/non-volatile
RBPpreserved/volatile
RSPpreserved/volatile
R8scratch/non-volatile
R9scratch/non-volatile
R10scratch/non-volatile
R11scratch/non-volatile
R12preserved/volatile
R13preserved/volatile
R14preserved/volatile
R15preserved/volatile

64位 ARM-Cortext平台

  • 31个64位通用寄存器和32个128位矢量寄存器

31个通用寄存器的命名为X0,X1,... X30,其对应的低32位物理视图寄存器可以用于32位的运算,命名为W0,W1,... W30. 32个矢量寄存器命名为V0,V1,...,V31,其对应的低64位物理视图寄存器为D0,D1,...,D31,低32位物理视图寄存器为S0,S1,... S31. 此处,和32位ARM-Cortext平台的矢量寄存器的物理视图是不同的。

  • 参数传递与寄存器使用规则

从上面的图片可以看出,前8个寄存器用于函数参数的传递,其中X0还用于函数的返回值。X8到X18可以直接使用,X19到X28需要入栈保护之后才能被使用。实际上,X29和X30是否需要保护取决于当前函数是否有调用其它的函数,如果没有可以不用入栈保护。

和通用寄存器类似,矢量寄存器中的前8个用于参数传递或函数返回值。矢量寄存器的V8到V15不能直接使用,需要入栈保护,但只需要入栈保护低64位的物理视图寄存器,即D8到D15。而从V16到V31可以直接使用无需入栈保护。

  • 整数与浮点混合参数传递

假设函数原型为

 double  function(long, double, long, double)

则第一个整数类型的参数传给X0,第一个双精度浮点的参数传给D0,第二个整数参数传给X1,第二个双精度浮点参数传给D1,返回值位双精度存储到D0

  • 关于入栈和出栈

// code snippet 1
// Several pushes can share a single preparation step.
 sub   sp, x28, #32          // preparation
 stp   x3, x2, [x28, #-16]!  // push {x2}; push {x3};
  ...
 stp   x1, x0, [x28, #-16]!  // push {x0}; push {x1};

// code snippet 2
// push {x0, x1, x2, x3}
 stp   x0, x1, [sp, #-16]!
 stp   x2, x3, [sp, #8]

 // code snippet 3

 stp  x4, x5, [sp, #-8]!
 stp  x2, x3, [sp, #-8]!
 stp  x0, x1, [sp, #-8]!
 // vs.
 stp  x0, x1, [sp, #-24]!
 stp  x2, x3, [sp, #8]
 stp  x4, x5, [sp, #16]

把一些有用的资源放到参考文献里面:

参考文献:

  1. ARM参数传递规则_celerychen2009的博客-CSDN博客
  2. windows x64 ABI conventions
  3. https://cs140e.sergio.bz/docs/ARMv8-A-Programmer-Guide.pdf ARMv8编程指南
  4. Compiler Explorer 非常全面的任意一种高级语言和汇编语言的代码对比平台godbolt.org
  5. https://developer.arm.com/architectures/instruction-sets/intrinsics/ ARM官方intrinsics函数集
  6. https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html x86/x64 intrinsics函数集
  7. An Open Optimized Software Library Project for the Arm Architecture @ GitHub NE10 Project
  8. Quick C++ Benchmarks C++代码性能对比测试平台
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值