Create a new MachineBasicBlock in the LowerCall function

Background:
I’m currently working on creating a custom CALL instruction in LLVM, which takes two operands: the callee and the return label.

Goal:
My objective is to create a MachineBasicBlock within the LowerCall function, which should be inserted after the CALLSEQ_END instruction.

Progress so far:
During my implementation, I came across two functions that could be helpful: a. (MachineFunction) MF.CreateMachineBasicBlock() b. (SelectionDAG) DAG.getBasicBlock()

To achieve my goal, I created two MachineBasicBlock instances - one before the call lowering and one after it. I used ReplaceAllUsesWith to replace the former with the latter.

Problem:
However, I encountered an error during the execution, specifically:

ScheduleDAGRRList::ReleasePredecessors(llvm::SUnit*): Assertion `N && "Must find call sequence start"' failed.

To investigate further, I added some debug prints to the FindCallSeqStart function to track the instructions it processes. It appears that the newly created block is positioned after the CALLSEQ_START but before the actual call instruction.

Question:
Is there a way to move the newly created block after the CALLSEQ_END instruction? Additionally, do you have any recommendations for a better way to debug this issue?

I’d suggest using a post-isel hook (AdjustInstrPostInstrSelection) to insert the new basic block; creating a block during isel isn’t going to work.

Relevant information:

LowerCall()

SDValue TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
                                    SmallVectorImpl<SDValue> &InVals) const {
  // ...  
  MachineFunction &MF = DAG.getMachineFunction();

  MachineBasicBlock *temporaryBasicBlock = MF.CreateMachineBasicBlock();
  SDValue temporaryBlock = DAG.getBasicBlock(temporaryBasicBlock);

  // ..

  Ops.push_back(temporaryBlock);
  Chain = DAG.getNode(IsDirect ? BL : JL, dl, NodeTys, Ops);

  // ...

  MachineBasicBlock *NewMBB = MF.CreateMachineBasicBlock();
  MF.push_back(NewMBB);

  SDValue newBlock = DAG.getBasicBlock(NewMBB);
  
  SDNode* deleteNode = temporaryBlock.getNode();
  DAG.ReplaceAllUsesWith(temporaryBlock, newBlock);
  DAG.DeleteNode(deleteNode);

Traceback

clang: /llvm/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp:591: void {anonymous}::ScheduleDAGRRList::ReleasePredecessors(llvm::SUnit*): Assertion 'N && "Must find call sequence start"' failed.

 #0 0x00000000040ad05f PrintStackTraceSignalHandler(void*) Signals.cpp:0:0
 #1 0x00000000040aad94 llvm::sys::CleanupOnSignal(unsigned long) (/llvm/build/bin/clang+0x40aad94)
 #2 0x0000000003ff0b88 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #3 0x00007fcb32a14ce0 __restore_rt (/lib64/libpthread.so.0+0x12ce0)
 #4 0x00007fcb3150ca4f raise (/lib64/libc.so.6+0x4ea4f)
 #5 0x00007fcb314dfdb5 abort (/lib64/libc.so.6+0x21db5)
 #6 0x00007fcb314dfc89 _nl_load_domain.cold.0 (/lib64/libc.so.6+0x21c89)
 #7 0x00007fcb315053a6 (/lib64/libc.so.6+0x473a6)
 #8 0x00000000050a44eb (/llvm/build/bin/clang+0x50a44eb)
 #9 0x00000000050a9264 (anonymous namespace)::ScheduleDAGRRList::Schedule() ScheduleDAGRRList.cpp:0:0
#10 0x00000000050948f0 llvm::SelectionDAGISel::CodeGenAndEmitDAG() (/llvm/build/bin/clang+0x50948f0)
#11 0x0000000005097da4 llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) (/llvm/build/bin/clang+0x5097da4)
#12 0x0000000005099972 llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) (.part.1149) SelectionDAGISel.cpp:0:0
#13 0x00000000033a7b5f llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (.part.68) MachineFunctionPass.cpp:0:0
#14 0x000000000391dcd0 llvm::FPPassManager::runOnFunction(llvm::Function&) (/llvm/build/bin/clang+0x391dcd0)
#15 0x000000000391def9 llvm::FPPassManager::runOnModule(llvm::Module&) (/llvm/build/bin/clang+0x391def9)
#16 0x000000000391eb35 llvm::legacy::PassManagerImpl::run(llvm::Module&) (/llvm/build/bin/clang+0x391eb35)
#17 0x000000000495f27e clang::EmitBackendOutput(clang::DiagnosticsEngine&, clang::HeaderSearchOptions const&, clang::CodeGenOptions const&, clang::TargetOptions const&, clang::LangOptions const&, llvm::StringRef, llvm::Module*, clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream>>) (/llvm/build/bin/clang+0x495f27e)
#18 0x0000000004d93610 clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) CodeGenAction.cpp:0:0
#19 0x0000000005fae239 clang::ParseAST(clang::Sema&, bool, bool) (/llvm/build/bin/clang+0x5fae239)
#20 0x0000000004d92258 clang::CodeGenAction::ExecuteAction() (/llvm/build/bin/clang+0x4d92258)
#21 0x0000000004cbac59 clang::FrontendAction::Execute() (/llvm/build/bin/clang+0x4cbac59)
#22 0x0000000004c4e9e1 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/llvm/build/bin/clang+0x4c4e9e1)
#23 0x0000000004d88cab clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/llvm/build/bin/clang+0x4d88cab)
#24 0x0000000002fbee74 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/llvm/build/bin/clang+0x2fbee74)
#25 0x0000000002fb7f17 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&) driver.cpp:0:0
#26 0x0000000004ad77e5 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::'lambda'()>(long) Job.cpp:0:0
#27 0x0000000003ff0cf2 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/llvm/build/bin/clang+0x3ff0cf2)
#28 0x0000000004ad7ee6 clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const (.part.217) Job.cpp:0:0
#29 0x0000000004aa47f2 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const (/llvm/build/bin/clang+0x4aa47f2)
#30 0x0000000004aa52d3 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const (/llvm/build/bin/clang+0x4aa52d3)
#31 0x0000000004ab05cc clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) (/llvm/build/bin/clang+0x4ab05cc)
#32 0x0000000002fbce2d clang_main(int, char**) (/llvm/build/bin/clang+0x2fbce2d)
#33 0x00007fcb314f8ca3 __libc_start_main (/lib64/libc.so.6+0x3aca3)
#34 0x0000000002fb7a8e _start (/llvm/build/bin/clang+0x2fb7a8e)

that was quick lol thank you so much :smiley:
I will look into it!

I’ve tried to use it now, and ran into two issues.

First, it seems I still have to add a MachineBaisicBlock operand in the LowerCall function, otherwise it fails to select the instruction.

Second, I’m not able to add a new operand to the call instruction in the post-isel hook - “Trying to add an operand to a machine instr that is already done!”

Is there maybe a different function that can add an operand before it is done?

In addition, where can I find documentation about this?

Thanks a lot!

Define two instructions: one without the extra MBB operand, and one with it. Use the one without the operand during isel, transform it to the one with the extra MBB operand after isel.

Looking at implementations of AdjustInstrPostInstrSelection for other targets should give you some idea of how this is done in practice. (The way SELECT instructions are lowered on RISCV is similar to what you’re trying to do.)

I tried your solution.

The problem is, when I created the new MachineBasicBlock the new instructions that followed were created in the original MBB…

It worked when I created a post reg alloc pass, I just spliced the MBB and transfer the instructions to the new one.
When I tried to apply the same solution to the post isel hook it looked like the new instructions were not yet created so I could not transfer them.

Now, I have a working solution using the post reg alloc pass, but I wonder if it could be done better.

Thank you for your help :smiley: