I really like the fact that the output of this more closely approximates what a skilled Asm programmer (human) would write, than the usual compiler output. The algorithm also resembles more how the human would think --- where results should go, where inputs can come from, and what instructions can "fit" within those constraints. Contrary to what some compiler textbooks might imply, humans (or at least this one) don't think about graph colouring when they're writing Asm.
To load a value from memory we need the memory address in one of 3 suitable registers: bx, si, di.
You can use bp too, and for local variables it's the best one, since it defaults to using the stack segment. Adding support for non-movs with memory operands is probably the last step to this algorithm, and with that, its output could fool the majority of people into believing a human wrote the code instead of a compiler.
Brings me back to the compiler building lab I partook in university. Though before implementing an actual register allocator, we just emitted the same register operands for all instructions, mostly (e)ax, and spilled a lot, really a lot, on the stack. Stupid slow, but hey, seeing our compiler produce runnable code was a good motivator for the next steps!
But we were doing this for 32bit x86, so a lot of the limitations addressed here were not relevant anymore, and our register allocator code was likely far simpler, and probably still less efficient. (It's been at least 10 years now, not sure anymore.)
On a side note, I was surprised when some short time later I looked into Chrome V8's source code, and discovered that it didn't really have a register allocator either. I believe that was quickly addressed (by a new engine actually, if I recall right).
Yep, register allocators are really low priority early on if you're making a JIT(or AOT) compiler for a dynamic language such as JS or Python since there are much larger gains made by just avoiding the interpreter and high level optimizations avoiding type tests since they introduce branches, skipping reference counting, etc.
Once all that is done a register allocator starts becoming relevant, and even then you probably want another kind of allocator than is mainstream for lowlevel languages since you might not be doing a full-method compilation or a compilation that shouldn't require a full de-optimization if an divergent type is detected.
To load a value from memory we need the memory address in one of 3 suitable registers: bx, si, di.
You can use bp too, and for local variables it's the best one, since it defaults to using the stack segment. Adding support for non-movs with memory operands is probably the last step to this algorithm, and with that, its output could fool the majority of people into believing a human wrote the code instead of a compiler.