Why is Return address X1 defined as Callee Saved Register

Hey,

I wanted to ask why the X1 is defined as callee saved register in RISCV because it is in the ABI specification marked as caller saved register.

Thank you!

abi: https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf
upstream: llvm-project/llvm/lib/Target/RISCV/RISCVCallingConv.td at llvmorg-19.1.7 · llvm/llvm-project · GitHub

I think this is required in for LLVM to save the x1 register before using it in a function. If we don’t do that we can overwrite the register and won’t be able to return.

AArch64 has this comment in their callee saved register list where LR is their equivalent register.

// FIXME: LR is only callee-saved in the sense that *we* preserve it and are     
// presumably a callee to someone. External functions may not do so, but this    
// is currently safe since BL has LR as an implicit-def and what happens after a 
// tail call doesn't matter.                                                     
//                                                                               
// It would be better to model its preservation semantics properly (create a     
// vreg on entry, use it in RET & tail call generation; make that vreg def if we 
// end up saving LR as part of a call frame). Watch this space...
1 Like

ah ok, thank you!

Is this also the reason why the X1 register is defined as Def in InstrInfo.td? To indicate that the value has to be restored before the return?

It makes a function that contains a call know that it needs to save/restore X1 around the call. Performing a call changes the value of X1. When that call returns it doesn’t restore the original value of X1.