[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