[llvm] [BOLT] Skip function instead of aborting on jump table analysis failure (PR #206742)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 10:51:50 PDT 2026


https://github.com/NitroAshi updated https://github.com/llvm/llvm-project/pull/206742

>From fbdf24e2c57154b29adf58b363fc5688e215e3f0 Mon Sep 17 00:00:00 2001
From: NitroAshi <nitro123 at outlook.com>
Date: Tue, 30 Jun 2026 22:33:29 +0800
Subject: [PATCH] [BOLT] Skip function instead of aborting on jump table
 analysis failure

analyzeJumpTable() runs twice on a candidate table: while disassembling the
referencing function (from analyzeMemoryAt) and again in populateJumpTables()
once everything is disassembled. The second run is stricter -- the check that an
entry points at an instruction (getInstructionAtOffset) only applies once the
target function is in the Disassembled state. So a table can be accepted the
first time and rejected the second, e.g. when an entry lands at an address with
no instruction because a symbol's size exceeds its disassembled extent.

populateJumpTables() treated that as llvm_unreachable("jump table heuristic
failure") and aborted. Handle it like other functions BOLT cannot process: warn
and ignore the owning function(s) via setIgnored(). The JumpTable is left in the
maps so it is still freed by ~BinaryContext (an earlier attempt at this,
52cd00ca, was reverted in 468d4f6d because it erased the table without
deallocating it).
---
 bolt/lib/Core/BinaryContext.cpp              | 14 +++++-
 bolt/test/X86/jump-table-failed-reanalysis.s | 51 ++++++++++++++++++++
 2 files changed, 64 insertions(+), 1 deletion(-)
 create mode 100644 bolt/test/X86/jump-table-failed-reanalysis.s

diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index eb9caca3ea16e..76fcf455f101b 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -781,6 +781,9 @@ void BinaryContext::populateJumpTables() {
         analyzeJumpTable(JT->getAddress(), JT->Type, *(JT->Parents[0]),
                          NextJTAddress, &JT->EntriesAsAddress, &JT->IsSplit);
     if (!Success) {
+      // Re-analysis here is stricter than during disassembly (the referenced
+      // function is now disassembled), so it may fail on a table we accepted
+      // earlier. Ignore the owning function(s) instead of aborting.
       LLVM_DEBUG({
         dbgs() << "failed to analyze ";
         JT->print(dbgs());
@@ -789,7 +792,16 @@ void BinaryContext::populateJumpTables() {
           NextJTI->second->print(dbgs());
         }
       });
-      llvm_unreachable("jump table heuristic failure");
+      JT->EntriesAsAddress.clear();
+      JT->IsSplit = false;
+      // Keep JT in the map so it is still freed by ~BinaryContext.
+      for (BinaryFunction *Frag : JT->Parents) {
+        this->errs()
+            << "BOLT-WARNING: unable to analyze jump table in function "
+            << *Frag << "; ignoring the function\n";
+        Frag->setIgnored();
+      }
+      continue;
     }
     for (BinaryFunction *Frag : JT->Parents) {
       if (JT->IsSplit)
diff --git a/bolt/test/X86/jump-table-failed-reanalysis.s b/bolt/test/X86/jump-table-failed-reanalysis.s
new file mode 100644
index 0000000000000..6e2ab59baa27d
--- /dev/null
+++ b/bolt/test/X86/jump-table-failed-reanalysis.s
@@ -0,0 +1,51 @@
+## A jump table entry whose target is not an instruction boundary is accepted
+## while the function is being disassembled but rejected when the table is
+## re-analyzed afterwards. Check that BOLT skips the function instead of aborting
+## with "jump table heuristic failure".
+
+# REQUIRES: system-linux
+
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -no-pie -Wl,-q
+# RUN: llvm-bolt %t.exe -o %t.out 2>&1 | FileCheck %s
+
+# CHECK-NOT: jump table heuristic failure
+# CHECK: BOLT-WARNING: unable to analyze jump table in function {{.*}}; ignoring the function
+
+	.text
+	.globl	func
+	.type	func, @function
+func:
+	.cfi_startproc
+	jmp	*JT(,%rdi,8)
+.Lc0:
+	movl	$10, %eax          # 5-byte movl (b8 0a 00 00 00); .Lc0+1 is mid-instruction
+	ret
+.Lc1:
+	movl	$11, %eax
+	ret
+.Lc2:
+	movl	$12, %eax
+	ret
+	.cfi_endproc
+.Lfunc_end:
+	.size	func, .Lfunc_end - func
+
+	.globl	main
+	.type	main, @function
+main:
+	.cfi_startproc
+	xorl	%edi, %edi
+	call	func
+	xorl	%eax, %eax
+	ret
+	.cfi_endproc
+	.size	main, .-main
+
+	.section	.rodata
+	.align	8
+JT:
+	.quad	.Lc0               # entry 0: real instruction boundary
+	.quad	.Lc0 + 1           # entry 1: MID-INSTRUCTION - no instruction at this offset
+	.quad	.Lc1
+	.quad	.Lc2



More information about the llvm-commits mailing list