Segmentation fault when adding a new RISCV Intrinsic

Hi, folks,

I’m struggling to add a new intrinsic to the RISCV target. Following the instructions here, I’ve tried to add the definition of my intrinsic to IntrinsicsRISCV.td and the implementation to RISCVISelLowering.cpp (details below). The compiler builds but when I try to use it to compile my code I get a segmentation fault (details below) in SelectionDAG::getNode here.

I think the root cause might be that my implementation in RISCVISelLowering does not match my declaration in IntrinsicsRISCV.td, but I’m not sure. Can anybody give me any pointers?

Thanks,
-Russ

IntrinsicsRISCV.td:

let TargetPrefix = "riscv" in {
    def int_riscv_my_custom_intrinsic : DefaultAttrsIntrinsic<[], [llvm_i32_ty, llvm_i32_ty], [IntrNoMem, ImmArg<ArgIndex<0>>, ImmArg<ArgIndex<1>>]>;
}

RISCVISelLowering.cpp:

// in RISCVTargetLowering::LowerINTRINSIC_VOID
case Intrinsic::riscv_my_custom_intrinsic {
    SDLoc DL(Op);
    SDValue firstArg= Op.getOperand(1);
    SDValue secondArg = Op.getOperand(2);
    // Clear out everything but the bottom 2 bits of secondArg
    SDValue secondArgLowBits = DAG.getNode(ISD::AND, DL, MVT::i32, secondArg,
                                           DAG.getConstant(0x3, DL, MVT::i32));

    // Shift secondArgLowBits over to make room for firstArg
    SDValue shiftedSecondArg =
        DAG.getNode(ISD::SHL, DL, MVT::i32, secondArgLowBits ,
                    DAG.getConstant(30, DL, MVT::i32));

    // Clear out the top 2 bits of firstArg
    SDValue firstArgLowBits =
        DAG.getNode(ISD::AND, DL, MVT::i32, firstArg,
                    DAG.getConstant(0x3FFFFFFF, DL, MVT::i32));

    // OR the arguments together into a single argument for the CFS call.
    SDValue CfsArg =
        DAG.getNode(ISD::OR, DL, MVT::i32, shiftedSecondArg , firstArgLowBits);

    // Copy the combined argument to X12
    SDValue LI = DAG.getNode(ISD::CopyToReg, DL, MVT::Other,
                             {DAG.getRegister(RISCV::X12, MVT::i32), CfsArg});

    // All CFS instructions are offset from a fixed address stored in X13
    // (0xFFFFFE00)
    SDValue BaseAddr = DAG.getRegister(RISCV::X13, MVT::i32);

    SDValue CFSAddrWithOffset = DAG.getNode(ISD::ADD, DL, MVT::i32, BaseAddr,
                                            DAG.getConstant(2, DL, MVT::i32));

    // Store the value in X12 to CFSAddrWithOffset.
    return DAG.getNode(
        ISD::STORE, DL, MVT::Other,
        {LI, DAG.getRegister(RISCV::X12, MVT::i32), CFSAddrWithOffset});
}

Sample .ll

target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
target triple = "riscv32"

define void @main() {
  ; call the new intrinsic
  call void @llvm.riscv.my_custom_intrinsic(i32 500, i32 1)
  ret void
}

declare void @llvm.riscv.my_custom_intrinsic(i32, i32)

Segmentation Fault

Stack dump:
0.	Program arguments: ./install/bin/llc -march=riscv32 -filetype=asm -O3 test_custom_intrinsic.ll -o test_custom_intrinsic.S
1.	Running pass 'Function Pass Manager' on module 'test_custom_intrinsic.ll'.
2.	Running pass 'RISC-V DAG->DAG Pattern Instruction Selection' on function '@main'
 #0 0x000055cf5f25b960 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (./install/bin/llc+0x2ee9960)
 #1 0x000055cf5f259264 SignalHandler(int) Signals.cpp:0:0
 #2 0x00007f03c8bdc520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x000055cf5f01d2b1 llvm::SelectionDAG::getNode(unsigned int, llvm::SDLoc const&, llvm::SDVTList, llvm::ArrayRef<llvm::SDValue>, llvm::SDNodeFlags) (./install/bin/llc+0x2cab2b1)
 #4 0x000055cf5efac68f llvm::SelectionDAGBuilder::visitTargetIntrinsic(llvm::CallInst const&, unsigned int) (./install/bin/llc+0x2c3a68f)
 #5 0x000055cf5ef8b064 llvm::SelectionDAGBuilder::visitIntrinsicCall(llvm::CallInst const&, unsigned int) (./install/bin/llc+0x2c19064)
 #6 0x000055cf5efccce0 llvm::SelectionDAGBuilder::visit(llvm::Instruction const&) (./install/bin/llc+0x2c5ace0)
 #7 0x000055cf5f040056 llvm::SelectionDAGISel::SelectBasicBlock(llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction, false, false, void>, false, true>, llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction, false, false, void>, false, true>, bool&) (./install/bin/llc+0x2cce056)
 #8 0x000055cf5f04155c llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) (./install/bin/llc+0x2ccf55c)
 #9 0x000055cf5f04385e llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) (.part.0) SelectionDAGISel.cpp:0:0
