[IR] Add CallBr intrinsics support by ro-i · Pull Request #133907 · llvm/llvm-project (original) (raw)

@llvm/pr-subscribers-llvm-ir

Author: Robert Imschweiler (ro-i)

Changes

This commit adds support for using intrinsics with callbr. The uses of this will most of the time look like this example:

callbr void @llvm.amdgcn.kill(i1 %c) to label %cont [label %kill] kill: unreachable cont: ...

@arsenm


Full diff: https://github.com/llvm/llvm-project/pull/133907.diff

8 Files Affected:

diff --git a/llvm/include/llvm/Analysis/RegionInfoImpl.h b/llvm/include/llvm/Analysis/RegionInfoImpl.h index eb99d8bc6fb23..759e9c47bebb8 100644 --- a/llvm/include/llvm/Analysis/RegionInfoImpl.h +++ b/llvm/include/llvm/Analysis/RegionInfoImpl.h @@ -553,6 +553,18 @@ bool RegionInfoBase::isRegion(BlockT *entry, BlockT *exit) const { using DST = typename DomFrontierT::DomSetType; + // TODO? post domination frontier? + if constexpr (std::is_same_v<BlockT, BasicBlock>) { + if (DomTreeNodeT *PDTNode = PDT->getNode(exit); PDTNode) { + for (DomTreeNodeT *PredNode : *PDTNode) { + for (BasicBlock *Pred : predecessors(PredNode->getBlock())) { + if (isa(Pred->getTerminator())) + return false; + } + } + } + } + DST *entrySuccs = &DF->find(entry)->second; // Exit is the header of a loop that contains the entry. In this case, diff --git a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h index 6faff3d1fd8e3..59143d235eb93 100644 --- a/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h +++ b/llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h @@ -606,9 +606,9 @@ bool SplitIndirectBrCriticalEdges(Function &F, bool IgnoreBlocksWithoutPHI, // successors void InvertBranch(BranchInst *PBI, IRBuilderBase &Builder); -// Check whether the function only has simple terminator: -// br/brcond/unreachable/ret -bool hasOnlySimpleTerminator(const Function &F); +// Check whether the function only has blocks with simple terminators: +// br/brcond/unreachable/ret (or callbr if AllowCallBr) +bool hasOnlySimpleTerminator(const Function &F, bool AllowCallBr = true); // Returns true if these basic blocks belong to a presplit coroutine and the // edge corresponds to the 'default' case in the switch statement in the diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index f8afb42bf5535..0f698375ad6cf 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -3009,8 +3009,64 @@ bool IRTranslator::translateInvoke(const User &U, bool IRTranslator::translateCallBr(const User &U, MachineIRBuilder &MIRBuilder) { - // FIXME: Implement this. - return false; + const CallBrInst &I = cast(U); + MachineBasicBlock *CallBrMBB = &MIRBuilder.getMBB(); + + // TODO: operand bundles (see SelDAG implementation of callbr)? + assert(!I.hasOperandBundles() && + "Cannot lower callbrs with operand bundles yet"); + + if (I.isInlineAsm()) { + // FIXME: inline asm not yet supported + if (!translateInlineAsm(I, MIRBuilder)) + return false; + } else if (I.getIntrinsicID() != Intrinsic::not_intrinsic) { + switch (I.getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic for callbr"); + case Intrinsic::amdgcn_kill: + if (I.getNumIndirectDests() != 1) + report_fatal_error( + "amdgcn.kill supportes exactly one indirect destination"); + CallInst *CI = + CallInst::Create(I.getFunctionType(), I.getCalledFunction(), + SmallVector<Value *, 1>(I.args())); + bool Success = translateCall(*CI, MIRBuilder); + CI->deleteValue(); + if (!Success) + return false; + break; + } + } else { + report_fatal_error("Only know how to handle inlineasm/intrinsic callbr"); + } + + // Retrieve successors. + SmallPtrSet<BasicBlock *, 8> Dests; + Dests.insert(I.getDefaultDest()); + MachineBasicBlock *Return = &getMBB(*I.getDefaultDest()); + + // Update successor info. + addSuccessorWithProb(CallBrMBB, Return, BranchProbability::getOne()); + // TODO: For most of the cases where there is an intrinsic callbr, we're + // having exactly one indirect target, which will be unreachable. As soon as + // this changes, we might need to enhance + // Target->setIsInlineAsmBrIndirectTarget or add something similar for + // intrinsic indirect branches. + if (I.isInlineAsm()) { + for (BasicBlock *Dest : I.getIndirectDests()) { + MachineBasicBlock *Target = &getMBB(*Dest); + Target->setIsInlineAsmBrIndirectTarget(); + Target->setMachineBlockAddressTaken(); + Target->setLabelMustBeEmitted(); + // Don't add duplicate machine successors. + if (Dests.insert(Dest).second) + addSuccessorWithProb(CallBrMBB, Target, BranchProbability::getZero()); + } + } + CallBrMBB->normalizeSuccProbs(); + + return true; } bool IRTranslator::translateLandingPad(const User &U, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 6db2a5ffbfb84..c9501128cd593 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -3385,8 +3385,26 @@ void SelectionDAGBuilder::visitCallBr(const CallBrInst &I) { {LLVMContext::OB_deopt, LLVMContext::OB_funclet}) && "Cannot lower callbrs with arbitrary operand bundles yet!"); - assert(I.isInlineAsm() && "Only know how to handle inlineasm callbr"); - visitInlineAsm(I); + if (I.isInlineAsm()) { + visitInlineAsm(I); + } else if (I.getIntrinsicID() != Intrinsic::not_intrinsic) { + switch (I.getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic for callbr"); + case Intrinsic::amdgcn_kill: + if (I.getNumIndirectDests() != 1) + report_fatal_error( + "amdgcn.kill supportes exactly one indirect destination"); + CallInst *CI = + CallInst::Create(I.getFunctionType(), I.getCalledFunction(), + SmallVector<Value *, 1>(I.args())); + visitCall(*CI); + CI->deleteValue(); + break; + } + } else { + report_fatal_error("Only know how to handle inlineasm/intrinsic callbr"); + } CopyToExportRegsIfNeeded(&I); // Retrieve successors. @@ -3396,15 +3414,21 @@ void SelectionDAGBuilder::visitCallBr(const CallBrInst &I) { // Update successor info. addSuccessorWithProb(CallBrMBB, Return, BranchProbability::getOne()); - for (unsigned i = 0, e = I.getNumIndirectDests(); i < e; ++i) { - BasicBlock *Dest = I.getIndirectDest(i); - MachineBasicBlock *Target = FuncInfo.getMBB(Dest); - Target->setIsInlineAsmBrIndirectTarget(); - Target->setMachineBlockAddressTaken(); - Target->setLabelMustBeEmitted(); - // Don't add duplicate machine successors. - if (Dests.insert(Dest).second) - addSuccessorWithProb(CallBrMBB, Target, BranchProbability::getZero()); + // TODO: For most of the cases where there is an intrinsic callbr, we're + // having exactly one indirect target, which will be unreachable. As soon as + // this changes, we might need to enhance + // Target->setIsInlineAsmBrIndirectTarget or add something similar for + // intrinsic indirect branches. + if (I.isInlineAsm()) { + for (BasicBlock *Dest : I.getIndirectDests()) { + MachineBasicBlock *Target = FuncInfo.getMBB(Dest); + Target->setIsInlineAsmBrIndirectTarget(); + Target->setMachineBlockAddressTaken(); + Target->setLabelMustBeEmitted(); + // Don't add duplicate machine successors. + if (Dests.insert(Dest).second) + addSuccessorWithProb(CallBrMBB, Target, BranchProbability::getZero()); + } } CallBrMBB->normalizeSuccProbs(); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index ed86a10c3a25f..fbf6e087177c6 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -3249,11 +3249,30 @@ void Verifier::visitIndirectBrInst(IndirectBrInst &BI) { } void Verifier::visitCallBrInst(CallBrInst &CBI) { - Check(CBI.isInlineAsm(), "Callbr is currently only used for asm-goto!", &CBI); - const InlineAsm *IA = cast(CBI.getCalledOperand()); - Check(!IA->canThrow(), "Unwinding from Callbr is not allowed"); + if (!CBI.isInlineAsm()) { + switch (CBI.getIntrinsicID()) { + case Intrinsic::amdgcn_kill: { + Check(CBI.getNumIndirectDests() == 1, + "Callbr amdgcn_kill only supports one indirect dest"); + bool Unreachable = isa(CBI.getIndirectDest(0)->begin()); + CallInst *Call = dyn_cast(CBI.getIndirectDest(0)->begin()); + Check(Unreachable || (Call && Call->getIntrinsicID() == + Intrinsic::amdgcn_unreachable), + "Callbr amdgcn_kill indirect dest needs to be unreachable"); + visitIntrinsicCall(Intrinsic::amdgcn_kill, CBI); + break; + } + default: + CheckFailed( + "Callbr currently only supports asm-goto and selected intrinsics"); + } + visitIntrinsicCall(CBI.getIntrinsicID(), CBI); + } else { + const InlineAsm *IA = cast(CBI.getCalledOperand()); + Check(!IA->canThrow(), "Unwinding from Callbr is not allowed"); - verifyInlineAsmCall(CBI); + verifyInlineAsmCall(CBI); + } visitTerminator(CBI); } @@ -5211,7 +5230,7 @@ void Verifier::visitInstruction(Instruction &I) { (CBI && &CBI->getCalledOperandUse() == &I.getOperandUse(i)) || IsAttachedCallOperand(F, CBI, i)), "Cannot take the address of an intrinsic!", &I); - Check(!F->isIntrinsic() || isa(I) || + Check(!F->isIntrinsic() || isa(I) || isa(I) || F->getIntrinsicID() == Intrinsic::donothing || F->getIntrinsicID() == Intrinsic::seh_try_begin || F->getIntrinsicID() == Intrinsic::seh_try_end || diff --git a/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp b/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp index d1054b9b045ca..bdd8b5fbb3212 100644 --- a/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp +++ b/llvm/lib/Transforms/Scalar/StructurizeCFG.cpp @@ -486,11 +486,10 @@ void StructurizeCFG::analyzeLoops(RegionNode *N) { } else { // Test for successors as back edge BasicBlock *BB = N->getNodeAs(); - BranchInst *Term = cast(BB->getTerminator());

@@ -522,7 +521,7 @@ void StructurizeCFG::gatherPredicates(RegionNode *N) {

for (BasicBlock *P : predecessors(BB)) { // Ignore it if it's a branch from outside into our region entry

diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp index ce5bf0c7207c7..3090f65fac627 100644 --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -1907,11 +1907,11 @@ void llvm::InvertBranch(BranchInst *PBI, IRBuilderBase &Builder) { PBI->swapSuccessors(); }

-bool llvm::hasOnlySimpleTerminator(const Function &F) { +bool llvm::hasOnlySimpleTerminator(const Function &F, bool AllowCallBr) { for (auto &BB : F) { auto *Term = BB.getTerminator(); if (!(isa(Term) || isa(Term) ||