使用GCC编译器为特定代码部分保留寄存器

3

是否可以为C代码的特定部分保留寄存器?

-ffixed-reg选项或声明全局寄存器变量并不是我要寻找的答案。我想要在特定作用域内(比如特定函数)保留寄存器的值。

使用本地寄存器变量是不可能的,因为它不能保证在整个作用域内保持寄存器的值。

我正在寻找类似于asm volatile中的clobber列表,但用于C语句。


1
你所要求的并没有太多意义。如果你想要对寄存器分配有更多控制权,那就写汇编语言吧。如果你想用C语言编写,那么你将无法控制这些细节。 - BitBank
我很难理解“保留特定范围内的寄存器值”可能意味着什么。您是指每次调用函数时,它应该在该寄存器中找到先前的值(也称为静态)吗?还是由此函数调用的函数甚至不能使用此寄存器(即使它们在完成后恢复了该值)?老实说,我无法想象这两种情况如何有用。 - David Wohlferd
或许你的程序中有一些汇编代码位于函数顶部,还有一些位于底部,而且你希望确保在它们之间寄存器的值不受影响?如果是这种情况,请展示给我们代码,或许我们可以提供一些建议。 - David Wohlferd
我的意思是,我想告诉编译器不要为代码段生成使用特定的寄存器。@David Wohlferd - Dragonight
1个回答

1
GCC提供了三种机制:
  1. asm clobber
  2. 本地寄存器变量
  3. 全局寄存器变量
需要注意的是,编译器寄存器分配对于现代优化至关重要,保留寄存器可能会生成更糟糕的编译代码。在ARM模式和16个寄存器(仅13个可用)中,您应该能够像这样为函数保留一个寄存器而不会造成太大的伤害。但是,您不应轻易使用这些功能,并且可能会发现一些性能问题。
听起来全局寄存器变量最适合您。只需在每个需要它的'C'模块开头使用register int *foo asm ("r4");
如果有一小组/树函数,您可以使用宏将其保留并包含到一个'C'单元中。
#define RESERVE_REG(reg)  register int RR_##reg asm (#reg) \
                                 __attribute__((unused))

int bar(int a) { 
  RESERVE_REG(R4); 
  int b; 
  b += CRAZY_ASM(a); 
  return b;
}
void foo(void) { 
 RESERVE_REG(R4); 
 CRAZY_ASM_SET_R4(82);
 printf("value is %d\n", bar(1));
}

重要的是要知道变量的使用,因为有更有效的方法(如asm clobber)可以实现某些变量寿命和用途的相同效果。在大多数情况下,人们只会声明一个参数。也就是说,最好让编译器知道你正在使用它,因为它可以根据需要进行明智的决策。
很难想象有哪种情况保留寄存器是不明智的。一个可能有用的例子是与跨语言/解释器交互。上面的宏应该作为一种快速的方式来通过保留寄存器在例程之间传递信息。
您不应该使用R0-R3,因为这将限制可以在例程之间传递的参数。ARM ABI在R0-R3中传递参数。鉴于您可以灵活选择寄存器,请选择R4-R9(甚至可能R9不可用),因为这些是没有任何特殊用途的'callee'保存寄存器。此外,如果您选择R0-R3,则可能无法调用标准'C'库例程,或者保留的寄存器将保存在堆栈上。

参考资料:GCC本地寄存器变量
                    ARM寄存器调用约定


也许相关的是,在线程之间分享寄存器。 - artless noise
如果您有一个静态基础,就不能使用R9;而99%的ARM代码都不使用。也就是说,在某些系统中,R9可能是一个“保留寄存器”,通常是共享库的数据部分。请参见:ARM静态链接共享库 - artless noise
请注意,GCC不再官方支持使用register-asm局部变量。 https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html。(唯一支持的用途是使“r”约束选择某个寄存器用于扩展汇编。但实际上,这仍然有效,至少目前通常情况下在GCC中有效。clang除外,如果有影响的话。) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接