Add a custom intrinsic to the ARM backend

Hi,

I’m trying to add a new intrinsic to the ARM backend. The intrinsic should a custom comparison.
To do so, I started with first defining the intrinsic in llvm/include/llvm/IR/intrinsicsARM.td:

def int_foo_cmp : Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], []>;

The second step I did is adding a new pseudo instruction matching that intrinsic in lib/Target/ARM/ARMInstInfo.td:

def FOO_CMP : PseudoInst<(outs GPR:$Rd), (ins GPR:$src1, GPR:$src2), IIC_iCMPi,
[(set GPR:$Rd, (int_foo_cmp GPR:$src1, GPR:$src2))]>;

Now I am a bit lost how to proceed. I want the instrinsic to do a certain comparison chain and want to avoid any optimization on them.
Can someone guide me on how to proceed and emit machine instructions for that intrinsic?

Thanks & Cheers
Max

Hi Max,

Can someone guide me on how to proceed and emit machine instructions for
that intrinsic?

Next step is probably to handle FOO_CMP in ARMExpandPseudoInsts.cpp.
It's a pass that runs after most optimizations (maybe even all) so
it's where most sensitive pseudo-instructions are expanded.

There's plenty of other examples of how to do it in that file (the
ExpandMI function), all following pretty similar patterns.

Cheers.

Tim.

Hi Tim,

Thank you for your response. Meanwhile, I stumbled over the same function.
I added a case in ExpandMI for ARM::FOO_CMP.

First, I want to emit a simple equal comparison for that intrinsic. However, I don’t understand how to retrieve the return register for the intrinsic.
I tried the following:

case ARM::FOO: {
DebugLoc DL = MBBI->getDebugLoc();

BuildMI(&MBB, DL, TII->get(ARM::CMPrr))
.addReg(MI.getOperand(1).getReg())
.addReg(MI.getOperand(2).getReg())
.addImm(ARMCC::NE);

MI.eraseFromParent(); // The pseudo is gone now.
return true;
}

AFAIK, BuildMI() expects another parameter for the destination but how to retrieve that from the pseudo instruction?
In the future, the implementation of this instruction will get more complex and requires a temporary register for computation. How can I allocate a register at that stage of compiling?

Thanks & Cheers,

Max

Hi Max,

AFAIK, BuildMI() expects another parameter for the destination but how to
retrieve that from the pseudo instruction?

If you've got a MachineInstr that looks like this when dumped:

%R0<def> = FOO_CMP %R1, %R2

Then the registers are just the operands of the MachineInstr labelled
from left to right. So the "return" register, the def is operand 0.

In the future, the implementation of this instruction will get more complex
and requires a temporary register for computation. How can I allocate a
register at that stage of compiling?

You've got a couple of options there.

You could pick a random, fixed, register and tell LLVM that FOO_CMP
clobbers that one by (e.g. putting "let Defs = [R0]" in the
instruction definition. Then LLVM will make sure R0 isn't live across
that instruction, but of course that might lead to poorer code
generation.

The better way to do it would be to add an explicit extra out operand
to your instruction. Then after selection your instruction would look
something like:

  %vreg0<def>, %vreg1<def,dead> = FOO_CMP ...

and you'd eventually check operand 1 for the register you've been
allocated to use temporarily.

An example of this is AArch64's CMP_SWAP instructions in
AArch64InstrAtomics.td. The "@earlyclobber" is important, otherwise
LLVM might make one of the inputs the same as the scratch register,
and you probably don't want that.

One other slight wrinkle is that you'll have to write your pattern
separately, rather than as part of the instruction definition,
otherwise TableGen complains that $scratch isn't defined.

Hope this helps.

Tim.