当前位置:  开发笔记 > 编程语言 > 正文

什么寄存器保存在ARM C调用约定中?

如何解决《什么寄存器保存在ARMC调用约定中?》经验,为你挑选了4个好方法。

自从我上次编写手臂汇编程序以来已经有一段时间了,我对细节有点生疏.如果我从arm调用C函数,我只需要担心保存r0-r3和lr,对吧?

如果C函数使用任何其他寄存器,它是否负责保存堆栈中的那些并恢复它们?换句话说,编译器会生成代码来为C函数执行此操作.

例如,如果我在汇编程序函数中使用r10,我不必将其值放在堆栈或内存中,并在C调用后弹出/恢复它,是吗?

这是针对arm-eabi-gcc 4.3.0.



1> CesarB..:

这取决于您正在编译的平台的ABI.在Linux上,有两个ARM ABI; 旧的和新的.AFAIK,新的(EABI)实际上是ARM的AAPCS.完整的EABI定义目前存在于ARM的信息中心.

从AAPCS,§5.1.1:

r0-r3是参数和临时寄存器; r0-r1也是结果寄存器

r4-r8是被调用者保存寄存器

r9可能是一个被调用者保存寄存器(在AAPCS的某些变体上它是一个特殊的寄存器)

r10-r11是被调用者保存寄存器

r12-r15是特殊寄存器

被调用者必须保存被调用者保存寄存器(与调用者保存寄存器相对应,调用者保存寄存器); 所以,如果这是你正在使用的ABI,你不必在调用另一个函数之前保存r10(另一个函数负责保存它).

编辑:您使用的编译器没有区别; 特别是gcc可以配置为几个不同的ABI,甚至可以在命令行上进行更改.看着它生成的序言/结尾代码是不是有用的,因为它是专为每个功能编译器可以使用保存的寄存器(例如,保存在它的函数的中间)的其他方式.


我想更容易记住你必须保存和恢复`r4-r11`以防你打算使用它们; 这就是为什么他们被callee保存.

2> Pavel P..:

要在NEON寄存器上添加缺少的信息:

从AAPCS,§5.1.1核心寄存器:

r0-r3是参数和临时寄存器; r0-r1也是结果寄存器

r4-r8是被调用者保存寄存器

r9可能是一个被调用者保存寄存器(在AAPCS的某些变体上它是一个特殊的寄存器)

r10-r11是被调用者保存寄存器

r12-r15是特殊寄存器

从AAPCS,§5.1.2.1VFP寄存器使用约定:

必须保留s16-s31(d8-d15,q4-q7)

s0-s15(d0-d7,q0-q3)d16-d31(q8-q15)不需要保留

原帖:
arm-to-c-calling-convention-neon-registers-to-save



3> auselen..:

对于64位ARM,A64(来自ARM 64位架构的过程调用标准)

A64指令集可以看到31个64位通用(整数)寄存器; 这些标记为r0-r30.在64位上下文中,这些寄存器通常使用名称x0-x30来表示 ; 在32位上下文中,寄存器通过使用w0-w30指定.另外,堆栈指针寄存器SP可以与有限数量的指令一起使用.

SP堆栈指针

r30 LR链接寄存器

r29 FP帧指针

r19 ... r28 Callee保存的寄存器

r18平台注册,如果需要; 否则是临时登记.

r17 IP1第二个程序内调用临时寄存器(可由调用代理和PLT代码使用); 在其他时间可以用作临时登记.

r16 IP0第一个程序内调用暂存寄存器(可以由调用代理和PLT代码使用); 在其他时间可以用作临时登记.

r9 ... r15临时登记册

r8间接结果位置寄存器

r0 ... r7参数/结果寄存器

前8个寄存器r0-r7用于将参数值传递给子程序并从函数返回结果值.它们也可用于在例程中保存中间值(但通常仅用于子例程调用之间).

寄存器r16(IP0)r17(IP1)可以被用作例程和它调用的任何子例程之间的临时寄存器.它们也可以在例程中用于在子例程调用之间保存中间值.

寄存器r18的作用是特定于平台的.如果平台ABI需要专用通用寄存器来承载进程间状态(例如,线程上下文),那么它应该使用该寄存器来实现该目的.如果平台ABI没有这样的要求,那么它应该使用r18作为附加的临时寄存器.平台ABI规范必须记录该寄存器的用法.

SIMD

ARM 64位架构还有另外32个寄存器v0-v31,可由SIMD和浮点运算使用.寄存器的确切名称将更改,表示访问的大小.

注意:与AArch32不同,在AArch64中,SIMD和浮点寄存器的128位和64位视图在较窄的视图中不会与多个寄存器重叠,因此q1,d1和s1都指向寄存器中的相同条目银行.

前8个寄存器v0-v7用于将参数值传递给子程序并从函数返回结果值.它们也可用于在例程中保存中间值(但通常仅用于子例程调用之间).

寄存器v8-v15必须由被调用者跨子例程调用保留; 其余寄存器(v0-v7,v16-v31)不需要保留(或应由调用者保留).另外,只需要保存存储在v8-v15中的每个值的底部64位; 调用者有责任保留更大的值.



4> Sven..:

CesarB和Pavel的答案提供了AAPCS的引用,但仍有未解决的问题.被叫方是否保存r9?r12怎么样?r14怎么样?此外,答案非常笼统,并不是所要求的arm-eabi工具链所特有的.这是一种实用的方法,可以找出哪些寄存器是被调用者保存的,哪些不是.

以下C代码包含内联汇编块,声称修改寄存器r0-r12和r14.编译器将生成代码以保存ABI所需的寄存器.

void foo() {
  asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}

使用命令行arm-eabi-gcc-4.7 -O2 -S -o - foo.c 并为您的平台添加开关(-mcpu=arm7tdmi例如).该命令将在STDOUT上打印生成的汇编代码.它可能看起来像这样:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    bx  lr

注意,编译器生成的代码保存并恢复r4-r11.编译器不保存r0-r3,r12.它恢复r14(别名lr)纯粹是偶然的,因为我从经验中知道退出代码也可以将保存的lr加载到r0然后执行"bx r0"而不是"bx lr".通过添加-mcpu=arm7tdmi -mno-thumb-interwork或使用-mcpu=cortex-m4 -mthumb我们获得略有不同的汇编代码,如下所示:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}

同样,r4-r11被保存并恢复.但是r14(别名lr)没有恢复.

总结一下:

r0-r3 不是被调用者保存的

r4-r11是被调用者保存的

r12(alias ip)不是被调用者保存的

r13(别名sp)是被调用者保存的

r14(别名lr)不是被调用者保存的

r15(别名pc)是程序计数器,在函数调用之前设置为lr的值

这至少适用于arm-eabi-gcc的默认值.存在可能影响结果的命令行开关(特别是-mabi开关).


您的分析“不正确”;lr被作为PC弹出,以便更快地返回。您的r9问题的答案在[APCS]中(http://www.cl.cam.ac.uk/~fms27/teaching/2001-02/arm-project/02-sort/apcs.txt) 。在本文档中它称为“静态基准”,而“可重入与不可重入代码”部分是相对的。APCS支持多种配置,但是gcc通常是可重入的,没有堆栈限制。特别是,*在APCS的某些变体中,“ sb / r9”和“ sl / r10”具有专门的角色。在其他变体中,它们可用作被调用者保存的寄存器*
推荐阅读
手机用户2402852387
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有