It might help to get a sense of you look at calling conventions on RISC vs CISC. On CISC processors it was common to push the arguments to a function on the stack. On e.g. RISC-V 8 registers are set aside for passing function arguments. You also typically put the return address in a register rather than putting it on the stack. Then you got the stack pointer and frame pointer. Thus before we have done any calculations we have already spent 11 registers. That is more than the 8 found in the original x86.
On RISC-V we got another 8 designated for temporary values. Why is that good? It means when using then you know they are not guaranteed to be stored between function calls. By using them you avoid having to access memory to store and restore registers from memory. The rest are registers which will be preserved. But if your function does not need to use them, then the compiler can generate code which does not store them and thus we reduce memory access.
Registers are not that hard to use for a compiler. It keeps a list of free and used registers when compiling a function. Whenever it uses a variable it pulls a register from the free list. When a variable is no longer is use, it puts it back in the free list.
Basically every variable existing only within the scope of a function could exist only in registers if you got enough registers. You never even have to access memory to read or store the variable.