1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
# This reproduces a bug with instrumentation when trying to instrument
# functions that share a jump table with multiple indirect jumps. Usually,
# each indirect jump that uses a JT will have its own copy of it. When
# this does not happen, we need to duplicate the jump table safely, so
# we can split the edges correctly (each copy of the jump table may have
# different split edges). For this to happen, we need to correctly match
# the sequence of instructions that perform the indirect jump to identify
# the base address of the jump table and patch it to point to the new
# cloned JT.
#
# Here we test this variant:
# movq jt.2397(,%rax,8), %rax
# jmp *%rax
#
# Which is suboptimal since the compiler could've avoided using an intermediary
# register, but GCC does generate this code and it triggered a bug in our
# matcher. Usual jumps in non-PIC code have this format:
#
# jmp *jt.2397(,%rax,8)
#
# This is the C code fed to GCC:
# #include <stdio.h>
#int interp(char* code) {
# static void* jt[] = { &&op_end, &&op_inc, &&do_dec };
# int pc = 0;
# int res = 0;
# goto *jt[code[pc++] - '0'];
#
#op_inc:
# res += 1;
# printf("%d\n", res);
# goto *jt[code[pc++] - '0'];
#do_dec:
# res -= 1;
# printf("%d\n", res);
# goto *jt[code[pc++] - '0'];
#op_end:
# return res;
#}
#int main(int argc, char** argv) {
# return interp(argv[1]);
#}
# REQUIRES: system-linux,bolt-runtime
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \
# RUN: %s -o %t.o
# RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe --instrument --instrumentation-file=%t.fdata \
# RUN: -o %t.instrumented
# Instrumented program needs to finish returning zero
# RUN: %t.instrumented 120
# Test that the instrumented data makes sense
# RUN: llvm-bolt %t.exe -o %t.bolted --data %t.fdata \
# RUN: --reorder-blocks=ext-tsp --reorder-functions=hfsort+ \
# RUN: --print-only=interp --print-finalized | FileCheck %s
# RUN: %t.bolted 120
# Check that our two indirect jumps are recorded in the fdata file and that
# each has its own independent profile
# CHECK: Successors: .Ltmp1 (mispreds: 0, count: 1), .Ltmp0 (mispreds: 0, count: 0), .Ltmp2 (mispreds: 0, count: 0)
# CHECK: Successors: .Ltmp0 (mispreds: 0, count: 1), .Ltmp2 (mispreds: 0, count: 1), .Ltmp1 (mispreds: 0, count: 0)
.file "test.c"
.text
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.text
.p2align 4,,15
.globl interp
.type interp, @function
interp:
.LFB11:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
xorl %ebp, %ebp
pushq %rbx
.cfi_def_cfa_offset 24
.cfi_offset 3, -24
leaq 1(%rdi), %rbx
subq $8, %rsp
.cfi_def_cfa_offset 32
movsbl (%rdi), %eax
subl $48, %eax
cltq
movq jt.2397(,%rax,8), %rax
jmp *%rax
.p2align 4,,10
.p2align 3
.L3:
addl $1, %ebp
.L8:
movl %ebp, %esi
movl $.LC0, %edi
xorl %eax, %eax
addq $1, %rbx
call printf
movsbl -1(%rbx), %eax
subl $48, %eax
cltq
movq jt.2397(,%rax,8), %rax
jmp *%rax
.p2align 4,,10
.p2align 3
.L6:
addq $8, %rsp
.cfi_remember_state
.cfi_def_cfa_offset 24
movl %ebp, %eax
popq %rbx
.cfi_def_cfa_offset 16
popq %rbp
.cfi_def_cfa_offset 8
ret
.p2align 4,,10
.p2align 3
.L4:
.cfi_restore_state
subl $1, %ebp
jmp .L8
.cfi_endproc
.LFE11:
.size interp, .-interp
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB12:
.cfi_startproc
movq 8(%rsi), %rdi
jmp interp
.cfi_endproc
.LFE12:
.size main, .-main
.section .rodata
.align 16
.type jt.2397, @object
.size jt.2397, 24
jt.2397:
.quad .L6
.quad .L3
.quad .L4
.ident "GCC: (GNU) 8"
.section .note.GNU-stack,"",@progbits
|