#10 0x000055cf5e614bb4 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (.part.0) MachineFunctionPass.cpp:0:0
#11 0x000055cf5eb0c15a llvm::FPPassManager::runOnFunction(llvm::Function&) (./install/bin/llc+0x279a15a)
#12 0x000055cf5eb0c2e4 llvm::FPPassManager::runOnModule(llvm::Module&) (./install/bin/llc+0x279a2e4)
#13 0x000055cf5eb0cce4 llvm::legacy::PassManagerImpl::run(llvm::Module&) (./install/bin/llc+0x279ace4)
#14 0x000055cf5c98394b compileModule(char**, llvm::LLVMContext&) llc.cpp:0:0
#15 0x000055cf5c8c0086 main (./install/bin/llc+0x54e086)
#16 0x00007f03c8bc3d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#17 0x00007f03c8bc3e40 call_init ./csu/../csu/libc-start.c:128:20
#18 0x00007f03c8bc3e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#19 0x000055cf5c97b235 _start (./install/bin/llc+0x609235)
Segmentation fault (core dumped)

I think your intrinsic is INTRINSIC_W_CHAIN, not INTRINSIC_VOID.

Thanks for your reply, Krzysztof. I originally put that case Intrinsic::riscv_my_custom_intrinsic block in all 3 of LowerINTRINSIC_VOID, LowerINTRINSIC_W_CHAIN, and LowerINTRINSIC_WO_CHAIN just to be safe. I removed it from the other two and left it in LowerINTRINSIC_W_CHAIN based on your suggestion but I see the same segfault.

In fact I don’t think LLVM is even getting to my code in RISCVISelLowering.cpp, I put exit(1) statements at the top of the three LowerINTRINSIC methods but I still see the segfault.

It’s failing in the selection DAG builder (in visitTargetIntrinsic), so the lowering (i.e. legalization) hasn’t even started yet.

Have you implemented getTgtMemIntrinsic for your intrinsic?

Edit: I looked some more into it, and you’re declaring your intrinsic as not touching memory, which would likely cause it to be treated as WO_CHAIN, especially if you didn’t implement the getTgtMemIntrinsic function for it. At the same time it returns 0 values, which, by looking at the visitTargetIntrinsic code, would generate an empty VT list. The getNode functions that take VTList don’t expect it to be empty.

Thanks, Krzysztof

I updated my intrinsic definition to indicate that the implementation writes to memory:

def int_riscv_my_custom_intrinsic : DefaultAttrsIntrinsic<[], [llvm_i32_ty, llvm_i32_ty], [IntrWriteMem, ImmArg<ArgIndex<0>>, ImmArg<ArgIndex<1>>]>;

Now it’s failing here with the error Cannot select: intrinsic %llvm.riscv.my.custom.intrinsic.

I want to be clear about what I have and haven’t done, in case I missed anything:

  • I added my intrinsic definition to a new file (in the riscv scope) and included that file in IntrinsicsRISCV.td)
  • I added the implementation of the intrinsic as a case statement in all the RISCVISelLowering::LowerINTRINSIC* methods, just to be safe :slight_smile:

I did not:

  • Add a case statement to getTgtMemIntrinsic: it seems like all these operations expect that the intrinsic will pass a pointer as one of its operands (Info.ptrVal); the memory location is hard-coded in my implementation.
  • Add anything to RISCVISelDAGtoDAG.cpp: I think I would add something here if I were defining a new type of selection node? But my intrinsic is implemented as a composition of existing types of nodes.
