我注意到有时当我编译C代码时,汇编代码有时会在方法1中生成:
STR R11, [SP, #-4]!
ADD R11, SP, #0
SUB SP, SP, #4
有时在方法2中:
STMFD SP!, {R11, LR}
ADD R11, SP, #4
SUB SP, SP, #4
第一种方法和第二种方法的区别在于第二种方法将LR保存到堆栈中。
现在我遇到一个问题,我的函数,就像第一个方法一样,使用链接寄存器(BL)调用另一个函数,并且因为我的函数首先没有保存LR,所以它会导致一个严重的问题。 如果我可以告诉编译器使用第二种方法它可以解决我的问题。
这可能与函数使用内联汇编调用内部函数这一事实有关,因此"不能识别"有一个调用另一个函数,并没有看到保存LR没有意义。调用内联汇编是有义务的,因为被调用的函数将SP的值作为参数。
这是一个非常有吸引力的情况,希望有人可以帮我解决这个问题。 谢谢!
答案 0 :(得分:1)
将lr
标记为已被破坏以使编译器保存它:
extern void foo(void);
extern void bar(void)
{
asm ( "bl foo" : : : "lr" );
}
生成以下代码:
bar:
str lr, [sp, #-4]!
bl foo
ldr lr, [sp], #4
bx lr
有关详细信息,请参阅the documentation。请记住将您使用的所有其他寄存器标记为已破坏。否则编译器可能会在其中一个中放入一个变量。您可能还需要将所有调用者保存的寄存器标记为已破坏。
答案 1 :(得分:1)
最简单/直接的答案是将lr
添加到clobber中,但您可能需要添加更多内容!
<强> WHY ?? 强>
编译器根据函数的类型生成不同的序言/结尾。它们可以是通用函数,叶函数(不调用其他函数)或尾调用。您没有显示任何代码,因此很难了解您的具体情况。但是,如果你没有记录你的内联汇编程序正在调用另一个函数,那么gcc认为它不是 leaf 函数。
来自gcc assembler documentation,
在不使用输入/输出操作数的情况下从C程序访问数据(例如直接使用汇编程序模板中的全局符号)可能无法按预期工作。类似地,直接从汇编程序模板调用函数需要详细了解目标汇编程序和ABI。
出于这个原因,我认为最安全的做法是将r0
- r3
和lr
添加到clobbers中。也可能需要氖/浮点寄存器。应该让编译器意识到这些可以改变'内联asm
'语句。
如果汇编语句后面没有代码,并且假设没有使用“帧指针”或其他寄存器,则可以使用尾调用。即,仅使用bl inline_asm_target
替换b inline_asm_target
,其中inline_asm_target函数仍然执行bx lr
(或者适用于正在使用的编译器选项/ ABI的任何内容)。
相关:
- ARM link register and frame pointer
- ARM to C calling convention registers to save
答案 2 :(得分:0)
有几种解决方案可以保存LR。
1)您可以手动保存,因为您编写内联汇编,您可以在调用函数之前保存LR并在调用后恢复它。
2)您可以使用以下C代码声明一个将放入LR的虚拟变量:
register int foo asm ("lr");
3)使用clobbers in your inline assembly
请注意,最佳解决方案可能是第三种。