blob: dc074c113600c25c488f3493bbcaacb8f909f35a [file] [edit]
; RUN: opt -S -mtriple=bpfel -passes=bpf-func-table %s | FileCheck %s
; RUN: llc -mtriple=bpfel -mcpu=v4 -mattr=+func-table %s -o - | FileCheck --check-prefix=ASM %s
; RUN: llc -mtriple=bpfel -mcpu=v4 %s -o - | FileCheck --check-prefix=OFF %s
; The address of a function is its index in the table.
; CHECK: @vtable = internal constant { i64, ptr, ptr } { i64 8, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 4 to ptr) }
@vtable = internal constant { i64, ptr, ptr } { i64 8, ptr @a1, ptr @b1 }
; The address of a program is not.
; CHECK: @ops = constant { ptr } { ptr @prog }, section ".struct_ops.link"
@ops = constant { ptr } { ptr @prog }, section ".struct_ops.link"
; CHECK: @llvm.used = appending global [1 x ptr] [ptr @a2]
@llvm.used = appending global [1 x ptr] [ptr @a2], section "llvm.metadata"
; Functions of one type are next to each other.
; CHECK: @__bpf_func_table = internal constant [5 x ptr] [ptr null, ptr @a1, ptr @a2, ptr @a3, ptr @b1]
define internal i32 @a1(ptr %p) {
ret i32 1
}
define internal i32 @a2(ptr %p) {
ret i32 2
}
define internal void @b1(i64 %x) {
ret void
}
define internal i32 @a3(ptr %p) {
ret i32 3
}
define internal void @not_taken(i64 %x) {
ret void
}
define i32 @prog(ptr %ctx) section "struct_ops/prog" {
; CHECK-LABEL: define i32 @prog(
; CHECK: %p = load ptr, ptr %ctx
; CHECK-NEXT: %[[I:.*]] = ptrtoint ptr %p to i64
; CHECK-NEXT: %[[REL:.*]] = sub i64 %[[I]], 1
; CHECK-NEXT: %[[MIN:.*]] = call i64 @llvm.umin.i64(i64 %[[REL]], i64 2)
; CHECK-NEXT: %[[SLOT:.*]] = getelementptr inbounds ptr, ptr getelementptr inbounds (i8, ptr @__bpf_func_table, i64 8), i64 %[[MIN]]
; CHECK-NEXT: %[[FN:.*]] = load ptr, ptr %[[SLOT]]
; CHECK-NEXT: %r = call i32 %[[FN]](ptr %ctx)
%p = load ptr, ptr %ctx
%r = call i32 %p(ptr %ctx)
%q = getelementptr i8, ptr %ctx, i64 8
%f = load ptr, ptr %q
; One function of the type.
; CHECK: call void @b1(i64 1)
call void %f(i64 1)
; CHECK: call void @not_taken(i64 2)
call void @not_taken(i64 2)
; CHECK: store ptr inttoptr (i64 2 to ptr), ptr %ctx
; CHECK: store ptr inttoptr (i64 3 to ptr), ptr %q
store ptr @a2, ptr %ctx
store ptr @a3, ptr %q
; A helper.
; CHECK: call i64 inttoptr (i64 5 to ptr)()
%h = call i64 inttoptr (i64 5 to ptr)()
; No function of the type.
; CHECK: call i64 null(i64 1, i64 2)
%n = load ptr, ptr %q
%z = call i64 %n(i64 1, i64 2)
ret i32 %r
}
; ASM-LABEL: prog:
; ASM: r1 = *(u64 *)(r6 + 0)
; ASM-NEXT: r1 += -1
; ASM-NEXT: if r1 < 2 goto
; ASM: r1 = 2
; ASM: r1 <<= 3
; ASM-NEXT: r2 = __bpf_func_table ll
; ASM-NEXT: r2 += r1
; ASM-NEXT: r2 = *(u64 *)(r2 + 8)
; ASM: callx r2
; ASM: call b1
; There are no pointers in it anymore.
; ASM: .section .rodata
; ASM: vtable:
; ASM: .section .data.rel.ro
; ASM: __bpf_func_table:
; ASM-NEXT: .quad 0
; ASM-NEXT: .quad a1
; OFF-NOT: __bpf_func_table