diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp --- a/bolt/lib/Core/BinaryContext.cpp +++ b/bolt/lib/Core/BinaryContext.cpp @@ -503,6 +503,9 @@ // Is one of the targets __builtin_unreachable? bool HasUnreachable = false; + // Does one of the entries match function start address? + bool HasStartAsEntry = false; + // Number of targets other than __builtin_unreachable. uint64_t NumRealEntries = 0; @@ -567,14 +570,21 @@ continue; } + // Function start is another special case. It is allowed in the jump table, + // but we need at least one another regular entry to distinguish the table + // from, e.g. a function pointer array. + if (Value == BF.getAddress()) { + HasStartAsEntry = true; + addEntryAddress(Value); + continue; + } + // Function or one of its fragments. const BinaryFunction *TargetBF = getBinaryFunctionContainingAddress(Value); - - bool DoesBelongToFunction = BF.containsAddress(Value) || - (TargetBF && TargetBF->isParentOrChildOf(BF)); - - // We assume that a jump table cannot have function start as an entry. - if (!DoesBelongToFunction || Value == BF.getAddress()) { + const bool DoesBelongToFunction = + BF.containsAddress(Value) || + (TargetBF && TargetBF->isParentOrChildOf(BF)); + if (!DoesBelongToFunction) { LLVM_DEBUG({ if (!BF.containsAddress(Value)) { dbgs() << "FAIL: function doesn't contain this address\n"; @@ -589,8 +599,6 @@ } } } - if (Value == BF.getAddress()) - dbgs() << "FAIL: jump table cannot have function start as an entry\n"; }); break; } @@ -611,9 +619,9 @@ } // It's a jump table if the number of real entries is more than 1, or there's - // one real entry and "unreachable" targets. If there are only multiple - // "unreachable" targets, then it's not a jump table. - return NumRealEntries + HasUnreachable >= 2; + // one real entry and one or more special targets. If there are only multiple + // special targets, then it's not a jump table. + return NumRealEntries + (HasUnreachable || HasStartAsEntry) >= 2; } void BinaryContext::populateJumpTables() { diff --git a/bolt/test/X86/jump-table-func-entry.s b/bolt/test/X86/jump-table-func-entry.s new file mode 100644 --- /dev/null +++ b/bolt/test/X86/jump-table-func-entry.s @@ -0,0 +1,72 @@ +# REQUIRES: system-linux + +## Check that BOLT correctly processes jump table that contains function start +## as one of its entries. + +# 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 --print-normalized --print-only=foo -o %t.out \ +# RUN: |& FileCheck %s + + + + .text + .globl _start + .type _start, %function +_start: + .cfi_startproc + call foo + ret + .cfi_endproc + .size _start, .-_start + + .globl foo + .type foo, %function +foo: + .cfi_startproc +.LBB00: + movq 0x8(%rdi), %rdi + movzbl 0x1(%rdi), %eax +.LBB00_br: + jmpq *"JUMP_TABLE/foo.0"(,%rax,8) +# CHECK: jmpq {{.*}} # JUMPTABLE +# CHECK-NEXT: Successors: {{.*}}, {{.*}}, {{.*}}, {{.*}}, {{.*}} + +.Ltmp87085: + xorl %eax, %eax + retq + +.Ltmp87086: + cmpb $0x0, 0x8(%rdi) + setne %al + retq + +.Ltmp87088: + movb $0x1, %al + retq + +.Ltmp87087: + movzbl 0x14(%rdi), %eax + andb $0x2, %al + shrb %al + retq + + .cfi_endproc +.size foo, .-foo + +# Jump tables +.section .rodata +"JUMP_TABLE/foo.0": + .quad .Ltmp87085 + .quad .Ltmp87086 + .quad .Ltmp87087 + .quad .LBB00 + .quad .Ltmp87088 + +# CHECK: Jump table {{.*}} for function foo +# CHECK-NEXT: 0x{{.*}} : +# CHECK-NEXT: 0x{{.*}} : +# CHECK-NEXT: 0x{{.*}} : +# CHECK-NEXT: 0x{{.*}} : +# CHECK-NEXT: 0x{{.*}} :