LLVM ERROR: Cannot select: intrinsic %llvm.riscv.my.custom.intrinsic
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: ./install/bin/llc -debug -march=riscv32 -filetype=asm -O3 test_qpulse.ll -o test_qpulse.S
1.	Running pass 'Function Pass Manager' on module 'test_qpulse.ll'.
2.	Running pass 'RISC-V DAG->DAG Pattern Instruction Selection' on function '@main'
 #0 0x000055a6ee223056 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/rweeks/projects/llvm-project/llvm/lib/Support/Unix/Signals.inc:602:22
 #1 0x000055a6ee223435 PrintStackTraceSignalHandler(void*) /home/rweeks/projects/llvm-project/llvm/lib/Support/Unix/Signals.inc:675:1
 #2 0x000055a6ee220afa llvm::sys::RunSignalHandlers() /home/rweeks/projects/llvm-project/llvm/lib/Support/Signals.cpp:104:20
 #3 0x000055a6ee22296e SignalHandler(int) /home/rweeks/projects/llvm-project/llvm/lib/Support/Unix/Signals.inc:413:1
 #4 0x00007f8be37e9520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #5 0x00007f8be383da7c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #6 0x00007f8be383da7c __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #7 0x00007f8be383da7c pthread_kill ./nptl/pthread_kill.c:89:10
 #8 0x00007f8be37e9476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #9 0x00007f8be37cf7f3 abort ./stdlib/abort.c:81:7
#10 0x000055a6ee157cbb llvm::report_fatal_error(llvm::Twine const&, bool) /home/rweeks/projects/llvm-project/llvm/lib/Support/ErrorHandling.cpp:125:9
#11 0x000055a6edfc7398 std::function<void (llvm::SDNode*, llvm::SDNode*)>::function<llvm::SelectionDAGISel::UpdateChains(llvm::SDNode*, llvm::SDValue, llvm::SmallVectorImpl<llvm::SDNode*>&, bool)::'lambda'(llvm::SDNode*, llvm::SDNode*), void>(llvm::SelectionDAGISel::UpdateChains(llvm::SDNode*, llvm::SDValue, llvm::SmallVectorImpl<llvm::SDNode*>&, bool)::'lambda'(llvm::SDNode*, llvm::SDNode*)&&) /usr/include/c++/11/bits/std_function.h:435:2
#12 0x000055a6edfc6aa1 llvm::SelectionDAGISel::SelectCodeCommon(llvm::SDNode*, unsigned char const*, unsigned int) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:3796:9
#13 0x000055a6ebc6f8ae llvm::RISCVDAGToDAGISel::SelectCode(llvm::SDNode*) /home/rweeks/projects/llvm-project/build/lib/Target/RISCV/RISCVGenDAGISel.inc:989619:0
#14 0x000055a6ebc6671a llvm::RISCVDAGToDAGISel::Select(llvm::SDNode*) /home/rweeks/projects/llvm-project/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp:2128:0
#15 0x000055a6edfb75ab llvm::SelectionDAGISel::DoInstructionSelection() /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:1179:5
#16 0x000055a6edfb67f6 llvm::SelectionDAGISel::CodeGenAndEmitDAG() /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:950:3
#17 0x000055a6edfb4d6a llvm::SelectionDAGISel::SelectBasicBlock(llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction, true, false, void>, false, true>, llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction, true, false, void>, false, true>, bool&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:702:1
#18 0x000055a6edfba860 llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:1705:33
#19 0x000055a6edfb37e6 llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:483:7
#20 0x000055a6ebc6f616 llvm::RISCVDAGToDAGISel::runOnMachineFunction(llvm::MachineFunction&) /home/rweeks/projects/llvm-project/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h:39:3
#21 0x000055a6ed0ec39a llvm::MachineFunctionPass::runOnFunction(llvm::Function&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/MachineFunctionPass.cpp:91:33
#22 0x000055a6ed88273a llvm::FPPassManager::runOnFunction(llvm::Function&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1435:20
#23 0x000055a6ed882a10 llvm::FPPassManager::runOnModule(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1481:13
#24 0x000055a6ed882e7b (anonymous namespace)::MPPassManager::runOnModule(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1550:20
#25 0x000055a6ed87daf8 llvm::legacy::PassManagerImpl::run(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:535:13
#26 0x000055a6ed88378b llvm::legacy::PassManager::run(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1678:1
#27 0x000055a6ea58693e compileModule(char**, llvm::LLVMContext&) /home/rweeks/projects/llvm-project/llvm/tools/llc/llc.cpp:759:66
#28 0x000055a6ea58420b main /home/rweeks/projects/llvm-project/llvm/tools/llc/llc.cpp:420:35
#29 0x00007f8be37d0d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#30 0x00007f8be37d0e40 call_init ./csu/../csu/libc-start.c:128:20
#31 0x00007f8be37d0e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#32 0x000055a6ea582e65 _start (./install/bin/llc+0x90de65)

If the intrinsic survives until selection time, it means that none of your lowering code actually executes. Please check if your intrinsic is considered for custom lowering—you can use -debug-only=legalizedag to trace the lowering process. You can add isel to see the DAG after various steps of the lowering/instruction selection. If not, add setOperationAction for your case.

