How to cause a "deliberate spill"?

Hi all,

For education, and the fun of it, I’m writing an LLVM backend for the MC6809 processor. I’m using GlobalISel as that seems to be the future, and i’ve made a fair bit of progress with it.

The processor is strange by modern standards, so I have to make some design choices up front. I want LLVM to do as much of the work as I can, within reason. I’ve borrowed an idea from the llvm-mos project (for the 6502). Instruction are first lowered to pseudoinstructions modelling the processor’s general architecture, but leaving the registers in as classes, sometimes with only one register in the class. A later pass then lowers to the MC instruction once the register is known. So far so good.

I’ve got a tricky problem here, though. In trying to compile

char
subtract(char a)
{
  return 71 - a;
}

I get

bb.1.entry:
  liveins: $ab
  %0:_(s8) = COPY $ab
  %1:_(s8) = G_CONSTANT i8 71
  %2:_(s8) = G_SUB %1:_, %0:_
  $ab = COPY %2:_(s8)
  ReturnImplicit implicit $ab

This is good, except that $ab is now in a register, but the constant is still virtual. The processor can’t directly do the subtraction - the contents of $ab would need to be deliberately spilled. Note that while the MC6809 does have 2 accumulators, A and B, arithmetic directly between them is not possible.

The final MC assembler should look something like

  pshs    b          ; Push b onto the stack (or perhaps the frame)
  ldb     #71
  subb    ,s+     ; Subtract the value from ToS and pop stack (or frame)
  rts

My question - I reckon I could do this with instruction bundles, but is there a cleaner way of deliberately spilling $ab into some stack- or frame-local storage? MemoryOperands look like they may appear at this point?

Another way of doing this could be

  subb    #71
  negb
  rts

… but this may get messy with multibyte subtractions and sorting out signed vs unsigned.

M

There’s nothing really stopping you from generating push/pop sequences whenever you want. Later passes should tolerate the stack manipulation as long as you’re not overwriting memory that’s already allocated for something else. You don’t need to use instruction bundles or anything like that, and MemoryOperands are optional. On commonly used targets, this sort of sequence is used for calls, not simple arithmetic, but it’s not fundamentally different. Maybe look at the sequences we generate for calls on 32-bit x86.

Alternatively, you could explicitly allocate some space on the stack using CreateStackObject(); the location of an object allocated that way is called a “frame index”. It gets converted to a real address in PrologEpilogInserter, when the stack frame is laid out.

Great, thank you! I’d missed CreateStackObject() for some reason.

Good to know about just using the stack as well. That simplifies my approach.