A question about Intrinsic

In the latest version of llvm, I found that the old version of Intrinsic does not work.

def int_riscv_mvin : Intrinsic<[],[llvm_i64_ty, llvm_i64_ty]>
def: PatGprGpr<int_riscv_mvin, MVIN>;

llvm/lib/Target/RISCV/RISCVInstrInfoBuddyExt.td:165:1: error: In anonymous_68739: Type cast can only have one type!
def: PatGprGpr<int_riscv_mvin, MVIN>;
def int_riscv_mvin : Intrinsic<[llvm_void_ty],[llvm_i64_ty, llvm_i64_ty]>;
def: PatGprGpr<int_riscv_mvin, MVIN>;
// It will report that 
Type set is empty for each HW mode:
possible type contradiction in the pattern below (use -print-records with llvm-tblgen to see all expanded records).
anonymous_68740:        (intrinsic_w_chain:{ *:[] m1:[] } 8075:{}, GPR:{ *:[i32] m1:[i64] }:$rs1, GPR:{ *:[i32] m1:[i64] }:$rs2)
Generated from record:
anonymous_68740 {       // Pattern Pat PatGprGpr
  dag PatternToMatch = (XLenVT (int_riscv_mvin GPR:$rs1, GPR:$rs2));
  list<dag> ResultInstrs = [(MVIN GPR:$rs1, GPR:$rs2)];
  list<Predicate> Predicates = [HasBuddyExt];
  int AddedComplexity = 0;

I am a bit unable to understand this behavior and would be grateful if someone could help me.Thanks!

class PatGprGpr<SDPatternOperator OpNode, RVInst Inst, ValueType vt = XLenVT>
    : Pat<(vt (OpNode GPR:$rs1, GPR:$rs2)), (Inst GPR:$rs1, GPR:$rs2)>;

I guess you want to pass isVoid for vt?

Thank you for your reply.I have solved the problem, when defining Intrinsic, I need to specify the return type, in this case llvm_void_ty, because I specified a lot of Intrinsic, but I just modified the return type of one Intrinsic, so it still reports an error. I modified the return type of all Intrinsic and it was fine.
But I have a question, I can’t understand the error message below, is there a relevant document that describes the error message below?

(intrinsic_w_chain:{ *:[] m1:[] } 8075:{}, GPR:{ *:[i32] m1:[i64] }:$rs1, GPR:{ *:[i32] m1:[i64] }:$rs2)
(intrinsic_w_chain:{ *:[] m1:[] }

INTRINSIC_W_CHAIN node (“intrinsic with chain”) whose result has no inferred types for the default HwMode (RV32 in this case) and no inferred types for HwMode m1 (RV64 in this case)


Presumably the chain operand, which can be ignored here

GPR:{ *:[i32] m1:[i64] }:$rs1, GPR:{ *:[i32] m1:[i64] }:$rs2

Operands $rs1 and $rs2, set to be register class GPR, and inferred as i32 for the default HwMode (RV32) and i64 for HwMode m1 (RV64)


End of node

When the type sets are empty for all HwModes then you get an error (the one you’re getting here), and when there’s more than one element for a type set for any HwMode then you also get an error (that it’s ambiguous). Having some empty but some singleton sets is fine and quite common, it just means that the pattern will only ever match for some HwModes. In fact it can be useful to force that to prune the instruction selection tables of patterns you know will never apply (e.g. RV64-only).

Though I will say that an INTRINSIC_W_CHAIN that returns void is quite a strange thing to have. I suspect you should not be passing [llvm_void_ty] to Intrinsic and should instead leave it as [] to get an INTRINSIC_VOID. Maybe TableGen should shout at you if you try to do that.

Thanks!At the beginning, I passed the result type [], but I found that TableGen would report an error, but [llvm_void_ty] would not have this problem.

Did you indicate the side effects of your intrinsic, which it presumably has? (Otherwise it can’t possibly do anything observable)

Oh! This point is ignored by me, because in fact I am not sure what the specific effects of side effects, although I know the meaning of some side effects, but I do not really experience these side effects, for llvm back-end, I am still a beginner, is there any relevant learning materials?

I don’t think the TableGen side is well-documented (though LangRef surely documents the IR side of things), but the comments in Intrinsics.td should help. What does your intrinsic do, what has you tried and what errors did you hit?

Thank you.As an example, the role of int_riscv_mvin,is to load data from the host memory into the coprocessor and encounter the error as I mentioned above.

Sorry, I was wrong before, because yesterday another friend was solving this problem, he was using llvm_any_ty, not llvm_void_ty, using llvm_void_ty will still have the original problem. But I still expect to use [], like you said before, here I can use the side effect of IntrWriteMem. I found this code below.You can check it out here.Here it makes use of the [].

 class RISCVUSStore
        : DefaultAttrsIntrinsic<[],
                    [NoCapture<ArgIndex<1>>, IntrWriteMem]>, RISCVVIntrinsic {
    let VLOperand = 2;

I found another problem.This code is from RISCVGenGlobalISel.inc,this was generated using an older version of llvm.But now with the new version of llvm, even if I pass the compilation successfully, there is no such content as below.One thing that is clear is that intrinsic_void does exist, as I saw below.I think an important question is how to generate intrinsic_void, how should the return type of intrinsic be set but how to not report an error?

GI found another problem.IM_CheckIntrinsicID, /*MI*/0, /*Op*/0, Intrinsic::riscv_mvin,
        GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s64,
        GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s64,
        GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/RISCV::GPRRegClassID,
        GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/RISCV::GPRRegClassID,
        // (intrinsic_void 7659:{ *:[iPTR] }, GPR:{ *:[i64] }:$rs1, GPR:{ *:[i64] }:$rs2)  =>  (MVIN GPR:{ *:[i64] }:$rs1, GPR:{ *:[i64] }:$rs2)
        GIR_BuildMI, /*InsnID*/0, /*Opcode*/RISCV::MVIN,
        GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // rs1
        GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // rs2
        GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
        GIR_EraseFromParent, /*InsnID*/0,
        GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
        // GIR_Coverage, 59549,

On this issue, I have made progress.Here’s my latest findings.
This is the record of the latest version of llvm-tblgen.What you can see here is that an XLenVT appears.

def anonymous_68773 {   // Pattern Pat PatGprGpr
  dag PatternToMatch = (XLenVT (int_riscv_flush GPR:$rs1, GPR:$rs2));
  list<dag> ResultInstrs = [(FLUSH GPR:$rs1, GPR:$rs2)];
  list<Predicate> Predicates = [HasBuddyExt];
  int AddedComplexity = 0;

This is a record of an older version of llvm-tblgen.There is no XLenVT in this record.

ef anonymous_59291 {   // Pattern Pat PatGprGpr
  dag PatternToMatch = (int_riscv_mvin GPR:$rs1, GPR:$rs2);
  list<dag> ResultInstrs = [(MVIN GPR:$rs1, GPR:$rs2)];
  list<Predicate> Predicates = [HasBuddyExt];
  int AddedComplexity = 0;

I have a question.Why does XLenVT appear here?

1 Like

That’s for GlobalISel, which isn’t fully implemented for RISC-V. You’re using SelectionDAG.

I really appreciate you! After your help, I managed to solve the problem. And have a deeper understanding of the backend. I think I can have more in-depth thinking when I encounter other problems in the future.

Perhaps you should post your solution here so future readers who end up in the same situation as you can refer to it. It would also mean we could verify that you’ve done something sensible and aren’t setting yourself up for future issues.

1 Like

Sorry, sir.Reply to your message a little late,because of work.
Here’s how I do it.Here I no longer use PatGprGpr, I use Pat.I avoided XLenVT.

let Predicates = [HasBuddyExt] in 
def : Pat<(int_riscv_mvin GPR:$rs1, GPR:$rs2), (MVIN GPR:$rs1, GPR:$rs2)>;