[llvm] [llubi] Add basic support for icmp, terminators and function calls (PR #181393)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Sat Feb 14 01:15:51 PST 2026
================
@@ -219,6 +254,188 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
Status &= Handler.onInstructionExecuted(RI, None);
}
+ void visitBranchInst(BranchInst &BI) {
+ if (BI.isConditional()) {
+ switch (getValue(BI.getCondition()).asBoolean()) {
+ case BooleanKind::True:
+ jumpTo(BI, BI.getSuccessor(0));
+ return;
+ case BooleanKind::False:
+ jumpTo(BI, BI.getSuccessor(1));
+ return;
+ case BooleanKind::Poison:
+ reportImmediateUB("Branch on poison condition.");
+ return;
+ }
+ }
+ jumpTo(BI, BI.getSuccessor(0));
+ }
+
+ void visitSwitchInst(SwitchInst &SI) {
+ auto &Cond = getValue(SI.getCondition());
+ if (Cond.isPoison()) {
+ reportImmediateUB("Switch on poison condition.");
+ return;
+ }
+ for (auto &Case : SI.cases()) {
+ if (Case.getCaseValue()->getValue() == Cond.asInteger()) {
+ jumpTo(SI, Case.getCaseSuccessor());
+ return;
+ }
+ }
+ jumpTo(SI, SI.getDefaultDest());
+ }
+
+ void visitUnreachableInst(UnreachableInst &) {
+ reportImmediateUB("Unreachable code.");
+ }
+
+ void visitCallBrInst(CallBrInst &CI) {
+ if (isNoopInlineAsm(CI.getCalledOperand(), CI.getType())) {
+ jumpTo(CI, CI.getDefaultDest());
+ return;
+ }
+
+ Handler.onUnrecognizedInstruction(CI);
+ Status = false;
+ }
+
+ void visitIndirectBrInst(IndirectBrInst &IBI) {
+ auto &Target = getValue(IBI.getAddress());
+ if (Target.isPoison()) {
+ reportImmediateUB("Indirect branch on poison.");
+ return;
+ }
+ if (BasicBlock *DestBB = Ctx.getTargetBlock(Target.asPointer())) {
+ if (any_of(IBI.successors(),
+ [DestBB](BasicBlock *Succ) { return Succ == DestBB; }))
+ jumpTo(IBI, DestBB);
+ else
+ reportImmediateUB("Indirect branch on unlisted target BB.");
+
+ return;
+ }
+ reportImmediateUB("Indirect branch on invalid target BB.");
+ }
+
+ void returnFromCallee() {
+ // TODO: handle retval attributes (Attributes from known callee should be
+ // applied if available).
+ // TODO: handle metadata
+ auto &CB = cast<CallBase>(*CurrentFrame->PC);
+ CurrentFrame->CalleeArgs.clear();
+ AnyValue &RetVal = CurrentFrame->CalleeRetVal;
+ setResult(CB, std::move(RetVal));
+
+ if (auto *II = dyn_cast<InvokeInst>(&CB))
+ jumpTo(*II, II->getNormalDest());
+ else if (CurrentFrame->State == FrameState::Pending)
+ ++CurrentFrame->PC;
+ }
+
+ AnyValue callIntrinsic(CallBase &CB) {
+ Intrinsic::ID IID = CB.getIntrinsicID();
+ switch (IID) {
+ case Intrinsic::assume:
+ switch (getValue(CB.getArgOperand(0)).asBoolean()) {
+ case BooleanKind::True:
+ break;
+ case BooleanKind::False:
+ case BooleanKind::Poison:
+ reportImmediateUB("Assume on false or poison condition.");
+ break;
+ }
+ // TODO: handle llvm.assume with operand bundles
+ return AnyValue();
+ default:
+ Handler.onUnrecognizedInstruction(CB);
+ Status = false;
+ return AnyValue();
+ }
+ }
+
+ AnyValue callLibFunc(CallBase &CB, Function *ResolvedCallee) {
+ LibFunc LF;
+ // Respect nobuiltin attributes on call site.
+ if (CB.isNoBuiltin() ||
+ !CurrentFrame->TLI.getLibFunc(*ResolvedCallee, LF)) {
+ Handler.onUnrecognizedInstruction(CB);
+ Status = false;
+ return AnyValue();
+ }
+
+ Handler.onUnrecognizedInstruction(CB);
+ Status = false;
+ return AnyValue();
+ }
+
+ void enterCall(CallBase &CB) {
+ Function *Callee = CB.getCalledFunction();
+ // TODO: handle parameter attributes (Attributes from known callee should be
+ // applied if available).
+ // TODO: handle byval/initializes
+ auto &CalleeArgs = CurrentFrame->CalleeArgs;
+ CalleeArgs.clear();
+ for (Value *Arg : CB.args())
+ CalleeArgs.push_back(getValue(Arg));
+
+ if (!Callee) {
+ Value *CalledOperand = CB.getCalledOperand();
+ if (isNoopInlineAsm(CalledOperand, CB.getType())) {
+ CurrentFrame->ResolvedCallee = nullptr;
+ returnFromCallee();
+ return;
+ } else if (isa<InlineAsm>(CalledOperand)) {
+ Handler.onUnrecognizedInstruction(CB);
+ Status = false;
+ return;
+ }
+
+ auto &CalleeVal = getValue(CalledOperand);
+ if (CalleeVal.isPoison()) {
+ reportImmediateUB("Indirect call through poison function pointer.");
+ return;
+ }
+ Callee = Ctx.getTargetFunction(CalleeVal.asPointer());
+ if (!Callee) {
+ reportImmediateUB("Indirect call through invalid function pointer.");
+ return;
+ }
+ }
+
+ assert(Callee && "Expected a resolved callee function.");
+ CurrentFrame->ResolvedCallee = Callee;
+ if (Callee->isIntrinsic()) {
+ CurrentFrame->CalleeRetVal = callIntrinsic(CB);
+ returnFromCallee();
+ return;
+ } else if (Callee->isDeclaration()) {
+ CurrentFrame->CalleeRetVal = callLibFunc(CB, Callee);
+ returnFromCallee();
+ return;
+ } else {
+ uint32_t MaxStackDepth = Ctx.getMaxStackDepth();
+ if (MaxStackDepth && CallStack.size() >= MaxStackDepth) {
+ reportImmediateUB("Maximum stack depth exceeded.");
----------------
nikic wrote:
This is not really immediate UB, maybe we should distinguish these limit error conditions?
https://github.com/llvm/llvm-project/pull/181393
More information about the llvm-commits
mailing list