LLVM中invoke指令的判断信号及转为call指令的实现方法
Great question! Let's break this down into two key parts: detecting the "signal" for normal return conditions in an LLVM IR Pass, and converting invoke instructions to call instructions with proper exception handling.
Checking for Normal Return Conditions in an LLVM IR Pass
First, let's clarify: there's no single "signal" variable that directly tells you if an invoke will take its normal branch. Instead, the distinction between normal and abnormal returns is encoded in the invoke instruction's control flow edges and the behavior of the called function. Here's how you can track this in a Pass:
Access the
invoke's destination blocks
Everyinvokeinstruction has two explicit successors:- The normal destination (where execution goes if the callee returns via
ret) - The unwind destination (where execution goes if the callee exits via
unwindorresume)
In your Pass, you can retrieve these blocks using:
if (auto *invokeInst = dyn_cast<InvokeInst>(¤tInstruction)) { BasicBlock *normalDest = invokeInst->getNormalDest(); BasicBlock *unwindDest = invokeInst->getUnwindDest(); }- The normal destination (where execution goes if the callee returns via
Check the callee's attributes and body
To predict whether theinvokewill take the normal branch, analyze the called function:- If the callee has the
nounwindattribute, it guarantees no exceptions will be thrown—so the unwind branch is never taken. You can check this withcallee->doesNotThrow(). - For non-declaration functions (functions with a body), scan for return and exit instructions:
- Presence of
ReturnInstmeans the function can return normally. - Presence of
UnwindInstorResumeInstmeans the function can exit abnormally.
Example code for this analysis:
- Presence of
Function *callee = invokeInst->getCalledFunction(); if (callee && !callee->isDeclaration()) { bool canReturnNormally = false; bool canThrow = false; for (BasicBlock &bb : *callee) { for (Instruction &inst : bb) { if (isa<ReturnInst>(&inst)) canReturnNormally = true; else if (isa<UnwindInst>(&inst) || isa<ResumeInst>(&inst)) canThrow = true; } } // Use these flags to reason about the invoke's behavior }- If the callee has the
Converting invoke to call with Exception Handling
Converting an invoke to a call requires preserving the original exception handling logic, since call instructions don't have explicit unwind destinations—exceptions from a call propagate up the call stack unless caught by an enclosing EH pad. Here's how to do it properly:
Replace the
invokewith acall
UseIRBuilderto create aCallInstthat mirrors theinvoke's arguments, calling convention, and metadata:IRBuilder<> builder(invokeInst); CallInst *callInst = builder.CreateCall( invokeInst->getCalledFunction(), invokeInst->getArgOperands(), "" // Optional name ); // Copy over all metadata and attributes callInst->copyMetadata(*invokeInst); callInst->setCallingConv(invokeInst->getCallingConv());Preserve the normal control flow
After creating thecall, replace theinvoke's normal branch with a directbrinstruction to the original normal destination:// Remove the original invoke invokeInst->eraseFromParent(); // Jump to the normal destination builder.CreateBr(normalDest);Ensure exception handling is preserved
The original unwind destination (from theinvoke) must still be a valid EH pad (a block starting withlandingpadorcatchswitch). If the callee can throw exceptions, thecallwill automatically unwind to this pad if an exception occurs—no extra code is needed here, as long as the EH pad's structure remains intact.If the callee has the
nounwindattribute, you can safely discard the unwind destination entirely, as exceptions will never be thrown.
内容的提问来源于stack exchange,提问作者Luba A

