When task switch happens in an OS, how to decide which registers should be preserved?
Is this purely decided by hardware architecture? Or also involve the OS implementation?
I once did some naïve implementation on ARM architecture that preserve all the R1 ~ R15 registers (if I remember it correctly). But that seems too much.
I also tried the x86 hardware task switching support, the TSS segment covers a lot of registers which doesn't have good performance as well.
I guess the design philosophy of an OS, especially the implementation of a task state should decide this. But I am not sure if there's any best practice or conventions. Or other factors.
Normally most of a scheduler would be written in a higher level language (e.g. C), and the low level task switch code will be written as a small assembly language function (and NOT inline assembly) because there's no sane way to predict what a compiler might do with the stack and local variables.
Because of this; which registers the low level assembly function needs to save/restore depends on the ABI ("calling convention") the compiler felt like using. For example, the System V AMD64 ABI says the callee must preserve RBX, RSP, RBP, and R12 to R15 (and can trash RAX, RCX, RDX, and R8 to R11 if they aren't used as return parameters).
This does depend on the nature of the OS though. E.g. it's possible to design an OS where the kernel runs like a separate task and anything that causes a switch from user-space to kernel-space acts like a task switch and has to save everything before any higher level kernel code is executed.