Ah yeah, great observation. If you call a function and need to use the a0 to a7 registers after the call then the caller needs to save them. You can do that by either pushing them to the stack or putting them in the s0 to s7 registers because the called function (callee) is responsible for preserving those registers.
So you can see that sooner or later you need to use the stack. However if you have very shallow call hierarchies you may be able to avoid it entirely. You can shuffle values between registers to preserve registers before being forced to use the stack.
The compiler knows what registers it uses and which it needs to preserve the value so it can keep this to a minimum.
But I agree that passing arguments on the stack is easier for a programmer. You just do the same thing every time. But a compiler is better at keeping track of large number of registers than us and RISC instructions are really made more for compilers than people hand coding.
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf