[llvm] [BOLT] Skip function instead of aborting on jump table analysis failure (PR #206742)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 30 07:50:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-bolt
Author: nitro (NitroAshi)
<details>
<summary>Changes</summary>
`analyzeJumpTable()` runs twice on a candidate jump table: once from
`analyzeMemoryAt` while the referencing function is being disassembled, and
again from `populateJumpTables()` after disassembly. The second run is stricter,
because the check that an entry points at an instruction
(`getInstructionAtOffset`) only fires once the target function reaches the
`Disassembled` state. A table can therefore be accepted the first time and
rejected the second -- for instance when an entry lands at an address with no
instruction (a symbol whose declared size is larger than its disassembled
extent). `populateJumpTables()` reached `llvm_unreachable("jump table heuristic
failure")` and aborted in that case.
Handle it the way BOLT already handles indirect branches it can't analyze: drop
the table and skip the owning function(s) via `addFragmentsToSkip()`. The
`JumpTable` is deliberately 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).
New test `jump-table-failed-reanalysis.s` builds such a table; before this change
BOLT aborts, after it the function is skipped and the rest of the binary is
processed normally.
---
Full diff: https://github.com/llvm/llvm-project/pull/206742.diff
2 Files Affected:
- (modified) bolt/lib/Core/BinaryContext.cpp (+9-1)
- (added) bolt/test/X86/jump-table-failed-reanalysis.s (+51)
``````````diff
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index eb9caca3ea16e..88cca51b31107 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. Skip the function(s) instead of aborting.
LLVM_DEBUG({
dbgs() << "failed to analyze ";
JT->print(dbgs());
@@ -789,7 +792,12 @@ 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)
+ addFragmentsToSkip(Frag);
+ 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..8996cb9cd99ff
--- /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: skipped {{.*}} function{{.*}} due to cold fragments
+
+ .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
``````````
</details>
https://github.com/llvm/llvm-project/pull/206742
More information about the llvm-commits
mailing list