* The language you are using to write the compiler. You have to know it well enough to write a complex application.
* The language you are compiling. You have to completely understand it.
* The language you are compiling to: x86, JVM, etc. You have to understand it well enough to write every complex application.
Unless you are writing an optimizing compiler, you just have to understand the target language well enough to write snippets that resemble the operations of your source or intermediate language, which can be pretty little. See:
http://t3x.org/subc/cg386.c.html
Anecdotally: I have once written an AXP21164 back-end without any prior knowledge, only based on the reference manual. It was not pretty or fast, but it worked.
If you are compiling for x86 you have to think about register allocation.
If you are compiling for JVM or an HLL, you don't. A good chunk of what is discussed in that article just is not relevant anymore.
Also, if you aren't writing a register allocator because you're using the JVM or LLVM, then someone else needs to on your behalf. We can't forget these skills.
Normally the cost isn't too bad and of course they're spilled to the stack not kept in registers, but if the language you are implementing has thousands of locals you may even reach the limit of the frame size. We've seen this in practice in more than one language and application.
Also, only some frames are JIT compiled - what about the rest?
But if you're really running into the 64k limit on max_locals even with index reuse, then you're out of luck.
For every local variable index, the JVM needs to keep the value live for the duration of the method because that's what the debugger will show.
And yes we have seen the 64k limit blown by real programs in the wild that aren't designed to be awkward (I'm in the VM research group at Oracle).
Inventing whatever semantics you have in mind without considering how it's going to be compiled is a recipe for slow languages.