我想创建通过GCC编译器保存/恢复CPU寄存器状态的函数。 在PowerPC中它是8个条件4位寄存器('cr0' - 'cr7'),我想得到它们的值并将其保存在内存中。我的解决方案(不起作用):
register int cr0 __asm__("cr0");
这适用于通用寄存器('r1' - 'r30'),在定义寄存器之后,可以以任何方式使用它。 但是在编译上面的代码时,它会因以下错误而失败:
hello.c: In function ‘foo’:
hello.c:58:22: error: register specified for ‘cr0’ isn’t suitable for data type
register int cr0 __asm__("cr0");
我认为麻烦是cr0寄存器是4位宽,所以它不能放入32位int变量。 (16位和8位也失败了)
如何处理这个问题?在GCC中有4bits整数的解决方法吗?或者如何处理完整的cr
32位寄存器,不仅仅是它的部分?
答案 0 :(得分:3)
gcc扩展名unitTrustsPnl = 0;
用于为C变量(本地甚至全局)分配特定的寄存器。它不能用于您的目的,因为您提到的注册表确实不适合存储register int cr0 __asm__("cr0");
类型的值。可以通过这种方式使用的寄存器集还有其他限制,它不是保存寄存器值的通用方法。
您应该使用内联汇编将这些特殊寄存器的值读入局部变量并将其保存在其他位置。
答案 1 :(得分:2)
我想通过GCC编译器创建保存/恢复CPU寄存器状态的函数。
注册 - asm
局部变量对此无用。
当它被用作扩展asm语句(gcc manual)的操作数时,它们只能保证在指定的寄存器中。这允许编译器在需要时跨函数调用溢出/重新加载寄存器。
更重要的是,对于您的情况,为函数内的register-asm局部变量赋值将使编译器在函数序言/结尾中保存/恢复调用者的值。见this example on the Godbolt compiler explorer:
int call_clobbered(int x) {
register int a asm("r2") = 123;
asm("" :: "r"(a)); // force the compiler to have the value in the register
return a;
}
# gcc4.8.5 -O3 -mregnames
li %r2,123
li %r3,123 # return-value register
blr
int call_preserved(int x) {
register int a asm("r22") = 123;
asm("" :: "r"(a)); // force the compiler to have the value in the register
return a;
}
# gcc4.8.5 -O3 -mregnames
stwu %r1,-48(%r1)
stw %r22,8(%r1) # save caller's r22
li %r22,123
li %r3,123
lwz %r22,8(%r1) # restore caller's r22
addi %r1,%r1,48 # deallocate stack space
blr
因此,您可以创建适用于保存调用者注册表的代码,但是如果没有内联,您将无法编写恢复寄存器的代码作为上下文切换的一部分。
此外,您还不想分别保存/恢复CR
的所有8个半字节!像普通人一样保存整个32位寄存器。或者更好,使您的上下文切换功能成为编译器生成的代码调用的实际功能,因此您不必保存/恢复任何调用被破坏的寄存器。 (因为编译器期待你的函数在它返回之前破坏所有这些寄存器。)
我不知道PowerPC呼叫惯例,但我猜测所有CR都是呼叫破坏的。在只有一个FLAGS /条件代码寄存器的ISA上,它总是被称为clobbered。
如果确实需要保存/恢复CR,则可能必须在纯asm中编写整个函数,因为任何编译器生成的代码都可能在恢复后破坏CR。
要保存/恢复整个CR,请参阅this PPC ISA quick reference。
使用 mfcr r1
(从CR 移动)将所有32位复制到整数寄存器(然后可以将其存储到内存中)。还原时,使用 mtcr r1
移至CR 。适用于任何注册; r1
只是一个例子。