This is obviously mostly for swifttailcc, but it's trivial to being tailcc and fastcc along for the ride so I did those too.
The code is pretty closely modelled on how AArch64 handles this because most RISC backends will need approximately the same behaviour, so there are a few main components:
- Track how much stack space the callee is expected to restore on return (and do it).
- In the tail call case arguments might not simply be stored to sp + N, but to an object at a fixed offset from the stack on function entry. We use a fixed frame-index for this, which naturally reserves the space so callee-saved registers won't be placed there.
- The tail call itself needs to track how much stack space it actually needs (because different tail calls vary this).