现在在 riscv-gcc 中实现了什么:
呼叫保存的 GPR:X8-X9 x18-x27
调用保存的 FPR(如果启用了浮点):F8-F9 F18-F27
所有其他寄存器都是固定的或调用破坏。
#define REG_ALLOC_ORDER \
{ \
/* Call-clobbered GPRs. */ \
15, 14, 13, 12, 11, 10, 16, 17, 6, 28, 29, 30, 31, 5, 7, 1, \
/* Call-saved GPRs. */ \
8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, \
/* GPRs that can never be exposed to the register allocator. */ \
0, 2, 3, 4, \
/* Call-clobbered FPRs. */ \
47, 46, 45, 44, 43, 42, 32, 33, 34, 35, 36, 37, 38, 39, 48, 49, \
60, 61, 62, 63, \
/* Call-saved FPRs. */ \
40, 41, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, \
/* None of the remaining classes have defined call-saved \
registers. */ \
64, 65 \
}
ra 由调用者处理,如果您愿意,可以查看 riscv.c ,您将看到 ra 何时被 riscv_save_reg_p 保存。
对于 LLVM 移植:
被调用者保存的 GPR 是:X1、X3-X4、X8-X9 和 X18-X27
被调用方保存的 FPR(如果启用了浮点):F8-F9 F18-F27
def CSR_ILP32_LP64
: CalleeSavedRegs<(add X1, X3, X4, X8, X9, (sequence "X%u", 18, 27))>;
def CSR_ILP32F_LP64F
: CalleeSavedRegs<(add CSR_ILP32_LP64,
F8_F, F9_F, (sequence "F%u_F", 18, 27))>;
def CSR_ILP32D_LP64D
: CalleeSavedRegs<(add CSR_ILP32_LP64,
F8_D, F9_D, (sequence "F%u_D", 18, 27))>;
这与 gcc 所做的有点不同。在 gcc 中 X3 和 X4 被认为是固定的。
对于 LLVM 中的 X2,它由 setStackPointerRegisterToSaveRestore 处理。
尝试用简单的方式解释。如果一个函数使用一个被调用寄存器(例如 x8),它将保存并恢复它,这意味着之后我们将拥有相同的值。
如果它使用了一个不属于这个列表的寄存器(例如使用 gcc 的 ra),它就不会做这个工作,由调用者函数在使用它之前设法恢复值。