[GlobalISel] Customize IRTranslator::translate?

Hello!

Is there a way to customize the initial translating from llvm-ir to gMIR?

For example, when lowering getElementPtr,

  %idx = getelementptr inbounds i32, i32 addrspace(1)* %out, i32 %0

GlobalISel now generates the following gMIR:

  // IRTranslator::translateGetElementPtr

  %1:_(s32) = COPY $arg0
  %4:_(s32) = G_CONSTANT i32 4
  %offset:_(s32) = G_MUL %1:_, %4:_
  %6:_(p1) = G_PTR_ADD %ptr_base:_, %offset:_(s32)

But on my target, I would like to lower it to:

  %6:_(p1) = G_MY_MUL_ADD %ptr_base:_, %1:_(s32), i32 4

I can match the pattern in combine pass, but I am curious about if I can translate to my preferred form at the beginning?

Thanks,
CY

My first guess is that you want an intrinsic.

Hi tschuett,

Thanks for your reply :slight_smile:
Is this good practice in GlobalISel?

CY

As you see in your example the IRTranslator faithfully translates LLVM IR into gMIR.
There are two exceptions: (a) call lowering is target dependent and (b) intrinsics can be target dependent.

There is no G_AVX512_MUL_ADD and no G_NEON_MUL_ADD. They have to be intrinsics in LLVM IR. On the other hand llvm.prefetch is a target independent intrinsic.

I hope that helps you.

No, IRTranslator is not designed to allow that. It would be much more “normal” to write a combine to match the G_MUL and G_PTR_ADD and turn it into your preferred opcode.

No, IRTranslator is not designed to allow that. It would be much more “normal” to write a combine to match the G_MUL and G_PTR_ADD and turn it into your preferred opcode.

Thanks Jayfoad :slight_smile:
Yes that’s my current understanding. If I really want to customize IRTranslator::translate I guess I have to hack it directly.

As you see in your example the IRTranslator faithfully translates LLVM IR into gMIR.
There are two exceptions: (a) call lowering is target dependent and (b) intrinsics can be target dependent.

Thanks tschuett :slight_smile:
I guess I still need a frontend to lower LLVM IR to intrinsics, that probably means hacking IRTranslator::translate!

Many thanks for the input!
CY

Hi @cycheng,

In the early stages of SPIR-V backend development we had to tackle this problem exactly. And for purposes of overriding the way IRTranslator lowers some LLVM IR instructions target-specific intrinsics have really proved to be beneficial. To support that you basically need the following:

  1. Describe the intrinsics in include/llvm/IR/IntrinsicsTargetName.td
  2. Add some pre-IRTranslator IR pass which is going to convert corresponding instructions to your intrinsics
  3. Handle them at Legalizer/InstructionSelector stage

We did exactly the thing for GEPs, e.g. if you have
%idx = getelementptr inbounds i32, i32 addrspace(1)* %out, i32 %0
then after inserting intrinsics it becomes
%idx = call i32 target.gep i32 addrspace(1)* %out, i32 %0
and after IRTranslator
%idx = G_INTRINSIC target.gep, %1, %0
where target.gep (i.e. the id of an intrinsic itself for you to process) is a separate operand and is accessible with MachineOperand::getIntrinsicID and even more convenient MachineInstr::getIntrinsicID for the whole instruction since its position is fixed.
https://llvm.org/docs/GlobalISel/GenericOpcode.html#g-intrinsic-g-intrinsic-w-side-effects

1 Like

Hi @zuban32

Thanks a lot for the details :slight_smile:
This is what I am looking for!

CY

== SPIRV Source Appendix ==

include/llvm/IR/IntrinsicsSPIRV.td

let TargetPrefix = "spv" in {
  ...
  def int_spv_gep : Intrinsic<[llvm_anyptr_ty], [llvm_i1_ty, llvm_any_ty, llvm_vararg_ty], [ImmArg<ArgIndex<0>>]>;

lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

class SPIRVEmitIntrinsics
    : public FunctionPass,
      public InstVisitor<SPIRVEmitIntrinsics, Instruction *> {
  ..
  Instruction *visitGetElementPtrInst(GetElementPtrInst &I);
  ..
};

Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst(GetElementPtrInst &I) {
  ..
  auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
  ..
}

bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
  ..
  for (auto *I : Worklist) {
    TrackConstants = true;
    if (!I->getType()->isVoidTy() || isa<StoreInst>(I))
      IRB->SetInsertPoint(I->getNextNode());
    I = visit(*I);
    processInstrAfterVisit(I);
  }
  ..
}

lib/Target/SPIRV/SPIRVTargetMachine.cpp

void SPIRVPassConfig::addISelPrepare() {
  addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
  TargetPassConfig::addISelPrepare();
}

lib/Target/SPIRV/SPIRVInstructionSelector.cpp

bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
                                               const SPIRVType *ResType,
                                               MachineInstr &I) const {
  switch (I.getIntrinsicID()) {
  ..
  case Intrinsic::spv_gep:
    return selectGEP(ResVReg, ResType, I);
    break;
  ..
}

bool SPIRVInstructionSelector::selectGEP(Register ResVReg,
                                         const SPIRVType *ResType,
                                         MachineInstr &I) const {
  ..
}