Occasionally we will generate code that writes to a subregister, then spills / restores a super-register to the stack, for example:
$eax = MOV32ri 0 MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax $rcx = MOV64rm $rsp, 1, $noreg, 8, $noreg
On x86 at least, memory instructions are widened to 32/64 bits to achieve the same spill/restore but save on instruction length. Instr-Ref LiveDebugValues isn't able to follow the value however, as it only stores the value in $rax, not the one in $eax.
Address this by tracking all the subregister values of anything that's stored to the stack. Multiple LocIdx locations are created for each slot, each paired with a subregister number, and any spill/restore instruction will transfer all subregister values to/from the stack locations.
While we're here, add a little more type safety by wrapping stack slot numbers in a class, to avoid them being automatically treated as a register. Overload getLocID so that a subregister index *must* be provided if you fetch a stack slot location. Convert getRegMLoc to return an optional, so that we get assertions if the two are ever mixed.
When transferring subregister values on spill and restore, the number transferred are limited to be only those that will fit in the stack slot, by the getStackSlotMaxSuperReg method. This is because AArch64 has some extensions that allow huge chunks of general purpose registers to be addressed en-mass, and so the full register hierarchy can have a
superregister covering almost all the general purpose registers. This can violate an assumption I've made, that LLVM doesn't spill non-zero bit offset subregisters into the same slot as other registers, i.e.:
MOV8mr $rsp, 1, $noreg, 16, $noreg, $ah MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax
Which would be more difficult to address if encountered.