[llvm] [BPF] Support jump-table style callx (PR #159798)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 19 08:43:23 PDT 2025
https://github.com/yonghong-song created https://github.com/llvm/llvm-project/pull/159798
The following is an example:
```
typedef int (*op_t)(int, int);
__attribute__((section("_add"))) static int add(int a, int b) { return a + b; }
__attribute__((section("_mul"))) static int mul(int a, int b) { return a * b; }
__attribute__((noinline)) static int apply(op_t *ops, int index, int a, int b) {
// indirect call via function pointer
return ops[index](a, b);
}
#ifdef STATIC
static op_t ops[] = { add, mul, add, add, mul, mul };
#endif
#ifdef GLOBAL
op_t ops[] = { add, mul, add, add, mul, mul };
#endif
int result(int i, int j) {
#ifdef PRIVATE
op_t ops[] = { add, mul, add, add, mul, mul };
#endif
int x = 2, y = 3;
int r1 = apply(ops, 0, x, y);
int r2 = apply(ops, 4, x, y);
return r1 + r2;
}
```
Compilation for three different modes:
```
clang --target=bpf -DPRIVATE -O2 -S t.c -o t.s.private
clang --target=bpf -DSTATIC -O2 -S t.c -o t.s.static
clang --target=bpf -DGLOBAL -O2 -S t.c -o t.s.global
```
The assembly for the above three different modes. For example, for PRIVATE mode:
```
.text
.globl result # -- Begin function result
.p2align 3
.type result, at function
result: # @result
# %bb.0:
r1 = BPF.__const.result.ops ll
w2 = 0
call apply
w6 = w0
r1 = BPF.__const.result.ops ll
w2 = 4
call apply
w0 += w6
exit
.Lfunc_end0:
.size result, .Lfunc_end0-result
...
.text
.p2align 3 # -- Begin function apply
.type apply, at function
apply: # @apply
# %bb.0:
r2 = w2
r2 <<= 3
r1 += r2
r3 = *(u64 *)(r1 + 0)
w1 = 2
w2 = 3
callx r3
exit
.Lfunc_end3:
.size apply, .Lfunc_end3-apply
...
.type BPF.__const.result.ops, at object # @BPF.__const.result.ops
.section .calltables,"a", at progbits
.p2align 3, 0x0
BPF.__const.result.ops:
.quad add
.quad mul
.quad add
.quad add
.quad mul
.quad mul
.size BPF.__const.result.ops, 48
```
STATIC and GLOBAL modes are similar except the callx table. For GLOBAL:
```
.type BPF.ops, at object # @BPF.ops
.section .calltables,"aw", at progbits
.globl BPF.ops
.p2align 3, 0x0
BPF.ops:
.quad add
.quad mul
.quad add
.quad add
.quad mul
.quad mul
.size BPF.ops, 48
```
For STATIC:
```
.type BPF.ops, at object # @BPF.ops
.section .calltables,"a", at progbits
.p2align 3, 0x0
BPF.ops:
.quad add
.quad mul
.quad add
.quad add
.quad mul
.quad mul
.size BPF.ops, 48
```
Will add selftests after the implementation is validated in kernel.
>From 4609b1d2675a3cc445029ef8aba33078ef428122 Mon Sep 17 00:00:00 2001
From: Yonghong Song <yonghong.song at linux.dev>
Date: Thu, 4 Sep 2025 10:02:56 -0700
Subject: [PATCH] [BPF] Support jump-table style callx
The following is an example:
```
typedef int (*op_t)(int, int);
__attribute__((section("_add"))) static int add(int a, int b) { return a + b; }
__attribute__((section("_mul"))) static int mul(int a, int b) { return a * b; }
__attribute__((noinline)) static int apply(op_t *ops, int index, int a, int b) {
// indirect call via function pointer
return ops[index](a, b);
}
#ifdef STATIC
static op_t ops[] = { add, mul, add, add, mul, mul };
#endif
#ifdef GLOBAL
op_t ops[] = { add, mul, add, add, mul, mul };
#endif
int result(int i, int j) {
#ifdef PRIVATE
op_t ops[] = { add, mul, add, add, mul, mul };
#endif
int x = 2, y = 3;
int r1 = apply(ops, 0, x, y);
int r2 = apply(ops, 4, x, y);
return r1 + r2;
}
```
Compilation for three different modes:
```
clang --target=bpf -DPRIVATE -O2 -S t.c -o t.s.private
clang --target=bpf -DSTATIC -O2 -S t.c -o t.s.static
clang --target=bpf -DGLOBAL -O2 -S t.c -o t.s.global
```
The assembly for the above three different modes. For example,
for PRIVATE mode:
```
.text
.globl result # -- Begin function result
.p2align 3
.type result, at function
result: # @result
# %bb.0:
r1 = BPF.__const.result.ops ll
w2 = 0
call apply
w6 = w0
r1 = BPF.__const.result.ops ll
w2 = 4
call apply
w0 += w6
exit
.Lfunc_end0:
.size result, .Lfunc_end0-result
...
.text
.p2align 3 # -- Begin function apply
.type apply, at function
apply: # @apply
# %bb.0:
r2 = w2
r2 <<= 3
r1 += r2
r3 = *(u64 *)(r1 + 0)
w1 = 2
w2 = 3
callx r3
exit
.Lfunc_end3:
.size apply, .Lfunc_end3-apply
...
.type BPF.__const.result.ops, at object # @BPF.__const.result.ops
.section .calltables,"a", at progbits
.p2align 3, 0x0
BPF.__const.result.ops:
.quad add
.quad mul
.quad add
.quad add
.quad mul
.quad mul
.size BPF.__const.result.ops, 48
```
STATIC and GLOBAL modes are similar except the callx table.
For GLOBAL:
```
.type BPF.ops, at object # @BPF.ops
.section .calltables,"aw", at progbits
.globl BPF.ops
.p2align 3, 0x0
BPF.ops:
.quad add
.quad mul
.quad add
.quad add
.quad mul
.quad mul
.size BPF.ops, 48
```
For STATIC:
```
.type BPF.ops, at object # @BPF.ops
.section .calltables,"a", at progbits
.p2align 3, 0x0
BPF.ops:
.quad add
.quad mul
.quad add
.quad add
.quad mul
.quad mul
.size BPF.ops, 48
```
Will add selftests after the implementation is validated in kernel.
---
llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp | 36 +++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
index b202b20291aff..9c54f4068d8ac 100644
--- a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
+++ b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
@@ -55,6 +55,7 @@ class BPFCheckAndAdjustIR final : public ModulePass {
bool sinkMinMax(Module &M);
bool removeGEPBuiltins(Module &M);
bool insertASpaceCasts(Module &M);
+ bool renameCallTableGlobal(Module &M);
};
} // End anonymous namespace
@@ -527,12 +528,47 @@ bool BPFCheckAndAdjustIR::insertASpaceCasts(Module &M) {
return Changed;
}
+bool BPFCheckAndAdjustIR::renameCallTableGlobal(Module &M) {
+ bool Changed = false;
+ for (GlobalVariable &Global : M.globals()) {
+ if (Global.getLinkage() != GlobalValue::PrivateLinkage &&
+ Global.getLinkage() != GlobalValue::InternalLinkage &&
+ Global.getLinkage() != GlobalValue::ExternalLinkage)
+ continue;
+ if (Global.getLinkage() == GlobalValue::PrivateLinkage &&
+ !Global.isConstant())
+ continue;
+ if (!Global.hasInitializer())
+ continue;
+
+ Constant *CV = dyn_cast<Constant>(Global.getInitializer());
+ if (!CV)
+ continue;
+ ConstantArray *CA = dyn_cast<ConstantArray>(CV);
+ if (!CA)
+ continue;
+
+ for (unsigned i = 1, e = CA->getNumOperands(); i != e; ++i) {
+ if (!dyn_cast<Function>(CA->getOperand(i)))
+ continue;
+ }
+ Global.setName("BPF." + Global.getName());
+ if (Global.getLinkage() == GlobalValue::PrivateLinkage)
+ Global.setLinkage(GlobalValue::LinkageTypes::InternalLinkage);
+ Global.setSection(".calltables");
+ Changed = true;
+ }
+
+ return Changed;
+}
+
bool BPFCheckAndAdjustIR::adjustIR(Module &M) {
bool Changed = removePassThroughBuiltin(M);
Changed = removeCompareBuiltin(M) || Changed;
Changed = sinkMinMax(M) || Changed;
Changed = removeGEPBuiltins(M) || Changed;
Changed = insertASpaceCasts(M) || Changed;
+ Changed = renameCallTableGlobal(M) || Changed;
return Changed;
}
More information about the llvm-commits
mailing list