Thanks, Krzysztof! I think your comment set me on the right path. I noticed that RISCVISelLowering does not override getCustomOperationAction from TargetLowering.h. I thought it would be an easy fix to override that method to match my intrinsic but it turned out to be more complicated than I expected.

The problem was that LegalizeDAG is passing the ISD::INTRINSIC_VOID opcode to TLI.getOperationAction here. So getOperationAction doesn’t have the context to know if this is an intrinsic that needs custom lowering or not.

I was able to kludge around that in a really ugly way but would welcome any advice on the “right way” to fix it:

RISCVIselLowering.cpp (getOperationAction is virtual now!)

TargetLoweringBase::LegalizeAction RISCVTargetLowering::getCustomOperationAction(SDNode &Op) const {
  LLVM_DEBUG(dbgs() << "In getCustomOperationAction, Op.getOpcode() == " << Op.getOpcode() << "\n");
  if (Op.getNumOperands() > 1) {
    switch (Op.getConstantOperandVal(1)) {
    default:
      break;
    case Intrinsic::riscv_my_custom_intrinsic: {
      LLVM_DEBUG(dbgs() << "Signaling custom lowering for my custom intrinsic\n");
      return LegalizeAction::Custom;
    }
    }
  }
  if (Op.getOpcode() == ISD::INTRINSIC_VOID) {
    LegalizeAction OpAction = TargetLoweringBase::getOperationAction(Op.getOpcode(), MVT::Other);
    return OpAction == LegalizeAction::Custom ? LegalizeAction::Legal : OpAction;
  }
  return LegalizeAction::Legal;
}

TargetLoweringBase::LegalizeAction RISCVTargetLowering::getOperationAction(unsigned Op, EVT VT) const {
  // this is a horrible kludge: not all INTRINSIC_VOIDs require custom lowering
  // but we don't have the context here to limit only to my custom intrinsic.
  // Fortunately this call will be followed by a call to getCustomOperationAction
  // which will let us declare other intrinsics as whatever they should be.
  return Op == ISD::INTRINSIC_VOID ? LegalizeAction::Custom :
    TargetLoweringBase::getOperationAction(Op, VT);
}

Now it seems like the lowering left over a node that it shouldn’t have: there is an EntryToken in the CSEMap, and I guess there shouldn’t be. Not sure if this is related to my kludge or a different problem.

The intrinsic seems to be lowered properly:

Legalizing: t4: ch = llvm.riscv.my_custom_intrinsic t0, TargetConstant:i32<8227>, TargetConstant:i32<500>, TargetConstant:i32<1>
Trying custom legalization
Creating constant: t6: i32 = Constant<3>
Creating constant: t7: i32 = Constant<0>
Creating constant: t8: i32 = Constant<30>
Creating constant: t9: i32 = Constant<1073741823>
Creating constant: t10: i32 = Constant<8227>
Creating new node: t12: ch = CopyToReg Register:i32 $x12, Constant:i32<8227>
Creating new node: t14: i32 = undef
Creating new node: t15: ch = store<(store (s32) into constant-pool)> t12, Register:i32 $x12, Register:i32 $x13, undef:i32
Successfully custom legalized node
 ... replacing: t4: ch = llvm.riscv.my_custom_intrinsic t0, TargetConstant:i32<8227>, TargetConstant:i32<500>, TargetConstant:i32<1>
     with:      t15: ch = store<(store (s32) into constant-pool)> t12, Register:i32 $x12, Register:i32 $x13, undef:i32

But the Legalize() function fails when removing a glue (?) node from the DAG:

 #0 0x0000555559b024ea llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/rweeks/projects/llvm-project/llvm/lib/Support/Unix/Signals.inc:602:22
 #1 0x0000555559b028c9 PrintStackTraceSignalHandler(void*) /home/rweeks/projects/llvm-project/llvm/lib/Support/Unix/Signals.inc:675:1
 #2 0x0000555559afff8e llvm::sys::RunSignalHandlers() /home/rweeks/projects/llvm-project/llvm/lib/Support/Signals.cpp:104:20
 #3 0x0000555559b01e02 SignalHandler(int) /home/rweeks/projects/llvm-project/llvm/lib/Support/Unix/Signals.inc:413:1
 #4 0x00007ffff7a79520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #5 0x00007ffff7acda7c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #6 0x00007ffff7acda7c __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #7 0x00007ffff7acda7c pthread_kill ./nptl/pthread_kill.c:89:10
 #8 0x00007ffff7a79476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #9 0x00007ffff7a5f7f3 abort ./stdlib/abort.c:81:7
#10 0x00007ffff7a5f71b _nl_load_domain ./intl/loadmsgcat.c:1177:9
#11 0x00007ffff7a70e96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
#12 0x000055555981902a llvm::SelectionDAG::RemoveNodeFromCSEMaps(llvm::SDNode*) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:1195:31
#13 0x00005555598181af llvm::SelectionDAG::DeleteNode(llvm::SDNode*) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp:1044:25
#14 0x00005555597771f7 llvm::SelectionDAG::Legalize() /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp:5497:9
#15 0x0000555559894eb7 llvm::SelectionDAGISel::CodeGenAndEmitDAG() /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:906:3
#16 0x00005555598938d4 llvm::SelectionDAGISel::SelectBasicBlock(llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction, true, false, void>, false, true>, llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction, true, false, void>, false, true>, bool&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:702:1
#17 0x00005555598993e0 llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:1705:33
#18 0x0000555559892350 llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp:483:7
#19 0x000055555754e6b4 llvm::RISCVDAGToDAGISel::runOnMachineFunction(llvm::MachineFunction&) /home/rweeks/projects/llvm-project/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h:39:3
#20 0x00005555589cab66 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) /home/rweeks/projects/llvm-project/llvm/lib/CodeGen/MachineFunctionPass.cpp:91:33
#21 0x0000555559160f2c llvm::FPPassManager::runOnFunction(llvm::Function&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1435:20
#22 0x0000555559161202 llvm::FPPassManager::runOnModule(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1481:13
#23 0x000055555916166d (anonymous namespace)::MPPassManager::runOnModule(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1550:20
#24 0x000055555915c2ea llvm::legacy::PassManagerImpl::run(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:535:13
#25 0x0000555559161f7d llvm::legacy::PassManager::run(llvm::Module&) /home/rweeks/projects/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:1678:1
#26 0x0000555555e6593e compileModule(char**, llvm::LLVMContext&) /home/rweeks/projects/llvm-project/llvm/tools/llc/llc.cpp:759:66
#27 0x0000555555e6320b main /home/rweeks/projects/llvm-project/llvm/tools/llc/llc.cpp:420:35
#28 0x00007ffff7a60d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#29 0x00007ffff7a60e40 call_init ./csu/../csu/libc-start.c:128:20
#30 0x00007ffff7a60e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#31 0x0000555555e61e65 _start (/home/rweeks/projects/llvm-project/install/bin/llc+0x90de65)

   1191	  default:
   1192	    // Remove it from the CSE Map.
   1193	    assert(N->getOpcode() != ISD::DELETED_NODE && "DELETED_NODE in CSEMap!");
-> 1194	    assert(N->getOpcode() != ISD::EntryToken && "EntryToken in CSEMap!");
   1195	    Erased = CSEMap.RemoveNode(N);
   1196	    break;
   1197	  }
(lldb) p N->getOpcode()
(unsigned int) $0 = 1
(lldb) p N->dumpr(this)
t0: ch,glue = EntryToken

There should be setOperationAction(ISD::INTRINSIC_VOID, ...) in RISCVISelLowering.cpp somewhere (if there isn’t, add one). This one should apply to your case, and if it doesn’t then maybe there needs to be another one with a different type (the ... argument). What that type refers to depends on the operation, so you usually need to look it up in the source (LegalizeDAG.cpp, LegalizeOp function is a good start).

The getCustomOperationAction is for custom operations, i.e. RISCVISD::SOMETHING_NEW, not for things that are already defined in ISD. You’re adding a new intrinsic, which in your case is a variant of ISD::INTRINSIC_VOID, so your lowering should be handled in the code that lowers INTRINSIC_VOID on RISCV.

The setOperationAction(ISD::INTRINSIC_VOID, ...) doesn’t care about any specific intrinsic, it will apply to all such intrinsics. Your code will be called for all of them. It should identify the specific cases that it wants to custom-lower, and ignore the rest.

When the legalizer calls LowerOperation for some node N, and your code returns an sdvalue V, then

  1. If you just return the same node back (dressed as SDValue, i.e. SDValue(N, 0)), then N is considered legal.
  2. If you return SDValue(), i.e. null node, then N is considered not legal, and the legalizer will continue trying to legalize it (usually via expansion).
  3. If you return V that is neither of the above, then V will be treated as the legalized version of N.

If your lowering wants to ignore something, then it needs to do either 1 or 2 of the above.

Amazing, thanks very much for the detailed response! I was able to get my intrinsic lowered without any ugly kludges. The other problem that I was seeing, EntryToken in CSEMap!, was because I wasn’t setting the chain value correctly as the 0th operand of my new node.