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
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
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
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:
- Describe the intrinsics in include/llvm/IR/IntrinsicsTargetName.td
- Add some pre-IRTranslator IR pass which is going to convert corresponding instructions to your intrinsics
- 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
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 {
..
}